자동화 slecs

송금 실패 시 파트너 잔액 복구 프로세스 개선

목차

결제 플랫폼의 출금 송금이 실패했을 때 파트너 계정의 잔액을 제대로 복구하고, PG가 비활성화된 상태에서 배치가 즉시 실패하도록 처리를 보강했다. Codex 정적 검증 도구에서 6차 검증 시 지적한 사항들을 개선한 작업인데, 돌아보니 이 부분이 생각보다 많은 엣지 케이스를 감싸고 있더라.

송금 실패 처리의 불완전함

출금 기능을 구현할 때 "정상 케이스"는 대부분 잘 처리하는 편이다. 파트너가 출금을 신청하면 → 잔액이 차감되고 → 송금이 진행된다. 그런데 송금 중간에 PG 응답 지연, 일시적 네트워크 오류, 또는 PG 시스템 장애 같은 실패가 발생하면?

초기 구현에서는 이 부분이 그냥 "실패 상태로 표시"만 되고 있었다. 데이터베이스에는 송금 요청 레코드가 PENDING이나 ERROR로 남아있고, 파트너 계정의 잔액은? 이미 차감된 상태였다. 결과적으로 파트너 입장에서는 "내 잔액이 빠져나갔는데 받지 못한" 상황이 되는 것. 이건 단순한 UX 문제가 아니라 재정 결산의 정확성 문제다.

변경 사항: 3가지 계층에서의 보강

1. 파트너포털 Web Layer에서의 복구

파트너가 출금을 요청했을 때 송금이 실패하면, 그 시점에 파트너의 잔액을 원래대로 돌려놔야 한다. 이는 단순히 덧셈 역산이 아니라, 언제 어느 단계에서 실패했는지를 추적해야 한다는 뜻이다. 송금 요청 생성 직후 실패? 그럼 차감된 잔액을 즉시 복구. 약간의 시간이 지난 후 실패? 그럼 기존 잔액 스냅샷으로 복구하되, 그 사이 다른 거래가 있었다면? 이 부분에서 격리성(isolation)을 어떻게 보장할지가 핵심이다.

2. 배치 처리 Layer에서의 PG_DISABLED 즉시 실패

배치는 보통 야간이나 정기적으로 돌아가면서 미결제 항목들을 다시 시도한다. 문제는 어떤 PG가 이미 비활성화 상태면, 재시도해봤자 무조건 실패한다는 점. 과거엔 이 상태를 무시하고 계속 PENDING 상태로 두었다가, 나중에 운영자가 수동으로 정리했다. 이번 작업에서는 배치 시작 단계에서 PG 상태를 먼저 확인하고, 만약 PG_DISABLED면 그 PG의 모든 대기 중인 송금을 즉시 FAILED로 마킹하도록 했다. 이렇게 하면 파트너포털도 정확한 최종 상태를 표시할 수 있고, 정산 보고서도 깔끔해진다.

3. PgPaymentRouter에서의 상태 관리 개선

결제 라우팅 로직은 "어느 PG로 보낼까"를 결정하는 핵심 부분이다. 각 PG의 상태(활성/비활성/점검 중 등)를 체크하지 않고 단순히 설정된 PG로 보내다 보니, 비활성 PG에 계속 요청을 던지게 되었다. 이번엔 라우팅 직전에 해당 PG의 상태를 명시적으로 검증하도록 추가했다.

왜 이게 Codex 검증에서 지적되었나

정적 코드 검증 도구(Codex)가 6차 검증에서 이 부분을 지적한 이유는, 상태 전이 다이어그램이 불완전했기 때문이다.

보통 기대하는 상태 전이:
PENDING  PROCESSING  SUCCESS
            
          FAILED

실제 발생하던 상태 전이:
차감 완료  PENDING  (실패)  FAILED 
          (잔액 미복구) 
       (파트너는 손실)

정적 분석 도구는 이런 "의도하지 않은 상태"를 찾아내도록 학습되어 있다. 특히 금융 시스템에서 "들어온 돈은 정확히 추적되어야 한다"는 원칙을 위반하는 경로가 있으면 플래그를 띄운다.

배운 점

이번 작업을 하면서 느낀 건, 결제 관련 로직은 "정상 케이스"와 "실패 케이스"를 동시에 설계해야 한다는 것이다. 요구사항 정의할 때부터 "만약 이 단계에서 실패하면 어떻게?"를 필수 항목으로 넣어야 했다. 그게 아니면 처음엔 간단해 보이는 기능도, 시간이 지나면서 엣지 케이스가 계속 발견되고 패치가 늘어난다.

또 하나는 상태 관리의 원자성이다. "차감한다" → "송금한다" 두 작업이 있을 때, 둘 다 성공하거나 둘 다 실패해야 한다. 부분적으로 성공하는 상황이 가장 위험하다. 이번에 추가한 복구 로직도 결국 "원래 상태로 돌아가기"를 보장하기 위한 것이었다.

배치 처리에서 PG 상태를 미리 확인하는 부분도 마찬가지. 배치가 실행되는 시점에 상태를 한 번 더 체크함으로써, 불필요한 재시도를 줄이고 최종 상태의 일관성을 높였다. 이런 "한 번의 추가 확인"이 나중의 수동 정리 작업을 줄일 수 있다.

사실 이런 보강은 처음부터 설계에 포함되었어야 하는데, 실제로는 서비스가 운영되면서 발견되는 경우가 많다. 그래서 정적 검증, 코드 리뷰, 실전 모니터링을 조합해서 차근차근 메워나가는 것. 다음 번에 비슷한 기능을 설계할 땐 실패 경로를 "첫 번째 반복"에 포함시키는 게 목표다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.