들어가며
스타트업 의뢰를 받을 때 “결제 모듈만 붙여 주세요”라는 요청이 종종 들어온다. 결제 SDK 연동 자체는 며칠이면 끝나지만, 실제로 운영을 시작한 뒤 한 달 안에 따라오는 일들은 그보다 훨씬 무겁다. 이 글은 토스페이먼츠·KCP·이니시스·Stripe 어느 PG를 쓰든 공통으로 챙겨야 하는 7가지를 정리한다.
1. 멱등성(Idempotency) — 같은 결제 두 번 들어오는 사고
가장 흔한 운영 사고다. 사용자가 결제 버튼을 더블클릭했거나, 네트워크가 끊겨 클라이언트가 자동 재시도했을 때 같은 결제가 두 번 잡힌다. 해결은 단 하나다.
- 주문 단계에서 idempotency key 발급
- 같은 키로 들어오는 결제 요청은 서버에서 한 건만 처리
- 웹훅 수신도 같은 키로 중복 검사
2. 웹훅 검증 — ‘돈 들어왔다’ 메시지의 진위
PG의 결제 완료 통보(웹훅)를 그대로 믿고 주문 상태를 ‘PAID’로 바꾸면 안 된다. 위조된 요청을 받을 수 있다.
운영에서 가장 골치 아픈 사고가 “결제 안 됐는데 PAID로 바뀜” 또는 그 반대다. 이걸 막는 게 웹훅 서명 검증이다.
모든 PG는 웹훅에 서명(HMAC, x-signature 등)을 붙인다. 받은 즉시 검증하고, 실패하면 즉시 반려해야 한다.
3. 결제 상태와 주문 상태의 분리
초기 구현에서 가장 흔한 안티패턴이 주문 상태 = 결제 상태로 묶어 두는 것이다. 시간이 지나면 부분 환불, 분할 결제, 정기결제처럼 1:N 관계가 생기는데, 그때 마이그레이션이 지옥이 된다.
- 주문(Order)과 결제(Payment)는 별도 테이블
- 한 주문에 여러 결제·환불 레코드가 붙을 수 있게 설계
- 상태 변경은 항상 이력 테이블에 남기기
4. 환불 정책 — 코드보다 정책이 먼저
환불 코드를 짜기 전에 정책이 정해져야 한다. 우리가 의뢰받을 때 가장 자주 묻는 질문이다.
- 부분 환불 허용 여부
- 환불 가능 기간
- 환불 시 부가세·할인쿠폰의 처리
- 이미 발행된 세금계산서·현금영수증 처리
이걸 정하지 않고 코드부터 짜면, 나중에 회계팀이 들어왔을 때 100% 갈아엎는다.
5. 정기결제 — ‘실패 처리’가 진짜 일
정기결제(빌링)는 첫 결제 성공보다 두 번째 결제 실패를 어떻게 처리할지가 진짜 일이다.
- 카드 한도 초과·정지 — 며칠 후 재시도?
- 카드 만료 — 사용자에게 어떻게 알릴 것인가?
- 세 번 실패 시 자동 해지 vs 휴면 전환
- 재시도 중에 사용자가 카드 변경했을 때 즉시 결제 시도
이 시나리오들을 명세로 정리하지 않으면, 운영에서 매주 CS 티켓이 쏟아진다.
6. 정산 — 매출과 입금의 시차
PG는 결제일 + N영업일에 정산금을 입금한다. 이 시차 때문에 “결제 = 매출”이 아니다.
- 발생 매출 (결제일 기준)
- 인식 매출 (서비스 제공일 기준, 회계 기준)
- 현금 입금 (PG 정산일 기준)
세 가지가 모두 다르고, 모두 추적되어야 한다. 회계 자동화로 이어지려면 이 구분이 처음부터 DB에 들어가 있어야 한다.
7. 세금계산서·현금영수증 — 발행 책임의 위치
B2B 거래라면 세금계산서, B2C 결제라면 현금영수증이 따라온다. 발행 의무가 PG에 있는지 가맹점에 있는지 PG·결제수단별로 다르다.
- 토스페이먼츠 — 가맹점이 직접 발행 책임
- 일부 PG — 자체 발행 옵션 제공
- 해외 PG(Stripe) — 국내 세금계산서 무관, 별도 솔루션 필요
국내 가맹점이 Stripe를 쓰는 SaaS의 경우, 별도의 세금계산서 발행 시스템을 함께 구축해야 한다.
맺으며
결제 모듈을 “PG SDK 연결”이라고만 생각하면 출시 후 6개월이 비명으로 채워진다. 위 7가지를 의뢰 단계의 명세서에 명시하기만 해도, 그 비명은 절반으로 줄어든다.
결제·정산 시스템 설계 자문이 필요하시면 코드벤터에 문의해 주세요.



