Skip to content

필사 모드: 결제 시스템 설계 입문: 결제 플로우·PG·카드망·정산

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

들어가며 — 결제 버튼 뒤의 세계

사용자가 "결제하기" 버튼을 누르는 순간은 겉보기에는 1초도 안 걸립니다. 하지만 그 1초 안에서, 카드 번호는 대여섯 개의 회사와 최소 두 개의 결제 네트워크를 지나 발급 은행까지 다녀옵니다. 그리고 진짜로 돈이 계좌에서 계좌로 옮겨지는 것은 그 순간이 아니라 며칠 뒤입니다.

결제는 다른 어떤 시스템보다도 "틀리면 안 되는" 시스템입니다. 검색 결과가 한 건 틀리면 사용자는 불편할 뿐이지만, 결제가 한 건 틀리면 누군가는 돈을 잃습니다. 그래서 결제 시스템에는 정합성(consistency), 멱등성(idempotency), 감사 가능성(auditability)이 다른 무엇보다 앞섭니다.

이 글은 결제 시스템을 처음 설계하거나 이해하려는 엔지니어를 위한 지도입니다. 승인과 매입과 정산이 어떻게 다른지, 그 사이에 어떤 주체들이 끼어 있는지, 3-D Secure와 차지백은 무엇인지, 그리고 대사와 멱등성과 PCI-DSS가 왜 결제의 상수인지를 짚겠습니다. 결제에는 보안이 필수이므로, 인증과 토큰의 기초가 궁금하다면 이 사이트의 인증·보안 실습실을, 아래에서 자주 등장하는 응답 코드의 의미가 헷갈린다면 HTTP 상태 코드 도감을 함께 참고하세요.

다섯 명의 등장인물

카드 결제를 이해하는 첫걸음은 "누가 무엇을 하는가"를 아는 것입니다. 한 번의 카드 결제에는 최소 다섯 주체가 등장합니다.

  • 가맹점(merchant): 물건이나 서비스를 파는 쪽입니다. 우리가 만드는 서비스가 여기에 해당합니다.
  • PG / PSP(payment gateway / payment service provider): 가맹점과 복잡한 금융망 사이를 이어 주는 중개자입니다. Stripe, 토스페이먼츠, Adyen 같은 회사입니다. 카드 정보를 안전하게 받고, 아래의 여러 주체와 통신하는 일을 대신해 줍니다. 한국에서는 흔히 "PG사"라 부릅니다.
  • 매입사(acquirer / acquiring bank): 가맹점을 대신해 카드 대금을 "매입"하는 은행입니다. 가맹점의 정산 계좌에 돈이 들어오게 하는 쪽입니다.
  • 카드 네트워크(card network / scheme): Visa, Mastercard, 국내 카드사 망처럼 매입사와 발급사 사이에서 거래를 라우팅하는 네트워크입니다. 규칙(rule)과 수수료(interchange)를 정하는 주체이기도 합니다.
  • 발급사(issuer / issuing bank): 카드를 발급한 은행입니다. 실제로 "이 거래를 승인할지"를 결정하는 최종 권한을 가집니다. 한도, 잔액, 사기 여부를 판단하는 곳입니다.

이 다섯이 어떻게 연결되는지 그림으로 보면 이렇습니다.

  [가맹점] --카드정보--> [PG/PSP] --> [매입사]
                                  [카드 네트워크]
                                   [발급사] --승인/거절--> (역방향으로 응답이 돌아옴)

핵심 직관은 이렇습니다. 카드 네트워크는 "라우터"이고, 발급사는 "결정권자"이며, 매입사는 "가맹점의 은행", PG는 "이 모든 복잡함을 감춰 주는 어댑터"입니다. 우리가 코드에서 마주하는 것은 대개 PG의 API 하나뿐이지만, 그 뒤에는 이 사슬 전체가 매번 움직입니다.

승인 vs 매입 vs 정산 — 결제의 세 단계

결제 시스템을 이해할 때 가장 흔한 오해가 "결제 = 돈이 옮겨지는 것"이라는 생각입니다. 실제로는 세 단계가 시간을 두고 나뉩니다. 이 셋을 구분하지 못하면 결제 상태 관리가 반드시 꼬입니다.

