삼심SAMSIM

점검 가이드 · 결제

결제 중복 청구 막기 — 새로고침·두 번 클릭·재시도 대비

2026-10-24 기준

세 줄 요약
  • 결제는 "단추 클릭 → 결제창 → 완료 화면 → 서버 승인 → 주문 처리"를 거치는데, 이 중 어느 단계든 두 번 불릴 수 있습니다.
  • 해결의 핵심은 주문번호 하나에 처리를 딱 한 번만 하게 만드는 것(멱등성)입니다.
  • 결과를 모르는 오류(시간 초과 등)는 "실패"로 단정하지 말고, 주문번호로 결제사에 다시 물어 확인합니다.

중복 청구가 생기는 흔한 장면

카드사에서 실제로 두 번 승인되지 않더라도, 우리 DB 에 이용권이 두 번 생기거나 포인트가 두 번 쌓이는 것도 같은 종류의 문제입니다.

  • 손님이 결제 단추를 빠르게 두 번 누름 — 주문이 두 개 만들어지고 결제도 두 번 진행됨
  • 결제 완료 화면에서 새로고침하거나 뒤로 갔다 다시 옴 — 완료 화면이 다시 불리며 승인·이용권 지급이 한 번 더 일어남
  • 휴대폰 통신이 끊겨 앱이 같은 요청을 다시 보냄
  • 결제사가 보내는 알림(웹훅)이 다시 옴 — 토스페이먼츠는 내 서버가 정상 응답(200)을 못 주면 웹훅을 최대 7회까지 다시 보낸다고 안내합니다
  • 매일 도는 자동 결제(정기결제 예약 작업)가 동시에 두 번 실행됨

멱등성 — "같은 요청은 몇 번 와도 한 번만"

멱등성은 같은 요청을 여러 번 받아도 결과가 한 번 받은 것과 같게 만드는 성질입니다. 결제에서는 주문번호를 기준으로 삼습니다.

토스페이먼츠는 같은 결제를 다시 승인하려 하면 「이미 처리된 결제」(ALREADY_PROCESSED_PAYMENT) 오류를 돌려줍니다. 또 POST 요청에 Idempotency-Key 헤더를 붙이면, 같은 키로 다시 보낸 요청은 다시 실행하지 않고 첫 응답을 그대로 돌려줍니다(키는 첫 요청부터 15일 유효). 하지만 결제사가 막아 줘도 내 DB 의 "이용권 지급"은 내 코드가 막아야 합니다.

막는 방법

  1. 결제 단추를 누르면 바로 단추를 잠그고 "처리 중"으로 바꿉니다(화면 쪽 1차 방어).
  2. 결제창을 열기 전에 서버가 주문번호를 먼저 만들어 DB 에 「결제 대기」로 저장합니다. 같은 손님이 같은 상품을 다시 누르면 새 주문 대신 기존 대기 주문을 씁니다.
  3. 완료 화면이나 웹훅이 오면, 「결제 대기 → 처리 중」으로 바꾸는 일을 DB 에서 조건부로 한 번에 합니다(예: 상태가 대기일 때만 바꾸기). 바꾸기에 성공한 요청 하나만 승인과 이용권 지급을 진행합니다.
  4. 주문번호에 DB 고유 제약(같은 값이 두 번 들어가지 못하게)을 겁니다.
  5. 이미 처리된 주문으로 다시 오면 오류 대신 "이미 결제됐습니다" 화면을 보여 줍니다.
  6. 시간 초과·연결 끊김처럼 결과를 모르는 응답은 「결과 모름」으로 두고, 주문번호로 결제사에 결제 상태를 조회해 맞춥니다. 바로 "실패"로 적고 다시 결제를 시키면 이중 결제가 됩니다.

내 AI 코딩 도구에 이렇게 맡겨 보세요

내 AI 에게 그대로 붙여 넣기이 프로젝트의 결제 흐름에서 중복 청구·중복 지급이 생길 수 있는 곳을 찾아 줘. 다음 경우를 하나씩 따져 줘: 결제 단추 두 번 클릭, 완료 화면 새로고침·뒤로 가기, 같은 웹훅 재전송, 완료 화면과 웹훅이 동시에 도착, 예약 결제 작업이 동시에 두 번 실행, 결제사 응답 시간 초과. 각 경우에 승인 요청·DB 기록·이용권(또는 포인트) 지급이 몇 번 일어나는지 적어 줘. 고칠 때는 주문번호 기준으로 한 번만 처리되게 해 줘: 주문번호 고유 제약, 상태가 '대기'일 때만 '처리 중'으로 바꾸는 조건부 UPDATE(바뀐 줄이 1개일 때만 진행), 결제사 POST 요청에 Idempotency-Key, 결과를 모르는 응답은 실패로 적지 말고 주문번호로 조회해 맞추기. 고치기 전에 수정안을 먼저 보여 줘.

자주 묻는 질문

단추를 잠그면 충분하지 않나요?

화면 잠금은 실수로 두 번 누르는 것만 막습니다. 새로고침, 웹훅 재전송, 통신 재시도는 화면과 상관없이 서버로 다시 오기 때문에 서버에서도 막아야 합니다.

이미 두 번 결제된 손님이 있어요.

결제사 관리자 화면에서 같은 주문의 결제 내역을 확인하고, 중복된 쪽을 취소(환불)합니다. 그다음 손님께 안내하고, 같은 일이 다시 생기지 않게 위 방법으로 고칩니다.

시험할 때는 어떻게 확인하나요?

테스트 키로 결제한 뒤 완료 화면에서 새로고침을 여러 번 하고, DB 에 주문·이용권이 한 줄만 있는지 봅니다. 단추를 빠르게 연속으로 눌러 보는 것도 해 보세요.

내 코드에 이런 문제가 있는지, AI 세 개가 따로 봐 드려요.

삼심은 Claude·GPT·Gemini 가 서로 모르게 점검하고 지적을 교차 확인해, 개발부터 법률까지 8개 분야의 고칠 순서와 수정 지시문을 드립니다.

함께 보면 좋은 글

참고

이 글은 일반적인 정보이며 법률·보안 자문이 아닙니다. 서비스마다 상황이 다르니 고치기 전에 확인하세요.