테마 선택

📚 지식 노트

AI·하드웨어·주변기기·게임·컴퓨터 시스템·이론 컴퓨터 과학·수학·개발과 운영·보안·응용 분야에 대해 알아 두면 좋은 내용을 정리한 노트다. 위에서 분야를 고르고 아래에서 노트를 고른다.

결제 시스템

버튼 한 번으로 끝나 보이는 결제 뒤에는 고객·가맹점·PG·카드사가 주고받는 여러 단계가 있다. 카드 결제의 승인·매입·정산, PG 연동과 금액 검증, 간편결제와 토큰화, 이중 결제를 막는 멱등성과 상태 기계, 대사와 환불·차지백, 카드 데이터 보안 규정과 환율·정산 주기까지를 개발자 쪽에서 정리한다.

목차 (16)
  1. 결제에 등장하는 주체
  2. 카드 결제 흐름: 승인 → 매입 → 정산
  3. 승인과 매입의 차이
  4. 수수료 구조
  5. PG 연동 흐름과 금액 검증
  6. 간편결제와 토큰화
  7. 계좌이체·가상계좌·휴대폰 결제
  8. 정합성: 이중 결제, 멱등성, 웹훅
  9. 대사(reconciliation)
  10. 환불과 부분 환불
  11. 차지백
  12. 보안과 규정
  13. 해외 결제와 환율
  14. 정산 주기
  15. 흔한 오해
  16. 핵심 자료

이 노트는 결제가 어떻게 흘러가는지와 그 흐름을 코드로 받을 때 무엇을 지켜야 하는지를 다룬다. 수수료율·환율·정산 주기처럼 계약마다 다른 값은 원리를 보이려는 가상의 예이고, 실제 값은 쓰는 PG·카드사 계약서와 문서를 따른다. 법령은 2026년 10월에 국가법령정보센터에서 연 조문 기준이다. 결제 원장과 정확성이 중요한 시스템의 설계는 시스템 설계 › 정확성이 중요한 시스템, 멱등한 API 설계는 API 설계 노트에 있다.

결제에 등장하는 주체

카드 한 번 긁는 데에도 여러 회사가 끼어 있다. 고객(카드 회원)은 카드를 내밀고, 가맹점은 카드사와 계약을 맺고 카드를 받는 판매자다. 온라인에서는 가맹점이 카드사마다 따로 계약하는 대신 PG(결제대행)에 맡긴다. 여신전문금융업법은 이런 곳을 "신용카드등에 의한 거래를 대행하는 자", 곧 결제대행업체로 정의하고 가맹점의 하나로 본다(제2조 제5호 나목). 오프라인 단말기와 카드사 사이에서 승인·매입 전문을 실어 나르는 곳은 VAN(부가통신업자)이다. 법은 이를 단말기 설치, 조회·승인, 매출전표 매입·자금정산을 위한 전기통신서비스로 정의한다(제2조 제8호의2).

카드 쪽에는 카드를 내주고 고객에게 청구하는 발급사(issuer), 가맹점의 매출을 사들여 대금을 주는 매입사(acquirer), 그리고 Visa·Mastercard처럼 서로 다른 나라의 발급사와 매입사를 잇는 카드 네트워크가 있다. 국내 카드 거래에서는 카드사 한 곳이 이 일을 함께 하는 경우가 많다. 법이 정한 신용카드업 자체가 카드 발행·관리, 대금 결제, 가맹점 모집·관리를 아우른다(제2조 제2호).

카드 결제의 주체: 고객, 가맹점, PG, VAN이 승인 요청을 넘기고, 매입사와 카드 네트워크를 거쳐 발급사가 승인한다. 대금은 거꾸로 발급사에서 가맹점으로 온다고객(카드 회원)카드로 산다가맹점물건·서비스를 판다PG(결제대행)온라인 결제를 대신 처리VAN(부가통신)승인·매입 전문을 중계발급사(카드사)카드를 내주고 청구카드 네트워크해외 브랜드 결제망매입사가맹점 매출을 사들임승인·매입해외 카드승인 요청대금 청구(결제일)승인 요청은 화살표 방향으로 가고, 대금은 발급사 → 매입사 → (PG) → 가맹점으로 거꾸로 온다.국내 카드는 카드사 한 곳이 발급과 가맹점 매입을 함께 맡아 네트워크 칸을 건너뛰기도 한다.
카드 결제의 주체. 승인 요청은 오른쪽·아래로 흐르고, 대금은 발급사에서 가맹점으로 거꾸로 흐른다. 고객은 결제일에 발급사에 갚는다.

카드 결제 흐름: 승인 → 매입 → 정산

