결제 시스템이란?
위키백과에 따르면 "금전적 가치의 이전을 통해 금융 거래를 정산하는데 사용되는 모든 시스템"이다.
1단계: 설계 범위
1. 기능 요구사항
- 대금 수신(pay-in) 흐름: 결제 시스템이 판매자를 대신하여 고객으로부터 대금을 수령한다.
- 대금 정산(pay-out) 흐름: 결제 시스템이 전 세계의 판매자에게 제품 판매 대금을 송금한다.
2. 비기능 요구사항
- 신뢰성 및 내결함성: 결제 실패는 신중하게 처리해야 한다.
- 내부 서비스와 외부서비스 간의 조정 프로세스: 시스템 간의 결제 정보가 일치하는지 비동기적으로 확인한다.
3. 개략적인 규모 추정
- 하루에 100만건의 트랜잭션을 처리해야 한다.
- 이는 초당 10건의 트랙잭션(TPS)이다.
- 일반적인 데이터베이스로 별 문제 없이 처리 가능한 양이다.
2단계: 개략적 설계안
결제 흐름은 자금의 흐름을 반영하기 위해 크게 두 단계로 세분화 된다.
- 대금 수신 흐름
- 구매자가 주문을 하면 아마존의 은행 계좌로 돈이 들어오는데, 이것이 대금 수신 흐름이다.
- 해당 돈은 아마존의 은행 계좌에 있지만 소유권이 전부 아마존에 있는 것은 아니다.
- 대금 정산 흐름
- 제품이 배송되고 나면, 계좌에 묶여 있던 판매 대금에서 수수료를 제외한 잔액이 판매자의 은행 계좌로 지급된다.
1. 대금 수신 흐름
1.1 시스템의 각 구성 요소
결제 서비스
- 사용자로부터 결제 이벤트를 수락하고 결제 프로세스를 조율한다.
- 가장 먼저 하는 일은 일반적으로 AML/CFT 와 같은 규정을 준수하는지, 자금 세탁이나 테러 자금 조달과 같은 범죄 행위의 증거가 있는지 평가하는 위험 점검이다.
- 결제 서비스는 위험 확인을 통과한 결제만 처리한다.
- 일반적으로 매우 복잡하고 고도로 전문화되어 있기 때문에 제 3자 제공업체를 이용한다.
결제 실행자
- 공급자(PSP)를 통해 결제 주문 하나를 실행한다.
- 하나의 결제 이벤트에는 여러 결제 주문이 포함될 수 있다.
결제 서비스 공급자
- PSP(Payment Service Provider)는 A계정에서 B계정으로 돈을 옮기는 역할을 담당한다.
카드 유형
- 신용 카드 업무를 처리하는 조직이다.
- 카드 생태계는 매우 복잡하다.
원장 지갑
- 결제 트랜잭션에 대한 금융 기록이다.
- 원장 시스템은 전자상거래 웹사이트의 총 수익을 계산하거나 향후 수익을 예측하는 등, 결제 후 분석에서 매우 중요한 역할을 한다.
지갑
- 판매자의 계정 잔액을 기록한다.
1.2 결제 서비스 API
POST /v1/payments
- 하나의 결제 이벤트에는 여러 결제 주문이 포함될 수 있다.
결제 이벤트 실행을 위한 API 인자
필드 | 설명 | 자료형 |
buyer_info | 구매자 정보 | json |
checkout_id | 해당 결제 이벤트를 식별하는 전역적으로 고유한 ID | string |
credit_card_info | 암호화된 신용 카드 정보 또는 결제 토큰, PSP마다 다른 값 | json |
payment_orders | 결제 주문 목록 | list |
payment_orders
- payment_order_id는 전역적으로 고유한 ID라는 점을 유의
- 결제 실행자가 타사 PSS에 결제 요청을 전송할 때, SPS는 payment_order_id를 중복 제거 ID로 사용한다.
- 멱등 키라고도 한다.
필드 | 설명 | 자료형 |
seller_account | 대금을 수령할 판매자 | string |
amount | 해당 주문으로 전송되어야 할 대금 | string |
currency | 주문에 사용된 통화 단위 | string(ISO 4217) |
payment_order_id | 해당 주문을 식별하는 전역적으로 고유한 ID | string |
GET /v1/payments/{:id}
- payment_order_id가 가리키는 단일 결제 주문의 실행 상태를 반환한다.
- 잘 알려진 일부 PSS의 API와 유사하다.
1.3 결제 서비스 데이터 모델
결제 서비스에는 결제 이벤트(payment event)와 결제 주문(payment order)의 두 개 테이블이 필요하다.
결제 시스템용 저장소 솔루션을 고를 때는 일반적으로 성능이 가장 중요한 고려사항이 아니다.
- 안정성이 검증되었는가? 다른 대형 금융 회사에서 수년동안 긍정적인 피드백을 받으며 사용된 적이 있는가?
- 모니터링 및 데이터 탐사에 필요한 도구가 풍부하게 지원되는가?
- 데이터베이스 관리자 채용 시장이 성숙했는가?
일반적으로 ACID 트랜잭션을 지원하는 전통적인 관계형 데이터베이스를 선호한다.
결제 이벤트 스키마
이름 | 자료형 |
checkout_id | string PK |
buyer_info | string |
seller_info | string |
credit_card_info | 카드 제공업체에 따라 다르다 |
is_payment_done | boolean |
결제 주문 스키마
이름 | 자료형 |
payment_order_id | string PK |
buyer_account | string |
amount | string |
currency | string |
checkout_id | string FK |
payment_order_status | string |
ledger_updated | boolean |
wallet_updated | boolean |
- checkout_id는 한 번의 결제 행위는 하나의 결제 이벤트를 만들고, 하나의 결제 이벤트에는 여러 개의 결제 주문이 포함될 수 있다.
- 구매자의 신용 카드에서 금액을 제공하기 위해 타사 PSP를 호출하면 판매자 대신 전자상거래 웹사이트의 은행 게좌에 이체가 이루어지는데, 이 프로세스를 대금 수신이라고 부른다.
- payment_order_status는 결제 주문의 실행 상태를 유지하는 열거 자료형이다.
- NOT_STARTED, EXECUTING, SUCCESS, FAILED 등이 있다.
- 순서
- payment_order_status의 초기 값은 NOT_STARTED
- 결제 서비스는 결제 실행자에 주문을 전송하면 EXECUTING로 바뀐다.
- 결제 서비스는 결제 처리자의 응답에 따라 SUCCESS, FAIL로 변경한다.
- 해당 절차가 긑나면 원장 데이터베이스의 ledger_updated 필드를 TRUE로 갱신한다.
- 동일한 checkout_id 아래의 모든 결제 주문이 성공적으로 처리되면 결제 서비스는 결제 이벤트 테이블의 is_payment_done을 TRUE로 업데이트한다.
1.4 복식부기 원장 시스템
- 원장 시스템에는 복식부기라는 아주 중요한 설계 원칙이 있다.
- 복식부기는 모든 결제 시스템에 필수 요소이며 정확한 기록을 남기는 데 핵심적 역할을 한다.
- 복식 부기 시스템에서 모든 거래 항목의 합게는 0이어야 한다.
- 누군가는 1센트를 읽으면 다른 누군가는 1센트를 가져야 한다.
- 해당 시스템을 활용하면 자금의 흐름을 시작부터 끝까지 추적할 수 있으며 결제 주기 전반에 걸쳐 일관성을 보장할 수 있다.
1.5 외부 결제 페이지
- 대부분의 기업은 복잡한 규정을 준수해야 하기 때문에 신용 카드 정보를 내부에 저장하지 않는다.
- PSP에서 제공하는 외부 신용 카드 페이지를 사용한다.
- PSP가 제공하는 외부 결제 페이지가 직접 고객 카드 정보를 수집한다는 것이다.
1.6 대금 정산 흐름
- 대금 정산 흐름의 구성 요소는 대금 수신 흐름과 아주 유사하다.
- 한 가지 차이는 PSP를 사용하여 구매자의 신용 카드에서 전자상거래 웹사이트 은행 계좌로 돈을 이체하는 대신, 정산 흐름에서는 타사 정산 서비스를 사용하여 전자상거래 웹사이트 은행 계좌에서 판매자 은행 계좌로 돈을 이체한다는 점이다.
2. 대금 정산 흐름
3단계: 상세 설계
1. PSP 연동
- 결제 시스템이 은행이나 비자 또는 마스터카드와 같은 카드 시스템에 직접 연결할 수 있다면 PSP 없이도 결제할 수 있다.
(하지만 직접 연결은 아주 특수한 경우로 한정된다.) - 대부분의 회사는 두 가지 방법 중 하나로 결제 시스템을 PSP와 연동한다
- 회사가 민감한 결제 정보를 안전하게 저장할 수 있다면 API를 통해 PSP와 연동하는 방법을 택한다.
- 복잡한 규정 및 보안 문제로 인해 민감한 결제 정보를 저장하지 않기로 결정한 경우, PSP에 안전하게 저장할 수 있도록 외부 결제 페이지를 제공한다.
- 클라이언트는 결제 주문 정보를 담아 결제 서비스를 호출한다.
- 결제 서비스는 결제 등록 요청을 PSP로 전송한다.
- 결제 주문이 정확히 한 번만 등록될 수 있도록 UUID 필드(비중복 난수)를 둔다.
- PSP는 결제 서비스에 토큰을 반환한다.
- 토큰은 등록된 결제 요청을 유일하게 식별하는 PSPS가 발급한 UUID이다.
- 결제 서비스는 토큰을 데이터베이스에 저장한다.
- 클라이언트는 PSP가 제공하는 외부 결제 페이지를 표시한다.
(외부 결제 페에지는 일반 적으로 두 가지 정보를 필요로 한다.)
- 4단계에서 받은 토큰: 해당 토큰을 사용하여 PSP의 백엔드에서 결제 요청에 대한 상세 정보를 검색한다.
- 리다이렉션 URL: 결제가 완료되면 호출될 웹 페이지 URL이다.
- 사용자는 결제 세부 정보를 PSP의 웹 페지에 입력하여 결제를 한다.
- PSP가 결제 상태를 반환한다.
- 사용자는 리다이렉션 URL 가리키는 웹 페이지로 보내진다.
- 비동기적으로 PSP는 웹훅을 통해 결제 상태와 함께 결제 서비스를 호출한다.
- 웹훅은 결제 시스템 측에서 PSP를 처음 설정할 때 등록한 URL이다.
2. 조정
- PSP나 은행 같은 외부 시스템도 비동기 통신을 선호하는데 어떻게 정확성을 보장할까?
- 관련 서비스 간 상태를 주기적으로 비교하여 일치하는지 확인하는데, 결제 시스템의 마지막 방어선으로 받아들여진다.
- PSP나 은행은 고객에게 정산 파일을 보낸다. 정산 파일에는 은행 계좌의 잔액과 하루 동안 해당 계좌에서 발생한 모든 거래 내역이 기재되어 있다. 조정 시스템은 정산 파이르이 세부정보를 읽어 원장 시스템과 비교한다.
- 조정 중에 발견된 차이는 재무팀에 의뢰하여 수동으로 고치고 불일치 문제 및 해결 방안은 세 가지 범주로 나눈다.
- 어떤 유형의 문제인지 알고 있으며 문제 해결 절차를 자동화할 수 있는 경우
- 어떤 유형의 문제인지 알지만 문제 해결 절차를 자동화할 수 없는 경우
- 분류할 수 없는 유형의 문제인 경우
3. 결제 지연 처리
- 결제 요청이 평소보다 오래 걸리게 되는 몇 가지 사례
- PSP가 해당 결제 요청의 위험성이 높다고 보고 담당자 검토를 요구하는 경우
- 신용 카드사가 구매 확인 용도로 카드 소유자의 추가 정보를 요청하는 3D 보안 인증 같은 추가 보호 장치를 요구하는 경우
- PSP는 해당 문제를 다음과 같이 처리한다.
- 결제가 대기 상태임을 알리는 상태 정보를 클라이언트에게 반환하고, 클라이언트는 이를 사용자에게 표시한다.
- PSP는 회사를 대신하여 대기 중인 결제의 진행 상황을 추적하고, 상태가 바뀌면 PSP에 등록된 웹훅을 통해 결제 서비스에 알린다.
- 결제 요청이 최종적으로 완료되면 PSP는 언급한 사전에 등록된 웹훅을 호출한다.
- 어떤 PSP는 우베훅을 통해 결제 서비스에 상태 변경을 알리는 대신, 결제 서비스로 하여금 대기 중인 결제 요청의 상태를 주기적으로 확인하도록 한다.
4. 내부 서비스 간 커뮤니케이션
내부 서비스 통신에는 동기식과 비동기식이 있다.
4.1. 동기식 통신
HTTP와 같은 동기식 통신은 소규모 시스템에서는 잘 작동하지만 규모가 커지면 단점이 분명해진다.
- 성능 저하: 관계된 서비스 가운데 하나가 발생한 성능 문제가 전체 시스템의 성능에 영향을 끼친다.
- 장애 격리 곤란: PSP 등의 서비스에 장애가 발생하면 클라이언트는 더 이상 응답을 받지 못한다.
- 높은 결합도: 요청 발신자는 수신자를 알아야만 한다.
- 낮은 확장성: 큐를 버퍼로 사용하지 않고서는 갑작스런 트래픽 증가에 대응할 수 있도록 시스템을 확장하기 어렵다
4.2. 비동기 통신
비동기 통신은 크게 두 가지 범주로 나눌 수 있다.
- 단일 수신자
- 각 요청은 하나의 수신자 또는 서비스가 처리한다.
- 복수의 구독자가 있을 수 있으나 처리된 메세지는 큐에서 바로 제거 된다.
- 다중 수신자
- 각 요청은 여러 수신자 또는 서버가 처리한다.
- 소비자가 수신한 메세지는 바로 사리지지 않아서 동일한 메세지를 여러 서비스가 받아 처리할 수 있다.
동기식 통신은 설계하기는 쉽지만 서비스의 자율성을 높이기에 적합하지 않다.
비동기 통신은 설계의 단순성과 데이터 일관성을 시스템 확장성 및 장애 감내 능력과 맞바꾼 결과다.
5. 결제 실패 처리
- 모든 결제 시스템은 실패한 결제를 적절히 처리할 수 있어야 한다.
- 안정성 및 결함 내성은 결제 시스템의 핵심적 요구 사항
5.1 결제 상태 추적
- 결제 주기의 모든 단계에서 결제 상태를 정확하게 유지하는 것은 매우 중요하다.
- 실패가 일어날 때마다 결제 거래의 현재 상태를 파악하고 재시 또는 환불이 필요한지 여부를 결정한다.
- 결제 상태는 데이터 추가만 가능한 데이터베이스 테이블에 보관한다.
5.2 재시도 큐 및 실패 메세지 큐
- 실패를 우아하게 처리하기 위해서는 재시도 큐와 실패 메세지 큐를 두는 것이 바람직하다.
- 재시도 큐: 일시적 오류 같은 재시도 간으 오류는 재시도 큐에 보낸다.
- 실패 메세지 큐: 반복적으로 처리에 실패한 메세지는 실패 메세지 큐로 보낸다.
=> 문제가 있는 메세지를 디버깅하고 격리하여 성공적으로 처리되지 않은 이유를 파악하기 위한 검사에 유용하다.
- 재시도가 가능한지 확인한다.
a. 재시도 가능한 실패는 재시도 큐로 보낸다.
b. 잘못된 입력과 같이 재시도가 불가능한 실패는 오류 내역을 데이터베이스에 저장한다. - 결제 시스템은 재시도 큐에 쌓인 이벤트를 읽어 실패한 결제를 재시도 한다.
- 결제 거래가 다시 실패하는 경우에는 다음과 같이 처리한다.
a. 재시도 횟수가 임계값 이내라면 해당 이벤트를 다시 재시도 큐로 보낸다.
b. 재시도 횟수가 임계값을 넘으면 해당 이벤트를 실패 메세지 큐에 넣는다.
=> 이런 이벤트에 대해서는 별도 조사가 필요할 수 있다.
6. 정확히 한 번 전달
- 결재 시스템에 발생 가능한 가장 심각한 문제 중 하나는 고객에게 이중으로 청구하는 것이다.
- 메세지를 정확히 한 번 전달하는 것은 매우 어렵게 느껴지짖만, 해당 문제를 두 부분으로 나누면 쉽게 해결할 수 있다.
- 최소 한 번은 실행된다.
- 최대 한 번 실행된다.
- 재시도를 통해 최소 한 번 실행을 보증하는 방법과, 멱등성 검사를 통해 최대 한 번 실행을 보증하는 방법을 알아보도록 하자.
6.1 재시도
- 재시도 메커니즘을 활용하면 어떤 결제가 최소 한 번은 실행되도록 보장 가능하다.
- 재시도 메커니즘을 도입할 때는 얼마나 간격을 두고 재시도할지 정하는 것이 중요하다.
- 즉시 재시도: 클라이언트는 즉시 요청을 다시 보낸다.
- 고정 간격: 재시도 전에 일정 시간 기다리는 방안이다.
- 증분 간격: 재시도 전에 기다리는 시간을 특정한 양만큼 점진적으로 늘려 나가는 방안이다.
- 지수적 백오프: 재시도 전에 기다리는 시간을 직전 재시도 대비 두 배씩 늘려 나가는 방안.
- 취소: 요청을 철회하는 방안.
- 모든 상황에 맞는 해결책인 적절한 재시도 전략은 없다.
- 재시도 시 발생할 수 있는 잠재적 문제는 이중 결제이다.
- 시나리오 1: 결제 시스템이 외부 결제 페이지를 통해 PSP와 연동하는 환경에서 클라이언트가 결제 버튼을 두 번 중복 클린한다.
- 시나리오 2: SPS가 결제를 성공적으로 하였으나 네트워크 오류로 인해 응답이 결제 시스템에 도달하지 못했다. 사용자가 '결제' 버튼을 다시 클릭하거나 클라이언트가 결제를 다시 시도한다.
- 이중 결제를 방지하려면 결제는 '최대 한 번' 이루어져야 한다. 다른 말로 멱등성이라고도 부른다.
6.2 멱등성
- 최대 한 번 실행을 보장하기 위한 핵심 개념이다.
- 위키백과에 따르면, '연산을 여러 번 실행하여도 최초 실행 결과가 그대로 보존되는 특성을 일컫는다"
- API 관점에서 보면 멱등성은 클라이언트가 같은 API 호출을 여러 번 반복해도 항상 동일한 결과가 나온다는 뜻이다.
- 스트라이프, 페이팔 같은 기술 회사가 UUID를 멱등 키로 권장하며 실제로 널리 쓰인다.
- 멱등성이 어떻게 이중 결제 문제 해결에 도움이 되는지 알아보자
- 시나리오 1: 고객이 '결제' 버튼을 빠르게 두 번 클리하는 경우
- 사용자가 '결제'를 클리하면 멱등 키가 HTTP 요청의 일부로 결제 시스템에 전송된다.
(전자상거래 웹사이트에서 멱등 키는 일반적으로 결제가 이루어지기 직전의 장바구니 ID다) - 결제 시스템은 두 번째 요청을 재시도로 처리하는데, 요청에 퐇마된 멱등 키를 이전에 받은 적이 있기 때문이다. 이런 경우 결제 시스템은 이전 결제 요청의 가장 최근 상태를 반환한다.
- 동일한 멱등 킬고 동시에 많은 요청을 받으면 결제 서비스는 그 가운데 하나만 처리하고 나머지에 대해서는 429 Too Many Requests 상태 코드를 반환한다.
- 사용자가 '결제'를 클리하면 멱등 키가 HTTP 요청의 일부로 결제 시스템에 전송된다.
- 시나리오 2: PSP가 결제를 성공적으로 처리했지만 네트워크 오류로 응답이 결제 시스템에 전달되지 못하여, 사용자가 '결제' 버튼을 다시 클릭하는 경우
- 결제 서비스는 PSP에 비중복 난수를 전송하고 PSP는 해당 난수에 대응되는 토큰을 반환한다. 이 난수는 결제 주문을 유일하게 식별하는 구실을 하며, 해당 토큰은 그 난수에 일대일로 대응된다.
- PSP는 이 토큰을 멱등 키로 사용하므로, 이중 결제로 판단하고 종전 실행 결과를 반환한다.
- 시나리오 1: 고객이 '결제' 버튼을 빠르게 두 번 클리하는 경우
7. 일관성
결제 실행 과정에서 상태 정보를 유지 관리하는 여러 서비스가 호출된다.
- 결제 서비스는 비중복 난수, 토큰, 결제 주문, 실행 상태 등의 결제 관련 데이터를 유지 관리한다.
- 원장은 모든 회계 데이터를 보관한다.
- 지갑은 판매자의 계정 잔액을 유지한다.
- PSP는 결제 실행 상태를 유지한다.
- 데이터는 안정성을 높이기 위해 여러 데이터베이스 사본에 복제될 수 있다.
분산 환경에서는 서비스 간 통신 실패로 데이터 불일치가 발생할 수 있다.
내부 서비스 간에 데이터 일고나성을 유지하려면 요청이 '정확히 한 번 처리'되도록 하는 보장이 아주 중요하다.
- 내부 서비스와 외부 서비스 간의 데이터 일관성을 유지하기 위해서는 일반적으로 멱등성과 조정 프레스를 활용한다.
- 외부 서비스가 멱등성을 지원하는 경우, 결제를 재시도할 때는 같은 멱등 키를 사용해야 한다.
- 외부 시스템이 항상 옳다고 가정할 수는 없으므로, 조정 절차를 생략할 수는 없다.
데이터를 다중화하는 경우에는 복제 지연으로 인해 기본 데이터베이스와 사본 데이터가 불일칠하는 일이 생길 수 있다.
- 주 데이터베이스에서만 읽기와 쓰기 연산을 처리한다.
- 설정하기는 쉽지만 규모확장성이 떨어진다는 단점이 있다.
- 모든 사본이 항상 동기화되도록 한다.
- 팩서스, 래프트 같은 합의 알고리즘을 사용하거나 합의 기반 분산 데이터베이스를 사용한다.
8. 결제 보안
사이버 공격과 카드 도난에 대응하기 위한 몇 가지 기술을 간략하게 살펴보자.
문제 | 해결책 |
요청/응답 도청 | HTTPS 사용 |
데이터 변조 | 암호화 및 무결성 강화 모니터링 |
중간자 공격 | 인증서 고정과 함께 SSL 사용 |
데이터 손실 | 여러 지역에 걸쳐 데이터베이스 복제 및 스냅숏 생성 |
분산 서비스 거부 공격 | 처리율 제한 및 방화벽 |
카드 도난 | 토큰화. 실제 카드 번호를 사용하는 대신 토큰을 저장하고 결제에 사용 |
PCI 규정 준수 | PCI DSS는 브랜드 신용 카드를 처리하는 조직을 위한 보안 표준이다. |
사기 | 주소 확인, 카드 확인번호, 사용자 행동분석 등 |
4단계: 마무리
결제 오류 처리와 보안에 대한 사항을 살펴보았지만, 언급할 가치가 있는 주제가 남아 있다.
- 모니터링: 주요 지표를 모니터링 하는 것은 모든 현대적 애플리케이션에 아주 중요하다.
- 경보: 비정상적인 상황이 발생하면 온콜 중인 개발자에게 알려 신속하게 대응할 수 있도록 하는 것이 중요하다.
- 디버깅 도구: 왜 결제가 실패하나요는 아주 흔한 질문이다. 엔지니어와 고객 지원팀이 더 쉽게 디버깅할 수 있또록 도구를 개발하는 것이 중요하다.
- 환율: 국제적인 결제 시스템을 설계할 때 환율은 중요한 고려사항이다.
- 지역: 지역마다 가용한 결제 수단이 완전히 달라질 수 있다.
- 현금 결제: 현금 결제는 인도, 브라질 등의 국가에서 매우 일반적이다.
- 구글/애플 페이 연동
'읽은 책 > [책] 가상 면접 사례로 배우는 대규모 시스템 설계 기초 2권' 카테고리의 다른 글
12장. 전자 지갑 (0) | 2024.08.28 |
---|---|
9장. S3와 유사한 객체 저장소 (0) | 2024.08.28 |
10장. 실시간 게임 순위표 (7) | 2024.08.24 |
8장. 분산 이메일 서비스 (0) | 2024.08.21 |
7장. 호텔 예약 시스템 (0) | 2024.08.18 |