1. 승인(authorization). "이 카드로 이 금액을 결제해도 되는가?"를 발급사에 묻고 답을 받는 단계입니다. 발급사는 한도와 잔액, 사기 신호를 확인하고 승인 또는 거절을 응답합니다. 승인이 나면 그 금액만큼 카드의 가용 한도가 **홀드(hold)**됩니다. 하지만 이 순간 돈이 실제로 빠져나간 것은 아닙니다. "예약"에 가깝습니다.

2. 매입(capture). 승인된 금액을 실제로 청구하겠다고 확정하는 단계입니다. 온라인 커머스에서는 보통 상품이 발송될 때 매입합니다. 승인과 매입을 분리하면, 재고가 없어 주문을 취소해야 할 때 매입 전에 승인만 취소(void)하면 되므로 깔끔합니다. 승인 금액보다 적게 매입(부분 매입)하는 것도 가능합니다.

3. 정산(settlement). 실제로 자금이 발급사에서 매입사로, 그리고 가맹점의 계좌로 이동하는 단계입니다. 이것은 보통 배치(batch)로, 하루 단위로 일어납니다. 그래서 사용자가 결제한 날과 가맹점이 돈을 실제로 받는 날 사이에는 며칠의 시차가 있습니다.

이 셋의 관계를 타임라인으로 보면 명확합니다.

  T+0초    승인(authorization)   -> 한도 홀드, 돈은 아직 안 움직임
  T+수시간~일  매입(capture)         -> "이 금액을 청구한다"고 확정
  T+1~3일  정산(settlement)      -> 실제 자금 이동, 가맹점 계좌 입금

간편결제나 즉시결제 상품에서는 승인과 매입이 사실상 한 번에 일어나는 것처럼 보이기도 합니다(sale/purchase 방식). 하지만 내부적으로는 여전히 이 단계들이 존재하며, 정산은 언제나 나중에 배치로 일어난다는 점은 변하지 않습니다. 결제 시스템의 상태 기계(state machine)는 바로 이 세 단계를 반영해서 설계해야 합니다.

승인 플로우를 따라가기

승인 한 건이 어떻게 흐르는지 단계별로 따라가 봅시다. 사용자가 카드 정보를 입력하고 결제를 누른 순간부터입니다.

  1) 사용자가 카드 정보 입력 -> 가맹점 프론트엔드
  2) 가맹점(또는 PG SDK)이 카드 정보를 PG로 전송 (토큰화됨)
  3) PG -> 매입사로 승인 요청
  4) 매입사 -> 카드 네트워크로 라우팅
  5) 카드 네트워크 -> 해당 발급사로 전달
  6) 발급사가 한도/잔액/사기 판단 -> 승인 또는 거절
  7) 응답이 같은 경로를 역순으로 되돌아옴
  8) 가맹점이 결과를 받아 주문 상태를 갱신

여기서 엔지니어가 특히 신경 써야 할 지점이 몇 가지 있습니다.

첫째, 이 왕복은 여러 네트워크 홉(hop)을 지나므로 느리고 실패할 수 있습니다. 타임아웃, 네트워크 단절, PG 장애가 언제든 일어납니다. 그래서 "응답을 못 받았다"는 상황을 항상 가정해야 합니다. 이것이 뒤에서 다룰 멱등성이 필요한 이유입니다.

둘째, 거절(decline)에는 여러 종류가 있습니다. 잔액 부족, 한도 초과, 카드 분실 신고, 사기 의심, 발급사 일시 장애 등입니다. 어떤 거절은 재시도하면 될 수도 있지만(soft decline), 어떤 거절은 재시도해도 소용없고 오히려 카드사의 사기 점수를 악화시킵니다(hard decline). 이 둘을 구분해 처리하는 것이 결제 성공률(conversion)에 직접 영향을 줍니다.

셋째, 응답은 대개 **승인 코드(authorization code)**와 함께 옵니다. 이 코드는 나중에 매입, 취소, 환불, 대사에서 이 거래를 식별하는 열쇠가 되므로 반드시 저장해야 합니다.

3-D Secure — 책임을 옮기는 인증

