웹훅은 "입금됐다" 를 알려 주는 HTTP 요청이에요. 받는 쪽 구현이 허술하면 주문이 두 번 확정되거나, 남이 보낸 가짜 요청에 속아요. 세 가지만 지키면 돼요.
1. 서명을 반드시 검증하세요
웹훅 주소는 결국 공개된 URL 이에요. 주소만 알면 누구나 요청을 보낼 수 있어요. 그래서 요청에 서명이 붙어 오고, 받는 쪽에서 그걸 확인해야 해요.
- 헤더의 타임스탬프와 본문을 이어 붙여 HMAC 을 계산해요
- 계산한 값과 헤더의 값을 비교해요 — 반드시 상수 시간 비교 함수를 쓰세요
- 타임스탬프가 너무 오래됐으면 거절해요 (재전송 공격 방지, 보통 5분)
문자열을 == 로 비교하면 걸리는 시간 차이로 서명을 추측당할 수 있어요. 언어마다 있는 안전 비교 함수(hash_equals, crypto.timingSafeEqual)를 쓰세요.
2. 같은 요청이 두 번 와도 한 번만 처리하세요
네트워크가 끊기면 보낸 쪽은 "실패했다" 고 보고 다시 보내요. 그런데 받는 쪽은 이미 처리했을 수 있어요. 그래서 **중복이 정상**이라고 가정하고 만들어야 해요.
- 요청마다 고유한 전달 ID 가 헤더로 와요
- 그 ID 를 저장해 두고, 이미 있으면 처리하지 않고 200 만 돌려줘요
- ID 저장과 실제 처리는 같은 트랜잭션에 넣으세요 — 따로 하면 중간에 끊겼을 때 어긋나요
3. 빨리 200 을 돌려주세요
무거운 일을 웹훅 처리 안에서 다 하면 응답이 늦어지고, 보낸 쪽은 타임아웃으로 보고 다시 보내요. 그게 중복을 만들어요.
- 받자마자 저장하고 200 을 돌려주세요
- 메일 발송·재고 차감 같은 건 뒤에서 처리하세요
- 처리 실패를 알리고 싶으면 5xx 를 주세요 — 그러면 다시 보내 줘요
응답 코드를 어떻게 줄까
- 200 — 잘 받았어요. 다시 보내지 않아요
- 4xx — 요청이 잘못됐어요. 다시 보내도 같으니 포기해요
- 5xx — 지금은 못 받아요. 간격을 늘려 다시 보내 줘요
처리에 실패했는데 200 을 주면 그 입금 알림은 영영 다시 오지 않아요. 실패했으면 5xx 를 주세요.
테스트는 어떻게 하나요
실제로 돈을 보내지 않고 웹훅을 받아 볼 수 있어요. 콘솔의 테스트 기능으로 모의 입금을 만들면 실제와 같은 모양의 요청이 가요. 서명 검증과 멱등 처리를 여기서 먼저 확인하세요.
