Articles · How-To GuidesUpdated October 2026

Add subscriptions to an app and most of the work happens in the store console, not in your code.

To add subscriptions to an app you create the product in the store console first and write the code second. That order is not a preference. The price, the billing period, the free trial, the introductory discount and what happens when a renewal payment is declined are all forms in App Store Connect and in Google Play Console. Your code reads those settings and cannot set them. The purchase call itself is the same one you use for one-off in-app purchases. Renewal is the part that differs, and renewal is almost entirely console work.

Below: the current path through each console, the offer types each store really supports, the three states that are not a successful purchase, and the part where the platforms are badly asymmetric, which is testing. Every path and limit here was read from Apple's and Google's own documentation on 3 October 2026.

See your setup order

The short version

The store sells it and your app only asks who is entitled.

Recurring billing in an app is two systems sharing one identifier. The store owns the money: it charges the card, retries a failed charge, applies the trial, honours the cancellation, and holds the answer to who is currently entitled. Your app owns access: it asks for the current entitlements, unlocks the feature, and locks it again when the entitlement disappears.

Almost every subscription bug sits on that seam. An app that writes subscribed: true into its own database has already lost, because the store can change that answer while your app is closed. Ask the store on every launch and keep your own copy only as a cache for the offline case.

The order of operations, and why Android reverses it

An auto-renewable subscription on iOS can be created before a single line of code exists. In App Store Connect, open your app, then Monetization in the sidebar, then Subscriptions. Create a subscription group, add a subscription with a reference name and a product ID, then set a level, a duration and a price. The level decides the upgrade, downgrade and crossgrade path inside the group. Apple's durations are one week, one month, two months, three months, six months and one year, and there is nothing shorter: App Review guideline 3.1.2(a) requires a subscription period of at least seven days, available across all of the user's devices. A group holds up to 100 subscriptions and a customer can hold one of them at a time, which is why Apple's own help text calls a single group the best practice for most apps.

Google Play subscriptions setup runs the other way, and this is the step that costs people a day. Google's setup documentation says you must publish a version of your app that includes the Google Play Billing Library before the billing features in Play Console are enabled, and any track counts, including internal testing. So the dependency and an upload come first. Only then do you open Monetize with Play, then Products, then Subscriptions, to create the subscription, add a base plan and activate it. The current library is version 9.1.0, Google requires version 8 or later for new apps and updates, and the library runs on a published two year deprecation cycle with a dated deadline per version. If you are reading an older tutorial, the vocabulary moved too: querySkuDetailsAsync was removed in Billing Library 8.0.0, and the current call is queryProductDetailsAsync, returning ProductDetails where the old API returned SkuDetails.

Both stores make the identifiers permanent, so spend five minutes on them. A Play product ID must start with a number or a lowercase letter, may contain underscores and periods, caps at 40 characters, and cannot be reused, and base plan and offer IDs cannot be changed or reused once activated. Play also caps each subscription at 250 base plans and offers combined, with 50 active at once. Name them as though a stranger will read them in a revenue report in two years.

Try it

Your setup, in the order the stores force

Pick what you are shipping. The list reorders, because Android makes you publish a build before the console will show you a subscription form.

0 of 8 done. Every step tagged Console is a web form, which is why a subscription can be flawless in code and still unbuyable.

Free trials and introductory prices are offer objects, not code

Apple calls the first discount an introductory offer and publishes exactly three types: free trial, pay up front, and pay as you go. You create one from the subscription's Subscription Prices section in App Store Connect. Free trial lengths are a fixed list: three days, one or two weeks, one, two, three or six months, or one year. Each customer is eligible for one introductory offer per subscription group rather than per product, so someone who used a trial on your monthly plan gets nothing on the annual plan in the same group. An offer cannot be edited once created, either: you delete it and build a new one. Three further offer kinds live in the same screens: promotional offers for existing subscribers, offer codes, and win-back offers aimed at people who have already lapsed. Roles differ per screen, which catches teams out: a subscription can be created by Account Holder, Admin, App Manager, Developer or Marketing, while introductory offers drop Developer from that list.