카드 결제는 세 단계로 나뉜다. 승인(authorization)은 카드사에 "이 카드로 이 금액을 쓸 수 있는가"를 묻고, 된다면 그만큼 한도를 잡아 두는 일이다. 이 순간 돈은 움직이지 않는다. 매입(capture, 국내에서는 매출 전표 매입)은 가맹점이 승인된 거래를 카드사에 청구해 "이 매출을 사 달라"고 하는 일이고, 매입이 확정돼야 카드사가 대금을 줄 의무가 생긴다. 정산(settlement)은 카드사가 수수료를 뗀 돈을 가맹점(또는 PG) 계좌로 보내는 일이다. 예를 들어 50,000원 결제에 가상의 수수료율 2.00%면 가맹점은 49,000원을 받는다.

카드 결제 시퀀스: 승인 요청, 승인 번호, 매입 요청, 매입 확정, 수수료를 뗀 정산, 결제일 대금 납부고객가맹점PG · VAN카드사① 승인 요청 50,000원 — 가맹점·PG를 거쳐 간다② 승인 번호 — 한도에서 50,000원을 잡아 둔다③ 매입 요청 — 승인 건을 모아 매출로 청구④ 매입 확정 — 다음 청구서에 50,000원⑤ 정산 49,000원 입금 (수수료 1,000원 뗌)⑥ 결제일 — 고객이 50,000원을 갚는다
1 / 6

고객이 카드로 50,000원을 결제한다. 요청은 가맹점과 PG(또는 VAN)를 거쳐 카드사에 닿고, 카드사는 카드가 유효한지, 한도가 남았는지, 이상 거래인지 본다.

카드 결제의 세 단계. 승인은 '쓸 수 있는가'를 묻고 한도를 잡아 두는 일, 매입은 그 매출을 카드사가 사들이는 일, 정산은 수수료를 뗀 돈이 가맹점 계좌로 들어오는 일이다.

단계가 나뉘어 있어서 시점도 어긋난다. 고객 카드 앱에는 승인 직후 "승인" 내역이 뜨지만 청구는 매입 뒤에 잡히고, 가맹점 통장에 돈이 들어오는 날은 정산 주기에 따라 며칠 뒤다. 결제 시스템이 "결제 완료" 하나로 끝나지 않고 상태를 여럿 갖는 이유다.

승인과 매입의 차이

대부분의 온라인 결제는 승인과 매입이 거의 동시에 일어나지만, 둘을 일부러 떼어 놓는 경우가 있다. 호텔은 체크인 때 숙박비를 먼저 승인해 두고(가승인, 홀드) 퇴실 때 실제 금액만 매입한다. 120,000원을 승인하고 95,000원만 매입하면 남은 25,000원은 해제된다. 렌터카·주유·주문 제작처럼 최종 금액을 나중에 아는 곳도 같은 방식을 쓴다.

승인 120,000원 가운데 95,000원만 매입하고 25,000원은 해제되는 막대와, 승인 뒤 매입까지 남은 기간을 보여 주는 시간 축승인(홀드)120,000원 — 한도만 묶이고 돈은 안 움직인다매입95,000원 청구25,000원 해제시간0일1일2일3일4일5일6일7일체크인: 승인퇴실: 매입승인 만료(예: 7일)매입 전 취소 = 승인 취소: 홀드만 풀리고 매출이 생기지 않는다.매입 뒤 취소 = 환불: 이미 생긴 매출을 되돌리는 새 거래다.기한 안에 매입하지 않으면 승인은 저절로 풀린다. 기한은 카드 브랜드·거래 종류마다 다르다.
승인 120,000원 가운데 95,000원만 매입하는 부분 매입. 승인은 기한이 있어 그 안에 매입하지 않으면 저절로 풀린다.
  • 승인에는 기한이 있다. Stripe 문서 기준으로 온라인 카드 승인은 대개 7일 동안 유지되고, 카드 브랜드와 고객이 직접 결제했는지(CIT)·가맹점이 걸었는지(MIT)에 따라 더 짧기도 하다. 기한을 넘기면 홀드가 풀리고 결제는 취소 상태가 된다.
  • 취소와 환불은 다르다. 매입 전이면 승인을 취소하면 끝이라 매출이 생기지 않는다. 매입 뒤라면 이미 생긴 매출을 되돌리는 환불이 되고, 처리 수수료가 돌아오지 않을 수 있다.
  • 부분 취소는 남은 승인을 푸는 일이다. 일부만 매입하면 차액은 자동으로 해제된다. 대부분 한 승인에 매입은 한 번이라, 나머지를 나중에 다시 매입하려면 새 결제를 만들어야 한다.

수수료 구조

카드 결제의 비용은 대부분 가맹점이 내는 가맹점 수수료다. 결제 금액의 일정 비율을 정산 때 떼고, 그 안에서 카드사(신용을 빌려주고 부정 사용 위험과 포인트 비용을 지는 대가), 해외 카드라면 카드 네트워크, 그리고 중계를 맡은 VAN이나 결제창·정산을 대신하는 PG가 몫을 나눈다. PG를 쓰는 온라인 가맹점은 보통 PG와 맺은 하나의 수수료율만 보지만, 그 안에 이런 몫이 들어 있다.

