Mobile money is a rail, not a plugin
In much of East Africa, mobile money is not an alternative payment method, it is the payment method. Treating M-Pesa as a bolt on is how integrations fail at two in the morning when nobody is watching. Done properly, the Daraja API is a first class rail that deserves the same rigour you give cards, and getting it right is a capability very few global firms actually have.
Normalize the phone number before anything else
The same number arrives as 0712345678, +254712345678, and 254712345678 depending on where the user typed it. Pick one canonical form and convert to it at the edge, before the value touches the API or the database. Every downstream bug in a mobile money system can be traced back to two records that were the same person in two formats.
function normalizeMsisdn(input: string): string {
const digits = input.replace(/\D/g, "");
if (digits.startsWith("254")) return digits;
if (digits.startsWith("0")) return "254" + digits.slice(1);
if (digits.startsWith("7") || digits.startsWith("1")) return "254" + digits;
throw new Error("unrecognized phone format");
}
Money is an integer
Kenyan shillings are handled as whole numbers. Represent amounts as integers from end to end and never let a floating point value near the money path, because floating point arithmetic reconciles wrong in ways that only show up in the monthly totals, long after the bug shipped.
Refresh the token before it expires
Access tokens expire on a timer. Refresh proactively, ahead of expiry, and guard the refresh against the race where two requests both notice the token is stale and both try to renew it. A checkout that fails because the token died mid flight is an avoidable lost sale.
Make the callback idempotent
Daraja delivers payment confirmations by calling your endpoint, and it can call more than once for the same payment. Key the handler on the transaction id and record which ones you have processed, so a duplicate callback never credits a ticket or a wallet twice.
const seen = await markProcessed(callback.transactionId); // false if first time
if (seen) return ok(); // already handled, acknowledge and stop
await creditOrder(callback);
Why it matters commercially
Pairing a first class Daraja rail with Stripe for international cards is a rare, differentiated capability, and it is proven in production in Tukutane and Kipaji. A firm that can take mobile money correctly reaches customers that a cards only competitor simply cannot.
