Payments and push notifications in React Native: the parts that bite
· 3 min read · Amrith Vengalath
- React Native
- Payments
- Push Notifications
- Mobile app development
Payments and push notifications are on the feature list of almost every commerce app I've worked on, and both have the same shape: the integration tutorial takes twenty minutes, and then production takes two weeks. The gap between "it worked in the demo" and "it works for real users on flaky networks across two platforms" is where all the actual engineering lives.
Here's what that gap looked like, for both.
Payments: never trust the client
The single most important thing about mobile payments: the client cannot be the source of truth for whether a payment succeeded. The phone is the least trustworthy device in the system - bad networks, backgrounded apps, users who close the screen at the worst moment.
The flow that survived contact with real users looked like this:
- The app asks your backend to create an order. The backend talks to the payment gateway and returns an order token.
- The app opens the gateway's checkout with that token.
- The user pays. The app gets a "success" callback - but you treat this as a hint, not proof.
- The gateway calls your backend via webhook with the authoritative result. Your backend marks the order paid.
- The app polls or refreshes order status from your backend.
The webhook is the truth. The client callback is just a nice-to-have for snappy UI. I learned this when a user paid successfully, lost connection before the success callback fired, and the app showed a failure - but the money had moved. If you'd trusted the client, you'd have a paid order marked as failed. The backend webhook caught it.
The other non-obvious bit: handle the cases that aren't "success" or "failure." Users cancel. The app gets backgrounded mid-payment. The network dies. Each of those needs a defined state, or you get stuck orders and angry support tickets.
Push notifications: the setup is the easy part
Getting a test notification to show up is quick. Getting notifications to work reliably for every user, in every app state, on both platforms, is the actual task.
A few things the quickstart doesn't dwell on:
Permissions are a UX problem, not a code problem. On iOS you must ask, and if the user says no, that's mostly final - they have to go into Settings to undo it. So when you ask matters enormously. Asking on first launch, before the user understands why, gets you a lot of denials. Asking right after they do something where notifications obviously help converts far better. The code to request permission is three lines; the decision of when to call it is the real work.
App state changes everything. A notification behaves differently if the app is in the foreground, backgrounded, or fully killed. In the foreground you often have to display it yourself - the OS won't show a banner over your active app by default. Killed-state handling, especially the "user tapped a notification that launched the app" path, needs explicit code to read the notification that opened the app and route to the right screen.
// the cold-start case people forget: app was killed, opened via a notification
const initial = await messaging().getInitialNotification();
if (initial) {
navigateFromNotification(initial.data);
}
// and the warm case: app was backgrounded, brought up by a tap
messaging().onNotificationOpenedApp((msg) => {
navigateFromNotification(msg.data);
});Miss the getInitialNotification path and notifications "work" in testing (because your app is always running) but do nothing for users who tap one while the app is closed - which is most of them.
Tokens rotate. The device push token isn't permanent. It changes on reinstall, sometimes on OS updates. You need to send the current token to your backend on every launch and update the stored one, or you'll be pushing to dead tokens and wondering why delivery rates are dropping.
The common thread
Both features look done when the happy path works on your desk. Both are actually about the unhappy paths: the dropped network mid-payment, the user who denied permission, the notification tapped while the app was dead. Budget for those from the start. The demo is the first 20%, and it's genuinely the easy 20%.