결제 금액 50,000원에서 가맹점 수수료를 떼고 가맹점이 받는 금액, 그리고 수수료를 나눠 갖는 카드사, 카드 네트워크, VAN과 PG고객이 낸 돈 50,000원가맹점이 받는 돈 49,000원수수료 1,000원(가상의 2.00%, 칸 크기는 개념도)카드사(발급·매입)신용 공여·부정 사용 위험·포인트네트워크해외 브랜드 카드일 때VAN·PG중계·결제창·정산 대행수수료율은 카드사와 가맹점의 계약(업종·매출 규모 등)으로 정해지고, 한국에서는 영세·중소 가맹점에금융위원회가 정하는 우대수수료율을 적용해야 한다(여신전문금융업법 제18조의3).
50,000원 결제에서 가상의 수수료 2.00%(1,000원)를 떼는 모습. 수수료 칸 안의 나눔은 구조를 보이는 개념도이고 실제 비율이 아니다.

한국에서는 법이 수수료를 직접 다룬다. 여신전문금융업법 제18조의3은 카드사가 가맹점 수수료율을 공정하고 합리적으로 정하고 부당하게 차별하지 말 것, 그리고 대통령령으로 정한 규모 이하의 영세한 중소 가맹점에는 금융위원회가 정하는 우대수수료율을 적용할 것을 정한다. 거꾸로 큰 가맹점이 우월적 지위로 부당하게 낮은 수수료율이나 보상금을 요구하는 것도 금지한다. 제19조는 가맹점이 수수료를 카드 회원에게 떠넘기는 것을 금지한다. 구체적인 우대 구간과 비율은 시행령과 금융위원회 고시로 정해지고 바뀌므로, 이 노트에 숫자를 적지 않는다.

PG 연동 흐름과 금액 검증

온라인 결제를 붙일 때 개발자가 직접 다루는 것은 PG와의 연동이다. 토스페이먼츠의 해설처럼 많은 PG가 결제를 요청 → 인증 → 승인으로 나눈다. 결제창에서 고객이 카드를 고르고 인증까지 마치면, PG는 브라우저를 가맹점의 성공 URL로 돌려보내며 결제 키·주문 번호·금액을 넘긴다. 그다음 가맹점 서버가 승인 API를 불러야 결제가 끝난다. 인증과 승인을 나누면 가맹점 서버가 결과를 직접 받아 승인하므로 "PG는 완료, 가맹점은 실패" 같은 어긋남이 줄어든다.

PG 연동 시퀀스: 주문 저장, 결제창, 카드 인증, 성공 URL 리다이렉트, 서버의 금액 검증, 승인 API 호출, 승인 결과 확인브라우저가맹점 서버PG카드사① 주문 저장: orderId, 금액 50,000원② 결제창 열기: orderId, 금액, 상품명③ 인증: 카드·본인 확인④ 성공 URL로 돌려보냄: paymentKey, orderId, amount⑤ 금액 검증: 저장한 50,000원 = 돌아온 값?⑥ 승인 API 호출: paymentKey, orderId, amount + 비밀 키⑦ 승인 결과 — 상태·금액을 다시 확인하고 주문 완료
1 / 7

결제창을 열기 전에 서버가 주문 번호와 최종 금액(쿠폰·적립금을 반영한 50,000원)을 먼저 저장한다. 나중에 맞춰 볼 기준값이다.

PG 결제 연동의 흐름. 인증과 승인을 나누고, 승인 전에 서버가 금액을 직접 맞춰 본다. 브라우저에서 온 값은 믿지 않는다는 것이 핵심이다.
  • 브라우저가 준 값은 믿지 않는다. 금액은 결제창을 열기 전에 서버에 저장한 주문 금액과 비교한다. 다르면 승인을 부르지 않는다. 상품 가격·할인을 클라이언트가 계산해 보내게 하면 안 되는 것과 같은 원리다.
  • 비밀 키는 서버에만 둔다. 승인 API를 부르는 키가 프런트엔드 코드나 앱에 들어가면 누구나 승인·취소를 부를 수 있다. 환경 변수나 비밀 관리 도구에 두고, 결제 화면에는 공개용 키만 쓴다.
  • 기한이 있다. 토스페이먼츠 문서 기준으로 결제창에서 30분 안에 인증하지 않거나 인증 뒤 10분 안에 승인 API를 부르지 않으면 결제가 만료된다. 승인 요청이 늦어지는 경로(큰 트랜잭션, 외부 호출)를 앞에 두지 않는다.
  • 응답을 못 받으면 상태를 묻는다. 승인 요청이 타임아웃 나면 "실패"로 단정하지 않는다. 실제로는 승인됐을 수 있으니 같은 멱등성 키로 재시도하거나 조회 API로 확인한다(아래 정합성 절).

간편결제와 토큰화

