Inspiration Digital payments stop where the signal stops. A relative abroad can send money in seconds, but the recipient still cannot spend it at a village shop with no network. Yet the internet is rarely far away: someone at the bus stop, the market or the clinic reaches it within the hour. No payment system uses those people as the last mile. We wanted to.

What it does PayOffline lets a phone pay a shop with no internet. The sealed payment then hops between nearby phones over Bluetooth until one of them reaches the internet.

A sender funds an offline allowance with PayPal from a web page (Orders API v2). The server signs an allowance credential. The recipient's phone stores it. The recipient scans the merchant's QR code. The two phones exchange signed records over Bluetooth with no internet, and the merchant verifies them offline. The sealed payment hops from phone to phone. An on-device model predicts which phone is likely to reach the internet within 60 minutes and forwards a copy only when the peer is clearly better (p(peer) - p(self) > 0.10) or already online. The first phone that reaches the internet uploads it. The server verifies the signatures, deduplicates, detects double-spends, and pays the merchant through PayPal Payouts. A payout batch id is saved before PayPal is called, so a retry can never pay twice. PayPal never authorises anything offline. Offline authorisation is our signed allowance, backed by funds already captured through PayPal.

How we built it PayPal developer platform: Orders API v2 funds every allowance and Payouts API settles every merchant, both against the sandbox. A local mock mode lets anyone run the whole system without credentials. AI: a logistic-regression routing model (13 features, AUC 0.946 and Brier 0.023 on 30 unseen simulated days), trained on 1.3 million simulated encounters and shipped inside the apps as a 1.3 KB JSON file, so inference needs no network. We also built the codebase with Claude Code. Backend: FastAPI and SQLAlchemy. Signed canonical JSON, sealed packets, idempotent reconciliation, websocket events. Phones: Android (Kotlin) and iOS (SwiftUI) apps with wallet, QR, Bluetooth relay and the routing model. Shared test vectors keep all three platforms signing identical bytes. Operator dashboard: React and Vite. Every table is an AG Grid grid whose rows update in place by transaction id as websocket events arrive. Simulator: a 4 km village with up to 600 phones and seven routing strategies compared. Virtual lab: a page that replays a recorded simulator hour, runs seven real signed scenarios against the backend, and runs the test suite from the browser, so anyone can test the system without phones. Challenges we ran into Making offline payments safe without a server: double-spend can only be detected after the fact, so the allowance caps the loss. Keeping three platforms byte-identical when signing JSON. Choosing who gets a copy under congestion: our results show that load balancing gives no measurable benefit in this simulator, and we say so.

Accomplishments that we're proud of A working end-to-end path from funding to verified payout, with fraud cases (duplicate upload, double-spend, tampered amount, forged receipt, overspend, expired credential) covered by live scenarios and tests. An honest evaluation. On an ordinary simulated day the ML router delivers 84.6% of payments with 2.5 copies each, against 61.5% for spray-and-wait. PRoPHET delivers 92.7% but uses 25.3 copies. In the festival scenario ML matches spray-and-wait on delivery (61.8% against 62.4%) and only wins on copies. What we learned A small, explainable model that runs on the phone is enough to beat naive relaying, and being clear about where it does not win is worth more to a reader than a headline number.

What's next for PayOffline Pilot with opt-in encounter logs and retrain on real mobility. Hardware-backed keys and merchant risk limits. Merchants, buses and clinics as always-on relays. Local rails such as UPI through licensed aggregators. Background Bluetooth on iOS.

How to test it No account, credentials or hardware needed. The README has the steps: run the backend in mock mode, start the web app, and open /#/lab. Press Run all under Live tests (seven scenarios), then follow a payment in the virtual village. To use the real PayPal sandbox instead, add sandbox credentials to .env and set PAYPAL_MODE=sandbox.

Sandbox prototype: no real money moves. The routing model is trained on simulated data only, so the results are simulator results, not measurements from real phones.

Built With

Share this project:

Updates

Submission history