A travel eSIM platform: app, commerce backend and operations dashboard
A client engaged Paymentery to build a product that sells travel eSIMs: a customer picks a data plan for the country they are visiting, pays, and the eSIM profile is issued and installed on their phone, in Arabic or English. The client is not named at their request. The development scope described here was completed and handed over; what remains before launch is listed in the closing section.
The requirement
A travel eSIM looks simple from the outside: choose a destination, pay, scan a code. Underneath sit a wholesale eSIM provider with its own catalogue and issuing flow, card payments that must never be captured before a profile actually exists, usage that has to be polled from the provider and shown honestly, refunds that touch real money, and an operations team that needs to see all of it without ever seeing activation data.
The product had to work equally in Arabic and English, on iOS and Android, and the business had to be able to add or swap a provider without rewriting the app.
What was built?
Mobile app
A Flutter application for iOS and Android with 27 screens: destination search by country and region, plan comparison, guest checkout that can be upgraded to an account, installation by QR code, manual entry or a direct install where the platform allows it, remaining data per eSIM, top-ups, notifications, help articles, troubleshooting and support tickets.
Commerce backend
A modular NestJS service on PostgreSQL and Redis: catalogue sync from the provider into one canonical plan model, a pricing engine with per-currency rounding, quotes with a price hold, orders with an explicit state machine, authorise-then-capture payments, provisioning, usage polling with thresholds, top-ups, refunds, promotions, referrals, notifications and background jobs.
Operations dashboard
A server-rendered Next.js dashboard with 17 sections: orders with payment and provisioning state, eSIM inventory, customers, support queue, payments, refunds, pricing, currencies, plans, providers, promotions, webhooks, feature flags, team and an audit log. Access is enforced per page by role and enforced again by the API.
Design system
A Figma file whose 147 variables and 34 text and effect styles are generated from the same token source the app and dashboard compile from, so the file documents what actually ships. Arabic screens were rebuilt right-to-left rather than mirrored.
Engineering decisions
One provider interface
The app and the business logic never talk to the wholesale provider directly. Every catalogue, issuing and usage call goes through a single adapter interface with two implementations: a deterministic mock used by every test, and the real provider written against its documented contract and exercised with recorded fixtures. Adding a provider is a new adapter, not a rewrite.
Money that is hard to get wrong
Amounts are integer minor units throughout. Cards are authorised first and captured only after the provider has issued the profile, so a failed provisioning never leaves a customer charged. Refunds cannot exceed what was captured, enforced under a row lock, and need two people: one raises, another approves.
Explicit states, exactly once
Orders move through a defined state machine that logs every transition. Critical events go through a transactional outbox, and every purchase, top-up and refund carries an idempotency key with a canonical fingerprint, so a retried request can never create a second order.
Installation that works on every phone
The universal path, a QR code and manual activation details, always ships. Direct in-app installation is negotiated at runtime and kept behind a feature flag, because on iOS it depends on an entitlement only Apple can grant. The app checks device compatibility before purchase rather than after.
Usage shown honestly
Remaining data is polled from the provider, timestamped and labelled as possibly out of date when it is stale. Thresholds trigger notifications, expiry is handled by scheduled jobs, and a plan starts when the eSIM first connects at the destination, not when it is bought. The app says so.
Security by default
Passwords are hashed with Argon2id, refresh tokens rotate with reuse detection, provider secrets are write-only in the dashboard, activation data is never displayed in the console, not even to a super admin, and the dashboard ships a content security policy with a per-request nonce.
Arabic as a first language
Arabic is a first-class locale, not a translation layer: 341 strings in both languages with none left untranslated, a build gate that fails on any hard-coded user-facing text, per-locale type scaling, and left-to-right isolation for prices, codes and identifiers inside Arabic sentences. A visual review on the device caught two defects the tests could not see, a price that read backwards and chevrons pointing the wrong way, and both were fixed at the root.
The operations dashboard
The dashboard is where the complexity is allowed to show. Operators can follow an order from payment to provisioning, search eSIMs by truncated ICCID without ever seeing activation data, work the support queue, raise and approve refunds, check provider health and balance, sync the catalogue, and read the audit log of every sensitive action.
Quality and security
- Unit tests for the contracts, API and dashboard, plus 186 integration tests against a real PostgreSQL and 42 provider contract tests over recorded fixtures covering success, rate limits, timeouts, duplicate webhooks and provider failures.
- A 79-step customer journey run end to end against the running API, including the failure paths: declined cards, provider outages, a capture that fails after provisioning.
- 78 browser tests for the dashboard against the live API, an automated accessibility pass over every section, and a colour-contrast gate on all 22 token pairs.
- 121 Flutter unit and widget tests, static analysis, and the internationalisation gate described above.
- An adversarial audit before handover raised 46 findings and confirmed 36 after a refutation pass. Each was a claim the code did not keep: a partial refund left pending forever, a delivery without capture, a promo code reusable past its limit. All were fixed with tests that were watched to fail first.
- Encrypted database backups verified by a full restore into a throwaway database with every table compared, and write-ahead-log archiving confirmed active.
What the product tells its users
The app states plainly what a travel eSIM is and is not: data only, no calls or SMS, no phone number; the plan starts when the eSIM first connects, not at purchase; an eSIM can be installed once and must not be deleted; installation needs Wi-Fi and, for a QR code, a second device. Device compatibility is checked before paying, and remaining data is marked when the figure may be out of date.
Result
Delivered and handed over: the source of the app, backend and dashboard; the Figma file; database and API references; deployment, monitoring, backup and restore procedures; store listing assets and the release checklist; and documentation of every decision taken and every known limitation.
Going live depends on accounts only the client can hold: the wholesale provider account, the payment provider account, the app-store accounts, an Apple build environment and the Apple eSIM entitlement. No live provider or payment call has been made; both integrations are exercised against recorded fixtures and are documented as such. Screens on this page use test data.
Building a telecom or subscription product?
Describe the requirement and the providers involved. Provider, payment and store constraints are assessed before the scope is defined.