Wrapping a native payments SDK in React Native
Building YellPay for PAY ROUTE: bridging a native iOS/Android payments SDK into one React Native codebase, with flows designed for users who struggle with passwords.
YellPay is a payments app I built for PAY ROUTE, a Japanese fintech whose platform does something unusual: authentication without IDs or passwords. The catch was that all of that capability lived inside a native iOS and Android SDK, and the product needed one cross-platform app around it.
The bridge is a contract, keep it small
The core of the work was wrapping the native SDK behind a React Native bridge. The temptation is to expose everything the SDK can do. Resist it. Every method you expose is a surface you maintain on three sides: iOS, Android, and the JavaScript that calls it.
I kept the bridge to the handful of operations the app actually needs: authenticate, register a card, make a QR payment, fetch history. Everything else stays native-side. When the SDK updates, the blast radius is a short file, not the whole app.
// The entire JS surface of the payments bridge.
export interface YellPayBridge {
authenticate(): Promise<AuthResult>;
registerCard(input: CardInput): Promise<CardResult>;
payByQR(code: string): Promise<PaymentResult>;
getHistory(cursor?: string): Promise<HistoryPage>;
}The other rule: the bridge returns typed results, never raw SDK objects. Native SDKs change shape between versions. Your app's types shouldn't.
Errors need to cross the bridge too
A payments app cannot swallow errors. Every native failure maps to a typed error code on the JavaScript side: network, declined, SDK-not-ready, user-cancelled. The UI decides what each one means for the person holding the phone. "Something went wrong" is not an acceptable message when money is involved.
Design for the person the system was built for
PAY ROUTE's whole reason to exist is serving people underserved by ID/password systems, including elderly and assisted-care users. That shaped the UI more than any technical decision.
Concretely: one action per screen, large tap targets, no timed steps that punish slow readers, and a transaction history that reads like a receipt rather than a ledger. Card registration, the scariest step for a non-technical user, walks through one field at a time instead of presenting a form wall.
None of this is exotic accessibility engineering. It's mostly restraint, deciding that a screen does one thing, and accepting more screens in exchange for less confusion on each one.
Test on the phones people actually own
Payments plus native bridging means the simulator lies to you twice. QR scanning needs a real camera, and SDK behaviour differs on real devices. The habit that saved me: every flow, on a physical iPhone and a mid-range Android, before every release. Boring, repeatable, and it caught real issues the simulator never would have shown.