Skip to content

Customer to Business

C2B covers payments a customer initiates themselves — through the M-Pesa app, USSD, SIM toolkit or a scanned QR code. You register two URLs and Safaricom notifies you when money arrives.

POST mpesa/c2b/v2/registerurl
Daraja::c2b()->registerUrls(
confirmationUrl: 'https://your-domain/daraja/c2b/confirmation',
validationUrl: 'https://your-domain/daraja/c2b/validation',
);

Both default to the configured URLs, so with DARAJA_C2B_CONFIRMATION_URL and DARAJA_C2B_VALIDATION_URL set you can call it bare, or use the command:

Terminal window
php artisan daraja:register-urls

responseType decides what M-Pesa does when your validation URL is unreachable:

Value Behaviour
Completed Accept the payment anyway (default)
Cancelled Reject the payment

Spelling and case are exact — Safaricom rejects anything else.

Fires after a successful payment:

use Starnerz\LaravelDaraja\Events\C2BPaymentReceived;
public function handle(C2BPaymentReceived $event): void
{
$t = $event->transaction;
Payment::create([
'receipt' => $t->transactionId,
'amount' => $t->amount,
'account' => $t->billReferenceNumber,
'payer' => $t->fullName(),
'phone' => $t->msisdn, // masked: 2547 ***** 126
]);
}

The MSISDN is masked in v2. v1 sent a SHA-256 hash instead; neither gives you the full number.

Validation is optional and disabled by default — email apisupport@safaricom.co.ke to enable it on your short code. Once on, Safaricom asks permission before completing each payment and waits roughly 8 seconds.

Because that is synchronous, it uses a decision callback rather than an event:

// In a service provider's boot method
use Starnerz\LaravelDaraja\Data\Callbacks\C2BTransaction;
use Starnerz\LaravelDaraja\Facades\Daraja;
Daraja::validateC2BUsing(function (C2BTransaction $t): bool|string {
$invoice = Invoice::where('reference', $t->billReferenceNumber)->first();
if (! $invoice) {
return 'C2B00012'; // Invalid Account Number
}
if ((float) $t->amount < $invoice->balance) {
return 'C2B00013'; // Invalid Amount
}
return true;
});

Return true to accept, false to reject generically, or a specific code:

Code Meaning
C2B00011 Invalid MSISDN
C2B00012 Invalid Account Number
C2B00013 Invalid Amount
C2B00014 Invalid KYC Details
C2B00015 Invalid Short code
C2B00016 Other Error

Keep the callback fast. Queue anything slow — a timeout falls back to your responseType.

Sandbox only. The package throws if mode is live.

Daraja::c2b()->simulatePayBill('0712345678', 500, 'INV-77');
Daraja::c2b()->simulateBuyGoods('0712345678', 500);

If your endpoint was down, Pull Transactions retrieves anything from the last 48 hours.