간편결제는 카드나 계좌를 한 번 등록해 두고 비밀번호·생체 인증만으로 결제하는 방식이다. 그 바탕이 토큰화다. EMVCo의 결제 토큰은 카드 번호(PAN)를 고유한 대체 값으로 바꾸고, 그 토큰을 특정 가맹점·기기·결제 상황 밖에서는 쓸 수 없게 묶는다. 휴대폰 지갑에 넣은 카드도 기기 전용 토큰으로 결제한다. 토큰이 새어 나가도 다른 곳에서 결제에 쓸 수 없고, 토큰만 폐기하면 되니 카드를 다시 발급할 필요가 없다.

카드 번호를 처음 한 번 등록하면 토큰이 발급되고, 가맹점은 토큰만 저장하며, 결제 때 토큰 서비스가 실제 카드 번호로 되돌린다. 새어 나간 토큰은 다른 가맹점에서 쓸 수 없다처음 한 번 등록카드 번호를 토큰서비스에 넘긴다토큰 발급tok_8f3a…(이 가맹점 전용)가맹점·앱 저장토큰과 끝 4자리,유효기간만 둔다결제 때토큰으로 요청 →망 안에서 PAN 복원토큰이 새어 나가면다른 가맹점·기기에서는 거절된다.토큰만 폐기하면 카드는 그대로 쓴다.카드 번호가 새어 나가면어디서든 결제에 쓰일 수 있다.카드를 재발급해야 한다.
토큰화. 카드 번호는 처음 등록할 때 한 번만 토큰 서비스로 가고, 가맹점과 앱은 토큰과 끝 4자리 같은 표시용 정보만 갖는다.

가맹점이 정기 결제나 원클릭 결제를 만들 때도 같은 원리를 쓴다. PG에 카드를 등록하면 PG가 빌링 키(카드 토큰)를 주고, 가맹점은 다음 결제부터 그 키로 결제를 요청한다. 가맹점 데이터베이스에는 카드 번호가 아니라 키만 남는다.

계좌이체·가상계좌·휴대폰 결제

카드 말고도 수단마다 돈의 길과 확정 시점이 다르다. 계좌이체는 고객 계좌에서 바로 출금된다. 가상계좌는 주문마다 입금 전용 계좌 번호를 내주고 고객이 직접 송금하는 방식이라, 주문 시점에는 결제가 끝나지 않고 "입금 대기"로 남는다. 입금 사실은 PG가 웹훅으로 알려 주므로 웹훅을 받지 않으면 주문을 확정할 수 없다. 토스페이먼츠 문서에는 송금 한도 초과 같은 입금 오류가 나면 상태가 완료에서 입금 대기로 되돌아가는 경우도 적혀 있다. 휴대폰 결제는 통신 요금에 합쳐 청구되고, 가맹점 정산은 통신사가 요금을 거둔(수납) 뒤에 이뤄지는 방식이 있다.

결제 수단별 돈의 길, 결제가 확정되는 시점, 특징을 비교한 표. 가상계좌만 고객이 입금해야 확정된다수단돈이 오는 길확정특징카드카드사 신용(외상)승인 즉시매입·정산은 나중간편결제등록한 카드·계좌·충전금승인 즉시토큰으로 기존 수단을 부름계좌이체고객 계좌 → 가맹점(PG)출금 즉시본인 계좌 인증 필요가상계좌고객이 직접 입금입금될 때입금 대기 상태 · 웹훅 필수휴대폰 결제통신 요금에 합산승인 즉시통신사 수납 뒤 정산
결제 수단별 비교. 가상계좌만 고객의 다음 행동(입금)이 있어야 확정되므로 기다림과 만료를 상태로 다뤄야 한다.

정합성: 이중 결제, 멱등성, 웹훅

결제 코드에서 가장 무서운 버그는 돈이 두 번 빠지거나, 빠졌는데 주문이 없는 것이다. 원인은 대개 네트워크다. 요청은 성공했는데 응답이 끊기면 클라이언트는 결과를 모른 채 다시 보내고, 서버는 새 요청으로 처리해 버린다. 이를 막는 것이 멱등성 키다. 클라이언트가 요청마다 충분히 무작위한 키(UUID 등)를 붙이면, 서버는 같은 키의 재시도를 알아보고 저장해 둔 첫 응답을 그대로 돌려준다. Stripe는 첫 요청의 상태 코드와 본문을 저장하고, 같은 키에 파라미터가 다르면 오류를 내며, 키는 최소 24시간 뒤에 지울 수 있다고 설명한다. 키에 이메일 같은 개인정보를 쓰지 말라는 주의도 있다.

요청 다섯 개의 표: 키 없는 재시도는 결제를 두 번 만들고, 같은 키의 재시도는 저장된 응답을 돌려주며, 같은 키에 다른 금액은 거부된다#멱등성 키주문금액서버 판단건수1(없음)A-1730,000원새 결제1건2(없음)A-1730,000원새 결제(중복!)2건3b9e1-4cB-4230,000원새 결제3건4b9e1-4cB-4230,000원저장된 응답 재사용3건5b9e1-4cB-423,000원거부: 내용이 다름3건키는 요청 내용과 응답을 함께 저장하고 일정 시간(예: 24시간) 뒤 지운다. 개인정보를 키로 쓰지 않는다.
1 / 5

