Skip to content

SDK Reference ​

Complete API reference for the Bahn Web SDK. For the full shape of every parameter, result and callback, see Type Definitions — it also has a downloadable bahn.d.ts for TypeScript projects.

Installation ​

Load the SDK as a plain, version-pinned script — no bundler required:

html
<script
  src="https://bn.cake.vn/bn-sdk/v2.4.0/index.js"
  integrity="sha384-jBbonUZLgKU82F+X/ITqcmeInXYYwZYdmCDoPFX8wkWL9dCkeUHyi0Fs1EUnkeDS"
  crossorigin="anonymous"
></script>

This exposes Bahn as a global (window.Bahn) — no import needed. This is the same script URL for both sandbox and production; only the environment option passed to new Bahn() changes which API/iframe domains it talks to. Each release lives at its own /bn-sdk/v<version>/index.js path with a matching index.js.sha384 SRI hash alongside it. See Getting Started and Environments for details.


Bahn Class ​

The main SDK class. A singleton — calling new Bahn() a second time returns the existing instance and merges new options.

typescript
const bahn = new Bahn(options: BahnOptions);

BahnOptions ​

ParamTypeRequiredDefaultDescription
pkstringYes—Publishable key sent as X-Bn-Publishable-Key header
environment"sandbox" | "production"Yes—Target API environment
appIdstringNo—Tenant app ID for 3DS routing
teamIdstringNo—Tenant team ID for 3DS routing
paymentIntentPollAttemptsnumberNo10Max polling attempts for payment intent status

Methods ​

bahn.mountCard(containerId, options?): CardComponent ​

Mounts the PCI-compliant card input component. See Card Payments for full details.


card.unmount(): void ​

Removes the card input and cleans up event listeners. Called on the CardComponent returned by mountCard().


card.applyConfig(fields, inputStyle?): void ​

Re-applies field configuration to mounted iframes without remounting. Called on the CardComponent returned by mountCard().


card.applyStyles(stateStyles): void ​

Re-applies state-based styles (base/focus/invalid) to mounted iframes without remounting. Called on the CardComponent returned by mountCard(). See Card Payments → State-Based Styling.


bahn.mountPaymentQR(selector, clientSecret, options?): IQRComponent ​

Mounts a Napas QR code iframe and starts polling. See QR Payments for full details.


qr.unmount(): void ​

Removes the QR iframe and cleans up. Called on the IQRComponent returned by mountPaymentQR().


bahn.getConfig(): Promise<BahnConfig> ​

Fetches your merchant's Apple Pay configuration — merchant_name, supported_networks, merchant_capabilities. Not required before calling mountApplePay(), which resolves these same values internally. Call getConfig() directly only if you need these values for something other than mountApplePay() — for example, your own UI. Unlike every other payment method, this is a plain direct fetch — no component needs to be mounted first.

typescript
interface BahnConfig {
  apple_pay: {
    enabled: boolean;
    merchant_name: string;
    supported_networks: string[];
    merchant_capabilities: string[];
  };
}

bahn.isApplePayAvailable(): Promise<boolean> ​

Optional pre-flight check for whether the shopper's browser/device can use Apple Pay. mountApplePay() runs this same check internally and simply won't render the button if it fails, so calling this first isn't required — use it only if you want to control your own layout (e.g. hide a whole "Or pay with Apple Pay" section, not just the button) before calling mountApplePay(). This is a client-side check only — it doesn't confirm merchant-level Apple Pay enablement, which surfaces as an "error" event from mountApplePay() at the point the shopper taps the button. A true result on Chrome/Firefox/Edge reflects only that the browser version supports Apple's cross-browser fallback, and requires a separate merchant enrollment with Apple to actually work — see Apple Pay → Browser support.


bahn.mountApplePay(selector, clientSecret, options): IApplePayButtonComponent ​

Renders an Apple Pay button at selector and manages the full payment sheet lifecycle — merchant validation, authorization, and (if triggered) 3D Secure — internally. The 3D Secure challenge, if triggered on the direct-confirmation flow, mounts on your own page via bahn.mount3DS(). Requires Safari 17 / iOS 17+. Every options field besides amount/currency/country is optional; merchantName/supportedNetworks/merchantCapabilities resolve to your merchant's configured values when omitted. Renders nothing if Apple Pay isn't available — call bahn.isApplePayAvailable() yourself first only if you'd rather hide the entry point entirely than react to an "error" event. See Apple Pay for full details.

