블로그
스위치 옆에서 은행 카드와 영수증을 갈라놓은 빨간 유리 차단막.

2026년 8월 26일4분 분량Администратор

결제를 다시 시도할까, 멈출까: 거절이 이중 출금이 되지 않게 하는 법

타임아웃, 거절, 허용된 반복 시도는 각각 다른 대응이 필요합니다. 두 개의 카운터, 장애 후 대기열 점검, 복구된 주문의 비용까지.

타임아웃 뒤 가장 위험한 버튼은 첫 거래가 어떻게 되었는지 아무도 모를 때의 '다시 시도'입니다. 고객, 서버, 백그라운드 대기열이 거의 동시에 이 버튼을 누를 수 있습니다. 재시도 정책은 먼저 원결제에 대한 질문에 답하고, 그다음 허용된 후속 조치를 확인하고, 그 뒤에야 복구된 매출을 계산해야 합니다.

첫 번째 질문: 거절이 확인되었는가?

세 가지 상황을 구분하십시오. 요청이 파라미터 검증을 통과하지 못한 경우, 결제가 확정적인 부정 결과를 받은 경우, 응답이 유실되어 결과를 모르는 경우입니다. 마지막 경우에 새 결제를 다시 만드는 것은 특히 위험합니다. 먼저 가능한 방법으로 원거래의 상태를 확인하고 관련 알림을 처리하십시오. 사용자에게 보여 주는 문구도 불확실성을 반영해야 합니다. 예를 들면 "결제 결과를 확인하고 있습니다. 업데이트될 때까지 잠시 기다려 주십시오."

Stripe는 네트워크 장애가 거래가 실행되지 않았다는 증거가 아니라고 밝힙니다. 같은 요청을 허용된 범위에서 반복할 때는 정해진 기간 안에 같은 멱등성 키와 파라미터를 사용하며, 결과가 불확실하면 대사가 필요합니다. 구체적인 규칙은 API 버전에 따라 다릅니다. 출처: Stripe, Advanced error handling.

soft와 hard는 거절 코드와 함께일 때만 의미가 있다

일시적 거절과 지속적 거절로 나누는 실무 구분은 다음 조치를 고르는 데 도움이 됩니다. 하지만 soft decline, hard decline이라는 이름이 해당 결제수단의 문서를 대신하지는 않습니다. 일시적인 사유는 나중에 사라질 수 있지만, 다른 거절은 데이터 수정, 고객의 참여, 또는 시도 중단이 필요합니다. 매출을 되살리고 싶다는 이유만으로 알 수 없는 사유를 자동으로 일시적이라고 간주해서는 안 됩니다.

Stripe 레퍼런스에서 추가 인증 요구는 그에 맞는 고객 행동으로 이어지고, 카드 유효기간 만료는 다른 카드가 필요하며, 일반 거절은 카드 발급사에 문의해야 할 수도 있습니다. 권장 조치가 코드마다 다르므로 '모든 거절은 다시 시도'라는 단일 규칙은 틀렸습니다. 출처: Stripe, Decline codes.

작고 안전한 정책의 예

팀이 일회성 카드 결제를 처리하며 자동화를 제한하고 싶다고 합시다. 팀은 PG사가 명시적으로 허용한 일시적 사유에 한해, 권장 간격이 지난 뒤에만 추가 자동 시도를 최대 1회 허용합니다. 그래도 실패하면 프로세스는 멈추고 가능한 다음 단계를 보여 줍니다. 여기서 '1회'는 내부의 보수적인 한도를 보여 주는 예시이지, 카드 네트워크의 규칙이 아닙니다.

정책은 PG사와 카드 네트워크의 더 엄격한 제한도 함께 지켜야 합니다. 자동 반복이 금지되어 있다면 내부 한도가 허가가 되지는 않습니다. 인증이 필요하다면 기다리는 것만으로는 인증을 대신할 수 없습니다. 고객이 작업을 취소했거나 출금 동의가 철회되었다면 백그라운드 프로세스는 기존 시나리오를 계속해서는 안 됩니다.

실패한 주문 100건이 있다고 해 봅시다. 60건은 아직 결과를 모르고, 25건은 최종 거절로 표시되었으며, 15건은 PG사 규칙상 반복이 가능합니다. 결제 100건을 다시 보내서는 안 됩니다. 먼저 미확정 60건을 정리하고, 25건에는 적절한 고객 조치를 안내하며, 나머지 그룹만 제한적인 반복 대상으로 검토합니다. 이렇게 나누면 기술적 복구와 새로운 결제 결정이 뒤섞이지 않습니다.

한도 하나 대신 카운터 두 개

