Automatic M-Pesa reconciliation: matching rules that never double-count
How to match M-Pesa paybill, till and bank statement lines to the payments your system expected - which fields to match on, what makes a match safe, and the rules that stop one payment being counted twice.
· 8 min read · Noria Technologies
Reconciliation is the part of payments nobody demos. A customer pays on the paybill with a typo in the account number, a callback never arrives, a payment is reversed three days later, and at month end someone sits with the M-Pesa statement and a spreadsheet trying to work out which invoice each line belongs to.
Automating it is mostly a matter of deciding, in advance, what counts as a safe match and what does not. This guide sets out matching rules that settle the clear cases automatically and leave only the genuinely unclear ones for a person.
What you are matching
On one side are the payments your system expected: charges that were sent and are still waiting, such as an STK push with no confirmed outcome or an invoice waiting on a paybill payment.
On the other side is what actually arrived: lines on the M-Pesa organisation statement or a bank statement, each with a receipt number, amount, time, and usually a phone number and an account reference.
The goal is to settle each expected payment exactly once, from the line that genuinely paid it, and to touch nothing else.
Rule 1: never create payments from a statement
A statement line that matches nothing is information, not a payment to invent. If reconciliation creates a payment for every unmatched line, a reversed transaction, a payment meant for another business on a shared paybill, or a line imported twice all become phantom revenue.
Statement import should only ever settle charges that already exist. Unmatched lines go to a list for a person.
Rule 2: the receipt number is unique
Every M-Pesa transaction has a receipt number, such as TKE4X7Q2LM. Store it with every settled payment under a unique constraint in your database.
That one constraint protects you from the most common double-count: the same payment reported by the callback, by your status poll, and again by the statement. Whichever arrives first settles the charge; the others are recognised as the same receipt and ignored.
Rule 3: match on several fields, and require all of them
A safe automatic match agrees on all of these:
| Field | Why |
|---|---|
| Amount | Must equal what the charge expected. M-Pesa settles whole shillings, so compare exactly. |
| Account reference or phone | The thing that ties the payment to a customer or invoice. |
| Provider and account | A line from your paybill should only settle charges sent to that paybill. |
| Time window | The payment arrived after the charge was created, and not long after. |
Amount alone is never enough. On a busy paybill, many customers pay the same amount on the same day.
Rule 4: one candidate or none
If a statement line could settle exactly one open charge, settle it. If it could settle two, settle neither and flag it. Picking the oldest, the newest or the first one found turns a small ambiguity into a wrong ledger that someone discovers months later.
The same applies the other way: if two statement lines could both settle one charge, leave both for a person.
Rule 5: keep projects and accounts apart
If you run more than one business or product on the same system, a reference like INV-1001 can exist in both. Matching must stay inside the account the money was paid into, so a payment to one paybill can never settle an invoice belonging to another.
Rule 6: look it up when in doubt
When a customer insists they paid and gives you a transaction code, do not type the amount in by hand. Ask the provider about that code directly and record what the provider says. The amount, status and time should come from the provider’s own answer, never from what someone typed.
Handling the awkward cases
- Wrong account number on a paybill payment. Rule 3 fails, so it is flagged. A person can then match it by phone and amount, with the receipt recorded.
- Underpayment. Settle what was received and mark the charge underpaid, rather than pretending it is paid in full.
- Reversals. A reversed payment appears as a separate line. Record the reversal against the original payment instead of deleting it.
- Duplicate statement imports. Rule 2 makes importing the same statement twice harmless.
How Noria Pay reconciles
Noria Pay’s reconciliation follows these rules:
- Statement import, for M-Pesa and bank statements, only settles charges that already exist and match unambiguously on amount, reference or phone, provider and time window. It never creates a payment.
- Settlement happens once from a single open state, and the provider receipt is unique, so a callback, a poll, a provider notification and a statement row cannot settle the same payment twice.
- Matching stays inside the project and the account the money was paid into.
- Any payment can be looked up by its transaction code. Pay asks the provider again and records or reconciles only from the provider’s answer.
- Payments that SasaPay or KCB Buni report without being started, such as a customer paying the paybill directly, are confirmed with the provider, matched against open charges first, and recorded once.
The result is a short list of genuinely unclear lines for a person, instead of a whole statement.
Checklist
- Statements settle existing charges only.
- Receipt numbers are unique in your database.
- Matches require amount, reference or phone, account and time window together.
- Ambiguous matches are flagged, never guessed.
- Matching never crosses accounts or projects.
- Lookups record the provider’s answer, not typed values.