daraja-b2b · diff
git:20261019.15b8109 to git:20261019.9e3c915
42 added, 20 removed. Audit A to A.
---
name: daraja-b2b
- description: Implement Safaricom Daraja B2B (Business-to-Business) payments between shortcodes — Paybill and Buy Goods payments from a business, CommandIDs, identifier types, SecurityCredential and result handling. Use when one business pays another business's Paybill or Till through the API.
+ description: Implements Safaricom Daraja B2B payments between shortcodes: BusinessPayBill (to a Paybill and account), BusinessBuyGoods (to a Till), BusinessPayToBulk (top up a B2C utility account) and B2B Express Checkout (a USSD push asking a merchant's till operator to pay your Paybill). Covers identifier types, the initiator and SecurityCredential, and Result handling. Use when one business pays another business, a supplier, or its own disbursement account.
---
- # B2B — Business to Business
+ # B2B: Business to Business
- ## Request
+ ## Payment request
`POST /mpesa/b2b/v1/paymentrequest`
+
```json
{
"Initiator": "testapi",
- "SecurityCredential": "<encrypted base64>",
+ "SecurityCredential": "<encrypted>",
"CommandID": "BusinessPayBill",
"SenderIdentifierType": "4",
"RecieverIdentifierType": "4",
"Amount": 1000,
"PartyA": "600992",
"PartyB": "600000",
"AccountReference": "INV-778",
"Requester": "254712345678",
"Remarks": "Supplier invoice 778",
"QueueTimeOutURL": "https://example.com/payments/b2b/timeout",
"ResultURL": "https://example.com/payments/b2b/result"
}
```
- (Note Safaricom's own spelling: `RecieverIdentifierType`.)
- | CommandID | Use |
- |---|---|
- | `BusinessPayBill` | Pay to a Paybill (needs `AccountReference`) |
- | `BusinessBuyGoods` | Pay to a Till / merchant store |
- | `DisburseFundsToBusiness` | Move funds utility → utility |
- | `BusinessToBusinessTransfer` | Working → working account transfers |
- | `MerchantToMerchantTransfer` | Between merchant accounts |
+ `RecieverIdentifierType` is spelled exactly as Daraja spells it.
- Identifier types: `1` MSISDN, `2` Till number, `4` Organisation shortcode.
+ | CommandID | PartyB | RecieverIdentifierType | AccountReference |
+ |---|---|---|---|
+ | BusinessPayBill | Paybill | 4 | Account number at the receiver |
+ | BusinessBuyGoods | Till | 2 | Any reference |
+ | BusinessPayToBulk | Your B2C shortcode | 4 | Any reference. Moves MMF/working funds into the B2C utility account. The initiator needs the "Org Business Pay to Bulk API initiator" role |
+ | PayTaxToKRA | 572572 | 4 | KRA PRN, sent to a different endpoint (see `daraja-tax-remittance`) |
+ - `Requester` is optional: the customer's phone, for the record.
+ - Validate the body: `python ../mpesa-daraja/scripts/validate_request.py b2b body.json`
+
## Responses
- Sync: `ResponseCode "0"` + `ConversationID` + `OriginatorConversationID` → store `PENDING`.
+ - **Sync**: `ResponseCode "0"`, `ConversationID` and `OriginatorConversationID`. Store the record as PENDING.
+ - **Async Result**: the same envelope as B2C.
+ - `ResultParameters` includes `Amount`, `DebitAccountBalance`, `DebitPartyAffectedAccountBalance` (`Working Account|KES|…`), `TransCompletedTime`, `ReceiverPartyPublicName` and `Currency`.
+ - A `QueueTimeOutURL` hit means **unknown**: run `transaction_status` before any retry.
- Async `Result` on `ResultURL` has the same envelope as B2C (`ResultCode`, `TransactionID`, `ResultParameters` with `Amount`, `DebitAccountBalance`, `TransCompletedTime`, `ReceiverPartyPublicName`, …). Treat `QueueTimeOutURL` as "unknown — check Transaction Status".
+ ## B2B Express Checkout (USSD push to a till)
- ## Related: B2B Express Checkout (USSD push to a merchant)
+ The vendor (you) asks a merchant to pay your Paybill from their Till. The merchant's operator gets a USSD prompt asking for their operator ID and PIN.
- `POST /v1/ussdpush/get-msisdn` pushes a prompt to a merchant till operator to pay a vendor Paybill; result posts to the `callbackUrl` you pass. Enable it on your app before use.
+ `POST /v1/ussdpush/get-msisdn`
+ ```json
+ {"primaryShortCode": "000001", "receiverShortCode": "000002", "amount": "100",
+ "paymentRef": "INV-55", "callbackUrl": "https://example.com/b2b-express/callback",
+ "partnerName": "Vendor Ltd", "RequestRefID": "<uuid per request>"}
+ ```
+
+ - The acknowledgment is `{"code": "0", "status": "USSD Initiated Successfully"}`.
+ - Callback when cancelled: `{"resultCode": "4001", "resultDesc": "User cancelled transaction", "requestId": "…"}`
+ - Callback on success: `{"resultCode": "0", "transactionId": "RDQ01NFT1Q", "status": "SUCCESS", "conversationID": "…", "requestId": "…"}`
+ - Correlate on `requestId` (your `RequestRefID`).
+
## Pitfalls
- - Using the wrong identifier type for a Till vs Paybill.
- - Insufficient balance in the *specific* account type (utility vs working).
- - Initiator lacks the "B2B ORG API initiator" role in the org portal → `2001`/permission errors.
+ - Identifier type 4 sent for a Till. Use 2.
+ - Insufficient balance in the *specific* account being debited (working vs utility).
+ - The initiator lacks the B2B role (`21` / `2001`).
+ - Sandbox and production certificates mixed up when encrypting the SecurityCredential.
+
+ ## MCP
+
+ - `mpesa:mpesa_b2b_payment(receiver_shortcode, amount, account_reference, command=…)` sets the identifier types automatically.
+ - `mpesa:mpesa_b2b_express_checkout(...)` runs B2B Express Checkout.