Skip to main content

Customer to Business (C2B)

Manage Customer-to-Business (C2B) flows: register Validation and Confirmation endpoints with Safaricom, validate incoming payments, and send confirmations/acknowledgements.

User Stories

  • As a fintech product owner, I want to programmatically manage C2B payments so that customers 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
ShortCoderequired
int
IntegerOrganization's PayBill or Till shortcode to register URLs for.
ResponseTyperequired
str
StringDefault behavior if ValidationURL cannot be reached. Allowed: 'Completed' or 'Cancelled'.
ConfirmationURLrequired
str
StringHTTPS endpoint that will receive payment confirmation notifications.
ValidationURLrequired
str
StringHTTPS endpoint that will receive validation callbacks before accepting payments.
TransactionTyperequired
str
StringType of transaction in validation payload (e.g. 'Pay Bill', 'Buy Goods').
TransIDrequired
str
StringUnique M-Pesa transaction identifier.
TransTimerequired
str
StringTimestamp of transaction in YYYYMMDDHHmmss format.
TransAmountrequired
float
FloatAmount transacted (whole numbers expected by M-Pesa).
BusinessShortCoderequired
int
IntegerReceiving organization's shortcode included in validation payload.
MSISDNrequired
int
IntegerCustomer mobile number making the payment.
BillRefNumber
str
StringAccount/reference number supplied by payer (PayBill only).
ResultCoderequired
str|int
String/IntegerValidation response code. '0' or one of the defined C2B validation error codes to reject.
ResultDescrequired
str
StringShort description for the validation response (<= 90 chars recommended).
ThirdPartyTransID
str
StringOptional partner transaction id to echo back in responses.

Overview

C2B (Customer-to-Business) covers URL registration with Safaricom (so their platform can call your services), validating incoming payments and acknowledging confirmations.

Quick Setup

Python
# Example: register C2B URLs using the high-level client
from mpesakit import MpesaClient
from mpesakit.c2b import C2BResponseType
client = MpesaClient(consumer_key="...", consumer_secret="...", environment="sandbox")
resp = client.c2b.register_url(
short_code=600999,
response_type=C2BResponseType.COMPLETED,
confirmation_url="https://your.example/confirmation",
validation_url="https://your.example/validation",
)
if resp.is_successful():
print("Registration accepted")
else:
print("Registration failed:", resp.ResponseDescription)

Webhook Handling (Validation & Confirmation)

Python
# Example: simple FastAPI endpoints for Validation and Confirmation
from fastapi import FastAPI, Request, HTTPException
from mpesakit.c2b import C2BValidationRequest, C2BValidationResponse, C2BConfirmationResponse
from mpesakit.security.ip_whitelist import is_mpesa_ip_allowed
app = FastAPI()
@app.post("/c2b/validation")
async def validation(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 = C2BValidationRequest(**payload) # will validate incoming fields
# perform business checks (account exists, limits, etc.)
result = C2BValidationResponse(ResultCode="0", ResultDesc="Accepted", ThirdPartyTransID=data.ThirdPartyTransID)
return result.model_dump(mode="json")
@app.post("/c2b/confirmation")
async def confirmation(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 final payment notification (store transaction, update balance, etc.)
ack = C2BConfirmationResponse() # ResultCode=0, ResultDesc="Success"
return ack.model_dump(mode="json")

Validation Result Codes

Common Result Codes
{
"0": "Accepted",
"C2B00011": "Invalid MSISDN",
"C2B00012": "Invalid Account Number",
"C2B00013": "Invalid Amount",
"C2B00014": "Invalid KYC Details",
"C2B00015": "Invalid Shortcode",
"C2B00016": "Other Error"
}

Responses & Helpers

Example Register URL Success Response
{
"OriginatorConversationID": "7619-37765134-1",
"ConversationID": "AG_20230601_123456789",
"ResponseCode": "0",
"ResponseDescription": "success",
"CustomerMessage": "URLs registered"
}

Validation & Safety Checks

Error Handling

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

Testing & Expected Behaviors

  • Register URL:

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

    • Incoming payloads are validated against C2BValidationRequest. Missing required fields or invalid formats will raise validation errors.
    • Use the provided enums for allowed ResultCode values; invalid codes are rejected by the model validator.
  • Confirmation:

    • Return a C2BConfirmationResponse (ResultCode 0 and ResultDesc "Success") to acknowledge receipt.

Next Steps