기획자 시점에서 보는 실제 서비스에서 발생한 결제 누락 장애 분석
서비스를 운영하다 보면 가끔 이해하기 어려운 장애가 발생합니다.
어느 날 고객센터로부터 한 문의가 들어왔습니다.
“결제를 했는데 주문이 없어요.”
처음에는 사용자의 실수라고 생각했습니다.
결제 통합 플랫폼과 PG사 관리자 콘솔을 확인해 보니 고객 결제 정보는 정상적으로 존재했습니다.
하지만 주문을 관리하는 관리자 페이지에는 해당 결제 내역이 반영되지 않았습니다.
결제 내역이 반영되지 않다 보니 주문 데이터 역시 존재하지 않았습니다.
즉 시스템 상태는 다음과 같았습니다.
결제 : 성공
주문 : 실패 (주문 생성 안됨)
이 문제는 일반적으로 결제 누락(Payment Missing) 또는 결제 DB 반영 실패라고 불립니다.
이번 글에서는 실제 운영 환경에서 겪은 경험을 바탕으로
결제 시스템 구조
결제 누락이 발생하는 이유
장애 해결 과정
을 기획자 관점에서 정리해 보려합니다.
문제가 발생한 상황을 정리하면 다음과 같습니다.
사용자는 정상적으로 결제를 진행했습니다.
상품 선택 → 결제 진행 → 카드 결제 완료
사용자는 화면에서 결제 완료 메시지까지 확인했습니다.
그러나 관리자 페이지에서는 해당 주문이 존재하지 않았습니다.
서비스 입장에서는 주문 데이터가 존재하지 않기 때문에
배송과 같은 다음 단계로 진행할 수 없는 상황이었습니다.
이처럼
결제는 성공
주문은 생성되지 않음
과 같이 결제와 주문 데이터가 일치하지 않는 상태를 보통 결제 누락 문제라고 합니다.
이 문제를 이해하려면 먼저 결제 시스템 구조를 알아야 합니다.
일반적인 온라인 결제 흐름은 다음과 같습니다.
사용자가 결제 버튼 클릭
→ PG 결제창 이동
→ 카드 결제 진행
→ PG가 결제 결과를 서버로 전달
→ 서버에서 주문 생성
→ 주문 데이터 DB 저장
여기서 중요한 점은 다음입니다.
결제 완료와 주문 생성은 같은 단계가 아닙니다.
결제는 'PG(Payment Gateway)'에서 처리되고
주문 생성은 서비스 서버에서 처리됩니다.
즉 결제 시스템은 하나의 시스템이 아니라
사용자 브라우저
서비스 서버
PG 서버
데이터베이스
여러 시스템이 함께 동작하는 구조입니다.
따라서 이 과정 중 어딘가에서 문제가 발생하면
결제는 성공했지만 주문이 생성되지 않는 상황이 발생할 수 있습니다.
결제 누락 문제는 대부분 결제 완료 이후 단계에서 발생합니다.
대표적인 원인은 아래와 같습니다.
1. PG 결제 결과 전달 문제
결제가 완료되면 PG는 서버로 결제 결과를 전달합니다.
이 과정은 보통 Callback, Webhook 방식으로 이루어집니다.
하지만 네트워크 오류, 서버 장애, 요청 처리 실패 등과 같은 상황이 발생하면
문제가 생길 수 있고
이 경우, 서버에서 주문 생성 로직이 실행되지 않을 수 있습니다.
2. 서버 세션 문제
일부 서비스에서는 결제 과정에서 사용자 세션 정보를 기반으로 주문을 생성합니다.
하지만 서버가 여러 대로 운영되는 경우 문제가 발생할 수 있습니다.
예를 들어
사용자 → WAS1 (결제 시작)
결제 완료 → WAS2 (결제 결과 처리)
이 경우 WAS2에서는 사용자 세션을 찾지 못할 가능성이 있습니다.
결과적으로 결제는 성공했지만 주문 생성에는 실패하는 상황이 발생할 수 있습니다.
3. 데이터베이스 트랜잭션 문제
주문 생성 과정에서는 주문 생성, 결제 정보 저장, 재고 차감 등과 같은
여러 작업이 동시에 이루어집니다.
이 과정 중 하나라도 오류가 발생하면 트랜잭션이 롤백될 수 있으며,
이 경우 결제는 성공했지만 주문 데이터는 저장되지 않을 수 있습니다.
내가 경험한 장애는 몇 가지 특징을 가지고 있었습니다.
1. 개발 환경에서는 문제가 발생하지 않았습니다.
2. 운영 환경에서만 간헐적으로 발생했습니다.
3. 운영 환경은 WAS 이중화 구조로 운영되고 있었습니다.
이 구조에서는 요청이 서버 간 분산됩니다.
따라서 결제 과정에서 결제 시작 서버와 결제 결과 처리 서버가
서로 다른 서버가 될 가능성이 있습니다.
만약 결제 로직이 세션 기반으로 동작한다면
이러한 구조에서 결제 누락 문제가 발생할 수 있습니다
이 문제를 해결하기 위해 우리는 결제 로직을 다시 살펴보기 시작했습니다.
기존 구조에서는 결제 결과를 받은 뒤 주문을 생성하는 방식이었습니다.
결제 성공 → 서버에서 주문 생성 → 주문 데이터 DB 저장
하지만 이 구조에서는 결제 결과 처리 과정에서 문제가 발생하면
주문 자체가 생성되지 않는 상황이 발생할 수 있었습니다.
특히 WAS 이중화 환경에서는 결제 결과 요청이 다른 서버로 전달되면서 세션 정보를 찾지 못하면
주문 생성에 오류가 발생할 가능성도 있었습니다.
그래서 우리는 주문을 먼저 생성시점을 변경했습니다.
주문을 먼저 생성하고 결제 여부를 확인하는 방식으로 변했습니다.
사용자 결제 요청 → 주문 먼저 생성 (결제 대기 상태) → 결제 진행 → 일정 시간 동안 결제 여부 확인 → 결제 확인 시 주문 확정.
그리고 일정 시간 내 결제가 확인되지 않으면
해당 주문은 자동으로 취소 처리하도록 했습니다.
이 방식의 장점은 다음과 같습니다.
결제 결과가 늦게 도착해도 주문 누락이 발생하지 않음
서버 간 요청이 분산되더라도 주문 데이터는 이미 존재
결제 상태만 업데이트하면 됨
덕분에 결제 누락 문제는 크게 줄어들었습니다.
하지만 예상하지 못한 또 다른 문제가 발생했습니다.
운영 로그를 확인하던 중 이상한 상황을 발견했습니다.
이번에는 주문은 정상적으로 생성되었지만 결제가 취소된 상태였습니다.
처음에는 단순한 결제 오류라고 생각했습니다.
하지만 로그를 확인해 보니 취소 API가 호출되어 결제가 취소된 것이었습니다.
결제 시스템에서는 종종 결제는 성공했지만 주문 생성이 실패하는 상황이 발생할 수 있습니다.
예를 들어 서버 오류로 주문 생성 실패,
DB 오류로 주문 저장 실패,
네트워크 문제로 주문 처리 중단 등과 같은 경우입니다
이 경우 결제는 완료되었지만 주문이 없기 때문에
환불이나 정산 문제가 발생할 수 있기 때문에 운영 관점에서 매우 크리티컬합니다.
그래서 많은 서비스에서는 주문 생성이 실패하면
결제를 자동으로 취소하는 로직을 두기도 합니다.
즉, 결제 성공 → 주문 생성 시도 → 주문 생성 실패 → 결제 취소 API 호출
이렇게 하면 결제만 되고 주문이 없는 상황을 방지할 수 있습니다.
문제는 내가 운영하는 프로젝트에서
이 취소 로직이 중복 결제 상황에서 동작했다는 것이었습니다.
로그를 확인해 보니 같은 주문번호로 결제가 두 번 발생한 것도 확인되었습니다.
시스템에서는 동일 주문번호에 결제가 두 번 발생하면 이상 상황으로 판단하도록 되어 있었고,
이 경우 결제 취소 API를 호출하도록 되어 있었습니다.
결국, 이번 주문은 존재하지만 결제가 취소된 상태가 되어
주문과 결제 상태가 일치하지 않는 상황이 발생하게 되었습니다.
결제 취소가 발생한 로그를 개발팀과 함께 확인해 보니
한 가지 특이한 상황이 확인되었습니다.
일부 결제는 모바일 환경에서 결제가 정상적으로 종료되지 않은 상태였습니다.
결제 과정에서 네트워크 문제가 발생하면
PG에서는 이를 망취소(Network Cancel) 상황으로 처리하기도 합니다.
망취소란 결제 요청은 이루어졌지만
네트워크 문제 등으로 인해 결제 완료 여부가 정상적으로 전달되지 않은 상태를 의미합니다.
이 경우 PG와 서비스 간 결제 상태가 일시적으로 맞지 않는 상황이 발생할 수 있습니다.
개발팀에서는 이러한 상황을 방지하기 위해
모바일 망취소 상황을 처리하는 방어 로직을 추가했습니다.
즉 다음과 같은 처리를 추가했습니다.
결제 상태 재조회 로직 추가
망취소 상태 발생 시 결제 상태 확인
실제 결제가 잡혀 있는 경우 주문 상태 보정
이 방어 로직을 추가한 이후에는
주문과 결제 상태가 일치하지 않는 문제가 크게 줄어들었습니다.
결제 시스템에서는 다양한 네트워크 환경과 예외 상황이 발생할 수 있기 때문에
이러한 방어 로직을 통해 상태 정합성을 유지하는 것이 중요합니다.
이번 장애를 겪으면서 느낀 점 중 하나는
결제 시스템에서는 상태(State) 설계가 매우 중요하다는 것입니다.
결제 시스템에서는 보통 다음과 같은 상태가 존재합니다.
주문생성 : 주문이 생성된 상태
결제대기 : 결제를 기다리는 상태
결제진행중 : 결제가 진행되고 있는 상태
결제완료 : 결제가 정상적으로 승인된 상태
결제취소 : 결제가 취소된 상태
주문완료 : 주문이 정상적으로 처리된 상태
주문취소 : 주문이 취소된 상태
일반적인 결제 흐름은 다음과 같이 진행됩니다.
주문 생성 → 결제 대기 → 결제 완료 → 주문 완료
하지만 실제 서비스에서는
결제는 성공했지만 주문 생성 실패,
중복 결제 발생,
결제 취소 요청,
네트워크 오류 등의 예외 상황이 발생될 수 있고,
이로 인해 결제와 주문 상태가 서로 맞지 않는 상황이 생길 수 있습니다.
따라서 결제 시스템에서는 이러한 상황을 방지하기 위해
다음과 같은 설계를 고려해야 합니다.
주문 상태와 결제 상태의 관계 정의
중복 결제 방지 로직
결제 실패 시 복구 로직
결제 취소 처리 기준
즉, 주문 상태와 결제 상태가 항상 일관성을 유지하도록 설계하는 것이 중요합니다.
그럼 결제 시스템을 붙일 때, 기획자는 어떤 부분을 고민해야 할까요?
결제 시스템은 서비스에서 가장 중요한 기능 중 하나입니다.
따라서 기획자도 결제는 PG에서 처리되고 주문 생성은 서비스 서버에서 처리된다는 것을 이해하고,
결제 성공이 곧 주문 생성 완료를 의미하지는 않는다는 것을 이해해야 하며,
결제 완료 이후에도 주문 생성 단계에서 문제가 발생할 수 있다는 것을 이해하고 있어야 합니다.
기획자가 결제 시스템의 기본 구조를 이해하고 있다면
이러한 문제 상황에서 개발자와 더 효과적으로 문제를 해결할 수 있을 것이라고 생각하기 때문입니다.
이번 사례는 결제 누락 문제를 해결하려는 과정에서
새로운 결제 정합성 문제를 경험하게 된 사례였습니다.
결제 시스템은 단순히 결제 기능 하나로 끝나는 것이 아니라
주문, 결제, 인프라 구조가 함께 맞물리는 시스템입니다.
이 경험을 통해 결제 시스템에서는 다음 두 가지를 고려해야 한다는 것을 알게 되었습니다.
주문 상태와 결제 상태의 일관성 유지
다양한 예외 상황을 고려한 시스템 설계
#결제누락
#PG결제오류
#결제DB반영안됨
#주문누락
#결제시스템