주문 A-17을 30,000원 결제한다. 서버는 처리했지만 응답이 오는 길에 연결이 끊겨 클라이언트는 결과를 모른다.

멱등성 키. 클라이언트가 요청마다 고유한 키를 붙이면, 서버는 같은 키의 재시도를 알아보고 결과를 한 번만 만든다. 네트워크가 끊겨도 마음 놓고 다시 보낼 수 있다.

다음은 결제 상태 기계다. 결제를 "성공/실패" 불리언으로 두지 않고 허용된 전이만 있는 상태로 둔다. 대기에서 승인으로, 승인에서 매입으로, 매입에서 정산으로 가는 것이 정상 경로이고, 취소·환불·실패는 더 나가지 않는 끝 상태다. 표에 없는 전이(이미 환불된 결제에 매입이 오는 등)는 버그나 늦게 도착한 메시지이므로 반영하지 않고 경보를 낸다. 상태를 바꿀 때는 "지금 상태가 승인일 때만 매입으로" 같은 조건부 갱신을 써서 동시에 온 두 요청이 둘 다 이기지 못하게 한다.

결제 상태 기계: 대기에서 승인, 매입, 정산으로 가고, 대기에서 실패, 승인에서 취소, 매입이나 정산에서 환불로 끝난다. 부분 환불은 상태를 바꾸지 않는다승인 성공매입정산거절만료승인 취소기한 만료전액환불부분 환불부분 환불대기승인매입정산실패취소환불점선 상자는 끝 상태다. 허용된 전이는 모두 11개이고, 그 밖의 사건은 거부하거나 보류한다.
결제 상태 기계. 부분 환불은 상태를 바꾸지 않고 환불 누적액만 늘리는 제자리 전이로 둔다. 전이 표는 payments.ts 한 곳에 있고 그림과 테스트가 함께 쓴다.

PG가 상태 변화를 알려 주는 웹훅은 "적어도 한 번" 배달이다. 받는 쪽이 제때 2xx로 답하지 않으면 다시 보낸다. 토스페이먼츠는 10초 안에 200 응답을 요구하고, 실패하면 1, 4, 16, 64, 256, 1024, 4096분 간격으로 최대 7번 다시 보낸다. Stripe는 운영 모드에서 최대 3일 동안 지수 백오프로 다시 보내고, 순서를 보장하지 않는다고 밝힌다. 그래서 웹훅 처리기는 처리한 사건 id를 기록해 중복을 버리고, 상태 기계로 순서가 뒤바뀐 사건을 걸러야 한다. 서명을 먼저 검증하지 않으면 누구나 가짜 "결제 완료"를 보낼 수 있다.

웹훅 네 개가 순서가 바뀌고 하나는 중복으로 도착한다. 처리기는 사건 id로 중복을 무시하고, 승인 상태에서 정산 사건은 전이할 수 없어 보류한다도착 순사건 id사건처리상태1번째evt_01승인 성공반영승인2번째evt_03정산전이 불가 — 보류승인3번째evt_02매입반영매입4번째evt_02매입중복 — 무시매입보류한 사건은 PG 조회 API로 최신 상태를 받아 맞추거나, 다음 재전송 때 다시 처리한다.처리기는 서명부터 검증하고, 2xx를 빨리 돌려준 뒤 무거운 일은 큐에서 한다.
웹훅 네 개가 순서가 바뀌고 하나는 중복으로 온다. 사건 id로 중복을 무시하고, 아직 매입 전인데 온 정산 사건은 보류한다.

주문 데이터베이스와 PG처럼 서로 다른 시스템의 상태를 한 트랜잭션으로 묶을 수는 없다. 대신 결제 의도를 먼저 기록하고(아웃박스), 외부 호출 결과를 멱등하게 반영하며, 끝까지 어긋난 것은 대사로 찾는다. 분산 트랜잭션과 그 한계는 분산 시스템 노트, 이중 기입 원장과 결제 설계 사례는 시스템 설계 › 정확성이 중요한 시스템에 있다.

대사(reconciliation)

멱등성과 웹훅으로도 모든 어긋남을 막지는 못한다. 그래서 정기적으로 대사를 한다. 우리 장부(주문·결제 기록)와 PG가 주는 정산 내역을 결제 id로 맞춰 보고, 일치하지 않는 건을 사람이 보거나 자동으로 고친다. 아래 예에서 장부 5건과 정산 내역 5건을 맞추면 3건이 어긋난다. 금액이 다른 건(부분 취소가 장부에 빠졌을 수 있다), 장부에만 있는 건(정산 전이거나 승인이 실패했는데 주문만 남았다), PG에만 있는 건(결제는 됐는데 주문이 없는, 가장 급한 경우)이다.

