Skip to Content
Khái niệmSessions

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 ở OrdersPayment 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ả)
EntityMục đíchVòng đời
CheckoutSessionHướng tới người mua — có session_key, checkout_url, TTLPhút (mặc định 30)
OrderTrạng thái catalog của bạn — line items, tổng, refundBản ghi vĩnh viễn
PaymentIntentMột lần thử trả trên một chain/assetGiờ; đượ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-SO92J7HNWkwMHEHjD4oO1Z

URL-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
ACTIVESession đã được tạo và mở. URL checkout có thể dùng.
COMPLETEDMột PaymentIntent trên session này đã được settle. Order hiện là PAID (hoặc PARTIAL_PAID nếu thiếu).
EXPIREDexpires_at đã qua mà chưa settle. Cleanup worker đã chuyển trạng thái và hủy mọi Order đang mở.
CANCELEDHủ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_in khi 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ó. Path POST /b2b/v1/checkout-sessions/quick luôn fallback về 30 phút khi expires_in bị bỏ trống, bất kể cấu hình theo merchant. Nếu cần default khác trên quick path, gửi expires_in tườ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ư EXPIRED ngay 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ớisession_keycheckout_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 coi idempotency_key là 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_url từ response đầu tiên — gọi /quick lại sẽ không trả về URL cũ. Để liệt kê mọi session đã được tạo cho một order, dùng GET /b2b/v1/checkout-sessions/by-order/:order_id.

API endpoints

MethodPathGhi chú
POST/b2b/v1/checkout-sessions/quickTạo order + session trong một lệnh
POST/b2b/v1/checkout-sessionsTạo session ứng với order đã có
GET/b2b/v1/checkout-sessions/by-order/:order_idLiệt kê mọi session của một order (lịch sử retry)
GET/checkout/:session_keyPublic — 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