Offline Payment Relay
Product lab
An experiment in resilient device-to-device payment workflows for unreliable connectivity.
The problem
A merchant with unreliable connectivity still needs to keep selling — but most payment flows assume the network is there at the exact moment of the sale.
The interesting question isn’t “can two phones talk offline” (they can); it’s whether transaction information can be exchanged between nearby devices with enough integrity that both sides hold a trustworthy record to reconcile later — without ever pretending that money actually moved.
The approach
MerchantOS is a Flutter prototype that lets a merchant device and a customer device exchange a cryptographically signed promise-to-pay over Bluetooth Low Energy, with QR codes as a full-fidelity fallback. Each side signs with a device-held Ed25519 key; both sides store immutable, matched receipts in their own local SQLite ledger.
The boundary is explicit in code and UI alike: exchanging transaction data offline is not completing an authorized payment offline. Settlement in this build is a local simulator, the wallet is labeled simulated on screen, and a licensed payment partner exists only as an interface awaiting a real integration.
My role — Product thinking, mobile architecture and offline workflow exploration. No official UPI or financial-network integration is claimed.
How it works, step by step
Offline exchange — signed data, not money
The implemented device-to-device flow. Amounts and identities are dummy values.
01Merchant creates the transaction
The merchant enters an amount. The transaction is recorded locally first — if nothing else happens, there’s still a truthful record that it was offered.
Select any node in the diagram to inspect it.
- Person
- This project
- Internal layer
- Connected system
- Data at rest
- Planned (not built yet)
- Success
- Info
- Pending
Example output
Transaction certainty — the state ladder
- Recorded
- exists on this device
- Exchanged
- signed by both devices, receipts match
- Authorized offline
- settlement pending — no money moved
- Simulated settlement
- local simulator only, labeled in the UI
- Real settlement
- planned — requires a licensed partner
The product’s own vocabulary, shown with dummy states. Each rung is a different level of certainty, and the UI never blurs them.
Architecture
Architecture
Offline-first taken literally: in this build, nothing has a network to talk to.
- Connected system
- Internal layer
- This project
- Data at rest
- Planned (not built yet)
- Connected system
- Internal layer
- This project
- Data at rest
- Planned (not built yet)
Strict clean-architecture layering per feature (presentation → application → domain → data), enforced by project convention and ADRs. NFC exists only as a token-codec stub with no radio; it hands off to BLE discovery.
Key engineering decisions
- Offline-first state
- BLE exchange
- QR fallback
- Reconciliation model
- Device-to-device workflow
Switch to the technical view for the engineering trade-offs behind these.
Challenges & limitations
- No real UPI, NPCI, bank or PSP integration — settlement is a local simulator, labeled as such in the UI, and a licensed partner is an empty interface awaiting a real integration.
- This build has no network client at all: “reconcile once online” is a designed seam (interfaces + a local sync-event trail), not working sync.
- BLE is implemented and its message framing is unit-tested, but it has not been through physical-device acceptance — the project’s own release review says “not ready for pilot,” and this page agrees with it.
- A device-reset / recovery flow does not exist; local encrypted backup and restore do.
What I learned
Offline UX is honesty engineering. Recorded, exchanged, authorized and settled are four different certainties, and the product only stays trustworthy if every screen — and every diagram — refuses to round one up to the next.
Project status
259 passing tests, clean static analysis and CI-built debug artifacts, with the project’s own production-acceptance review honestly concluding “not ready for pilot”: no signed release binaries and no physical-device BLE acceptance yet.