Skip to content

Thanh toán một lần ​

Luồng thanh toán của Bahn được chia thành hai phạm vi trách nhiệm. Merchant API (backend) của bạn điều phối phiên thanh toán và xử lý kết quả của nó, trong khi Bahn SDK (frontend) được nhúng thu thập thông tin thẻ và điều khiển trải nghiệm thanh toán ngay trong trang.

Điều kiện tiên quyết ​

Luồng tổng quan ​

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

Cách hoạt động ​

Giai đoạn 1 — Tạo PaymentIntent ​

  1. Người dùng nhấn "Pay Now" — Merchant Web nhận thao tác này và yêu cầu checkout từ backend của bạn.
  2. Frontend yêu cầu checkout — Merchant Web gửi request checkout đến Merchant API của bạn.
  3. Backend tạo một PaymentIntent — Merchant API của bạn gọi CreatePaymentIntent với amount và currency. Bahn trả về đối tượng payment_intent và client_secret tương ứng.
  4. Backend chỉ chuyển tiếp client_secret — Merchant API của bạn trả về { client_secret } cho frontend. payment_intent.id và API key vẫn ở phía backend.
  5. Frontend khởi tạo SDK — Merchant Web truyền client_secret cho Bahn SDK được nhúng.

INFO

client_secret chỉ có hiệu lực cho một phiên thanh toán duy nhất và an toàn khi expose cho client. payment_intent.id và API key của bạn không bao giờ được rời khỏi backend.

Giai đoạn 2 — Thu thập thẻ & xác nhận ​

  1. SDK hiển thị form thanh toán — SDK hiển thị các ô nhập thẻ an toàn trên trang. Frontend của bạn không bao giờ xử lý dữ liệu thẻ thô.
  2. Người dùng nhập thông tin thẻ — Người dùng điền PAN, ngày hết hạn, CVV, và thông tin thanh toán rồi submit.
  3. SDK xác nhận thanh toán — SDK gọi ConfirmPaymentIntent với client_secret và dữ liệu thẻ.

Nếu ngân hàng phát hành thẻ yêu cầu xác thực bổ sung, luồng 3DS sẽ được kích hoạt:

  1. Bahn báo hiệu cần thêm hành động — Bahn trả về một response yêu cầu hành động cho SDK.
  2. SDK hiển thị thử thách 3DS — SDK hiển thị thử thách OTP hoặc sinh trắc học cho người dùng.
  3. Người dùng hoàn tất thử thách — Người dùng xác thực, và SDK tiếp tục.

Nếu không cần 3DS, các bước 9–11 sẽ được bỏ qua.

Giai đoạn 4 — Kết quả thanh toán ​

Khi thanh toán được xử lý xong, Bahn sẽ thông báo cho backend của bạn:

  • Thông báo qua webhook — Bahn gửi các event charge.capture và payment_intent.status đến webhook endpoint đã đăng ký của bạn. Merchant API của bạn xử lý các event này và cập nhật trạng thái đơn hàng. Webhook là nguồn sự thật chính thức cho kết quả thanh toán cuối cùng.

Giai đoạn 5 — Hoàn tất ​

  1. Bahn đẩy kết quả về SDK — Bahn API gửi kết quả thanh toán cuối cùng trực tiếp đến SDK.
  2. SDK trả kết quả về — SDK phát ra kết quả thanh toán cho frontend Merchant Web của bạn.
  3. Frontend lấy thông tin đơn hàng — Merchant Web gọi Merchant API của bạn để lấy trạng thái đơn hàng mới nhất.
  4. Backend trả về đơn hàng — Merchant API của bạn phản hồi với đơn hàng, đã được cập nhật từ webhook event.
  5. Người dùng thấy kết quả — Merchant Web hiển thị màn hình thành công hoặc thất bại cho người dùng.

Trách nhiệm của merchant API ​

Backend của bạn chịu trách nhiệm tạo phiên thanh toán và xử lý kết quả cuối cùng của nó.

1. Tạo một PaymentIntent ​

Khi frontend của bạn yêu cầu checkout, gọi CreatePaymentIntent. Bahn trả về một đối tượng payment_intent với client_secret được SDK dùng để xác nhận thanh toán thay mặt khách hàng.

http
POST /api/payment_intents

Request

json
{
  "amount": "10000",
  "currency": "usd",
  "description": "Order #1234",
  "metadata": {
    "order_id": "1234"
  }
}
TrườngBắt buộcMô tả
amountCóSố tiền theo đơn vị tiền tệ nhỏ nhất (ví dụ cent).
currencyCóMã tiền tệ ISO 4217 ba ký tự.
descriptionKhôngMô tả dễ đọc hiển thị trên sao kê của khách hàng.
metadataKhôngCác cặp key-value tùy ý để lưu thêm thông tin.

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

Chỉ trả về client_secret cho frontend của bạn — SDK sử dụng nó để khởi tạo và xác nhận thanh toán. Giữ payment_intent.id ở backend để đối chiếu webhook. Không bao giờ để lộ API key của bạn cho client.

2. Xử lý webhook ​

Khi thanh toán đạt trạng thái cuối, Bahn sẽ gửi kết quả đến webhook endpoint đã đăng ký của bạn. Backend của bạn phải coi webhook là nguồn sự thật chính thức khi cập nhật trạng thái đơn hàng.

EventMô tả
payment_intent.status.succeededThanh toán đã được capture thành công.
payment_intent.status.canceledThanh toán đã bị hủy.
charge.capturedCharge đã được capture.

Developers