점검 가이드 · 결제
결제 중복 청구 막기 — 새로고침·두 번 클릭·재시도 대비
2026-10-24 기준
- 결제는 "단추 클릭 → 결제창 → 완료 화면 → 서버 승인 → 주문 처리"를 거치는데, 이 중 어느 단계든 두 번 불릴 수 있습니다.
- 해결의 핵심은 주문번호 하나에 처리를 딱 한 번만 하게 만드는 것(멱등성)입니다.
- 결과를 모르는 오류(시간 초과 등)는 "실패"로 단정하지 말고, 주문번호로 결제사에 다시 물어 확인합니다.
중복 청구가 생기는 흔한 장면
카드사에서 실제로 두 번 승인되지 않더라도, 우리 DB 에 이용권이 두 번 생기거나 포인트가 두 번 쌓이는 것도 같은 종류의 문제입니다.
- 손님이 결제 단추를 빠르게 두 번 누름 — 주문이 두 개 만들어지고 결제도 두 번 진행됨
- 결제 완료 화면에서 새로고침하거나 뒤로 갔다 다시 옴 — 완료 화면이 다시 불리며 승인·이용권 지급이 한 번 더 일어남
- 휴대폰 통신이 끊겨 앱이 같은 요청을 다시 보냄
- 결제사가 보내는 알림(웹훅)이 다시 옴 — 토스페이먼츠는 내 서버가 정상 응답(200)을 못 주면 웹훅을 최대 7회까지 다시 보낸다고 안내합니다
- 매일 도는 자동 결제(정기결제 예약 작업)가 동시에 두 번 실행됨
멱등성 — "같은 요청은 몇 번 와도 한 번만"
멱등성은 같은 요청을 여러 번 받아도 결과가 한 번 받은 것과 같게 만드는 성질입니다. 결제에서는 주문번호를 기준으로 삼습니다.
토스페이먼츠는 같은 결제를 다시 승인하려 하면 「이미 처리된 결제」(ALREADY_PROCESSED_PAYMENT) 오류를 돌려줍니다. 또 POST 요청에 Idempotency-Key 헤더를 붙이면, 같은 키로 다시 보낸 요청은 다시 실행하지 않고 첫 응답을 그대로 돌려줍니다(키는 첫 요청부터 15일 유효). 하지만 결제사가 막아 줘도 내 DB 의 "이용권 지급"은 내 코드가 막아야 합니다.
막는 방법
- 결제 단추를 누르면 바로 단추를 잠그고 "처리 중"으로 바꿉니다(화면 쪽 1차 방어).
- 결제창을 열기 전에 서버가 주문번호를 먼저 만들어 DB 에 「결제 대기」로 저장합니다. 같은 손님이 같은 상품을 다시 누르면 새 주문 대신 기존 대기 주문을 씁니다.
- 완료 화면이나 웹훅이 오면, 「결제 대기 → 처리 중」으로 바꾸는 일을 DB 에서 조건부로 한 번에 합니다(예: 상태가 대기일 때만 바꾸기). 바꾸기에 성공한 요청 하나만 승인과 이용권 지급을 진행합니다.
- 주문번호에 DB 고유 제약(같은 값이 두 번 들어가지 못하게)을 겁니다.
- 이미 처리된 주문으로 다시 오면 오류 대신 "이미 결제됐습니다" 화면을 보여 줍니다.
- 시간 초과·연결 끊김처럼 결과를 모르는 응답은 「결과 모름」으로 두고, 주문번호로 결제사에 결제 상태를 조회해 맞춥니다. 바로 "실패"로 적고 다시 결제를 시키면 이중 결제가 됩니다.
내 AI 코딩 도구에 이렇게 맡겨 보세요
자주 묻는 질문
단추를 잠그면 충분하지 않나요?
화면 잠금은 실수로 두 번 누르는 것만 막습니다. 새로고침, 웹훅 재전송, 통신 재시도는 화면과 상관없이 서버로 다시 오기 때문에 서버에서도 막아야 합니다.
이미 두 번 결제된 손님이 있어요.
결제사 관리자 화면에서 같은 주문의 결제 내역을 확인하고, 중복된 쪽을 취소(환불)합니다. 그다음 손님께 안내하고, 같은 일이 다시 생기지 않게 위 방법으로 고칩니다.
시험할 때는 어떻게 확인하나요?
테스트 키로 결제한 뒤 완료 화면에서 새로고침을 여러 번 하고, DB 에 주문·이용권이 한 줄만 있는지 봅니다. 단추를 빠르게 연속으로 눌러 보는 것도 해 보세요.
삼심은 Claude·GPT·Gemini 가 서로 모르게 점검하고 지적을 교차 확인해, 개발부터 법률까지 8개 분야의 고칠 순서와 수정 지시문을 드립니다.
함께 보면 좋은 글
참고
이 글은 일반적인 정보이며 법률·보안 자문이 아닙니다. 서비스마다 상황이 다르니 고치기 전에 확인하세요.