Skip to content

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ụ ​

typescript
// 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().

typescript
confirmCardSetup(payload: Bahn.ConfirmCardSetupPayload): Promise<Bahn.ConfirmCardSetupResult>;

Tham số — ConfirmCardSetupPayload:

TrườngKiểuBắt buộcMô tả
client_secretstringCóClient secret của setup intent mà backend của bạn đã tạo
payment_method.billing_detailsConfirmationTokenBillingDetailsKhôngTê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_optionsRecord<string, unknown>KhôngCác tùy chọn riêng của phương thức thanh toán

Giá trị trả về — ConfirmCardSetupResult:

TrườngKiểuMô tả
setup_intentRecord<string, unknown>Đối tượng SetupIntent
three_ds_payloadThreeDSPayloadDecoded | undefinedChỉ 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).

typescript
pollSetupIntentStatus(clientSecret: string, callbacks: Bahn.PaymentIntentPollCallbacks): void;
stopSetupIntentPolling(): void;

Developers