clientSecret selects the flow: pass it for direct confirmation ("success"/"error" events); omit it (undefined) for the token-based flow ("token" event with a confirmation_token — send it to your own backend, then call bahn.handleNextAction() with the resulting client_secret, same as card's token-based flow).

options.requestShipping: true collects a shipping address: listen for "shippingcontactselected"/"shippingmethodselected", each firing with { contact | shippingMethod, resolve, reject }; the sheet stays open until you call one of those. See Apple Pay → Collecting a shipping address.


ap.unmount(): void ​

Removes the Apple Pay button and cleans up. Called on the IApplePayButtonComponent returned by mountApplePay(). bahn.unmountApplePay()/bahn.cleanup() also unmount it.


bahn.isGooglePayAvailable(): Promise<boolean> ​

Optional pre-flight check for whether the shopper's browser can use Google Pay. mountGooglePay() runs this same check internally and simply won't render the button if it fails, so calling this first isn't required — use it only if you want to control your own layout (e.g. hide a whole "Or pay with Google Pay" section, not just the button) before calling mountGooglePay(). See Google Pay → Browser support.


bahn.mountGooglePay(selector, clientSecret, options): IGooglePayButtonComponent ​

Renders a Google Pay button at selector and manages the full payment sheet lifecycle — authorization and (if triggered) 3D Secure — internally. The 3D Secure challenge, if triggered on the direct-confirmation flow, mounts on your own page via bahn.mount3DS(). Works in every browser Google Pay supports, including Safari and Firefox. Every options field besides amount/currency/country is optional. Renders nothing if Google Pay isn't available — call bahn.isGooglePayAvailable() yourself first only if you'd rather hide the entry point entirely than react to an "error" event. See Google Pay for full details.

clientSecret selects the flow: pass it for direct confirmation ("success"/"error" events); omit it (undefined) for the token-based flow ("token" event with a confirmation_token — send it to your own backend, then call bahn.handleNextAction() with the resulting client_secret, same as card's token-based flow).

options.requestShipping: true collects a shipping address: listen for "shippingaddresschanged"/"shippingoptionchanged", each firing with { address | shippingOption, resolve, reject }; the sheet stays open until you call one of those. See Google Pay → Collecting a shipping address.


gp.unmount(): void ​

Removes the Google Pay button and cleans up. Called on the IGooglePayButtonComponent returned by mountGooglePay(). bahn.unmountGooglePay()/bahn.cleanup() also unmount it.


bahn.mount3DS(sessionId, callbacks, options?): IThreeDSecure ​

Mounts the 3DS challenge modal and returns the component. The modal auto-unmounts on success/failure; call unmount() on the returned component to dismiss it early. See 3DS Authentication for full details.


bahn.getChallengeRedirectUrl(sessionId, returnUrl, options?): string ​

Builds a full redirect URL to the standalone 3DS challenge page for full-page redirect flows (e.g. from mobile apps, external checkout pages) where an iframe overlay is not possible. The challenge page handles the complete 3DS flow and redirects the user back to returnUrl on completion.

Parameters:

ParamTypeRequiredDescription
sessionIdstringYes3DS session ID from confirmPayment().three_ds_payload.id
returnUrlstringYesWhere the challenge page redirects back on completion
options.brandNamestringNoBrand name shown while returning ("Returning to {brandName}…"). Default: "Cake"

Returns: A fully constructed URL string ready for window.location.href.

typescript
const url = bahn.getChallengeRedirectUrl(sessionId, "https://merchant.com/callback", {
  brandName: "MyStore",
});
window.location.href = url;

See Standalone Challenge Page for the full flow and URL contract.


bahn.confirmPayment(payload): Promise<{ payment_intent, three_ds_payload? }> ​

Confirms a payment intent via the card-number iframe. Requires mountCard() first. See Confirm Payment for the full flow.

Payload (ConfirmPaymentPayload):

FieldTypeRequiredDescription
client_secretstringYesClient secret of the payment intent from your backend
payment_method{ billing_details?: ConfirmationTokenBillingDetails }NoOptional billing details (card data is merged by the iframe)
payment_method_optionsRecord<string, unknown>NoProvider-specific options
confirmation_tokenstringNoToken from createConfirmationToken()
amountstringNoOptional amount override, in the smallest currency unit

Returns:

FieldTypeDescription
payment_intentRecord<string, unknown>The PaymentIntent object
three_ds_payloadThreeDSPayloadDecoded | undefined3DS payload if required

bahn.confirmCardSetup(payload): Promise<{ setup_intent, three_ds_payload? }> ​

Confirms a setup intent via the card-number iframe — saves a card without charging it. Requires mountCard() first. See Save a Card.

Payload (ConfirmCardSetupPayload):

FieldTypeRequiredDescription
client_secretstringYesClient secret of the setup intent from your backend
payment_method{ billing_details?: ConfirmationTokenBillingDetails }NoOptional billing details (card data is merged by the iframe)
payment_method_optionsRecord<string, unknown>NoProvider-specific options

Returns:

FieldTypeDescription
setup_intentRecord<string, unknown>The SetupIntent object
three_ds_payloadThreeDSPayloadDecoded | undefined3DS payload if required

bahn.createConfirmationToken(payload): Promise<ConfirmationTokenResult> ​

Creates a one-time confirmation token. Requires mountCard() and pk first. See Token-Based Flow.

typescript
interface ConfirmationTokenResult {
  confirmation_token: string;
}

bahn.handleNextAction(input, callbacks): Promise<void> ​

Handles the next action for a payment intent (3DS, polling, or terminal state). input is either a client_secret string (SDK fetches the payment intent itself, requires mountCard()/mountApplePay(), and polls after 3DS) or a payment_intent object your own backend already returned (skips that fetch, must include client_secret, and never polls — onSuccess/onFailure resolve immediately from the 3DS outcome with the same object, no iframe required; a non-terminal, non-requires_action status calls onError instead of silently doing nothing). See Token-Based Flow → handleNextAction for full details.


bahn.pollPaymentIntentStatus(clientSecret, callbacks): void ​

Starts polling payment intent status. Call stopPaymentIntentPolling() to cancel. See Token-Based Flow → pollPaymentIntentStatus for full details.


bahn.stopPaymentIntentPolling(): void ​

Stops any active payment intent polling.


bahn.pollSetupIntentStatus(clientSecret, callbacks): void ​

Starts polling setup intent status. Call stopSetupIntentPolling() to cancel. See Save a Card.


bahn.stopSetupIntentPolling(): void ​

Stops any active setup intent polling.


bahn.cleanup(): void ​

Cleans up all resources: unmounts card, 3DS, QR, and Apple Pay components, and stops payment/setup intent polling.


Singleton Behavior ​

new Bahn() returns a singleton. Subsequent calls merge new options into the existing instance — existing non-empty values are kept, new values fill gaps. Conflicts are logged as warnings.

typescript
// First init
const bahn = new Bahn({ pk: "pk_1", environment: "sandbox" });

// Later — returns same instance, appId is applied to it
const same = new Bahn({ pk: "pk_2", appId: "app_123" });
// pk kept as "pk_1" (existing non-empty), appId set to "app_123" (was empty)
// Warning logged: pk conflict — keeping "pk_1", ignoring "pk_2"

window.BahnCake ​

The singleton is also set on window.BahnCake:

typescript
declare global {
  interface Window {
    BahnCake?: Bahn;
  }
}

You can access the instance directly:

typescript
const bahn = window.BahnCake;
if (bahn) {
  bahn.cleanup();
}

ThreeDSPayloadDecoded ​

Shape of the decoded 3DS base64 payload returned by confirmPayment(). Every other SDK type is on Type Definitions.

typescript
interface ThreeDSPayloadDecoded {
  provider: string;    // 3DS provider identifier
  app_id: string;     // Tenant app ID
  team_id: string;    // Tenant team ID
  id: string;         // 3DS session ID — pass to mount3DS()
}

Developers