Taking a sale, matching the payment, keeping the books. On a phone, often on a slow network, with a merchant who will abandon the app the first time the numbers disagree with their wallet. MOMO Lens does that in select markets. That is the bar I mean when I say production: not a login screen, not a dashboard that only works on office wifi. I do this work from Manchester.
What production actually means here
A demo that works on office wifi is not the product. Merchants take money on 2G and 3G, sometimes in a stall with one bar of signal. The ledger has to survive a duplicate SMS, a reversal that arrives hours later, a sale captured while offline, and a retry that must not double-count. Those are not edge cases. They are Tuesday afternoon. If you leave them for “v2”, the merchant will trust a notebook again. My opinion is blunt: production SaaS in this lane is the set of failures you have already designed for, not the happy path you showed in a pitch.
Phone for the stall, web for the exceptions.
React Native is what the merchant uses to take a sale: products, cash or mobile money, a receipt before the customer walks off. Next.js is what someone uses when a match is wrong, a reversal needs review, or the day’s cash-up does not line up. Same ledger, different jobs, different permission sets. If you only ship the phone, the ugly cases end up in WhatsApp screenshots and a spreadsheet nobody owns. If you only ship the web, nobody at the stall will use it. I build both surfaces against one NestJS API so an exception on the web is correcting the same books the phone wrote, not a second source of truth.
The ledger is the product
Reconciliation is a matching problem, not a form. You get a payment signal (API event or SMS), a sale the merchant recorded, and a pile of ambiguity: phone numbers with different formats, amounts that almost match, timing windows, duplicates, reversals. Automated rails such as Flutterwave and fee-sensitive manual SMS have to live in one system, because many merchants mix both in the same day. A wrong match is worse than no match. Confidence scoring, a queue, and an operator path to confirm or reject beat a spreadsheet every time. That engine is why MOMO Lens is a product instead of a payment button with a hope.
Offline is not a nice-to-have
React Native with an offline queue. Capture the sale on device. Sync when the radio comes back. Never corrupt the books because the request retried twice or the SMS arrived late. That means idempotent writes, clear sync state, and UI that tells the merchant what is pending versus settled. It is slower to build than a happy-path checkout that assumes London wifi. It is also why the thing is still in production in markets where the network is the constraint, not the exception. If your SaaS cannot tell the truth after a drop, it is not production. It is a demo with a store listing.
What I shipped
NestJS, PostgreSQL, Redis, and Prisma on the backend, with RBAC and adapters for telco and payment rails. React Native on the App Store and Play Store in select locations. Next.js for operators who fix matches and read the day. Coverage designed for seven African markets and more than twelve mobile-money providers, because the product shape changes by country even when the ledger rules stay the same. The merchant loop is complete: sales, payments, matching, receipts, expenses, reporting. That is the case I point at when someone asks what production SaaS looks like in my hands.