M-Pesa Daraja Payment Integration UI in React Native
M-Pesa Daraja is a server-to-server API, so the app triggers and waits. Here is the honest STK Push flow in React Native, confirmed by callback, keys server-side.
TL;DR
An M-Pesa payment in React Native runs on Safaricom's Daraja API, and the key fact is that it is server-to-server: your app asks your server to start an STK Push, the user gets the M-Pesa PIN prompt on their own phone, and Safaricom sends a callback to your server confirming the result. So the app never holds your Daraja keys and never decides the outcome itself; it shows an honest check-your-phone waiting state and confirms from your server. It is for real-world goods and services, not digital in-app content. Build the checkout from a free VP0 design and keep the integration on your server.
An M-Pesa payment in React Native runs on Safaricom’s Daraja API, and the defining fact is that it is server-to-server. Your app asks your server to start an STK Push, the user gets the M-Pesa PIN prompt on their own phone, and Safaricom sends a callback to your server with the result. So the app never holds your Daraja keys and never decides the outcome itself; it shows an honest waiting state and confirms from your backend. The checkout is fastest to start from a free VP0 design, the same shape as a Flutterwave payment gateway UI.
What Daraja actually is
Daraja is Safaricom’s developer platform for M-Pesa, and the Daraja API documentation describes a set of server-side endpoints: you authenticate with a consumer key and secret, request an access token, and call operations like STK Push. None of that belongs in a mobile app. The credentials can move money, so they live on your server, and your React Native app talks only to your backend, which talks to Daraja.
That architecture is the whole integration in one sentence: app to your server, your server to Daraja, Safaricom back to your server. The UI sits at one end of that chain and never touches the keys.
The STK Push flow, honestly
STK Push is the heart of it. Your server asks Safaricom to send a payment prompt to the customer’s phone; the customer sees an M-Pesa prompt, enters their PIN, and approves, entirely outside your app. Your app cannot show or capture that PIN, which is exactly right: the approval happens in M-Pesa’s trusted surface, not yours.
So the app’s job during payment is to be honest about waiting. After it triggers the push through your server, it shows a clear state: the prompt has been sent, check your phone, enter your M-Pesa PIN. It does not pretend to know the result yet, because it cannot.
[App] enter amount + phone
-> POST to YOUR server
[Your server] -> Daraja STK Push (with keys)
[Customer phone] M-Pesa prompt -> PIN -> approve
[Safaricom] -> callback to YOUR server (result)
[App] poll your server / receive push -> show confirmed
Why the callback is the source of truth
The result arrives where the keys are: your server. Safaricom posts a callback to a URL you registered, and that callback is the authoritative confirmation that the money moved. Your app learns the outcome by polling your server or receiving a push from it, never by assuming success because the prompt was sent. This is the same server-confirms-the-payment discipline behind any solid checkout, including a Paystack React checkout: the client triggers, the server confirms, and fulfillment waits for the verified event.
Marking an order paid because the STK Push was requested, rather than because the callback confirmed it, is how M-Pesa integrations ship goods on payments the customer never actually approved.
The product-type rule
Before wiring any of this, confirm what you are selling. Apple’s App Store Review Guidelines require digital content consumed in the app to use in-app purchase, which means M-Pesa, like any external processor, fits real-world goods and services: a physical product, a ride, an in-person booking. A digital subscription must go through StoreKit. The React Native client itself follows the standard React Native patterns; the M-Pesa specificity lives in your server and the waiting UI.
What to choose
To integrate M-Pesa in React Native, run Daraja from your server and keep the app thin: collect the amount and phone number, call your backend to trigger the STK Push, and show an honest check-your-phone waiting state while the customer approves with their PIN in M-Pesa. Treat the Safaricom callback to your server as the only confirmation of success, never the fact that the push was sent, and never embed the Daraja keys in the app. Confirm you are selling real-world goods or services, since digital content needs in-app purchase. Start the checkout and waiting screens from a free, $0 VP0 design, and put the integration where it belongs, on your server.
Frequently asked questions
How do I integrate M-Pesa into a React Native app?
Use Safaricom’s Daraja API, and run it from your server, not the app. The app collects the amount and phone number and calls your backend, which holds the Daraja keys and triggers an STK Push. The user approves with their M-Pesa PIN on their own phone, and Safaricom sends a confirmation callback to your server. The app shows a waiting state and learns the result from your backend. Build the checkout screen from a free VP0 design and keep all Daraja credentials server-side.
What is an M-Pesa STK Push?
STK Push is the flow where your server asks Safaricom to send a payment prompt directly to the customer’s phone. The customer sees an M-Pesa prompt, enters their PIN, and approves, all outside your app. Your app cannot show or capture that PIN; it only triggers the push through your server and waits. Safaricom then posts a callback to your server with the outcome, which is the authoritative confirmation that the money moved.
Why must the Daraja keys stay on the server?
Because the consumer key and secret can initiate payments, so embedding them in the app would let anyone who extracts them trigger transactions or impersonate your business. Daraja is built as a server-to-server API for exactly that reason. Your React Native app calls your backend, and your backend calls Daraja with the keys. Shipping the keys in the bundle is the mistake that turns a payment integration into a liability.
Can I use M-Pesa for digital goods on the App Store?
Apple requires digital content and services consumed in the app to use in-app purchase, so M-Pesa, like any external processor, is appropriate for real-world goods and services rather than digital unlocks or subscriptions. Paying for a physical product, a ride, or an in-person service through M-Pesa is fine; selling a digital subscription through it is not. Match the method to the product type before you build.
Can VP0 give me a free M-Pesa checkout screen?
Yes. VP0 is a free iOS and React Native design library where each screen has an AI-readable source page, so you copy a link and Claude Code or Cursor builds the amount entry, phone input, and waiting state in React Native. You wire your backend and Daraja yourself. The design gives you an honest checkout-and-waiting layout to start from at no cost.
Other questions VP0 users ask
How do I integrate M-Pesa into a React Native app?
Use Safaricom's Daraja API, and run it from your server, not the app. The app collects the amount and phone number and calls your backend, which holds the Daraja keys and triggers an STK Push. The user approves with their M-Pesa PIN on their own phone, and Safaricom sends a confirmation callback to your server. The app shows a waiting state and learns the result from your backend. Build the checkout screen from a free VP0 design and keep all Daraja credentials server-side.
What is an M-Pesa STK Push?
STK Push is the flow where your server asks Safaricom to send a payment prompt directly to the customer's phone. The customer sees an M-Pesa prompt, enters their PIN, and approves, all outside your app. Your app cannot show or capture that PIN; it only triggers the push through your server and waits. Safaricom then posts a callback to your server with the outcome, which is the authoritative confirmation that the money moved.
Why must the Daraja keys stay on the server?
Because the consumer key and secret can initiate payments, so embedding them in the app would let anyone who extracts them trigger transactions or impersonate your business. Daraja is built as a server-to-server API for exactly that reason. Your React Native app calls your backend, and your backend calls Daraja with the keys. Shipping the keys in the bundle is the mistake that turns a payment integration into a liability.
Can I use M-Pesa for digital goods on the App Store?
Apple requires digital content and services consumed in the app to use in-app purchase, so M-Pesa, like any external processor, is appropriate for real-world goods and services rather than digital unlocks or subscriptions. Paying for a physical product, a ride, or an in-person service through M-Pesa is fine; selling a digital subscription through it is not. Match the method to the product type before you build.
Can VP0 give me a free M-Pesa checkout screen?
Yes. VP0 is a free iOS and React Native design library where each screen has an AI-readable source page, so you copy a link and Claude Code or Cursor builds the amount entry, phone input, and waiting state in React Native. You wire your backend and Daraja yourself. The design gives you an honest checkout-and-waiting layout to start from at no cost.
Part of the React Native & Expo: Mobile Frontend Architecture hub. Browse all VP0 topics →
Keep reading
Property Management App UI in React Native
A free React Native pattern for a property management app: units and tenants, maintenance requests, lease documents, and rent through a certified provider.
GitHub Actions + Fastlane for React Native iOS in 2026
match plus an API key is the whole signing story: the beta lane, the caches that tame $0.062 macOS minutes, and when EAS is honestly the better call.
How to Update an Old React Native App Using AI
The agent is for the grind, not the judgment: incremental version-by-version upgrades, the Upgrade Helper diff as the map, and verify-after-each-step.
Ionic to React Native: The AI Prompt That Works
No converter exists, and the prompt is a sequence: logic first, screens rebuilt natively one at a time, plugins mapped, with a hard ban on literal translation.
Migrate from Expo Go to a Development Build with AI
A development build is your own Expo Go, not an exit from Expo: install expo-dev-client, audit native deps, stay managed, and the JS loop is unchanged.
How to Fix React Native RTL Flexbox Layout (with AI)
RTL layouts break in React Native when you hard-code left and right. Use logical start and end props, let rows flip, and prompt AI for RTL-safe styles.