Skip to Content
개념세션

세션

CheckoutSession은 구매자가 상호작용하는 대상입니다 — TTL이 적용된 일회용 객체이며 호스팅 체크아웃 URL을 소유합니다. 세 가지 핵심 엔티티 중 가장 가벼운 편이며, 더 무거운 역할은 OrderPaymentIntent(아래 참조)가 담당합니다.

세 엔티티 데이터 모델

CheckoutSession ← 1:1 → Order ← 1:N → PaymentIntent (구매자 영역) (카탈로그) (결제 시도별)
엔티티용도수명
CheckoutSession구매자 영역 — session_key, checkout_url, TTL 보유분 단위(기본값 30)
Order가맹점의 카탈로그 상태 — 라인 아이템, 합계, 환불영구 기록
PaymentIntent하나의 체인/자산에서 한 번의 결제 시도시간 단위. 정산되거나 만료

세션과 주문은 함께 생성됩니다(POST /b2b/v1/checkout-sessions/quick 사용). 구매자가 체크아웃 페이지에서 자산을 선택할 때마다 해당 체인에 대해 새로운 PaymentIntent가 열립니다. 체크아웃 진행 중에 자산을 변경하면 이전 인텐트는 EXPIRED로 이동하고 새 인텐트가 시작됩니다.

식별자

세션은 session_key로 식별됩니다.

cst_G-SO92J7HNWkwMHEHjD4oO1Z

URL-safe하며 프리픽스 뒤에 약 24자입니다(18개의 무작위 바이트를 base64url 인코딩한 것이며, 패딩은 없음). 호스팅 체크아웃 페이지는 https://checkout.infraio.xyz/<session_key>이며 — 세션 키는 해당 단일 체크아웃에 대한 베어러 자격 증명으로 취급해야 합니다.

라이프사이클 — CheckoutSession

상태의미
ACTIVE세션이 생성되어 열려 있는 상태. 체크아웃 URL을 사용할 수 있습니다.
COMPLETED이 세션의 PaymentIntent가 정산됨. Order는 이제 PAID(또는 과소 결제 시 PARTIAL_PAID)가 됩니다.
EXPIRED정산 없이 expires_at이 경과함. 클린업 워커가 상태를 전환하고 열려 있던 Order를 취소했습니다.
CANCELED명시적 취소 — 구매자가 “취소”를 눌렀거나 가맹점이 취소 엔드포인트를 호출한 경우입니다.

세 가지 종료 상태는 상호 배타적이며 최종 상태입니다. 동일한 Order에 대해 재시도가 필요한 경우(예: 과소 결제 후) 새 CheckoutSession을 생성할 수 있습니다.

TTL

  • 기본값: 30분(생성 시 expires_in 필드로 초 단위 설정 가능).
  • 범위: 서버 측에서 강제되는 하드 최소/최대값은 없습니다. 합리적인 값을 사용하세요 — 60초 미만은 정상적인 구매자도 시간 초과될 위험이 있고, 7일 초과는 거의 확실히 포기된 토큰에 용량을 묶어 둡니다. 구매자의 예상 결정 시간에 맞는 값을 선택하세요.
  • 가맹점별 기본값: 대시보드에서 설정 가능하지만, 레거시 POST /b2b/v1/checkout-sessions(2단계) 경로만 이를 따릅니다. POST /b2b/v1/checkout-sessions/quick 경로는 expires_in이 생략된 경우 가맹점별 설정에 관계없이 항상 30분으로 폴백합니다. quick 경로에서 다른 기본값이 필요한 경우 매번 expires_in을 명시적으로 전송하세요.
  • 적용 방식: 읽기 시 lazy 평가 + 주기적인 클린업 워커. expires_at이 지난 세션은 상태 필드가 아직 업데이트되지 않았더라도 EXPIRED로 취급되므로 만료 시점에 API로 상태를 읽는 것에 의존하지 마세요.

과소 결제

구매자가 세션 금액보다 적게 송금하면, PaymentIntent는 부분 금액에 대해 여전히 정산되고 Order는 PARTIAL_PAID로 전이합니다. CheckoutSession은 COMPLETED로 이동하므로(PaymentIntent 하나가 정산됨) 더 이상 재사용할 수 없습니다.