Google models the same idea differently and more loosely. A subscription holds base plans, a base plan holds offers, and an offer holds one or more pricing phases. A free trial phase can run anywhere from three days to three years. An introductory pricing phase is either a single payment or a recurring discount for between one and 52 billing periods, and the discount can be an absolute price, a fixed amount off the base price, or a percentage off it. Eligibility is one of three rules: new customer acquisition, upgrade, or developer determined, where Google evaluates nothing and your own code decides. Google notes that an offer with developer determined eligibility cannot be purchased outside your app.

Your code should hardcode none of this. It asks the store for the product and gets back a localised price string, a currency and the offer this customer is eligible for, and the screen renders what came back. That matters most when you add a paywall, because a trial length typed into a component will be wrong in some country before long. Both stores also police the wording. Google's guidance says a subscription may not be named something like Free Trial, and that the benefit text may not mention the trial or the price at all, since not every user is eligible for it.

Apple, App Store Connect Help: set up introductory offers for auto-renewable subscriptions (read 3 October 2026)

The three states that are not a successful purchase

First, the purchase that is blocked. Neither store asks the user for a permission before a purchase, and there is no Info.plist key or Android manifest entry to declare for this: the store account authenticates the payment. What exists instead is a block you have to detect. On iOS, StoreKit exposes AppStore.canMakePayments, and Apple's documentation gives two reasons it returns false: Content and Privacy Restrictions set in Screen Time, or a mobile device management profile that prevents purchases. Apple's instruction is not to offer the purchase at all in that case, so a paywall with a dead button is the wrong handling. Apple documents the identical value under the older name SKPaymentQueue.canMakePayments() in the Original API for Apple In-App Purchase, so a page describing that call is dated rather than wrong. Google's current setup page lists no manifest permission step at all; the library dependency is the step.

Second, the purchase that is pending. On iOS a purchase can return the pending case of Product.PurchaseResult, which is what Ask to Buy looks like while a parent approves it. On Android the purchase arrives in PENDING state and Google is explicit that you grant entitlement only when it moves to PURCHASED. Android then adds a deadline that catches people badly: a purchase must be acknowledged within three days or it is automatically refunded and the entitlement revoked. Apple's equivalent, Transaction.finish, is called after you deliver the content, and its documentation attaches no refund deadline of that kind.

Third, the renewal that is declined, where the two stores diverge most. Google gives every auto-renewing base plan a grace period you choose, during which the subscriber keeps access, followed by an account hold, during which they should not have it. Account hold is on by default at 60 days minus whatever grace period you set; you can shorten or disable it, and Google warns that a shorter hold recovers fewer subscriptions. Apple's Billing Grace Period is per app rather than per product, and you turn it on in the Billing Grace Period section, choosing 3, 16 or 28 days, with weekly subscriptions capped at 6 days whatever you pick. Without it, Apple says, the subscriber's days of paid service pause while Apple retries the payment. One honest warning: Apple's Billing Grace Period page lists 3, 16 and 28 days while its subscriptions overview page quotes 6 or 16, so take the numbers from the screen you are filling in.

In code this means entitlement is not a boolean. Apple's Transaction.currentEntitlements returns the latest transaction for each auto-renewable subscription whose renewal state is subscribed or inGracePeriod, and both of those should unlock the app. Google's server-side equivalent is the Play Developer API, where purchases.subscriptionsv2.get reports the current state and real-time developer notifications push the changes to you. Whether you run that state machine yourself or rent it is a genuine decision rather than a detail, and it is the whole of revenuecat vs stripe.

Google Play Console Help: understanding subscriptions, base plans and offers (read 3 October 2026)

Testing: Simulator on iOS, real hardware on Android