대사 표: 결제 여섯 건 가운데 세 건이 일치하고, 한 건은 금액이 다르며, 한 건은 우리 장부에만, 한 건은 PG 정산 내역에만 있다. 아래에 합계와 수수료, 입금액이 있다결제 id우리 장부(원)PG 정산(원)판정P-10132,00032,000일치P-10215,00015,000일치P-10348,00046,000금액 불일치P-1049,900—우리 장부에만 있음P-10521,00021,000일치P-106—12,000PG에만 있음합계125,900126,000불일치 3건− 2,520수수료(가상의 2.00%)= 123,480통장에 들어와야 할 돈
대사 예. 장부 합계 125,900원, PG 매출 합계 126,000원, 수수료 2,520원를 떼면 입금액은 123,480원이다. 마지막으로 실제 통장 입금액과도 맞춰 본다.

대사는 세 겹으로 한다. 주문과 PG 거래 내역, PG 거래 내역과 정산 명세(수수료 포함), 정산 명세와 실제 은행 입금이다. 금액은 부동소수점이 아니라 정수(원, 센트 같은 최소 단위)로 다뤄야 합계가 1원도 틀리지 않는다.

환불과 부분 환불

환불은 매입된 결제를 되돌리는 새 거래다. 대부분의 PG는 한 결제에 여러 번 나눠 환불할 수 있게 하되, 누적 환불액이 원래 금액을 넘지 못하게 막는다. 환불은 원래 결제 수단으로만 돌아가고, 카드사를 거쳐 고객 명세서에 보이기까지 며칠이 걸린다(Stripe 기준 대략 5~10영업일). 결제 직후의 환불은 새 환불 대신 원거래 취소(reversal)로 처리돼 명세서에서 아예 사라지기도 한다.

매입 금액 95,000원에 대한 부분 환불 네 번. 30,000원과 20,000원은 받아들여지고, 50,000원은 남은 45,000원을 넘어 거절되며, 마지막 45,000원으로 전액이 환불된다매입 95,000원환불 1: 30,000원누적 30,000원 · 남은 금액 65,000원환불 2: 20,000원누적 50,000원 · 남은 금액 45,000원환불 3: 50,000원거절: 남은 45,000원보다 많다환불 4: 45,000원누적 95,000원 · 남은 금액 0원
매입 금액에서 나눠 환불하기. 세 번째 요청은 남은 금액보다 많아 거절된다. 환불 요청에도 멱등성 키를 붙여야 같은 환불이 두 번 나가지 않는다.
  • 환불도 실패할 수 있다. 카드가 해지됐거나 계좌가 닫혔으면 돈이 되돌아온다. 환불 상태를 웹훅으로 받아 실패하면 다른 방법을 안내한다.
  • 가상계좌·계좌이체 환불은 돌려줄 계좌 정보를 고객에게 받아야 하는 경우가 많다. 그동안의 상태를 따로 둔다.
  • 쿠폰·적립금이 섞인 주문은 부분 환불 때 어느 몫을 먼저 돌려줄지 규칙을 정해 둔다. 장부와 PG의 환불 금액이 어긋나는 흔한 원인이다.

차지백

차지백(chargeback, 분쟁)은 고객이 가맹점이 아니라 발급사에 결제에 이의를 제기하는 것이다. 물건을 받지 못했거나, 카드를 도용당했거나, 취소했는데 청구됐다는 이유다. Stripe 문서에 따르면 발급사가 카드 네트워크에 분쟁을 올리면 결제가 즉시 되돌려지고, 대금과 네트워크 분쟁 수수료가 매입 쪽을 거쳐 가맹점 잔액에서 빠진다. 가맹점은 배송 기록·이용 로그 같은 증빙을 내서 다투거나 받아들일 수 있고, 판정은 발급사와 네트워크 규칙이 한다.

차지백 흐름: 고객의 이의 제기, 발급사의 분쟁 등록과 대금 회수, 가맹점 잔액 차감, 가맹점의 증빙 제출, 발급사와 네트워크의 판정고객발급사에 이의 제기받지 못함·도용 등발급사네트워크에 분쟁 등록대금을 되돌림매입사 · PG가맹점 잔액에서 차감대금 + 분쟁 수수료가맹점증빙 제출 또는 수락배송 기록·로그 등발급사 · 네트워크판정이기면 대금이 돌아온다환불과 다른 점가맹점이 아니라 발급사가시작하고, 돈이 먼저 빠진다.수수료가 붙고, 비율이 높으면네트워크 감시 대상이 된다.
차지백 흐름. 환불은 가맹점이 시작하지만 차지백은 발급사가 시작하고, 돈이 먼저 빠진 뒤에 다툰다.