부족분을 전체 결제로 수용하려면 주문을 PAID로 해결할 수 있습니다 — 개념 → Order 참조(현재 공개 resolve API는 없습니다. 셀프 서비스 흐름에는 부족분을 대신 수집하는 방식이 권장됩니다). 부족분을 수집하려면 동일한 Order에 대해 잔여 금액으로 CheckoutSession을 생성하세요.

초과 결제

구매자가 세션 금액보다 많이 송금하는 경우(수동 송금에서 드물게 발생), 온체인 스캐너가 24시간 이내에 초과 결제를 감지하고 가맹점 알림을 발생시킵니다. 자동 환불은 없습니다 — 환불 API 또는 대시보드를 통해 수동으로 발행하세요.

잘못된 자산 결제

입금 주소는 (session, chain, asset) 튜플별로 생성됩니다. 구매자가 잘못된 자산을 해당 주소로 송금하면 온체인 매처가 이를 인식하지 못하고 PaymentIntent는 TTL 만료까지 열린 상태로 유지됩니다. 자금을 복구할 수는 있지만 이는 지원 플로우이며 자동이 아닙니다 — 구매자에게 체크아웃 페이지에 표시된 정확한 자산을 송금하도록 안내하세요.

생성 시 멱등성

POST /b2b/v1/checkout-sessions/quick는 요청 본문에 idempotency_key 필드를 허용합니다(주의: HTTP 헤더가 아닌 본문 필드). 클라이언트에 자연스러운 키가 없는 경우 UUID를 자동 생성하세요 — SDK는 기본적으로 이를 수행합니다.

이 키는 CheckoutSession이 아닌 Order에 대해 중복을 제거합니다. 동일한 키로 재시도하면 /quick원래 주문(order_id는 안정적)을 반환하지만 새로운 CheckoutSession을 발급합니다 — 매번 새 session_keycheckout_url이 생성됩니다. 이는 의도된 동작입니다. 하나의 Order가 여러 체크아웃 시도를 지원할 수 있으므로(Order 참조), 재시도된 /quick은 주문을 중복 생성하지 않고 구매자에게 깨끗한 세션을 제공합니다.

Stripe 스타일의 멱등성 레이어와 두 가지 동작이 다르므로 주의가 필요합니다.

  • 본문은 해시되거나 비교되지 않습니다. 동일한 키를 다른 본문과 함께 재사용해도 409를 반환하지 않습니다 — 서버는 해당 키에 저장된 주문을 조용히 반환하고 새 본문을 무시합니다. 따라서 idempotency_key는 단일 논리적 주문에 대한 일회성 토큰으로 다루고, 서로 다른 장바구니에 절대 재사용하지 마세요.
  • Order만 중복 제거되며, 세션은 그렇지 않습니다. 동일한 체크아웃 URL을 다시 받으려면 첫 응답의 session_key / checkout_url을 영구 저장하세요 — /quick을 다시 호출해도 이전 URL이 반환되지 않습니다. 하나의 주문에 대해 발급된 모든 세션을 열거하려면 GET /b2b/v1/checkout-sessions/by-order/:order_id를 사용하세요.

API 엔드포인트

메서드경로비고
POST/b2b/v1/checkout-sessions/quick한 번의 호출로 주문 + 세션 생성
POST/b2b/v1/checkout-sessions기존 주문에 대해 세션 생성
GET/b2b/v1/checkout-sessions/by-order/:order_id한 주문의 모든 세션 목록(재시도 이력)
GET/checkout/:session_key공개 — 구매자 브라우저가 호출

전체 생성 요청 본문과 서명은 빠른 시작을 참조하세요.

다음 단계

  • 개념 → Order — Order 엔티티(이행의 그라운드 트루스로 다루는 엔티티)를 다룹니다.
  • 개념 → 체인 및 자산 — 지원되는 네트워크와 체인별 파이널리티 가정을 다룹니다.
  • 웹훅 → 개요 — 각 세션 상태 전이에서 발생하는 이벤트를 다룹니다.