온라인 결제(카드 비대면, card-not-present)는 카드 실물이 없으므로 사기에 취약합니다. 이를 보완하기 위해 카드 네트워크가 만든 인증 프로토콜이 **3-D Secure(3DS)**입니다. Visa의 "Verified by Visa", Mastercard의 "Identity Check" 같은 브랜드로 알려져 있고, 현재 널리 쓰이는 것은 2.x 버전입니다.

3DS의 동작을 요약하면 이렇습니다. 결제 도중, 사용자를 발급사가 제어하는 인증 단계로 보냅니다. 발급사는 여기서 추가 인증(예: 앱 푸시 승인, 일회용 비밀번호, 생체인증)을 요구할 수 있습니다. 인증이 끝나면 결제 플로우로 돌아와 승인을 진행합니다.

  결제 요청
  3DS 인증 개시 -> 발급사 인증 화면(필요 시)
     │  (앱 승인 / OTP / 생체 등)
  인증 결과를 승인 요청에 실어 발급사로
  승인 또는 거절

3DS의 핵심은 기술이 아니라 **책임 전가(liability shift)**라는 상업적 개념입니다. 3DS로 인증된 거래에서 사기가 발생하면, 그 손실 책임이 가맹점에서 발급사로 넘어갑니다. 즉 3DS는 사기를 100% 막는 도구가 아니라, 사기 손실의 책임 소재를 바꾸는 장치에 가깝습니다.

3DS 2.x는 이전 버전보다 똑똑해졌습니다. 위험도가 낮아 보이는 거래는 사용자에게 아무것도 묻지 않고 통과시키고(frictionless flow), 위험해 보일 때만 추가 인증을 요구합니다(challenge flow). 유럽의 PSD2/SCA(강력한 고객 인증) 규제는 사실상 이런 인증을 의무화했습니다. 결제 시스템을 설계할 때는 3DS 인증 단계에서 사용자가 이탈하거나, 인증이 실패하거나, 콜백이 지연되는 경우를 모두 상태로 다뤄야 합니다.

차지백 — 사용자가 이의를 제기할 때

결제가 성공했다고 끝이 아닙니다. 사용자는 나중에 그 결제에 이의를 제기할 수 있습니다. 이것이 **차지백(chargeback)**입니다.

차지백은 사용자가 자신의 카드사(발급사)에 "이 거래는 잘못됐다"고 이의를 제기하는 것에서 시작합니다. 사유는 다양합니다. 물건을 안 받았다, 설명과 다르다, 내가 하지 않은 결제다(사기), 중복 청구다 등입니다. 발급사가 이의를 받아들이면, 이미 가맹점에 갔던 돈을 되돌려(reverse) 사용자에게 환급합니다.

  사용자 -> 발급사에 이의 제기
  발급사가 매입사를 통해 차지백 개시 -> 가맹점에서 대금 회수
  가맹점이 증빙 제출로 반박(representment) 가능
  카드 네트워크의 중재로 최종 판정

차지백은 환불(refund)과 다릅니다. 환불은 가맹점이 자발적으로 돈을 돌려주는 것이고, 차지백은 카드사를 통해 강제로 회수되는 것입니다. 차지백은 가맹점에게 여러 면에서 불리합니다. 대금을 잃을 뿐 아니라 별도의 차지백 수수료가 붙고, 차지백 비율이 높아지면 카드 네트워크로부터 제재를 받거나 심하면 가맹점 자격을 잃을 수 있습니다.

그래서 결제 시스템에는 차지백을 다루는 흐름이 필요합니다. 차지백 통지를 수신해 주문 상태를 갱신하고, 증빙을 모아 반박(representment)을 제출하고, 차지백 비율을 모니터링하는 것입니다. 사기로 인한 차지백을 줄이기 위해 앞서 본 3DS의 책임 전가나 사기 탐지 규칙을 함께 씁니다.

멱등성 — 이중 결제를 막는 상수

결제 플로우는 네트워크를 여러 번 왕복하므로 실패가 잦고, 실패하면 재시도하게 됩니다. 여기서 결제 특유의 무서운 문제가 생깁니다. 재시도가 이중 결제를 만들 수 있다는 것입니다.