개발 쪽에서 할 일은 증빙을 남기는 것이다. 주문·배송·로그인 기록을 결제 id와 이어 두고, 분쟁 웹훅을 받으면 해당 주문의 상태를 바꾸고 담당자에게 알린다. 고객이 쉽게 환불을 요청할 수 있으면 분쟁까지 가는 일이 줄고, 분쟁 비율이 높으면 네트워크의 감시 프로그램 대상이 될 수 있다.

보안과 규정

카드 데이터를 다루는 곳이 지키는 국제 기준이 PCI DSS다. 결제 계정 데이터를 보호하기 위한 기술·운영 요구사항의 기준선이고, 지금 유효한 판은 2024년 6월 11일에 나온 v4.0.1이다. v4.0은 2024년 12월 31일에 은퇴했고, v4.0에서 미래 시행으로 둔 요구사항은 2025년 3월 31일부터 적용됐다(PCI SSC 블로그). 가장 실무적인 원칙은 카드 번호를 직접 받지도, 저장하지도 않는 것이다. PG 결제창·SDK가 카드 정보를 받게 하면 카드 번호가 우리 서버를 지나지 않아 지켜야 할 범위가 크게 준다. 카드 뒷면의 보안 코드는 승인 뒤에는 암호화하든 고객이 동의하든 보관할 수 없다(PCI SSC FAQ).

가맹점이 저장해도 되는 결제 데이터(결제 키, 끝 4자리, 주문 정보, 승인 번호)와 저장하면 안 되는 데이터(카드 번호 전체, 보안 코드, PIN과 트랙 데이터, 로그 속 카드 정보)가맹점이 둬도 되는 것✓ PG가 준 결제 키·토큰✓ 카드 끝 4자리 · 브랜드✓ 주문 번호 · 금액 · 상태✓ 승인 번호 · 결제 시각두지 않는 것✗ 카드 번호 전체(PAN)✗ 카드 뒷면 보안 코드(CVC)✗ PIN · 마그네틱 · 칩 데이터✗ 로그·오류 추적에 찍힌 카드 정보보안 코드는 승인 뒤에는 암호화해도, 고객이 동의해도 보관할 수 없다(PCI SSC FAQ).카드 번호는 PG 결제창이 받게 해 우리 서버를 지나지 않게 한다.
가맹점 서버에 남겨도 되는 결제 데이터와 남기지 않는 데이터. 로그·오류 추적 도구에 요청 본문을 통째로 찍다가 카드 정보가 남는 일이 흔하다.

한국 법령도 결제에 직접 닿는다. 여신전문금융업법 제19조는 가맹점이 카드 결제를 이유로 결제를 거절하거나 카드 회원을 불리하게 대우하는 것, 수수료를 회원에게 부담시키는 것, 실제 매출 없이 카드 거래를 꾸미는 것을 금지한다. 전자금융거래법은 전자지급결제대행(PG) 업무를 하려면 금융위원회에 등록하게 하고(제28조 제2항), 금융회사와 전자금융업자에게 전자금융거래의 안전성 확보 의무를 지운다(제21조). 고객의 이름·연락처·주소 같은 개인정보를 다루는 원칙은 개인정보 보호 기술 노트에 있다. 이 절은 법률 조언이 아니다. 실제 서비스는 계약한 PG와 전문가에게 확인한다.

해외 결제와 환율

해외 결제에는 통화가 셋 끼어든다. 고객 카드의 통화, 결제를 표시하고 청구하는 결제 통화(presentment), 가맹점 계좌가 받는 정산 통화(settlement)다. 결제 통화와 정산 통화가 다르면 PG가 바꿔서 정산하고, 결제 통화와 고객 카드 통화가 다르면 발급사가 고객에게 해외 결제 수수료를 물릴 수 있다. API는 금액을 통화의 최소 단위 정수로 받는다. 10.99달러는 1099이고, 원·엔처럼 소수가 없는 통화는 금액 그대로다.

100달러 결제를 1달러당 1,350원에 원화로 바꾸면 135,000원, 환불 때 환율이 1,380원이면 138,000원이 되어 3,000원 차이가 난다결제 통화: USD 100.00 (API에는 최소 단위 10000)결제 때 1달러 = 1,350원135,000원환불 때 1달러 = 1,380원138,000원차이 3,000원결제 통화와 정산 통화가 다르면 누가, 언제의 환율로 바꾸는지를 먼저 정한다.고객 쪽에서는 발급사가 해외 결제 수수료를 따로 물릴 수 있다.환율 값은 원리를 보이려는 가상의 값이다.
100달러 결제를 가상의 환율 1,350원에 정산했는데 환불 때 환율이 1,380원이면 같은 100달러가 138,000원이 되어 3,000원 차이가 난다. 이 차이를 누가 지는지 정책으로 정해 둔다.

정산 주기

