What we solved
Sending airtime home from abroad usually means a sketchy web form, a card decline at 2am, or a reseller shop with a markup and no receipt. TSD needed one product that served two very different users: diaspora senders in Canada and the US who want Visa / PayPal and saved recipients, and resellers who need a dashboard, wholesale pricing, and 44-country coverage. Top-up had to clear in about five seconds or the sender assumes it got lost.
The system at a glance
Two surfaces, one shared core. A consumer mobile app built in React Native (iOS + Android). A web app + reseller dashboard built in React. Both talk to a FastAPI backend that runs the shared ledger, webhook pipeline, and the DT One integration. Payments route through Stripe + PayPal. Top-ups clear through DT One, a licensed telco aggregator that fronts 130+ operators across 44 countries.
What the user experiences
Consumer (diaspora)
- Sign up with Facebook, Google, or email.
- Add a recipient: phone number, operator auto-detected.
- Choose a top-up amount in CAD (or local currency). See the conversion to local currency (e.g., XOF).
- Pay with Visa / Discover / Amex / PayPal.
- Top-up clears to the recipient in ~5 seconds; SMS confirmation arrives on both sides.
- Recurring reload on a 7-day (or custom) cadence.
- Prepaid electricity - same flow, token code delivered to the recipient instantly.
- Saved recipients and contact import for repeat sends.
Reseller
- Dashboard with custom wholesale pricing.
- Transaction history, settlement reports, and per-partner analytics.
- 100% money-back guarantee policy surfaced in the flow.
How we built the pieces
The aggregator - DT One, one integration, 130+ operators
Integrating 130 mobile operators across 44 countries directly would take years. DT One exposes a unified API: we submit operator + phone + amount, DT One dispatches to the right carrier. TSD’s FastAPI backend talks to one API, not 130.
The 5-second SLA - webhooks, not polling
A “top up and wait” UX dies at 30 seconds. The backend submits the top-up to DT One, DT One confirms asynchronously via webhook, and our server notifies the app over a persistent connection. Within 5 seconds the sender sees “Delivered” and the recipient sees the airtime. Polling the aggregator every second doesn’t reach that latency; webhooks do.
Two-sided core - one ledger, two surfaces
The consumer app (React Native) and the reseller dashboard (React web) look like different products and act like different products - but they share the same FastAPI ledger, user model, and order state machine. A new operator added for resellers automatically lights up for consumers. A settlement reconciles both sides from the same transaction records.
Payments - multiple rails, one backend
Stripe, PayPal, and direct card entry all land in the same order row on Postgres. The webhook reconciles the payment; the top-up fires only after the payment is confirmed. A refund reverses both the top-up and the payment in one transaction.
Multi-currency - sender-local, recipient-local
Senders see CAD. Recipients see XOF (or whatever local currency). The exchange rate locks at submit; the conversion is explicit in the UI. No surprise fees on either side.
Frontend stack - React Native + React, same team
React Native on mobile and React on web let one team share components, types, and form logic across the three surfaces. Lower maintenance cost, consistent UX between the consumer app and the reseller dashboard.
The shared core is the product
Two-sided fintech products live or die on the seams. The consumer app and the reseller dashboard look like different products, but they share the same FastAPI core. Get that shared core right and both sides ship from one team. Get it wrong and every new operator costs you twice.
Results
- React Native iOS + Android consumer app, React web + reseller dashboard, all talking to one FastAPI backend.
- ~5-second top-up SLA hit across 130+ operators in 44 countries via DT One.
- Multi-gateway payments (Stripe, PayPal) settled into one ledger.
- Recurring reloads scheduled from the consumer app.
- Prepaid electricity top-ups delivering a token code instantly.
- Active social presence (Facebook, Instagram).
What an engineering team should take from this
If you are building any two-sided fintech or cross-border product:
- Aggregators, not direct integrations. Integrating 130 telcos yourself is not a reasonable project - DT One (or Reloadly) is your starting point.
- Webhooks, not polling. Sub-10-second SLAs need event-driven confirmation.
- One ledger, two surfaces. Every consumer-facing feature a reseller can resell should live on the same tables, not on a forked schema.
- React Native + React share more than you’d guess. Component patterns, form logic, and auth flows all carry across; one team ships all three surfaces.
Tech stack
- Consumer mobile: React Native (iOS + Android)
- Web app + reseller dashboard: React
- Backend: FastAPI (Python, typed)
- Database: Postgres (shared ledger)
- Telco aggregator: DT One
- Payments: Stripe + PayPal SDKs
- Auth: Facebook + Google + email social sign-in
- Event pipeline: webhook-driven order state machine
Screens
