C2B — Customer to Business

C2B handles payments the customer starts — paying your shortcode (Pay Bill / Buy Goods). You register two URLs we call during the flow: one to validate, one to confirm.

The flow

  1. Validation — called first, before money moves. Inspect the bill reference and amount; accept or reject.

  2. Money moves — if accepted, the customer's payment is applied to your collection account.

  3. Confirmation — called once it completes. This is your signal to fulfil the order.

POST /partner/c2b/register

Field

Type

Required

Description

validation_url

string

yes

We POST proposed payments here; you accept or reject.

confirmation_url

string

yes

We POST completed payments here; you fulfil.

response_type

string

no

completed or cancelled — the fallback if validation is unreachable.

curl -X POST https://<your-operator>/api/bridgepay/v1/partner/c2b/register \
  -H "Authorization: Bearer at_..." -H "Content-Type: application/json" \
  -d '{
    "validation_url":   "https://your-app.com/c2b/validate",
    "confirmation_url": "https://your-app.com/c2b/confirm",
    "response_type":    "completed"
  }'

Allowed mode (default response)

response_type is the fallback decision if your validation endpoint is unreachable or times out:

Value

Behaviour when validation is down

completed

Accept the payment by default (don't block paying customers).

cancelled

Reject by default (strict; only accept when you explicitly validate).

Validation request & expected response

We POST the proposed payment to your validation URL. Reply 200 with an accept or reject:

// we POST to your validation_url
{ "TransAmount": 500, "BillRefNumber": "ORDER-77",
  "MSISDN": "2547xxxxxxxx", "BusinessShortCode": "555550" }

// you reply 200 with:
{ "ResultCode": 0, "ResultDesc": "Accepted" }   // accept
{ "ResultCode": 1, "ResultDesc": "Rejected" }   // reject

Confirmation payload

When the payment completes we POST this to your confirmation URL (signed — verify it, see Webhooks):

{
  "TransactionType": "Pay Bill",
  "TransID": "BP2600000122",
  "TransAmount": 500,
  "BusinessShortCode": "555550",
  "BillRefNumber": "ORDER-77",
  "MSISDN": "2547xxxxxxxx",
  "OrgAccountBalance": 1234.00
}

Handle it

Node.js

app.post("/c2b/confirm", (req, res) => {
  if (!verify(req.rawBody, req.header("X-PesaBridge-Signature"), SECRET))
    return res.sendStatus(401);
  const { TransID, TransAmount, BillRefNumber } = req.body;
  fulfilOnce(TransID, BillRefNumber, TransAmount); // idempotent on TransID
  res.json({ ResultCode: 0, ResultDesc: "Received" });
});

Python (Flask)

@app.post("/c2b/confirm")
def confirm():
    if not verify(request.get_data(), request.headers.get("X-PesaBridge-Signature"), SECRET):
        return "", 401
    b = request.get_json()
    fulfil_once(b["TransID"], b["BillRefNumber"], b["TransAmount"])
    return {"ResultCode": 0, "ResultDesc": "Received"}

Test it (sandbox)

curl -X POST https://<your-operator>/api/bridgepay/v1/partner/c2b/simulate \
  -H "Authorization: Bearer at_..." -H "Content-Type: application/json" \
  -d '{ "msisdn":"2547xxxxxxxx", "amount":500, "bill_ref":"ORDER-77" }'

Be idempotent

Key fulfilment on TransID so a redelivered confirmation never ships an order twice.

Was this page helpful?