정산 주기는 매출일에서 며칠 뒤에 돈이 들어오는지다. 흔히 D+n으로 쓰고 n은 영업일로 센다. 토스페이먼츠의 안내를 보면 일정산·주정산·월정산처럼 주기가 여럿이고, 지급은 영업일에만 하며, 입금일 2영업일 전에 지급 금액을 확정한다. 금요일 매출을 D+2로 정산하면 주말을 건너 11월 3일 화요일에 들어온다. 현금 흐름 계획과 대사 모두 이 날짜를 기준으로 하므로, 영업일·공휴일 계산을 한 곳에 모아 테스트해 둔다.

2026년 10월 26일부터 두 주의 달력. 10월 30일 금요일 매출을 영업일 2일 뒤에 정산하면 주말을 건너 11월 3일 화요일에 입금된다월화수목금토일10/2610/2710/2810/2910/30매출일 D10/31휴무11/1휴무11/211/3입금 D+211/411/511/611/7휴무11/8휴무영업일로 세면 금요일 매출의 D+2는 11월 3일(화)이다. 달력 날짜로 세면 일요일이 된다.실제 주기는 PG·카드사 계약마다 다르고, 공휴일이 끼면 더 밀린다. 여기 D+2는 가상의 예다.
영업일 기준 D+2. 주말과 공휴일은 세지 않는다. 주기는 계약마다 다르고 여기 값은 가상의 예다.

흔한 오해

  • "승인되면 돈이 들어온 것이다" — 승인은 한도를 잡아 둔 것일 뿐이다. 매입이 돼야 매출이 되고, 정산이 돼야 통장에 들어온다. 승인만 하고 매입하지 않으면 기한이 지나 풀린다.
  • "결제창이 성공 URL로 돌아왔으니 끝났다" — 리다이렉트는 인증 결과일 뿐이다. 서버가 금액을 검증하고 승인 API를 불러 성공 응답을 받아야 결제가 끝난다.
  • "타임아웃이면 실패다" — 응답만 못 받았을 수 있다. 같은 멱등성 키로 다시 묻거나 조회 API로 확인하기 전에는 실패로 처리하지 않는다.
  • "웹훅은 한 번, 순서대로 온다" — 중복도 오고 순서도 바뀐다. 사건 id로 중복을 거르고 상태 기계로 전이를 검사한다.
  • "금액은 float로 충분하다" — 0.1 + 0.2는 정확히 0.3이 아니다. 최소 단위 정수나 십진 고정소수점 타입을 쓴다.
  • "카드 번호를 암호화해 두면 괜찮다" — 암호화한 카드 번호도 PCI DSS의 보호 대상이고 키 관리 부담이 따른다. 보안 코드는 아예 보관할 수 없다. 처음부터 토큰만 갖는 편이 낫다.

핵심 자료

요지는 직접 정리한 한 줄이다. 결제를 포함한 정확성 중심 설계 사례는 시스템 설계 노트, 멱등성 키를 받는 API 설계는 API 설계 노트에 있다.

자료종류요지링크
여신전문금융업법법령신용카드업·가맹점·결제대행업체·부가통신업(VAN)의 정의(제2조), 수수료율 차별 금지와 영세·중소 가맹점 우대수수료율(제18조의3), 가맹점 준수사항(제19조).국가법령정보센터
전자금융거래법법령전자금융거래·전자지급거래의 정의(제2조), 안전성 확보 의무(제21조), 전자지급결제대행(PG) 등 전자금융업의 등록(제28조).국가법령정보센터
PCI DSS보안 표준카드 계정 데이터를 다루는 곳이 지켜야 할 기술·운영 요구사항의 기준선. 현재 v4.0.1이다.PCI SSC
Can card verification codes be stored for card-on-file or recurring transactions?FAQ보안 코드는 승인 뒤 보관할 수 없다. 암호화해도, 고객이 동의해도 마찬가지다.PCI SSC
EMV Payment Tokenisation기술 표준카드 번호를 특정 가맹점·기기·결제 상황에서만 쓸 수 있는 토큰으로 바꾸는 방식.EMVCo
결제 요청, 인증, 승인… 이게 다 뭔가요?PG 해설요청·인증·승인을 나누는 이유와, 승인 전에 저장해 둔 금액으로 검증하는 흐름.토스페이먼츠
웹훅 연결하기PG 문서10초 안에 200 응답, 실패하면 1·4·16·64·256·1024·4096분 간격으로 최대 7번 재전송.토스페이먼츠
Idempotent requestsAPI 문서멱등성 키로 재시도를 안전하게 만든다. 첫 응답을 저장하고, 같은 키에 다른 파라미터가 오면 오류를 낸다.Stripe
Receive Stripe events in your webhook endpointAPI 문서서명 검증, 빠른 2xx 응답, 중복·순서 뒤바뀜 대비, 최대 3일 재전송.Stripe
Place a hold on a payment methodAPI 문서승인과 매입을 나누는 수동 매입, 승인 유효 기간, 부분 매입 시 남은 금액 해제.Stripe
DisputesAPI 문서차지백이 시작되면 대금과 분쟁 수수료가 먼저 빠지고, 가맹점은 증빙으로 다툰다.Stripe