자동화 slecs

결제대행사 웹훅 멱등 처리와 명세 불일치 극복기

목차

결제대행사 API 연동, 명세서부터 다시 읽음

결제대행사 연동 작업 들어가면서 받아둔 명세서 PDF를 처음부터 다시 정독함. 이전에 한 번 훑었을 때는 "어차피 표준 PG 흐름이지" 싶어서 대충 봤는데, 막상 코드로 옮기려니까 필드 단위에서 막히는 부분이 한둘이 아니었음.

특히 헷갈렸던 포인트:

  • 승인 응답과 webhook 통보 메시지의 필드 이름이 미묘하게 다름 (tradeNo vs tradeNumber 같은 류)
  • 금액 필드가 어떤 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

첫 댓글 달아줘.