Skip to content

One Time Payment ​

Bahn's payment flow is split across two scopes of responsibility. Your Merchant API (backend) orchestrates the payment session and handles its outcome, while the embedded Bahn SDK (frontend) collects card details and drives the in-page payment experience.

Preliminary ​

High-Level Flow ​

sequenceDiagram
    autonumber
    participant User as User
    participant MerchantWeb as Merchant Web<br/>(Frontend)
    participant SDK as SDK<br/>(Embedded)
    participant MerchantAPI as Merchant API<br/>(Backend)
    participant API as Bahn API

     Note over User, API: Phase 1: Create PaymentIntent
    User ->> MerchantWeb: Click "Pay Now" button
    MerchantWeb ->> MerchantAPI: Request checkout
    MerchantAPI ->> API: CreatePaymentIntent(amount, currency)
    API -->> MerchantAPI: {payment_intent, client_secret}
    MerchantAPI -->> MerchantWeb: {client_secret}
    MerchantWeb ->> SDK: Initialize SDK

    Note over User, API: Phase 2: Collect Card & Confirm
    SDK -->> User: Display payment form (card input fields)
    User ->> SDK: Enter card details (PAN, exp, CVV, billing)
    SDK ->> API: ConfirmPaymentIntent

    alt 3DS flow
        API -->> SDK: Action required
        SDK -->> User: Display 3DS challenge (OTP / biometric)
        User ->> SDK: Complete authentication challenge
    end

 
    Note over MerchantAPI, API: Phase 4: Payment Result 
    par Notify
        API ->> MerchantAPI: Webhook: charge.capture, payment_intent.status
        MerchantAPI ->> MerchantAPI: Consumer webhook event
    end

    Note over User, API: Phase 5: Complete
    API -->> SDK: Payment result
    SDK -->> MerchantWeb: Payment result

    MerchantWeb ->> MerchantAPI: GET Order status
    MerchantAPI -->> MerchantWeb: Order
    MerchantWeb -->> User: Show order status

How It Works ​

Phase 1 — Create PaymentIntent ​

  1. User clicks "Pay Now" — Merchant Web receives the action and requests a checkout from your backend.
  2. Frontend requests a checkout — Merchant Web sends a checkout request to your Merchant API.
  3. Backend creates a PaymentIntent — Your Merchant API calls CreatePaymentIntent with amount and currency. Bahn returns the payment_intent object and its client_secret.
  4. Backend forwards only the client_secret — Your Merchant API returns { client_secret } to the frontend. The payment_intent.id and API key remain on your backend.
  5. Frontend initializes the SDK — Merchant Web passes the client_secret to the embedded Bahn SDK.

INFO

The client_secret is scoped to a single payment session and safe to expose to the client. Your payment_intent.id and API key must never leave your backend.

Phase 2 — Collect Card & Confirm ​

  1. SDK renders the payment form — The SDK displays secure card input fields in the page. Your frontend never handles raw card data.
  2. User enters card details — The user fills in PAN, expiry, CVV, and billing details and submits.
  3. SDK confirms the payment — The SDK calls ConfirmPaymentIntent with the client_secret and card data.

If the card issuer requires additional authentication, the 3DS flow is triggered:

  1. Bahn signals action required — Bahn returns an action-required response to the SDK.
  2. SDK presents the 3DS challenge — The SDK renders the OTP or biometric challenge to the user.
  3. User completes the challenge — The user authenticates, and the SDK proceeds.

If no 3DS is required, steps 9–11 are skipped.

Phase 4 — Payment Result ​

Once the payment is processed, Bahn notifies your backend:

  • Webhook notification — Bahn delivers charge.capture and payment_intent.status events to your registered webhook endpoint. Your Merchant API consumes these events and updates its order state. The webhook is the authoritative source of truth for the final payment outcome.

Phase 5 — Complete ​

  1. Bahn pushes the result to the SDK — The Bahn API sends the final payment result directly to the SDK.
  2. SDK surfaces the result — The SDK emits the payment result to your Merchant Web frontend.
  3. Frontend fetches the order — Merchant Web calls your Merchant API to retrieve the latest order status.
  4. Backend returns the order — Your Merchant API responds with the order, updated from the webhook event.
  5. User sees the outcome — Merchant Web renders the success or failure screen to the user.

Merchant API Responsibilities ​

Your backend is responsible for creating the payment session and handling its final outcome.

1. Create a PaymentIntent ​

When your frontend requests a checkout, call CreatePaymentIntent. Bahn returns a payment_intent object whose client_secret is used by the SDK to confirm the payment on the customer's behalf.

http
POST /api/payment_intents

Request

json
{
  "amount": "10000",
  "currency": "usd",
  "description": "Order #1234",
  "metadata": {
    "order_id": "1234"
  }
}
FieldRequiredDescription
amountYesAmount in the smallest currency unit (e.g. cents).
currencyYesThree-letter ISO 4217 currency code.
descriptionNoHuman-readable description shown on the customer's statement.
metadataNoArbitrary key-value pairs for storing additional information.

Response

json
{
  "payment_intent": {
    "id": "pi_xxxxxxxx",
    "client_secret": "cs_xxxxxxxx",
    "amount": "10000",
    "currency": "usd",
    "status": "requires_payment_method",
    "description": "Order #1234",
    "metadata": {
      "order_id": "1234"
    }
  }
}

INFO

Return only the client_secret to your frontend — the SDK uses it to initialize and confirm the payment. Keep payment_intent.id on your backend for webhook reconciliation. Never expose your API key to the client.

2. Handle the Webhook ​

Once the payment reaches a terminal state, Bahn sends the result to your registered webhook endpoint. Your backend must treat the webhook as the authoritative source of truth when updating order state.

EventDescription
payment_intent.status.succeededPayment was captured successfully.
payment_intent.status.canceledPayment was canceled.
charge.capturedCharge was captured.

Developers