Skip to content

Adding Payments to a Mobile App: Stripe, Razorpay and In-App Purchase Rules

Mobile7 min readBy the Nexzem team

When an app must use App Store and Google Play billing, when Stripe or Razorpay is allowed, and how to build a secure server-driven payment flow.

In this article
  1. 01Start with the rule that decides everything
  2. 02The golden rule: the server decides, the app displays
  3. 03Stripe: PaymentIntents and the Payment Sheet
  4. 04Razorpay: orders, checkout and signature checks
  5. 05In-app purchases: products, receipts and server validation
  6. 06Should you use a subscription platform?
  7. 07Subscriptions outside the app stores
  8. 08Design the app for slow and uncertain payments
  9. 09Webhooks, idempotency and reconciliation
  10. 10Testing and launch checklist

Start with the rule that decides everything

Before choosing a payment provider, work out what you are selling, because Apple and Google decide part of the answer. In broad terms, digital goods and services used inside the app, such as premium features, subscriptions to app content, extra lives or ebooks, must be sold through the platform's in-app purchase system: StoreKit on iOS and Google Play Billing on Android. Physical goods and real-world services, such as groceries, taxi rides, hotel rooms or consulting sessions, must use a regular payment processor like Stripe or Razorpay.

There are exceptions, such as reader apps, and the rules for linking to outside payment options have changed in some regions, including the United States and the European Union, after legal and regulatory action. Google also runs alternative billing programs in some countries. These rules keep evolving, so check the current App Store Review Guidelines and Google Play payments policy for each market before you design the flow.

The golden rule: the server decides, the app displays

Whichever system you use, never let the app decide the price or confirm a payment on its own. The app can be modified and its network traffic replayed. Your server creates the payment with the amount it calculates, the payment provider confirms it, and your server marks the order paid only after verifying that confirmation through the provider's API or a signed webhook.

Never handle raw card numbers in your own code. Provider SDKs collect card details directly, which keeps most of the card data security burden (PCI DSS scope) with the provider.

Stripe: PaymentIntents and the Payment Sheet

With Stripe, the flow centers on a PaymentIntent, an object representing one attempt to collect a specific amount. The steps for a mobile app look like this:

  • The app asks your server to start checkout for an order
  • The server calculates the amount and creates a PaymentIntent, passing an idempotency key so retries do not create duplicates, and returns its client secret
  • For saved cards, the server also creates or reuses a Stripe Customer and an ephemeral key for the app
  • The app presents the Payment Sheet from Stripe's iOS, Android, React Native or Flutter SDK, which handles cards, Apple Pay, Google Pay and authentication challenges such as 3D Secure
  • Your server listens for the payment_intent.succeeded webhook and fulfills the order

Razorpay: orders, checkout and signature checks

Razorpay is widely used for Indian apps because it supports UPI, cards, net banking and wallets in one checkout. Your server first creates an Order through the Orders API with the amount in paise and a receipt reference. The app opens Razorpay Checkout with that order ID, and on success receives a payment ID, the order ID and a signature.

The app sends those values to your server, which recomputes the signature as an HMAC SHA-256 of the order ID and payment ID joined by a pipe character, using your key secret, and compares it. Only a matching signature, ideally confirmed by the payment.captured or order.paid webhook, marks the order paid. For UPI on Android, the intent flow lets users pick their UPI app directly, which tends to convert well.

In-app purchases: products, receipts and server validation

For digital goods, you define products in App Store Connect and the Google Play Console: consumables, non-consumables and auto-renewable subscriptions. The app uses StoreKit or Play Billing to show localized prices and start purchases; the stores handle payment and tax collection and take their commission.

Do not grant content based only on what the device reports. Send the transaction to your server and verify it with the App Store Server API or the Google Play Developer API, then record entitlements in your database. Subscribe to App Store Server Notifications and Google Play real-time developer notifications so renewals, refunds, grace periods and cancellations update entitlements automatically. On Android, purchases must be acknowledged, or they are refunded automatically after a few days.

Should you use a subscription platform?

Services such as RevenueCat sit on top of StoreKit and Play Billing, handle receipt validation and notifications and give you one entitlement API across platforms. They save significant work for subscription apps and add a dependency and a fee. If subscriptions are your main revenue, the time saved often justifies it; if you sell one or two one-time purchases, the native APIs are manageable.

Subscriptions outside the app stores

When subscriptions are allowed outside in-app purchase, for example a membership for physical deliveries or a service used mainly on the web, Stripe Billing and Razorpay Subscriptions manage plans, renewals, proration and failed payment retries. In India, recurring payments run through UPI AutoPay or card e-mandates under RBI rules, which require customer authentication when the mandate is set up and for charges above a set limit, so renewal reminders and retry logic must follow those rules rather than charging silently.

Design the app for slow and uncertain payments

Not every payment finishes instantly. A UPI payment can stay pending for minutes, a bank can ask for an authentication step, and the user can close the app halfway through. The app should always ask the server for the order's current status rather than assuming success or failure from what happened on screen.

Show clear states: processing, paid, failed and pending confirmation. Let users retry a failed payment on the same order instead of creating a new one, and when the app reopens, check for any order that was mid-payment and resume or resolve it. These details prevent double charges and support tickets.

Webhooks, idempotency and reconciliation

Payments are full of retries: the user taps twice, the network drops after the charge, the provider resends a webhook. Design every step to be safe to repeat. Use idempotency keys when creating payments, store provider event IDs and ignore duplicates, and make fulfillment check whether the order is already paid.

  • Verify every webhook signature with the provider's signing secret
  • Return 2xx quickly and process asynchronously
  • Run a daily reconciliation job comparing your orders with the provider's records
  • Handle refunds, disputes and partial captures, not only the happy path

Testing and launch checklist

Use each provider's test mode and test cards, including cards that trigger authentication and declines. Test StoreKit purchases with sandbox accounts or a StoreKit configuration file, and Play Billing with license testers and test tracks. Before launch, confirm that receipts, webhooks and refunds all update your database correctly, that prices shown in the app match what is charged, and that a payment interrupted by closing the app is recovered when it reopens.

Nexzem builds payment flows for apps selling both physical services and digital subscriptions; our mobile app development page explains our approach, and our native vs cross-platform comparison covers the SDK choices.

Planning something similar?

Get a straight answer on scope, cost and timeline.

Talk to the team

Tell us what you're building.

A solutions consultant replies within one business day with next steps, a rough estimate and a suggested team.