Community
Dennis Kipkorir
Dennis KipkorirTech
Oct 11

Anyone else building payment reconciliation tools? Here's my M-Pesa CSV matcher

I've been working on a small side project to help a friend who runs a retail shop in Nakuru. He gets M-Pesa payments from customers but keeps manual records in Excel, so reconciling at the end of the day is a mess. I built a simple tool that takes the M-Pesa statement CSV (the one you download from the business portal) and his own sales spreadsheet, then matches transactions by amount, phone number, and timestamp within a 5-minute window. It flags unmatched ones for manual review. Stack is Python with pandas and a little Flask for a local web UI, SQLite for storage. Nothing fancy, but it saves him about an hour a day. The hardest part was handling the timestamp format M-Pesa uses (it's like dd/mm/yyyy hh:mm:ss but only when the locale is set right), and also the fact that some customers send exact amounts but others round up, so I had to add a tolerance. Eventually I just normalized everything to UTC first and that killed most of the edge cases. If anyone's done similar, I'm curious how you handle partial payments or people who pay in two installments for one invoice. Happy to share the repo if there's interest, it's rough but it works.

6

Replies

6
AM
AmaniOct 11

Hey, this is a super practical project. For partial payments, one approach is to break each invoice into lines and match against a running total per customer, I've seen a few folks do that by grouping by phone number before matching. If you're sharing the repo, I'd love to take a look at how you handled the timezone normalization.

Joshua Ssemakula
Joshua SsemakulaOct 11

Nice one, the 5-minute window and tolerance are clever touches. How did you handle the edge case where two different customers send the exact same amount within that window? Did you prioritize phone number match over timestamp, or just flag both for review?

Kevin Mwangi
Kevin MwangiOct 11

This is really neat, man. The timestamp format from M-Pesa has tripped me up too, I ended up just telling pandas to parse it as `dd/mm/yyyy` explicitly with `dayfirst=True` and that sorted most of it. For partial payments, I've seen people add a "payment reference" column manually in the sales sheet, then match on that first before falling back to amount + phone. Saves a lot of headache. Would love to see your repo, even if it's rough, these tools help a ton for small shops around here.

Halima Abdi
Halima AbdiOct 11

This is so useful, honestly. I know a few people running shops in Mombasa who would love something like this. For the partial payments, have you tried grouping by phone number first and then matching against the oldest unpaid invoice? Curious what you ended up doing there.

Faith Chebet
Faith ChebetOct 12

This is such a practical build, honestly. For the partial payments, what worked for me was grouping by the customer's phone number first, then matching against the oldest unpaid invoice. It's not perfect, but it cut down the manual review to just a few stragglers. Also, dayfirst=True saved me too, that M-Pesa timestamp is a menace. Would love to see the repo if you're sharing it, these tools are gold for small shops here.

Mark Ochieng
Mark OchiengOct 12

This is exactly the kind of tool small shops around here need, nice work. For the two installment case, I'd keep a running balance per phone number and match the second payment against whatever's left on the invoice, that handles most of it without overcomplicating things. Also, would love to see the repo when you get a chance, even rough is fine.