Apple gives you two test environments and you want both. StoreKit Testing in Xcode reads a local .storekit configuration file instead of talking to App Store servers, so you can build the whole purchase flow before anything exists in App Store Connect, and Apple states that you can run it in Simulator or on real devices. The file can also be a synced one, pulled from products you have already configured. The sandbox is the second stage: real product data from App Store Connect, no charges, and a device signed in to a Sandbox Apple Account, which appears under Settings then Developer after your first purchase attempt in a development build. Apps installed from TestFlight always use the sandbox.

Android has no local file equivalent. Google says the Play Billing Library is blocked for apps that are not signed and uploaded to Google Play, and the way through that is to register your Google account as a license tester, which lets you sideload a debug build as long as the package name matches the app configured in Play Console. Google's testing page then states the hardware requirement plainly: you can test on any Android powered hardware device with the current Google Play application installed. It documents no emulator path, so plan on a real phone. Google also ships Play Billing Lab, a separate app you install from the Play Store, which is how you accelerate a renewal or push a test subscription into grace period or account hold instead of waiting. Draft apps and internal test track releases are also subject to spend limits.

So which platform is harder? Android, for setup and testing: a build has to ship before the console opens, acknowledgement has a three day fuse, and you need hardware. Apple is harder for offers: fixed trial durations, one introductory offer per customer per group, offers that cannot be edited after creation, and a grace period that applies to a whole app rather than one product. Neither is difficult in an interesting way. Both are a form you have not filled in yet. What to sell, at what price, with which trial, is a separate and longer question: see monetisation strategies.

iOS against Android, on the settings that bite

What you are setting upApp Store (iOS)Google Play (Android)Code or consoleFussier store
Shortest billing period1 week, and guideline 3.1.2(a) requires at least 7 daysWeeklyConsoleEven
Free trial lengthA fixed list: 3 days, 1 or 2 weeks, 1, 2, 3 or 6 months, 1 yearAny length from 3 days to 3 yearsConsoleApple
Introductory discountPay up front or pay as you go, one per customer per groupSingle or recurring payments; absolute, fixed or percentage offConsoleApple
Grace period on a declined renewalPer app, you turn it on, 3, 16 or 28 daysPer base plan, then account hold at 60 days minus the grace periodConsoleApple
Testing a real purchase flowSimulator works, against a StoreKit configuration fileAndroid hardware with the current Play app; no emulator path documentedBothGoogle

What a generated app does and does not remove

None of the console work disappears because a tool wrote the code. The App Store Connect account is yours, the Play Console account is yours, the bank details are yours, and the subscription, its price and its trial are created there, by you, in the screens described above.

Newly is an AI app builder: you describe the app in plain English and it writes a real React Native and TypeScript project you own, runs it on cloud iOS and Android simulators while it builds, then ships to TestFlight and to Google Play internal testing. It is $25 a month on effort-based credits, with no free plan. Ask the agent for subscriptions or a paywall and it shows a Connect RevenueCat card, then sets up the subscription products and builds the paywall screen, which removes the client code and the entitlement plumbing that most people get wrong. It does not remove the two console forms and it cannot, because Apple requires the owner of an app to submit it: the build is uploaded to TestFlight for you and you press submit yourself. Subscriptions and the RevenueCat connection are owner or admin only.

If you would rather own the whole path, the code comes out: a ZIP from project Settings, or the two-way GitHub sync in the Deploy tab. From there the StoreKit and Play Billing work is ordinary React Native work, and the console forms are still waiting for you.

Questions people ask when adding subscriptions

Both, but the console comes first. The subscription product, its billing period, its price, its free trial, its introductory discount and the grace period after a declined payment are all configured in App Store Connect and in Google Play Console. Your code asks the store for the product, starts the purchase, and checks the current entitlement. It cannot change any of those settings, which is why a subscription can be flawless in code and still unbuyable.

Write the product down before you write the code

One subscription, one duration, one price, and a decision on the trial and on the grace period. Create it in App Store Connect and in Play Console first, then build the paywall against whatever the store hands back.

Start building