Customer to Business (C2B)
Manage Customer-to-Business (C2B) flows: register Validation and Confirmation endpoints with Safaricom, validate incoming payments, and send confirmations/acknowledgements.
NB: C2B Transaction Validation is an optional feature that needs to be activated on M-Pesa. The owner of the shortcode must request activation by emailing apisupport@safaricom.co.ke or M-pesabusiness@safaricom.co.ke if they need their transactions validated before execution.
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
Parameter | Type | Description |
---|---|---|
ShortCoderequired int | Integer | Organization's PayBill or Till shortcode to register URLs for. |
ResponseTyperequired str | String | Default behavior if ValidationURL cannot be reached. Allowed: 'Completed' or 'Cancelled'. |
ConfirmationURLrequired str | String | HTTPS endpoint that will receive payment confirmation notifications. |
ValidationURLrequired str | String | HTTPS endpoint that will receive validation callbacks before accepting payments. |
TransactionTyperequired str | String | Type of transaction in validation payload (e.g. 'Pay Bill', 'Buy Goods'). |
TransIDrequired str | String | Unique M-Pesa transaction identifier. |
TransTimerequired str | String | Timestamp of transaction in YYYYMMDDHHmmss format. |
TransAmountrequired float | Float | Amount transacted (whole numbers expected by M-Pesa). |
BusinessShortCoderequired int | Integer | Receiving organization's shortcode included in validation payload. |
MSISDNrequired int | Integer | Customer mobile number making the payment. |
BillRefNumber str | String | Account/reference number supplied by payer (PayBill only). |
ResultCoderequired str|int | String/Integer | Validation response code. '0' or one of the defined C2B validation error codes to reject. |
ResultDescrequired str | String | Short description for the validation response (<= 90 chars recommended). |
ThirdPartyTransID str | String | Optional 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.
- Use the MpesaClient facade for simple and safe integration: it manages authentication, header injection and returns typed Pydantic models.
- Use the Direct API (C2B service + TokenManager + HttpClient) if you need full control over request/response handling, middleware, or custom error behaviors.
The facade handles token retrieval and attaches Authorization headers so you can call high-level operations like registering C2B URLs with minimal boilerplate.
Quick Setup
# Example: register C2B URLs using the high-level clientfrom mpesakit import MpesaClientfrom 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)
- The facade returns typed Pydantic models (e.g., C2BRegisterUrlResponse) for ergonomic access to fields and helpers like is_successful().
- Authentication tokens are handled transparently by the client.
Webhook Handling (Validation & Confirmation)
# Example: simple FastAPI endpoints for Validation and Confirmationfrom fastapi import FastAPI, Request, HTTPExceptionfrom mpesakit.c2b import C2BValidationRequest, C2BValidationResponse, C2BConfirmationResponsefrom 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 endpoints should return a C2BValidationResponse. Use ResultCode="0" to accept or one of the defined error codes (see Validation Result Codes) to reject.
- Confirmation endpoints should return an acknowledgement (ResultCode 0). This confirms receipt to Safaricom.
Validation Result Codes
{ "0": "Accepted", "C2B00011": "Invalid MSISDN", "C2B00012": "Invalid Account Number", "C2B00013": "Invalid Amount", "C2B00014": "Invalid KYC Details", "C2B00015": "Invalid Shortcode", "C2B00016": "Other Error"}
- Keep ResultDesc concise. The library warns if ResultDesc exceeds 90 characters.
Responses & Helpers
{ "OriginatorConversationID": "7619-37765134-1", "ConversationID": "AG_20230601_123456789", "ResponseCode": "0", "ResponseDescription": "success", "CustomerMessage": "URLs registered"}
- C2BRegisterUrlResponse provides is_successful() which treats any all-zero string (e.g., "0" or "00000000") as success.
- The SDK normalizes minor provider response typos (e.g., 'OriginatorCoversationID') so fields are accessible reliably.
Validation & Safety Checks
- When registering URLs avoid embedding sensitive or provider-related keywords such as 'm-pesa', 'mpesa', 'safaricom' or suspicious file/command keywords (exe, cmd, sql, query). The library warns about such keywords because Safaricom's API may reject them with a 400 error.
Error Handling
# Handle errors when calling the servicetry: 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)
- HTTP or network errors raised by the HttpClient bubble up; wrap calls in try/except for robust production behavior.
- Tests exercise error flows to ensure exceptions propagate when the HTTP client fails.
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
- Implement robust webhook handlers for validation and confirmation. Log and persist notifications to support reconciliation.
- Add observability and retry strategies around register_url calls and webhook processing to handle transient failures.
Related Documentation
- 📡 Webhook Setup Guide - Best practices for building reliable endpoints
- 🏗️ Production Setup - Go-live checklist, security and monitoring