Sessions
CheckoutSession là thứ người mua tương tác — một object có TTL, dùng một lần, sở hữu URL checkout do hệ thống host. Đây là entity mỏng nhất trong ba entity cốt lõi; phần xử lý nặng diễn ra ở Orders và Payment Intents (xem bên dưới).
Mô hình dữ liệu ba entity
CheckoutSession ← 1:1 → Order ← 1:N → PaymentIntent
(người mua) (catalog) (mỗi lần thử trả)| Entity | Mục đích | Vòng đời |
|---|---|---|
| CheckoutSession | Hướng tới người mua — có session_key, checkout_url, TTL | Phút (mặc định 30) |
| Order | Trạng thái catalog của bạn — line items, tổng, refund | Bản ghi vĩnh viễn |
| PaymentIntent | Một lần thử trả trên một chain/asset | Giờ; được settle hoặc hết hạn |
Bạn tạo session và order cùng nhau (qua POST /b2b/v1/checkout-sessions/quick).
Mỗi khi người mua chọn một tài sản trên trang checkout, một PaymentIntent
mới được mở ứng với chain tương ứng. Nếu họ đổi tài sản giữa chừng,
intent trước đó chuyển sang EXPIRED và một intent mới bắt đầu.
Định danh
Một session được nhận diện qua session_key:
cst_G-SO92J7HNWkwMHEHjD4oO1ZURL-safe, ~24 ký tự sau prefix (18 byte ngẫu nhiên mã hóa base64url, không
padding). Trang checkout do hệ thống host là
https://checkout.infraio.xyz/<session_key> — coi session key là một
bearer credential cho đúng phiên checkout đó.
Vòng đời — CheckoutSession
| Trạng thái | Ý nghĩa |
|---|---|
ACTIVE | Session đã được tạo và mở. URL checkout có thể dùng. |
COMPLETED | Một PaymentIntent trên session này đã được settle. Order hiện là PAID (hoặc PARTIAL_PAID nếu thiếu). |
EXPIRED | expires_at đã qua mà chưa settle. Cleanup worker đã chuyển trạng thái và hủy mọi Order đang mở. |
CANCELED | Hủy tường minh — hoặc người mua bấm “cancel” hoặc bạn gọi endpoint cancel. |
Ba trạng thái cuối loại trừ lẫn nhau và là final. Có thể tạo CheckoutSession mới ứng với cùng Order nếu bạn muốn retry (ví dụ sau underpayment).
TTL
- Mặc định: 30 phút (cấu hình qua trường
expires_inkhi tạo, đơn vị giây). - Bounds: Không có ngưỡng min/max cứng phía server. Hãy dùng giá trị hợp lý — dưới 60 giây có thể khiến người mua hợp lệ bị timeout; trên 7 ngày sẽ giữ chỗ cho một token gần như chắc chắn đã bị bỏ. Chọn con số khớp với khoảng thời gian quyết định kỳ vọng của người mua.
- Mặc định theo merchant: Cấu hình được qua dashboard, nhưng chỉ
path legacy
POST /b2b/v1/checkout-sessions(hai bước) tôn trọng nó. PathPOST /b2b/v1/checkout-sessions/quickluôn fallback về 30 phút khiexpires_inbị bỏ trống, bất kể cấu hình theo merchant. Nếu cần default khác trên quick path, gửiexpires_intường minh trên mỗi lệnh gọi. - Áp dụng: Lazy khi đọc + một cleanup worker chạy định kỳ. Một
session có
expires_atđã qua sẽ được coi nhưEXPIREDngay cả khi trường state chưa được ghi, nên đừng dựa vào việc đọc trạng thái qua API ngay tại thời điểm hết hạn.
Underpayment
Nếu người mua gửi ít hơn số tiền của session, PaymentIntent vẫn được
settle với số tiền nhận được và Order chuyển sang PARTIAL_PAID.
CheckoutSession chuyển sang COMPLETED (một PaymentIntent đã settle)
nên không còn tái sử dụng được nữa.
Để chấp nhận phần thiếu như là thanh toán đủ, order có thể được resolve
sang PAID — xem Khái niệm → Orders (hiện không
có resolve API công khai; với luồng self-serve, hãy thu phần còn lại
thay vì resolve). Để thu phần còn lại, tạo một CheckoutSession
mới ứng với cùng Order với số tiền residual.
Overpayment
Nếu người mua gửi nhiều hơn số tiền của session (hiếm, nhưng xảy ra với chuyển khoản thủ công), scanner on-chain sẽ ghi nhận phần dư trong vòng 24 giờ và phát thông báo cho merchant. Không có refund tự động — phát hành thủ công qua refund API hoặc dashboard.
Thanh toán sai tài sản
Địa chỉ deposit được sinh theo tuple (session, chain, asset). Nếu
người mua gửi sai tài sản đến địa chỉ đó, matcher on-chain không nhận
ra và PaymentIntent giữ trạng thái mở cho đến khi TTL hết hạn. Chúng
tôi có thể recover số tiền nhưng đó là luồng support, không tự động —
hãy hướng dẫn người mua gửi đúng tài sản hiển thị trên trang checkout.
Idempotency khi tạo
POST /b2b/v1/checkout-sessions/quick nhận trường idempotency_key
trong body của request (chú ý: trường body, không phải HTTP header).
Tự sinh UUID cho nó nếu client của bạn không có key tự nhiên — SDK
mặc định làm việc này.
Key dedup cho Order, không phải CheckoutSession. Khi retry cùng
một key, /quick trả về order ban đầu (order_id ổn định) nhưng
phát hành một CheckoutSession mới — session_key và checkout_url
mới mỗi lần. Đó là chủ đích: một Order có thể đứng sau nhiều lần thử
checkout (xem Orders), nên một /quick được
retry sẽ giao cho người mua một session sạch mà không nhân đôi order.
Hai hành vi khác với lớp idempotency kiểu Stripe — đừng bị mắc bẫy:
- Body không được hash hay so sánh. Tái sử dụng một key với body
khác không trả về
409— server lặng lẽ trả về order đã lưu với key đó và bỏ qua body mới. Vậy nên hãy coiidempotency_keylà token dùng một lần cho một order logic duy nhất; đừng tái sử dụng qua các giỏ hàng khác nhau. - Chỉ Order được dedup, không phải session. Nếu bạn cần lấy lại
cùng checkout URL, hãy lưu
session_key/checkout_urltừ response đầu tiên — gọi/quicklại sẽ không trả về URL cũ. Để liệt kê mọi session đã được tạo cho một order, dùngGET /b2b/v1/checkout-sessions/by-order/:order_id.
API endpoints
| Method | Path | Ghi chú |
|---|---|---|
POST | /b2b/v1/checkout-sessions/quick | Tạo order + session trong một lệnh |
POST | /b2b/v1/checkout-sessions | Tạo session ứng với order đã có |
GET | /b2b/v1/checkout-sessions/by-order/:order_id | Liệt kê mọi session của một order (lịch sử retry) |
GET | /checkout/:session_key | Public — endpoint mà trình duyệt người mua truy cập |
Xem Bắt đầu nhanh để biết body request tạo đầy đủ và cách ký.
Tiếp theo
- Khái niệm → Orders — entity Order (cái bạn coi là nguồn xác thực cho fulfillment).
- Khái niệm → Chains & tài sản — network được hỗ trợ và giả định finality theo từng chain.
- Webhooks → Tổng quan — event nào được phát ở mỗi transition trạng thái session.