Skip to Content
Khái niệmSessions
View as Markdown

Sessions

CheckoutSession là thứ người mua tương tác: một object có giới hạn thời gian, dùng một lần, sở hữu URL trang thanh toán dựng sẵn. Đây là entity nhẹ nhất trong ba entity cốt lõi. Phần lớn logic của bạn làm việc với 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ả)
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, khoảng 24 ký tự sau prefix. Trang thanh toán dựng sẵn 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. Mọi Order đang mở đều bị hủy.
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 tối thiểu hay tối đa nào được enforce. Hãy chọn giá trị khớp với khoảng thời gian quyết định kỳ vọng của người mua: dưới 60 giây có thể khiến người mua hợp lệ bị timeout, và một session mở quá 7 ngày gần như chắc chắn đã bị bỏ.
  • Mặc định theo merchant: Bạn có thể đặt default trong dashboard, nhưng chỉ POST /b2b/v1/checkout-sessions (hai bước) tôn trọng nó. POST /b2b/v1/checkout-sessions/quick dùng 30 phút khi expires_in bị bỏ trống, bất kể cài đặt trong dashboard. Để dùng default khác với /quick, gửi expires_in trên mỗi lệnh gọi.
  • Áp dụng: Một session có expires_at đã qua sẽ được coi như EXPIRED ngay cả khi state của nó chưa được cập nhật, nên đừng dựa vào giá trị state 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 (resolve không có sẵn qua API; để giữ 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), InfraIO Pay sẽ phát hiện phần dư trong vòng 24 giờ và thông báo cho bạn. Phần dư không được refund tự động. Hãy refund qua refund API hoặc dashboard.

Thanh toán sai tài sản

Địa chỉ nhận tiền riêng cho từng đơn được sinh cho một session, network và asset. Nếu người mua gửi một tài sản khác đến địa chỉ đó, khoản thanh toán không được khớp và PaymentIntent giữ trạng thái mở cho đến khi session hết hạn. Support có thể giúp recover số tiền, nhưng không tự động. Hãy nói người mua gửi đúng tài sản hiển thị trên trang checkout.

Trên TRON, Solana và TON không có địa chỉ nhận tiền riêng cho từng đơn, nên điều này chỉ áp dụng cho các network EVM: người mua trả thẳng vào ví của bạn. Xem Network thanh toán thẳng vào ví.

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). Sinh một UUID nếu bạn không có key tự nhiên.

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 cần lưu ý:

  • 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)

Xem Bắt đầu nhanh để biết body request tạo đầy đủ và cách ký.

Tiếp theo