Skip to main content

Business Buy Goods

Make business-to-business payments to Buy Goods accounts using M-Pesa by initiating payment requests and handling asynchronous result notifications.

User Stories

  • As a fintech product owner, I want to programmatically initiate B2B payments so that suppliers receive funds immediately after approval.
  • As an integrations developer, I want a simple client and clear webhook callbacks so I can implement reliable end-to-end flows with minimal boilerplate.
  • As a billing operations engineer, I want result and timeout notifications with acknowledgements so I can reconcile transactions and trigger retries or alerts when needed.
  • As a reseller partner, I want a tested SDK and examples so I can onboard quickly and reduce integration defects.

Parameters Definition

ParameterTypeDescription
Initiatorrequired
str
StringM-Pesa API operator username (must be pre-approved by Safaricom).
SecurityCredentialrequired
str
StringEncrypted security credential of the initiator (base64 encoded).
Amountrequired
int
IntegerTransaction amount to be transferred between accounts.
PartyArequired
int
IntegerShortcode from which money is deducted for the payment.
PartyBrequired
int
IntegerShortcode to which money is credited (Buy Goods till number).
AccountReferencerequired
str
StringAccount number associated with the payment.
Requester
str
StringConsumer's mobile number (optional).
Remarksrequired
str
StringAdditional transaction information (maximum 100 characters).
QueueTimeOutURLrequired
str
StringHTTPS endpoint that will receive timeout notifications.
ResultURLrequired
str
StringHTTPS endpoint that will receive result notifications.
Occassion
str
StringAdditional transaction information (optional).
CommandID
str
StringCommand ID for the transaction (default: 'BusinessBuyGoods').
SenderIdentifierType
int
IntegerIdentifier type for sender (default: 4 for shortcode).
RecieverIdentifierType
int
IntegerIdentifier type for receiver (default: 4 for shortcode).
OriginatorConversationID
str
StringUnique ID for the request message (returned in response).
ConversationID
str
StringUnique ID for the transaction (returned in response).
ResponseCode
str
StringStatus code of the request (0 means success, returned in response).
ResponseDescription
str
StringStatus message describing the request outcome (returned in response).

Overview

Business Buy Goods enables businesses to make payments to Buy Goods accounts. It's an asynchronous operation with callbacks for results and timeouts.

Quick Setup

Python
# Example: initiate Business Buy Goods payment using the high-level client
from mpesakit import MpesaClient
client = MpesaClient(consumer_key="...", consumer_secret="...", environment="sandbox")
resp = client.b2b.buygoods(
initiator="API_Username",
security_credential="encrypted_credential",
amount=239,
party_a=123456,
party_b=654321,
account_reference="353353",
requester="254700000000",
remarks="Payment for goods",
result_url="https://your.example/result",
queue_timeout_url="https://your.example/timeout",
occassion="Purchase"
)
if resp.is_successful():
print("Business Buy Goods payment initiated successfully")
else:
print("Business Buy Goods payment failed:", resp.ResponseDescription)

Webhook Handling (Result & Timeout)

Python
# Example: simple FastAPI endpoints for Business Buy Goods Result and Timeout
from fastapi import FastAPI, Request, HTTPException
from mpesakit.business_buy_goods import BusinessBuyGoodsResultCallback, BusinessBuyGoodsResultCallbackResponse, BusinessBuyGoodsTimeoutCallback, BusinessBuyGoodsTimeoutCallbackResponse
from mpesakit.security.ip_whitelist import is_mpesa_ip_allowed
app = FastAPI()
@app.post("/b2b/buygoods/result")
async def buygoods_result(request: Request):
payload = await request.json()
caller_ip = (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip()
if not is_mpesa_ip_allowed(caller_ip):
raise HTTPException(status_code=403, detail="forbidden")
data = BusinessBuyGoodsResultCallback(**payload) # will validate incoming fields
# process the result (update database, notify user, etc.)
ack = BusinessBuyGoodsResultCallbackResponse()
return ack.model_dump(mode="json")
@app.post("/b2b/buygoods/timeout")
async def buygoods_timeout(request: Request):
payload = await request.json()
caller_ip = (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip()
if not is_mpesa_ip_allowed(caller_ip):
raise HTTPException(status_code=403, detail="forbidden")
# process timeout notification (log, retry logic, etc.)
ack = BusinessBuyGoodsTimeoutCallbackResponse()
return ack.model_dump(mode="json")

Responses & Helpers

Example Business Buy Goods Success Response
{
"OriginatorConversationID": "5118-111210482-1",
"ConversationID": "AG_20230420_2010759fd5662ef6d054",
"ResponseCode": "0",
"ResponseDescription": "Accept the service request successfully."
}

Error Handling

Python
# Handle errors when calling the service
try:
resp = client.b2b.buygoods(...)
except Exception as exc:
# The underlying HTTP client may raise exceptions on network errors; log and retry as appropriate
print("Business Buy Goods payment failed:", exc)

Testing & Expected Behaviors

  • Business Buy Goods:

    • The service posts to /mpesa/b2b/v1/paymentrequest with Authorization header set via TokenManager.
    • Responses are returned as BusinessBuyGoodsResponse instances.
    • The implementation tolerates a common provider typo ("OriginatorCoversationID") and maps it to OriginatorConversationID.
  • Validation:

    • Incoming payloads are validated against BusinessBuyGoodsResultCallback and BusinessBuyGoodsTimeoutCallback. Missing required fields or invalid formats will raise validation errors.
    • Use the provided response schemas for acknowledgements; invalid codes are rejected by the model validator.

Next Steps