시나리오는 이렇습니다. 가맹점이 PG에 승인 요청을 보냈습니다. PG는 정상적으로 발급사까지 다녀와 승인에 성공했습니다. 그런데 그 성공 응답이 가맹점에게 돌아오는 도중에 네트워크가 끊겼습니다. 가맹점 입장에서는 "응답을 못 받았다"뿐이라, 성공했는지 실패했는지 알 수 없습니다. 그래서 안전하게 재시도합니다. 하지만 첫 요청은 이미 성공했으므로, 재시도가 두 번째 결제를 만들어 버립니다. 사용자는 두 번 청구됩니다.

이 문제의 표준 해법이 **멱등성 키(idempotency key)**입니다. 클라이언트가 결제 요청마다 고유한 키를 생성해 함께 보내면, 서버는 같은 키의 요청을 두 번째로 받았을 때 새로 처리하는 대신 첫 번째 결과를 그대로 돌려줍니다. 그러면 몇 번을 재시도해도 결제는 한 번만 일어납니다.

  요청 1: POST /charges  Idempotency-Key: abc-123  -> 결제 실행, 결과 저장
  (응답이 유실됨)
  요청 2: POST /charges  Idempotency-Key: abc-123  -> 저장된 결과를 그대로 반환
                                                       (결제는 다시 일어나지 않음)

이 글에서는 개념만 짚고 넘어가지만, 멱등성은 결제 시스템의 핵심 중의 핵심이라 별도의 글에서 키의 수명, 중복 제거 윈도우, 유니크 제약, 결제 상태 기계까지 깊이 다룹니다. 지금은 "결제 요청은 항상 멱등해야 한다"는 원칙만 확실히 새기면 됩니다.

대사(reconciliation) — 장부를 맞추는 일

결제 시스템에서 겉으로 잘 드러나지 않지만 결정적으로 중요한 작업이 **대사(reconciliation)**입니다. 대사란 "우리 시스템이 기록한 거래"와 "PG나 카드사가 기록한 거래", 그리고 "실제로 은행 계좌에 들어온 돈"이 서로 일치하는지 맞춰 보는 일입니다.

왜 이것이 필요할까요? 결제는 여러 주체와 여러 단계를 거치므로, 어느 지점에서든 불일치가 생길 수 있기 때문입니다.

  • 우리 시스템은 승인 성공으로 기록했는데, PG 정산 내역에는 그 거래가 빠져 있을 수 있습니다.
  • 매입은 100건인데 정산 입금은 98건분만 들어올 수 있습니다(수수료, 차지백, 환불 차감 때문).
  • 앞서 본 이중 결제나 부분 매입, 환불이 얽히면 금액이 미묘하게 어긋납니다.

대사의 기본 아이디어는 이렇습니다. 우리 내부 원장의 거래 목록과 PG가 매일 제공하는 정산 파일을 서로 대조해서, 양쪽에 다 있는 것, 우리에게만 있는 것, PG에만 있는 것으로 분류합니다.

  내부 원장의 거래   vs   PG 정산 파일의 거래
        │                      │
        ▼                      ▼
     대조(match by 거래ID / 승인코드 / 금액)
        ├─ 양쪽 일치           -> OK
        ├─ 우리에만 있음       -> 정산 누락? 조사 필요
        └─ PG에만 있음         -> 우리가 놓친 거래? 조사 필요

불일치가 나오면 그것을 예외(exception)로 분류해 사람이 조사하거나 자동 규칙으로 해소합니다. 잘 만든 결제 시스템은 매일 자동으로 대사를 돌리고, 불일치 건수와 금액을 지표로 관측합니다. 대사가 깨끗하게 맞는다는 것은 "돈을 잃지 않고 있다"는 가장 직접적인 증거입니다. 그리고 대사를 가능하게 하려면, 애초에 모든 거래가 변경 불가능한(immutable) 기록으로 남아 있어야 합니다. 이 지점에서 결제는 자연스럽게 회계 원장 설계와 만납니다.

PCI-DSS — 카드 정보를 다루는 규칙

카드 번호는 극도로 민감한 정보입니다. 유출되면 곧바로 금전 피해로 이어지기 때문입니다. 그래서 카드 업계는 **PCI-DSS(Payment Card Industry Data Security Standard)**라는 보안 표준을 만들어, 카드 정보를 저장·처리·전송하는 모든 곳이 이를 따르도록 요구합니다.

