M-Pesa Integration: What Runs in Each Direction
Money in and money out through your own Daraja credentials, with the one feature that removes a daily job: a C2B payment quoting an invoice number attaches itself to that invoice. What is built in both directions, and the four gaps worth knowing before you plan a payout process around it.
Almost every Kenyan business runs a daily ritual nobody has ever costed: somebody opens the M-Pesa statement, reads receipt numbers, and works out which customer paid which invoice. It takes between twenty minutes and two hours depending on volume, it is done by whoever is most trusted rather than whoever is cheapest, and it produces nothing except a state that should have existed automatically.
That ritual is what a payments integration is actually for. Everything else — the push notification on the customer's phone, the QR code, the disbursements — is useful, and none of it changes anybody's day the way the matching does.
What runs in each direction
The Daraja surface, honestly
| Capability | Built |
|---|---|
| STK push — payment prompt on the customer's phone | Yes |
| STK query — check the state of a prompt you sent | Yes |
| Dynamic QR generation | Yes |
| C2B URL registration and confirmation handling | Yes |
| Auto-match of a C2B payment to an invoice by invoice number | Yes |
| B2C — pay out from your shortcode to a phone number | Yes |
| B2B — pay out from your shortcode to a till or paybill | Yes |
| Transaction reversal | Yes |
| Statement feed to reconcile against | No |
| Bulk payout run — approve a list, then send | No |
| Paying someone who is not a registered supplier | No |
Built and maintained Configurable by you, not maintained by us Not built
Funds move through your organisation's own credentials and land in your own till. We are never in the settlement path, we never hold your money, and there is no float account anywhere in this. That is a positioning statement and it is also the honest answer to the question every finance manager asks second.
The auto-match, and its one condition
When a customer pays your paybill or till and quotes the invoice number as the account reference, the confirmation callback finds that invoice, attaches the payment, and updates the balance. It is idempotent, so a repeated callback does not pay the invoice twice. A payment quoting nothing recognisable is still recorded — it simply sits unattached, which is correct behaviour: losing the receipt would be worse than leaving it unmatched.
The condition is the account reference. Which means the operational work moves from reconciling afterwards to telling customers what to type, and that is a far cheaper problem.
Making the reference habit stick
- Put the invoice number on the invoice as the account reference, in words, next to the paybill number. Not in the footer — next to the number people are about to type.
- Use invoice numbers a person can read over a phone. Long strings with mixed case get mistyped and then mismatch.
- Check the unattached receipts weekly. A pattern in what people type wrong is worth more than the individual matches — usually it tells you your invoice layout is confusing.
- For recurring customers, send the reference in the reminder message as well as on the invoice.
- Accept that a proportion will never quote it. Aiming for 80% matched automatically is a large win; aiming for 100% is how people conclude the feature failed.
Paying out: what works and what will surprise you
B2C to a phone number and B2B to a till or paybill both work, matched back to the originating request so you can tell which payment was which. Both need initiator credentials configured for your organisation in addition to the collection credentials — a separate step, and they fail with a clear message rather than silently if it has not been done.
Then four limits that shape your process rather than annoying you:
-
The payee has to be a registered supplier
There is no way to pay an arbitrary person. Member payouts, beneficiary payments and casual-worker wages therefore have no home in the payout flow — a real constraint for cooperatives, SACCOs and NGOs, and one worth knowing before you design around it.
-
Every payment is raised individually
No batch, no schedule, no approve-a-list-then-send. Paying forty suppliers is forty actions. This is the single most requested thing in this area and it is not built.
-
Payroll does not actually pay
The payroll run records each employee as paid and posts the journal, and it calls no rail. Do not read a payroll marked as paid as money having moved — the transfer is a separate act, done outside the system today.
-
There is no statement feed
Nothing hands over statement lines for you to reconcile against, from M-Pesa or from any bank. Matching happens at the callback or not at all, which is why the account-reference habit matters so much: there is no second chance to match later in bulk.
The third point is the one to repeat out loud
A payroll run showing a mobile-money method against each employee is a label describing intent, not a record of a transfer. Anyone reading the payments register needs to know that, because it is exactly the kind of thing that reads as settled and is not.
What it changes in practice
A distributor invoicing 300 customers a month
The time saving is real and it is not the main benefit. The main benefit is that whoever chases receivables is working from a balance that is current rather than three days old — which changes the tone of the conversation, because you are no longer occasionally asking someone to pay an invoice they settled on Tuesday.
Setting it up
Collections need your Daraja consumer key and secret, your shortcode and passkey, and the callback URLs registered. Disbursements additionally need initiator credentials. All of it is your own — obtained from Safaricom under your own business name, held for your organisation, revocable by you at any time.
Test in the sandbox first, then move to production, and make the first live transaction a small one to yourself. The failure you are looking for is a callback URL that works from your machine and not from the internet, which is the most common setup problem and is invisible until a real payment arrives.
For the wider reconciliation picture — including why there is no bank side to this — bank reconciliation explained is the companion piece, and mobile money reconciliation across Africa covers what changes when you operate outside Kenya.
Our take
The auto-match is the reason to do this, and its whole dependency is whether customers quote the invoice number — so spend your effort on the invoice layout and the reminder wording rather than on the integration. Both payout directions work through your own credentials. Plan around three real limits: the payee must be a registered supplier, there is no batch payout, and a payroll marked as paid has not moved any money.
Stop reading the statement every morning
STK push, dynamic QR, C2B confirmation with idempotent auto-match to the invoice, plus B2C and B2B disbursement — all through your own Daraja credentials, with funds landing in your own till.
See plans & pricingFrequently asked questions
What M-Pesa capabilities are built?
Collections: STK push, STK query, dynamic QR generation, C2B URL registration and confirmation handling, and idempotent auto-matching of a C2B payment to an invoice when the payer quotes the invoice number. Disbursements: B2C to a phone number and B2B to a till or paybill, both matched back to the originating request. Plus transaction reversal. All of it runs on your own Daraja credentials with funds landing in your own till.
How does automatic invoice matching work?
A customer pays your paybill or till and quotes the invoice number as the account reference. The confirmation callback finds that invoice, attaches the payment and updates the balance, idempotently, so a repeated callback cannot pay it twice. A payment quoting nothing recognisable is still recorded but sits unattached — losing the receipt would be worse than leaving it unmatched.
Are you in the settlement path?
No. The integration uses your organisation's own Daraja credentials and your own shortcode, so funds move directly between your customer and your till. We never hold your money and there is no float account anywhere in the design.
Can we pay a list of suppliers in one batch?
No. Every disbursement is raised individually — there is no batch, no schedule and no approve-a-list-then-send. Paying forty suppliers is forty actions. It is the most requested capability in this area and it is not built.
Can we pay someone who is not a registered supplier?
No. The payee must be a vendor record, so member payouts, beneficiary payments and casual-worker wages have no home in the payout flow. This is a real constraint for cooperatives, SACCOs and NGOs and it is worth confirming against your own process before you plan around the feature.
Does the payroll run actually send money?
No, and this is important. A payroll run records each employee as paid and posts the journal, but it calls no payment rail — a mobile-money method shown against an employee is a label describing intent, not a transfer. The money moves separately, outside the system today. Anyone reading the payments register needs to know that, because it reads as settled and is not.
Is there a bank or M-Pesa statement feed to reconcile against?
No. Nothing hands over statement lines from M-Pesa or any bank, so matching happens at the callback or not at all. That is why the account-reference habit matters so much — there is no second chance to match a batch of receipts later.