결제대행사 웹훅 멱등 처리와 명세 불일치 극복기
목차
결제대행사 API 연동, 명세서부터 다시 읽음
결제대행사 연동 작업 들어가면서 받아둔 명세서 PDF를 처음부터 다시 정독함. 이전에 한 번 훑었을 때는 "어차피 표준 PG 흐름이지" 싶어서 대충 봤는데, 막상 코드로 옮기려니까 필드 단위에서 막히는 부분이 한둘이 아니었음.
특히 헷갈렸던 포인트:
- 승인 응답과 webhook 통보 메시지의 필드 이름이 미묘하게 다름 (
tradeNovstradeNumber같은 류) - 금액 필드가 어떤 API는 string, 어떤 API는 number
- 거래 식별자가 우리 쪽 주문번호인지 결제대행사 거래번호인지 케이스마다 다름
명세서를 그냥 한 번 읽는 게 아니라 API별로 요청/응답 필드를 직접 표로 옮겨 적으니까 그제야 흐름이 보였음.
컨트롤러 두 개로 나눈 이유
결제 요청 보내는 쪽이랑 webhook 받는 쪽을 한 컨트롤러에 욱여넣을까 잠깐 고민함. 근데 책임이 너무 다름.
| 구분 | 결제 요청 | Webhook 수신 |
|---|---|---|
| 호출 주체 | 우리 → 결제대행사 | 결제대행사 → 우리 |
| 인증 방식 | API 키 헤더 | 서명 검증 |
| 실패 시 | 사용자에게 즉시 노출 | 재시도 큐로 |
| 멱등성 | 클라이언트가 보장 | 서버가 보장해야 함 |
이렇게 정리하니까 분리가 자연스러웠음. webhook은 결제대행사가 같은 이벤트를 여러 번 쏠 수 있어서 멱등이 핵심이고, 요청 API는 사용자 응답시간이 핵심이라 관심사가 완전히 다름.
Webhook 멱등 처리에서 한 번 깨짐
처음에는 단순히 "거래번호 + 상태"로 unique 잡으면 되겠지 했는데, 부분 취소가 들어가면서 한 거래에 같은 상태가 여러 번 올 수 있음을 발견함. 부분취소 1차, 부분취소 2차가 둘 다 "취소 통보"로 떨어짐.
결국 결제대행사가 매 통보마다 새로 발급해주는 통보 ID를 키로 잡고, 그 ID 기준으로 처리 여부를 기록하는 식으로 바꿈.
1. webhook 수신
2. 통보 ID 조회 → 이미 있으면 200 OK 반환하고 종료
3. 트랜잭션 시작 → 거래 상태 갱신 + 통보 이력 적재
4. 커밋 후 200 OK
이렇게 하니까 결제대행사가 같은 통보를 10번 쏴도 1번만 반영되고, 나머지는 단순 200 응답만 떨어짐. 재시도 정책 때문에 실제로 중복은 자주 옴.
명세서 vs 실제 동작
명세서에는 분명 "성공 시 result_code = 0000" 이라고 적혀 있는데, 테스트 환경에서 어떤 케이스는 "00", 어떤 케이스는 "0" 으로 옴. 코드는 문자열 길이 체크 대신 숫자 변환 후 0 비교로 처리하는 게 안전했음.
명세서를 100% 믿지 말 것, 또 배움.
다음
댓글 0
첫 댓글 달아줘.