11장 결제 시스템
기능 요구사항
- 대금 수신 흐름
- 대금 정산 흐름
비기능 요구사항
- 신뢰성 및 내결함성
- 내부/외부 서비스 간의 조정 프로세스
대략적인 규모 추정
- 하루 100만건의 결제요청 -> 1,000,000 / 10^5초 = 초당 10건의 트랜젹선 = 10TPS
대금 수신 및 정산 흐름
구매자 -> 신용카드 -> (대금 수신) -> 은행계좌 -> (대금 정산) -> 은행계좌 -> 판매자
용어 정리
결제 서비스
결제 서비스는 사용자로부터 결제 이벤트를 수락하고 결제 프로세스를 조율한다. 이때 범죄 행위등을 평가하는 위험 점검후 통과한 결제만 처리한다. 위험 확인은 3자 제공업체를 이용한다.
결제 실행자
결제 실행자는 결제 서비스 공급자(psp) 를 통해 결제 주문을 하나 실행한다. 하나의 결제 이벤트에는 여거 결제 주문이 포함될 수 있다.
결제 서비스 공급자
PSP는 A 계정에서 B 계정으로 돈을 옮기는 역할을 담당..
카드 유형
비자, 마스터카드, 디스커버리 등등…
원장
원장(ledger) 결제 트랜잭션에 대한 금융 기록. 사용자가 판매자에게 1달러를 결제하면 사용자로부터 1달러를 인출하고 판매자에게 1달러를 지급하는 기록을 남김
지갑
지갑에는 판매자의 계정 잔액을 기록.
결제 서비스 API
POST /v1/payments
field | description | type |
---|---|---|
seller | 대금을 수령할 판매자 | string |
amount | 주문 대금 | string |
currency | 사용된 통화 단위 | string |
payment_order_id | 주문 식별 전역 고유 ID | string |
payment_order_id
는 전역적(globally)으로 고유한 값이다
이 값은 중복제거 ID로 사용되며 멱등 키라고도 한다
amount
의 경우 데이터 타입이 double이 아닌 string 임에 주의할것
프로토콜, 소프트웨어, 하드웨어에 따라 직렬/역직렬화 시에 의도치않게 값이 반올림 될 수 있고 숫자가 매우 클 수도, 매우 작을 수도 있어 string 으로 저장한다.
상세 설계
PSP 연동
결제 시스템이 은행이나 비자 같은 카드 시스템에 직접 연결한다면 PSP 없이도 결제할 수 있다. 하지만 그런 경우는 한정적이기에 다음 두 가지 방법중 하나로 결제 시스템을 PSP와 연동한다.
- 회사가 민감한 결제 정보를 저장할 수 있다면 API로 PSP 와 연동
- 민감한 결제 정보를 저장하지 않겠다면, PSP는 카드 결제 세부 정보를 수집하여 PSP에게 위임한다.
외부 결제 페이지가 잘 작동할때는 문제없지만 네트워크 문제로 실패할 경우
조정(reconciliation) 을 통해 해결한다.
조정
시스템 구성 요소가 비동기적으로 통신하는 경우 메세지 또는 응답이 반환된다는 보장이 없다.
이는 시스템 성능을 높이기 위해 비동기 통신을 자주 사용하는 결제 관련 사업의 일반적인 문제다.
따라서 정확성을 보장하기 위해 조정
이 필요하다.
매일 밤 PSP나 은행은 고객에게 정산 파일을 보낸다
정산 파일에는 계좌의 잔액과 하루 동안 발생한 모든 거래 내역이 기재되어 있다.
조정 시스템은 정산 파일을 읽어 원장과 비교한다.
조정 중에 발견된 차이는 일반적으로 재무팀에 의뢰해 수동으로 고친다.
불일치에 대한 문제 및 해결 방안은 3가지로 나뉜다.
- 어떤 문제인지 알고 있고, 해결을 자동화 할 수 있다 -> 프로그램을 통해 자동처리
- 어떤 문제인지 알고 있지만, 해결을 자동화 할 수 없다 -> 문제는 작업 대기열에 넣고 재무팀에서 수동 수정
- 분류할 수 없는 유형의 문제 -> 우선순위 대기열에 넣고 재무팀에서 수동 수정
결제 지연 처리
결제 요청은 많은 컴포넌트를 거치며 내부 및 외부의 다양한 처리 주체와 연동한다. 대부분의 경우 몇초만에 요청이 처리되지만, 완료 또는 거부까지 며칠까지 걸리는 경우도 있다.
- PSP가 해당 결제 요청의 위험성이 높다고 보고 담당자 검토를 요청하는 경우
- 카드사가 확인 용도로 카드 소유자의 추가 정보를 요청하는 경우 (3d secure authenticaction)
결제 서비스는 시간이 오래걸리는 요청도 처리 할 수 있어야 한다.
구매 페이지가 외부 PSP에 호스팅 되는 경우 PSP 는 다음과 같이 처리한다.
- psp 는 결제를 대기 상태임으로 클라이언트에 응답, 클라이언트는 사용자에게 대기 상태를 표시
- psp는 회사를 대신해 대기 중인 결제의 진행 상황을 추적하고, 상태가바뀌면 psp에 등록된 웹훅을 통해 결제 서비스에 알린다.
결제 실패 처리
모든 결제 시스템은 실패한결제를 적절히 처리할 수 있어야 한다.
결제 상태 추적
실패가 일어날 때 마다 결제 거래의 현재 상태를 파악하고 재시도 또는 환불이 필요한지 여부를 결정한다.
결제 상태는 데이터 추가만 가능한 테이블에 보관한다.
실패를 처리하기 위해서는 재시도 큐, 실패 메시지 큐를 두는게 바람직하다.
- 재시도 큐 : 일시적 오류 같은 재시도 가능 오류
- 실패 메시지 큐: 반복적으로 처리에 실패한 메세지. 문제가 있는 메세지를 디버깅하고 격리하여 성공적으로 처리 되지 않은 이유를 파악하는데 유용
정확히 한 번 전달
결제 시스템에 발생 가능한가장 심각한 문제 중 하나는 이중 청구다. 주문이 정확히 한번만 실행되도록 하는게 중요하다.
수학적으로 보자면, 다음 요건이 충족되면 정확히 한 번 실행된다.
- 최소 한 번은 실행한다.
- 최대 한 번 실행한다.
재시도를 통해 최소 한 번 실행을 보증하고, 멱등성 검사를 통해 최대 한 번을 보증한다.
재시도
네트워크 오류로 시간초과나 결제 거래르 다시 시도해야 하는 경우가 있다. 중복 요청을 3번하고 마지막 4번째 요청이 성공하는 가정을 한다.
재시도에는 여러 전략이 있다.
- 즉시 재시도 : 클라이언트는 즉시 다시 요청한다.
- 고정 간격 : 재시도 전에 일정 시간 기다린다.
- 증분 간격 : 재시도 전에 기다리는 시간을 점진적으로 늘린다.
- 지수적 백오프 : 재시도 전에 기다리는 시간을 직전 대비 두배씩 늘려가는 방안.
- 취소 : 요청을 철회. 실패가 영구적이거나 재시도를 하더라도 성공 가능성이 낮은 경우에 사용
일반적으로 네트워크 문제가 단시간내에 해결되지 않는다면 지수적 백오프를 사용하는게 바람직하다. 지나치게 공격적인 ㅐㅈ시도 전략은 리소스를 낭비하고 과부하를 유도한다.
에러코드를 반환할 때는 Retry-After 헤더를 같이 붙여 보내는게 좋다.
멱등성
최대 한 번 실행을 보장하기 위한 핵심 개념.
멱등성은 수학 또는 컴퓨터 과학적 연산이 가질 수 있는 한가지 속성으로, 연산을 여러 번 실행하여도 최초 실행 결과가 그대로 보존되는 특성
API관점에서 같은 API를 여러번 호출해도 동일한 결과가 나온다는 뜻.
HTTP 헤더에 <멱등 키 : 값>
형태로 멱등키를 추가해사용