UPI_Without_Internet: How a Mesh Network Becomes a Payment Rail
A Spring Boot proof of concept that treats offline payments as encrypted, store-and-forward instructions, then uses idempotent ingestion to prevent duplicate settlement.
- UPI_Without_Internet is really about making delayed, duplicated packets safe to accept once, not about pretending the network is reliable.
- The repo's sharpest idea is to wrap payment data in an encrypted envelope and push duplicate rejection ahead of expensive decryption and settlement.
- Its simulator turns a hard hardware problem into something you can reason about, test, and demo in plain Spring Boot.
- The project is valuable as a design pattern even before it is production-ready, because it separates transport, trust, and settlement.
The payment that survives the second, third, and fourth delivery
The failure mode here is not glamorous. A packet can bounce through a mesh, arrive at more than one bridge node, and hit the backend several times. If the system settles every copy, the user pays twice. If it trusts the network, it loses.
That is why this repo is interesting. It treats offline UPI as a duplicate-processing problem first, and a connectivity problem second. The hard part is not moving the instruction. The hard part is making the same instruction harmless when it shows up again.
The project description on GitHub frames it as a Bluetooth-style mesh for zero-connectivity environments, which is the right mental model. Phones can relay. Phones can delay. Phones can duplicate. The backend still has to say: seen, accepted, settled, and never again.
What UPI_Without_Internet actually is
This is a Spring Boot 3.3.5 proof of concept written in Java 17, with H2, Spring Data JPA, Thymeleaf, and the Maven wrapper. That stack matters because it keeps the demo simple enough to inspect without hiding the flow behind infrastructure.
The repo is not trying to ship a production payment rail. It is trying to make the idea legible. The codebase can stay small because the point is the sequence: create packet, encrypt packet, relay packet, ingest packet, dedupe packet, then settle once.
Java 17
Spring Boot 3.3.5
H2 + JPA
Thymeleaf dashboard
Maven wrapper
That choice also keeps the project portable. You can run the whole thing locally, watch the simulator move packets around, and inspect the backend logic without provisioning brokers, edge nodes, or real Bluetooth hardware.
The real trick: treat the packet as the product
The repo's core move is a hybrid encryption envelope. The sender encrypts the payment payload with the server's public key, and the mesh only sees ciphertext. That means the relay path does not need to be trusted with the contents of the payment, only with moving bytes around.
The README and research notes point to RSA for key exchange and AES-GCM for the payload. That combination matters. RSA gets the secret across. AES-GCM protects the actual instruction with authenticated encryption, so tampering is not just inconvenient, it is rejected.
In other words, the mesh is not the trust boundary. The packet is. Once you accept that, the architecture becomes cleaner: move an immutable instruction through a hostile transport, then validate it at the bridge.
How the ingestion path prevents double spending
This is the article's technical center of gravity. The repo uses a claim-check style guard on the ciphertext hash before it does the expensive work. That means the backend does not wait until after decryption to learn that the packet has already arrived.
That ordering is the point. Hash first. Deduplicate first. Then decrypt. Then settle. If two bridge nodes forward the same packet at nearly the same time, the first one wins the compare-and-set race and the rest are rejected as duplicates.
The effect is exactly-once behavior on top of an at-least-once transport. That is a classic distributed-systems trick, but it is especially elegant here because the unit of deduplication is the ciphertext itself. The backend can ignore how many times the mesh gossiped the packet.
// Conceptual flow
String hash = sha256(ciphertext);
if (!idempotencyStore.claim(hash)) {
return DUPLICATE_REJECTED;
}
PaymentPayload payload = decrypt(ciphertext);
ledger.settle(payload);
return ACCEPTED;
The `IdempotencyConcurrencyTest` exists for a reason. If three bridge paths race into the server at the same millisecond, the test needs to prove that settlement still happens once. That is the difference between a demo and a useful demo.
| Approach | Transport model | Duplicate handling | Trust model |
|---|---|---|---|
| UPI_Without_Internet | Encrypted store-and-forward over mesh | Ciphertext hash claim-check before decryption | Untrusted relays, trusted backend gate |
| Offline wallet balance | Local funds reserved on device, then synced later | Usually handled by wallet state and sync rules | Higher device trust, less transport complexity |
| Manual retry or delayed online submission | User retries after connectivity returns | Weak or ad hoc deduplication | Relies on the network being available again |
Why the mesh simulator matters
A simulator is not a toy when the real hardware is the hard part. `MeshSimulatorService` gives the repo a way to model gossip rounds, TTL decay, and repeated broadcast without needing a room full of BLE devices.
That makes the idea teachable. You can see a packet fan out, age, and converge on a bridge node. You can watch the same instruction move through multiple paths. And you can do it without pretending the world is neatly connected.
The simulator also tells you what kind of problem this is. This is not a synchronous request-response API wearing an offline costume. It is a store-and-forward system with propagation, decay, and eventual ingestion.
What this approach beats, and what it does not
Compared with a simple QR handoff or a delayed online retry, this design is more honest about the transport. It assumes the path is lossy, duplicated, and partially trusted. That is a better fit for basements, dead zones, and crowded radio environments.
Compared with a wallet that reserves offline balance on-device, it pushes less trust into the handset and more of the policy into the backend. That is appealing, but it also raises the bar for key management, replay defense, and operational control.
And that is where the project stays refreshingly candid. Production questions remain. Key rotation is hard. Mesh congestion is real. Trust boundaries need more than a demo to survive contact with the world.
Still, the architectural lesson is strong. Separate transport, trust, and settlement. If that separation holds, the rest of the system gets easier to reason about.
Why this project is interesting even if it never ships
The repo's value is not that it proves a final answer for offline UPI. It is that it shows a useful way to think about the problem. The network can be flaky. The relay can be untrusted. The payment can still be deterministic.
That mental model travels. It applies anywhere you need to turn noisy delivery into safe once-only effects. Messaging. Payments. Audit trails. Edge workflows. The details change, but the discipline does not.
So the sharp takeaway is simple: this is not an offline networking project dressed up as fintech. It is a reliability design hiding inside a payment demo.