요청의 기술적 전달과 새로운 승인 시도를 분리하십시오. 로그에는 두 개의 카운터가 유용합니다. 신뢰할 수 있는 응답을 얻으려고 같은 작업을 몇 번 보냈는지, 그리고 주문에 독립적인 결제 시도가 몇 번 있었는지입니다. 그렇지 않으면 네트워크 전송 세 번을 새 결제 세 번으로 착각하거나, 반대로 새로운 시도를 '리트라이'라는 뭉뚱그린 말 뒤에 숨기게 됩니다.

허용된 후속 조치에는 근거를 저장하십시오. 구체적인 응답, 적용한 규칙, 시각, 확인된 주문 상태입니다. 그래야 장애 후에 결정을 설명할 수 있습니다. '자동으로 반복 허용됨'이라는 기록만으로는 그 허용이 바로 이 거래에 적용 가능했는지 알 수 없습니다.

연결이 복구될 때 한 가지를 더 확인하십시오. 쌓여 있던 대기열이 이제 필요 없어진 작업까지 한꺼번에 실행해서는 안 됩니다. 보류된 시도를 실행하기 전에 매번 주문 상태와 현재 유효한 허가를 다시 읽으십시오. 장애 동안 고객이 다른 수단으로 결제했다면, 이전 작업은 새 출금 없이 종료되어야 합니다.

중복 방지 장치를 어디에 둘 것인가

  • 주문 상태, 거래 식별자, 시도 횟수, 다음 조치가 허용되는 시각을 저장하십시오.
  • 화면, 서버, 대기열, PG사의 반복 시도를 하나의 프로세스 책임자를 통해 조율하십시오.
  • 다시 시도하기 전에 주문이 이미 결제되었거나, 취소되었거나, 처리 중이 아닌지 확인하십시오.
  • 이전 요청의 불확실한 결과를 우회하려는 목적만으로 새 키를 만들지 마십시오.
  • 금지, 한도 도달, 고객 참여 필요 시 자동화를 중단하십시오.

전송이 성공해도 비즈니스 결과는 확인해야 합니다. Adyen은 HTTP 200과 함께 승인 거절이 올 수 있으며, 결과는 응답의 결제 관련 필드가 설명한다고 밝힙니다. 따라서 HTTP 코드를 결제 완료 주문의 유일한 근거로 삼아서는 안 됩니다. 출처: Adyen, Refusal reasons.

복구 비용은 알맞은 그룹에서 평가하라

학습용 예시에서 반복이 가능했던 주문 15건으로 돌아가 봅시다. 추가 시도 1회씩으로 6건의 주문이 복구되었다고 합시다. 이 단계의 성공률은 6 / 15 = 40%이고, 복구된 주문 1건당 추가 시도는 15 / 6 = 2.5회입니다. 이는 해당 적격 그룹의 지표이지, 처음 100건 전체의 복구율이 아닙니다.

결과를 마무리의 품질과 비교하십시오. 중복 출금, 취소된 주문, 불필요한 상품 출고, 잘못된 고객 안내는 없었습니까? 기술적으로는 성공했지만 중복 때문에 환불이 필요했던 주문을 단서 없이 정책의 순수한 성과로 기록해서는 안 됩니다.

운영 규정에는 책임자가 있는 비상 정지 장치가 필요합니다. 누가 자동 반복을 멈출 수 있는지, 어떤 신호가 이를 작동시키는지, 정지 후 대기열은 어떻게 처리하는지입니다. 쌓인 상태를 점검하며 정상 운영으로 돌아가는 절차도 따로 마련하십시오. 장애 후 이전 대기열을 그냥 다시 켜면 의미가 이미 바뀐 요청들이 다시 실행될 수 있습니다.

중단 후 고객에게 무엇을 안내할 것인가

고객 지원팀에는 내부 부정거래 탐지 기준을 드러내지 않는 명확한 지침이 필요합니다. 해당 사유에 허용된 조치만 안내하십시오. 인증 완료, 보안 양식을 통한 결제 정보 수정, 은행 문의, 허용된 다른 결제수단 선택 등입니다. 이 구조는 카드 사례에 해당하며, 계좌 이체 등 다른 결제수단에는 각 수단 고유의 상태와 절차를 사용하십시오. 보안 점검과 참여자 제한은 모든 단계에서 유지되어야 합니다.

출처

  1. 1.docs.stripe.com/error-low-level
  2. 2.docs.stripe.com/declines/codes
  3. 3.docs.adyen.com/development-resources/refusal-reasons

댓글 · 0

아직 댓글이 없습니다 — 첫 댓글을 남겨 보세요.

로그인 · 댓글을 남기려면 로그인하세요.