Hoàn tất thanh toán trên server của bạn
Trong luồng thanh toán một lần tiêu chuẩn, backend của bạn tạo trước một PaymentIntent và SDK xác nhận nó trực tiếp. Nhưng đôi khi bạn cần kiểm tra thông tin thẻ trước khi thực hiện thanh toán — ví dụ, để áp dụng ưu đãi theo ngân hàng phát hành, điều chỉnh tổng đơn hàng, hoặc áp dụng quy tắc nghiệp vụ dựa trên loại thẻ hoặc nguồn tiền.
Hướng dẫn này mô tả một luồng xác nhận trì hoãn (deferred confirmation). SDK sẽ thu thập thông tin thẻ và tạo một ConfirmationToken thay vì xác nhận thanh toán ngay lập tức. Backend của bạn lấy token đó để đọc metadata của thẻ, áp dụng logic tùy chỉnh, và chỉ sau đó mới tạo và xác nhận PaymentIntent — hoàn toàn ở phía server.
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 Order
User ->> MerchantWeb: Click "Pay Now" button
MerchantWeb ->> MerchantAPI: Create order
MerchantAPI -->> MerchantWeb: {order_id}
Note over User, API: Phase 2: Collect Card & Create ConfirmationToken
MerchantWeb ->> SDK: Initialize SDK
SDK -->> User: Display payment form (card input fields)
User ->> SDK: Enter card details (PAN, exp, CVV, billing)
SDK ->> API: CreateConfirmationToken
API -->> SDK: confirmation_token
SDK -->> MerchantWeb: confirmation_token
Note over User, API: Phase 3: Perform Custom Logic
MerchantWeb ->> MerchantAPI: FinalizeOrder(order_id, confirmation_token)
MerchantAPI ->> API: GetConfirmationToken
API -->> MerchantAPI: ConfirmationToken Object
MerchantAPI ->> MerchantAPI: Perform custom logic
MerchantAPI -->> MerchantWeb: Order detail
MerchantWeb -->> User: display order detail
Note over User, API: Phase 4: Confirm Payment
User ->> MerchantWeb: Confirm to pay
MerchantWeb ->> MerchantAPI: Confirm Order
MerchantAPI ->> API: CreatePaymentIntent(amount, currency)
API -->> MerchantAPI: PaymentIntent
MerchantAPI ->> API: ConfirmPaymentIntent(id, confirmation_token)
alt 3DS flow
API -->> MerchantAPI: Action required
MerchantAPI -->> MerchantWeb: Action required
MerchantWeb ->> SDK: HandleNextAction
SDK -->> User: Display 3DS challenge (OTP / biometric)
User ->> SDK: Complete authentication challenge
end
Note over MerchantAPI, API: Phase 5: Payment Result
par Notify
API ->> MerchantAPI: Webhook: charge.capture, payment_intent.status
MerchantAPI ->> MerchantAPI: Consumer webhook event
end
Note over User, API: Phase 6: Complete
API -->> MerchantAPI: Payment result
MerchantAPI -->> MerchantWeb: Order Status
MerchantWeb -->> User: Show order status
Cách hoạt động
Giai đoạn 1 — Tạo đơn hàng
- Người dùng nhấn "Pay Now" — Merchant Web nhận thao tác này.
- Frontend tạo đơn hàng — Merchant Web gửi request đến Merchant API của bạn để tạo đơn hàng.
- Backend trả về order ID — Merchant API phản hồi với
{ order_id }. Chưa có PaymentIntent nào được tạo — số tiền thanh toán cuối cùng có thể thay đổi khi thẻ được xác định.
Giai đoạn 2 — Thu thập thẻ & tạo ConfirmationToken
- Frontend khởi tạo SDK — Merchant Web khởi tạo Bahn SDK được nhúng. Lúc này SDK đã sẵn sàng để thu thập thông tin thẻ.
- SDK hiển thị form thanh toán — SDK hiển thị các ô nhập thẻ an toàn trực tiếp trên trang. Frontend của bạn không bao giờ xử lý dữ liệu thẻ thô.
- 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.
- SDK tạo một ConfirmationToken — SDK gọi
CreateConfirmationTokenvới dữ liệu thẻ. Bahn tokenize thẻ phía server và trả về mộtconfirmation_tokencó thời hạn ngắn. - SDK trả token về — SDK truyền
confirmation_tokenvề cho frontend Merchant Web của bạn. Dữ liệu thẻ thô không bao giờ rời khỏi SDK.
Giai đoạn 3 — Thực hiện logic tùy chỉnh
- Frontend gửi token đến backend của bạn — Merchant Web gọi Merchant API của bạn với
FinalizeOrder(order_id, confirmation_token). - Backend lấy chi tiết token — Merchant API của bạn gọi
GetConfirmationTokenđể lấy đầy đủ đối tượng token, bao gồm metadata của thẻ như ngân hàng phát hành, BIN, và loại thẻ. - Backend thực hiện logic tùy chỉnh — Merchant API của bạn thực thi các quy tắc nghiệp vụ dựa trên thông tin thẻ — ví dụ, áp dụng ưu đãi theo ngân hàng phát hành hoặc điều chỉnh tổng đơn hàng.
- Backend trả về đơn hàng đã cập nhật — Merchant API phản hồi với chi tiết đơn hàng đã hoàn tất.
- Frontend hiển thị tóm tắt đơn hàng — Merchant Web hiển thị chi tiết đơn hàng (bao gồm mọi ưu đãi hoặc điều chỉnh đã áp dụng) cho người dùng.
Giai đoạn 4 — Xác nhận thanh toán
- Người dùng xác nhận thanh toán — Người dùng xem lại tóm tắt đơn hàng và xác nhận để tiếp tục.
- Frontend yêu cầu xác nhận thanh toán — Merchant Web gọi Merchant API của bạn để xác nhận đơn hàng.
- Backend tạo một PaymentIntent — Merchant API của bạn gọi
CreatePaymentIntentvớiamountvàcurrencycuối cùng. - Backend xác nhận PaymentIntent — Merchant API của bạn gọi
ConfirmPaymentIntentvớiconfirmation_token. Toàn bộ việc xác nhận thanh toán diễn ra ở phía server — dữ liệu thẻ không bao giờ quay trở lại hệ thống của bạn.
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:
- 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 (action-required) cho Merchant API của bạn.
- Backend chuyển tiếp hành động — Merchant API chuyển tín hiệu action-required đến Merchant Web.
- Frontend ủy quyền cho SDK — Merchant Web gọi
HandleNextActiontrên SDK. - 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.
- 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 18–22 sẽ được bỏ qua.
Giai đoạn 5 — 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.capturevà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 6 — Hoàn tất
- Bahn gửi kết quả thanh toán — Bahn API gửi kết quả thanh toán cuối cùng đến Merchant API của bạn.
- Backend trả về trạng thái đơn hàng — Merchant API phản hồi cho Merchant Web với trạng thái đơn hàng đã cập nhật.
- 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
Trong luồng này, backend của bạn điều khiển toàn bộ vòng đời thanh toán: tạo đơn hàng, kiểm tra card token để áp dụng logic nghiệp vụ, và vừa tạo vừa xác nhận PaymentIntent ở phía server.
1. Tạo một đơn hàng
Khi người dùng bắt đầu checkout, tạo một đơn hàng trong hệ thống của bạn và trả về order_id cho frontend. Lúc này SDK được khởi tạo mà chưa có PaymentIntent — số tiền cuối cùng có thể phụ thuộc vào thẻ được sử dụng.
2. Hoàn tất đơn hàng
Sau khi SDK thu thập thông tin thẻ, nó trả về một confirmation_token cho frontend của bạn, và frontend chuyển tiếp nó đến backend của bạn.
Gọi GetConfirmationToken để lấy đầy đủ đối tượng token và kiểm tra metadata của thẻ:
POST /api/confirmation_token/get{
"confirmation_token": "ct_xxxxxxxx"
}Response bao gồm các thông tin thẻ như:
| Trường | Mô tả |
|---|---|
card.brand | Mạng thẻ (ví dụ visa, mastercard). |
card.issuer | Tên ngân hàng phát hành. |
card.bin | 6–8 chữ số đầu của số thẻ. |
card.funding | Loại nguồn tiền: credit, debit, hoặc prepaid. |
Sử dụng thông tin này để áp dụng các quy tắc nghiệp vụ — ví dụ, áp dụng ưu đãi cho thẻ của một ngân hàng phát hành cụ thể — sau đó trả về chi tiết đơn hàng đã cập nhật cho frontend để người dùng xem lại.
3. Xác nhận thanh toán
Khi người dùng xác nhận đơn hàng, hãy tạo và xác nhận PaymentIntent theo trình tự. Cả hai lệnh gọi đều diễn ra hoàn toàn trên backend của bạn.
Bước 1 — Tạo một PaymentIntent
Gọi CreatePaymentIntent với số tiền cuối cùng của đơn hàng:
POST /api/payment_intents{
"amount": "10000",
"currency": "usd",
"description": "Order #1234",
"metadata": {
"order_id": "1234"
}
}| Trường | Bắt buộc | Mô tả |
|---|---|---|
amount | Có | Số tiền theo đơn vị tiền tệ nhỏ nhất (ví dụ cent). |
currency | Có | Mã tiền tệ ISO 4217 ba ký tự. |
description | Không | Mô tả dễ đọc hiển thị trên sao kê của khách hàng. |
metadata | Không | Các cặp key-value tùy ý để lưu thêm thông tin. |
Bước 2 — Xác nhận PaymentIntent
Gọi ConfirmPaymentIntent với confirmation_token từ SDK:
POST /api/payment_intents/{id}/confirm{
"confirmation_token": "ct_xxxxxxxx"
}Nếu response chỉ ra rằng cần thêm hành động (3DS), hãy chuyển tiếp payload hành động đó đến frontend của bạn. Merchant Web sẽ gọi HandleNextAction trên SDK, SDK sẽ hiển thị thử thách OTP hoặc sinh trắc học cho người dùng. Sau khi xác thực hoàn tất, không cần thêm hành động nào ở backend của bạn — Bahn sẽ gửi kết quả cuối cùng qua webhook.
INFO
API key và payment_intent.id của bạn không bao giờ rời khỏi backend của bạn. confirmation_token là một token chỉ dùng được một lần — nó không thể được dùng để khởi tạo một thanh toán thứ hai.
4. 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.
| Event | Mô tả |
|---|---|
payment_intent.status.succeeded | Thanh toán đã được capture thành công. |
payment_intent.status.canceled | Thanh toán đã bị hủy. |
charge.captured | Charge đã được capture. |