Skip to content
Case study 02 · B2B payments and treasury app

A business payments app, from an empty repo to store builds in weeks.

I built a multi-currency business payments app in React Native, with hardware-backed biometric sessions and an approvals flow.

  • React Native
  • Swift
  • Kotlin
Role
Main mobile engineer
Timeline
Aug – Oct 2026
Stack
React Native, Swift, Kotlin
Weeks
repo to stores
3
native modules
44
UI components

The problem

Finance teams needed to see balances across currencies, pay suppliers, convert currencies and approve each other’s payments from their phones. The web portal already did all of this, so the app had to bring the same features to mobile and be just as secure, starting from an empty repo.

In a payments app, security is part of every feature. Moving money needed a fresh check that the right person was holding the phone, and larger payments needed a second person to approve them.

What I built

  1. 1. A foundation that matches the web

    I set up a React Native app in TypeScript, organised by feature: approvals, payments, wallets, payees and notifications.

    • A typed API layer built on the backend’s OpenAPI contract, with safe retries so a flaky connection can’t send a payment twice.
    • TanStack Query for server data, Zustand for the session and MMKV for fast local storage.
    • Multi-currency amounts handled exactly and formatted for each currency.
    • A mock API server built from the same contract, so screens were built and tested while the backend was still in progress.
  2. 2. Security in native code

    I wrote three native modules in Swift and Kotlin:

    • Hardware-backed keys. Approvals and the stored session are protected by keys held in the phone’s secure hardware (Secure Enclave on iPhone, Android Keystore and StrongBox on Android) and unlocked with Face ID or a fingerprint.
    • Biometric prompts that work the same way on both platforms and notice when the enrolled biometrics change.
    • Screen protection that keeps sensitive screens out of screenshots, screen recordings and screen sharing.

    Much of this only behaves correctly on real devices, so I tested it on physical iPhones and Android phones across OS versions.

  3. 3. Payments with a second pair of eyes

    • Sign-in through the platform’s hosted login in a secure browser tab, so passwords never pass through the app.
    • Step-up checks. Sensitive actions ask the user to confirm with biometrics or an authenticator code before they go through.
    • Approvals with separation of duties. The person who raised a payment can’t approve it. Approvers can approve or reject with a reason, makers can withdraw their own payments, and expired requests are shown clearly.
    • Currency conversion with live quotes and a countdown, so users always know whether a rate is still valid.
    • Payments and payees, with the same approval rules as the web, purpose-of-payment codes, a quick Search & Pay, and bank-detail validation for several payment rails.
    • Push notifications and links. Firebase Cloud Messaging and Notifee for notifications, and universal links and App Links that open an approval straight from a notification or email, after the user unlocks the app.
  4. 4. A design system for the app

    I built the app’s design system on the brand’s design tokens, checked against Figma and generated into styles, with CI catching any drift. On top of NativeWind and React Native Reusables it grew from 11 to 44 components, and an accessibility pass made colours and contrast meet WCAG AA.

Shipping it

Codemagic builds a QA APK, dev TestFlight and Play tracks, and production App Store and Play builds, with checks that stop a production build from pointing at the wrong environment. GitHub Actions run type checks, lint, tests and bundle builds on every pull request, and a Claude Code action reviews each PR for money, auth, data exposure and release risk.

The app went from an empty repo to store builds in weeks, with around 60 test suites. Alongside it I set up the team’s AI workspace: shared agent skills, semantic code search and device automation, so agents could check changes on a real simulator.

Client work. The client and product aren’t named, and details are described at a level the engagement allows.