Lưu thẻ
Lưu thẻ cho các lần thanh toán sau mà không trừ tiền, bằng cách xác nhận một setup intent mà backend của bạn đã tạo. Setup intent chỉ hỗ trợ xác nhận trực tiếp — không có luồng dựa trên token cho setup intent.
Để trừ tiền thẻ đã lưu sau này, xem Lưu thẻ & thanh toán định kỳ.
Ví dụ đầy đủ
Code trình duyệt và server cho luồng này: Ví dụ tích hợp → Lưu thẻ.
Ví dụ
// client_secret đến từ backend của bạn (tạo setup intent phía server)
const { setup_intent, three_ds_payload } = await bahn.confirmCardSetup({
client_secret,
});
if (three_ds_payload) {
bahn.mount3DS(three_ds_payload.id, {
onSuccess: () => {
bahn.pollSetupIntentStatus(client_secret, {
onSuccess: (setupIntent) => redirectToSuccess(),
onFailure: (setupIntent) => redirectToFailure(),
onTimeout: () => showTimeoutMessage(),
onError: (error) => console.error("Poll error:", error.message),
});
},
onFailure: (error) => showError(error?.message),
});
} else if (setup_intent.status === "succeeded") {
redirectToSuccess();
} else if (["failed", "canceled", "requires_payment_method"].includes(setup_intent.status)) {
redirectToFailure();
} else {
// Chưa phải trạng thái cuối — poll cho đến khi đạt
bahn.pollSetupIntentStatus(client_secret, {
onSuccess: () => redirectToSuccess(),
onFailure: () => redirectToFailure(),
onTimeout: () => showTimeoutMessage(),
});
}Cách hoạt động
sequenceDiagram
participant App as Your App
participant SDK as Bahn SDK
participant CardIframe as Card Iframe
participant API as Cake API
App->>SDK: mountCard("#container", options)
App->>SDK: confirmCardSetup(payload)
SDK->>CardIframe: Delegate + merge card data
CardIframe->>API: POST /api/sdk/setup_intents/confirm
API-->>CardIframe: SetupIntent + optional base64 3DS payload
CardIframe-->>SDK: Decoded result (no card data)
SDK-->>App: { setup_intent, three_ds_payload? }
opt three_ds_payload present
App->>SDK: mount3DS(three_ds_payload.id, callbacks)
App->>SDK: pollSetupIntentStatus(client_secret, callbacks)
end
bahn.confirmCardSetup(payload)
Xác nhận một setup intent bằng thẻ trong các trường thẻ đã mount. Request được gửi từ bên trong iframe số thẻ, giống như confirmPayment().
confirmCardSetup(payload: Bahn.ConfirmCardSetupPayload): Promise<Bahn.ConfirmCardSetupResult>;Tham số — ConfirmCardSetupPayload:
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
client_secret | string | Có | Client secret của setup intent mà backend của bạn đã tạo |
payment_method.billing_details | ConfirmationTokenBillingDetails | Không | Tên, email, số điện thoại và địa chỉ của khách hàng. Dữ liệu thẻ do iframe thêm vào |
payment_method_options | Record<string, unknown> | Không | Các tùy chọn riêng của phương thức thanh toán |
Giá trị trả về — ConfirmCardSetupResult:
| Trường | Kiểu | Mô tả |
|---|---|---|
setup_intent | Record<string, unknown> | Đối tượng SetupIntent |
three_ds_payload | ThreeDSPayloadDecoded | undefined | Chỉ có mặt khi cần xác thực 3DS |
Reject với APIError — phải gọi mountCard() trước. Các trường hợp lỗi giống với Xác nhận thanh toán.
bahn.pollSetupIntentStatus(clientSecret, callbacks) / bahn.stopSetupIntentPolling()
Phiên bản setup intent của pollPaymentIntentStatus()/stopPaymentIntentPolling(), với cùng PaymentIntentPollCallbacks và ngữ nghĩa trạng thái — mỗi callback nhận setup_intent thay vì payment intent. Số lần tối đa dùng chung với bộ poll payment intent (paymentIntentPollAttempts trong BahnOptions).
pollSetupIntentStatus(clientSecret: string, callbacks: Bahn.PaymentIntentPollCallbacks): void;
stopSetupIntentPolling(): void;