PCI-DSS의 요구사항은 방대하지만, 엔지니어가 먼저 이해할 큰 원칙은 이렇습니다.

  • 저장하지 않을 수 있으면 저장하지 마라. 카드 번호 전체(PAN), 유효기간, CVC를 직접 저장하는 순간, PCI 준수의 범위(scope)가 폭발적으로 커집니다. CVC는 승인 이후에는 절대 저장하면 안 됩니다.
  • 토큰화(tokenization)를 활용하라. 실제 카드 번호 대신, 그것을 대체하는 무의미한 토큰만 우리 시스템에 두는 방식입니다. 진짜 카드 번호는 PCI 인증을 받은 PG나 별도의 볼트(vault)에만 보관됩니다. 대부분의 서비스는 이 방식으로 PCI 범위를 최소화합니다.
  • 범위를 최소화하라. 카드 데이터가 지나가는 시스템이 적을수록 감사와 관리가 쉬워집니다. PG가 제공하는 호스팅 결제창이나 클라이언트 측 토큰화(예: 카드 정보를 브라우저에서 바로 PG로 보내 토큰만 받아 오는 방식)를 쓰면, 카드 번호가 아예 우리 서버를 지나지 않게 만들 수 있습니다.

정리하면, 오늘날 대부분의 서비스에게 최선의 PCI 전략은 "카드 원본 데이터를 직접 만지지 않는 것"입니다. 토큰화와 PG의 결제창을 적극 활용해, 민감 데이터가 우리 시스템에 들어오지 않게 설계하는 것입니다. 인증과 토큰의 기초 개념이 더 궁금하다면 인증·보안 실습실에서 관련 개념을 실습해 볼 수 있습니다.

전체 그림을 다시 보기

지금까지의 조각들을 하나의 그림으로 모아 봅시다. 사용자가 결제를 누른 뒤부터 가맹점이 실제로 돈을 받고, 필요하면 환불하거나 차지백을 처리하기까지의 전체 여정입니다.

단계무슨 일이 일어나는가엔지니어의 관심사
결제 요청카드 정보 입력, 토큰화PCI 범위 최소화
3DS 인증필요 시 발급사 인증이탈/실패/콜백 지연 처리
승인발급사가 한도 확인, 한도 홀드멱등성, soft/hard decline 구분
매입청구 확정(발송 시점 등)부분 매입, 취소(void)
정산배치로 실제 자금 이동수수료/차감 반영
대사내부 기록과 정산 대조불일치 탐지, 예외 처리
환불/차지백사후 대금 반환상태 관리, 증빙, 비율 모니터링

이 표가 결제 시스템 설계의 요약본입니다. 각 행이 하나의 상태이자 하나의 실패 지점이며, 이 모두를 상태 기계로 엮어 관리하는 것이 결제 백엔드의 본질입니다.

마치며

결제 시스템은 "결제 버튼을 누르면 돈이 옮겨진다"는 단순한 겉모습 뒤에, 다섯 주체와 세 단계와 여러 실패 시나리오가 겹겹이 쌓인 세계입니다. 승인은 예약이고, 매입은 확정이며, 정산은 실제 이동입니다. 그 사이를 3DS가 인증으로 지키고, 차지백이 사후 이의를 처리하며, 멱등성이 이중 결제를 막고, 대사가 장부를 맞추고, PCI-DSS가 민감 데이터를 통제합니다.

결제 엔지니어링의 첫 번째 교훈은 "돈이 오가는 시스템은 틀릴 여지를 최소화해야 한다"는 것입니다. 그래서 멱등성과 변경 불가능한 기록, 그리고 매일의 대사가 선택이 아니라 상수가 됩니다. 이 지도를 손에 쥐고 나면, 다음으로 파고들 두 주제가 자연스럽게 보입니다. 이중 결제를 막는 멱등성의 구현, 그리고 돈을 절대 잃지 않는 이중 기입 원장의 설계입니다.

참고 자료

현재 단락 (1/116)

사용자가 "결제하기" 버튼을 누르는 순간은 겉보기에는 1초도 안 걸립니다. 하지만 그 1초 안에서, 카드 번호는 대여섯 개의 회사와 최소 두 개의 결제 네트워크를 지나 발급 은행까...

작성 글자: 0원문 글자: 7,270작성 단락: 0/116