Skip to content

payment recovery model

hyoguoo edited this page Sep 6, 2026 · 1 revision

결제 복구 모델 — 도착 상태 분리와 두 단계 스캔

확정 도중 멈춘 결제를 되살리는 복구 스케줄러가 정상 결제를 해치지 않도록 도착 상태와 전이 조건을 다시 세운 기록


사전 지식 요약

용어 짧은 풀이
복구 스케줄러 IN_PROGRESS 로 오래 머문 결제를 주기로 훑어 옮기는 안전망
확정 결과 pg-service 가 벤더 판정을 payment-service 로 돌려보내는 메시지
비재시도 분류 재시도해도 결과가 바뀌지 않는 예외를 백오프 없이 DLQ 로 보내는 목록
1차 기준 시간 IN_PROGRESS 를 멈춘 것으로 판정하는 경과 시간
2차 기준 시간 AWAITING_RESULT 를 회신 불가로 판정하는 경과 시간

문제 배경 — 복구가 만든 적용 불가 상태

복구 스케줄러가 필요한 이유

확정은 payment-service 가 명령을 보내고 pg-service 가 결과를 돌려주는 왕복이라, 그 사이에 프로세스가 죽으면 결제가 IN_PROGRESS 로 남는다.

  • 아무도 다시 건드리지 않으면 그대로 영구히 남음
  • 복구 스케줄러 (PaymentReconciler)가 주기로 훑어 다른 상태로 옮김

READY 로 되돌리던 초기 모델

일정 시간이 지나면 해당 결제 상태를 READY 로 변경했으나, READY 는 확정에 진입하기 전을 뜻하므로 확정 결과로 전이되지 않는 상태가 된다.

기대 실제
재시도 스케줄러가 재처리 재시도 대상은 payment_outbox 의 PENDING 행이라, 상태를 옮기는 것만으로는 아무 재처리도 일어나지 않음
늦게 온 승인으로 완료 DONE 전이가 IN_PROGRESS 에서만 허용돼 상태 예외로 거부
종결 못 하면 만료 EXPIRED 전이는 주문이 시작 전일 때만 허용 — 확정에 진입한 주문은 이미 실행 중

결국엔 결제가 종결도 만료도 못 하는 상태로 쌓였고, 운영자 목록에도 뜨지 않아 영구적으로 남는 결제가 생겼다.

부하 구간에서 사고가 커지는 구조

한 건의 거부가 뒤 메시지를 막고, 그 지연이 다시 기준 시간을 넘기는 결제를 만들어 복구 스케줄러가 READY 로 되돌리는 악순환이 반복된다.

flowchart TD
    A[부하 지속 - 확정 회신이 1차 기준 시간을 넘김] --> B[복구 스케줄러가 READY 로 되돌림]
    B --> C[뒤늦은 승인이 거부됨]
    C --> D[재시도가 리스너 스레드를 잡음]
    D --> E[뒤 메시지가 전부 대기]
    E -->|회신 지연| A
Loading
  • 거부 예외가 비재시도 목록에 없으면 컨슈머가 백오프를 끝까지 소진
  • 2026-09-01 실측에서 기준 시간을 넘긴 결제가 25초 만에 965 건에서 1,879 건으로 증가

도착 상태 설계 — 두 가지 안

안 장점 단점
READY 에서 DONE 전이 허용 코드 변경 최소, 상태 추가 없음 확정에 진입한 적 없는 결제가 승인으로 완료 — 상태의 정의가 무너진다
AWAITING_RESULT 신설 (채택) 거부 자체가 사라지고, 회신 없는 건도 끝낼 길이 생김 상태가 늘어 판정하는 곳도 함께 늘어남

이미 확정에 진입했고 결과를 기다린다는 뜻만 갖는 상태인 AWAITING_RESULT 를 신설하고, 1차 기준 시간 초과 시 IN_PROGRESS 에서 옮기도록 했다.

  • 복구 스케줄러는 결제 상태만 바꾸고 주문은 실행 중에 그대로 둠
  • 주문 단위 완료 전이는 원래부터 성립했고, 막고 있던 것은 결제 쪽 상태 하나뿐
  • DONE 전이 가드를 IN_PROGRESS 와 AWAITING_RESULT 두 상태를 받도록 확장

상태 머신

stateDiagram-v2
    [*] --> READY: 결제 접수
    READY --> IN_PROGRESS: 확정 진입
    IN_PROGRESS --> DONE: 승인 결과
    IN_PROGRESS --> FAILED: 실패 결과
    IN_PROGRESS --> QUARANTINED: 격리 결과
    IN_PROGRESS --> AWAITING_RESULT: 1차 기준 시간 초과
    AWAITING_RESULT --> DONE: 늦은 승인
    AWAITING_RESULT --> FAILED: 늦은 실패
    AWAITING_RESULT --> QUARANTINED: 늦은 격리 또는 2차 기준 시간 초과
    READY --> FAILED: 실패 결과
    READY --> EXPIRED: 만료 - 주문이 시작 전일 때만
    QUARANTINED --> FAILED: 운영자 안전 종결
Loading
  • READY 에서 DONE 으로 가는 화살표가 없는 것이 이 상태 머신의 요점
  • 부수 효과로 만료 경로 정상화 — READY 를 세팅하는 곳이 결제 접수 하나뿐이라 READY 인 결제는 전부 주문이 시작 전

두 단계 스캔

단계 대상 기준 시각 도착 상태
1차 IN_PROGRESS + 1차 기준 시간 초과 확정 진입 시각 AWAITING_RESULT
2차 AWAITING_RESULT + 2차 기준 시간 초과 상태 변경 시각 QUARANTINED

2차 기준 시각을 상태 변경 시각으로 잡은 이유

확정 진입 시각은 진입할 때 한 번 박히고 이후 갱신되지 않는다.

  • 그 값으로 재면 방금 AWAITING_RESULT 로 옮긴 건이 같은 주기에 곧바로 격리됨
  • 뒤늦게 도착할 정상 승인이 적용 불가 상태를 만나게 됨

2차가 QUARANTINED 로 보내는 이유

벤더가 승인했는지 모르는 채로 끝내면 실제 청구와 기록이 어긋나므로 판단을 보류한다.

  • 판단 보류는 이 시스템에서 QUARANTINED 가 담당하는 역할
  • 운영자가 벤더 상태를 조회한 뒤 안전 종결

상황별 복구 경로

확정 사이클의 어느 지점에서 멈추든 담당 주체가 정해져 있고, 결제는 종결이나 운영자 확인 중 하나에 도달한다.

멈춘 지점 결제 상태 회수 주체 다음 상태
확정에 진입하기 전 READY 만료 스케줄러 (5분 주기) EXPIRED
확정 명령이 아직 나가지 못함 IN_PROGRESS + Outbox PENDING OutboxWorker (5초 주기) IN_PROGRESS 유지 — 명령만 재발행
명령은 나갔으나 회신이 없음 IN_PROGRESS 복구 스케줄러 1차 AWAITING_RESULT
회신이 뒤늦게 도착 AWAITING_RESULT 확정 결과 소비 경로 DONE / FAILED
회신이 끝내 오지 않음 AWAITING_RESULT 복구 스케줄러 2차 QUARANTINED
자동 판정 불가 QUARANTINED 운영자 안전 종결 FAILED

관련 위키

Clone this wiki locally