Overview
This documentation describes the integration between Baokim and CashPlus for two business operations:
- Collection: customers transfer money to a virtual account; Baokim notifies CashPlus of the transaction.
- Revenue sharing: CashPlus registers the amounts to be shared with User, Merchant, and CashPlus, then calls a disbursement order for each receiver. Baokim does not disburse any funds on its own until a disbursement order is received.
Environments
| Environment | {env} | {BASE_URL} |
|---|---|---|
| Test (sandbox) | sanbox | Provided by Baokim when integration begins |
| Production | live | Provided by Baokim when go-live |
API version for revenue sharing
The flow in this documentation uses /transaction/v2/*. The old /transaction/v1/share-amount API (Baokim disburses automatically after registration) runs in parallel during the transition period and will be shut down according to a schedule announced in advance by Baokim.
- A transaction registered through a given version is processed with that version until completion. Do not mix v1 and v2 for the same transaction (code
143). - After the v1 shutdown date, every v1 API returns
143.
{env} appears only in the Collection API group URLs (/collection/{env}/...). The /transaction/... and /ats/... groups do not include {env}; environments are differentiated by {BASE_URL}.
Public keys for signature verification, QR image domain ({QR_DOMAIN}), the CashPlus collection webhook URL, and the CashPlus disbursement result webhook URL are exchanged at the same time for each environment.
Terminology
| Term | Meaning |
|---|---|
| TransactionId / TransIdBk | Collection transaction ID generated by Baokim. Same value appears as: TransIdBk in collection webhook, TransactionId in revenue sharing APIs |
| N | Amount available for sharing in a collection transaction: TransAmount − TransFeeAmount. If the transaction qualifies for the collection fee waiver, then TransFeeAmount = 0 and N = TransAmount |
| U, M, P | Amounts shared with User, Merchant, and CashPlus, registered once at Share amount registration |
| Receiver | Recipient: 1 = User, 2 = Merchant, 3 = CashPlus |
| Type | Disbursement type: 1 = realtime, 2 = T+1 |
| ReferenceId | Disbursement order ID generated by CashPlus, must be unique. Used to prevent duplicate orders. (In the collection webhook response, ReferenceId is the CashPlus-side transaction ID — a different meaning) |
| TransIdPayment | Disbursement order ID generated by Baokim. Remains unchanged throughout the order lifecycle |
| Active order | A disbursement order at Status 0, 99, or 200 |
| Day T | The day Baokim receives the funds of the collection transaction, Vietnam time (GMT+7) |
| Cutoff | 23:50:59 on day T, in Baokim system time when the request is received |
| T+1 | Settlement period of day T: the next business day (transactions on Friday, Saturday, and Sunday are settled on Monday) |
| Source | Order source: PARTNER = called by CashPlus, AUTO = created automatically by Baokim after cutoff (00:05 on day T+1), MANUAL = disbursed manually by Baokim |
Integration Flow
| Step | API | Notes |
|---|---|---|
| 1 | Collection webhook | IsFull=0 when payer account not yet available, IsFull=1 when available |
| 2 | Share amount registration | Once per TransactionId, before cutoff. Only records the split, does not transfer funds |
| 3, 4, 5 | Disbursement order | Each request is one order for one receiver, Amount exactly equal to the registered amount. Receivers do not block each other |
| 6 | Disbursement result webhook | When the order reaches a final result |
| * | Look up disbursement order | By ReferenceId (one order) or TransactionId (all orders of the transaction) |
Sharing and disbursement rules
Registration:
- Each collection transaction is registered once, with no modification.
U + M + P ≤ N. - The remainder
N − U − M − Pcannot be disbursed by order and is settled to the Merchant at cutoff.
Disbursement orders:
- The order's
Amountmust be exactly equal to the amount registered for that receiver. - Each receiver can have only 1 active order: an order can be called only when the receiver has no order at
Status0, 99, or 200. - A failed order (
Status=11) can be retried with a new order (newReferenceId) before cutoff, unlessRetryable=false. - One receiver's order does not block another receiver's orders. For example, while the Merchant order is at
99, CashPlus can still call disbursement for User and CashPlus.
| Receiver | Valid Type | Disbursement fee |
|---|---|---|
| 1 (User) | Always 1 (realtime) | Per realtime fee schedule |
| 2 (Merchant) | Exactly the registered Merchant.Type | Realtime: per fee schedule, waived under the fee waiver policy. T+1: free |
| 3 (CashPlus) | Always 2 (T+1) | Free |
Cutoff 23:50:59 on day T
From 23:50:59 on day T, Baokim does not accept new share amount registrations or new disbursement orders for collection transactions of day T (code 141). Re-calling an existing ReferenceId and look-ups still work.
At 00:05 on day T+1, Baokim automatically creates 1 T+1 order to the Merchant (Source = AUTO, no ReferenceId, free of charge) for each collection transaction of day T, including transactions whose funds are received after cutoff, with the amount:
AUTO order = N − Sum of Amount of the transaction's active orders, as of 23:50:59
This includes: the whole N if not registered; the share of any receiver not yet called; the share of failed orders; and the unallocated remainder. If it equals 0, no order is created. The AUTO order is part of the same T+1 settlement period as other T+1 orders of day T, and has a disbursement result webhook like any other order.
- Transactions whose funds are received between 23:50:59 and 23:59:59 on day T: registration is not accepted, the whole N goes to the Merchant on T+1.
- Orders at
Status=99at 23:50:59 are not included in theAUTOorder (even if they get a result before 00:05); they wait for their final result:200: done.11: Baokim sends the order'sStatus=11webhook, then Baokim disburses manually on the next business day:- Order with
Retryable=true: disbursed again to the same receiver (for User: using the account of the original order). - Order with
Retryable=false(e.g. User account error), or re-disbursement to the original receiver fails: that share is disbursed to the Merchant. - The manual disbursement result is reported via webhook with
Source = MANUAL, the original order'sReferenceId, a newTransIdPayment, andReceiverset to the actual recipient (may be2even if the original order was for User).
- Order with
- T+1 orders (Merchant
Type=2, CashPlus,AUTO) that fail during Baokim settlement: Baokim sends the order'sStatus=11webhook, then disburses manually to the same receiver (the Merchant, or CashPlus for a CashPlus order) and reports the result via aSource = MANUALwebhook. Baokim does not automatically move such an order into a later settlement period. - Call the APIs before 23:45 to avoid requests being processed after cutoff.
Retry and Error Handling
Baokim does not automatically retry failed disbursement orders. Baokim only returns Status=11 when it is certain the funds have not been sent; if not certain, the order stays at Status=99.
| Situation | CashPlus should do |
|---|---|
| Timeout / no response received for disbursement order | Retry with the same ReferenceId or look up by ReferenceId. Only use a new ReferenceId if lookup returns 137 and it is still before cutoff |
Order with Status=99 | Wait for webhook or look up. Do not call a new order for this receiver (it will be rejected with 134). Disbursement for other receivers can still be called |
Order with Status=11, Retryable=true | Call a new order with a new ReferenceId before cutoff. If not called → that share goes to the Merchant on T+1 |
User order with Status=11, Retryable=false (User account error) | Do not retry (it will be rejected with 119). Notify the customer. The U share goes to the Merchant on T+1 |
Rejected with 134 | Look up the previous order and handle according to its actual Status |
Rejected with 141 (past cutoff) | Stop, do not retry. That share goes to the Merchant on T+1 |
| Share amounts not registered before cutoff | The whole N goes to the Merchant on T+1. CashPlus receives the webhook of the AUTO order |
Examples
Collection transaction BK6AB9B10B92C04SZ, N = 14,010.
Scenario 1 — All three recipients disbursed
| # | Action | Result |
|---|---|---|
| 1 | Register U = 7,000, M = 6,860, P = 150 | 200, remainder 0 |
| 2 | Disbursement order Merchant 6,860 (Ref#1, Type 1) | Status 200 |
| 3 | Disbursement order User 7,000 (Ref#2) | Status 200 |
| 4 | Disbursement order CashPlus 150 (Ref#3, Type 2) | Status 0 → webhook 200 after the T+1 period |
| 5 | Cutoff | No AUTO order created (fully disbursed) |
Scenario 2 — No User account, CashPlus does not call disbursement for its own share
| # | Action | Result |
|---|---|---|
| 1 | Register U = 7,000, M = 6,860, P = 150 | 200 |
| 2 | Disbursement order Merchant 6,860 | Status 200 |
| 3 | Cutoff | AUTO order 7,150 to Merchant (U 7,000 + P 150) → webhook Source=AUTO |
Scenario 3 — User order failed, CashPlus retries
| # | Action | Result |
|---|---|---|
| 1 | Disbursement order User 7,000 (Ref#2) | Status 11, Retryable=true |
| 2 | Disbursement order User 7,000 (Ref#4, new ReferenceId) | Status 200 |
Scenario 4 — Blocked orders
| # | Action | Result |
|---|---|---|
| 1 | Disbursement order Merchant 6,860 (Ref#1) | Status 99 |
| 2 | Disbursement order Merchant 6,860 (Ref#5) while Ref#1 is 99 | Rejected 134 |
| 3 | Disbursement order User 5,000 (registered 7,000) | Rejected 136 |
| 4 | Disbursement order CashPlus 150 with Type=1 | Rejected 107 |
| 5 | Disbursement order User at 23:51 | Rejected 141 |
Security
All requests between Baokim and CashPlus are digitally signed. See Security.