Analytics

Trial to Paid Conversion Rate by Billing Platform

Apple counts completed trial offers, Google Play keeps grace-period users entitled, and Stripe gives a failed first charge 23 hours. Comparing trial-to-paid conversion by billing platform takes shared eligibility, state, and maturity rules.

Alex KhvoinitskiiNovember 9, 202616 min read

TL;DR: One trial to paid conversion rate cannot compare iOS, Android, and web trials until three things are fixed: who counts as a trial, which store states mean "converted", and when a cohort is mature. A normalization worksheet makes each decision explicit.

A lifecycle or subscription-ops lead usually discovers the problem in a review meeting: App Store Connect reports one trial conversion number, the Play Console reports another, and the web billing dashboard reports a third — for the same product and the same week. None of them is wrong. Each platform counts a different denominator, uses a different state vocabulary, and closes the books on a different schedule. To measure trial-to-paid conversion by billing platform, you do not need a better dashboard. You need three normalization decisions made once, on paper, before any rate is compared.

The decisions are: who counts as a trial start, which platform states map to "converted" and which only look like it, and how long a cohort must mature before its rate is reported. This is the same measurement discipline the business process automation ROI framework applies to any funnel — fix the units and the definitions before trusting the numbers.

Why one rate cannot describe three platforms

A single blended app trial conversion rate hides three structural differences.

Different denominators. Apple's own App Store Connect analytics define "Trial to Paid Conversion" as the percentage of completed trial offers that converted to a paid subscription — trials still running are excluded from the denominator (Apple App Store Connect analytics). Most internal dashboards divide by trials started. On the same data, a start-date denominator always produces a lower rate than a completed-trial denominator.

Different state vocabularies. A Stripe trial moves through trialing, incomplete, active, past_due, canceled, unpaid, or paused (Stripe subscription lifecycle). A Google Play subscription reports subscriptionState values such as SUBSCRIPTION_STATE_ACTIVE and SUBSCRIPTION_STATE_IN_GRACE_PERIOD, plus an offerPhase field that marks the free-trial phase (Google Play subscription lifecycle). Apple reports notifications like DID_FAIL_TO_RENEW with a GRACE_PERIOD subtype. None of these words mean the same thing across platforms.

Different observation windows. Trial length itself differs by platform: Apple offers fixed trial lengths from 3 days to 1 year, while Google Play allows any length between 3 days and 3 years (RevenueCat trial conversion analysis). Add grace periods and delayed payment recovery, and two cohorts that started on the same day reach a trustworthy rate weeks apart.

The fix is not averaging the three numbers. It is building one counting rule, one state map, and one maturity rule — then letting each platform keep its native reporting beside the normalized view. Whether the number in question is a SaaS trial conversion rate from web billing or a store trial in the same portfolio, the same three rules apply.

Step 1: Define trial eligibility and maturity

Count a trial the same way everywhere before comparing platforms: one person, one first trial, one cohort date. Three eligibility rules settle most disputes.

  • First trial only. A subscriber who trials, cancels, and re-trials a year later still counts once. Cohort tools already work this way — RevenueDot's trial conversion methodology, for example, groups customers by first trial start date and counts each customer once (RevenueDot methodology).
  • Split card-required from no-card trials. These are different populations, not one funnel. Recurly's 2022 merchant data put trial conversion at 39.0% when billing information was required up front versus 12.0% when it was not (Empact Partners compilation). Mixing them produces a rate that describes nobody.
  • Stamp the eligibility moment per platform. On web billing, the subscription trial starts when the subscription object is created. On stores, it starts when the trial offer is purchased in-app. If your product also gates trials behind an activation event — say, completing onboarding — define that as a qualifier, not the start. The same judgment call behind choosing SaaS activation metrics that predict value applies here: the qualifier must be observable and identical across platforms.

Maturity is the second half. A trial cohort is immature while any member can still change buckets, and still-running trials belong in the denominator — RevenueDot keeps them there and flags recent cohorts as incomplete rather than letting them inflate the rate. Adapty likewise notes that a large share of eventual conversions land after the formal trial end (it cites Totango research around 50%), which is why snapshot rates undersell young cohorts (Adapty on trial conversion rates).

Set the maturity cutoff as trial end + payment-recovery window, not trial end. On Play, that window includes the grace period; on Stripe, the first charge resolves inside hours, but failed renewals churn through past_due. The discipline resembles judging ad campaigns — the same patience rule covered in how long to run Google Ads before judging performance: decide the window before looking at the result, or every early look becomes a bad decision.

Step 2: Map each platform's lifecycle states

Map every platform-native status to one internal vocabulary of six stages: trialing, pending payment, converted, cancelled (still active), expired, and billing recovery. The map is what turns three state machines into one comparable column.

Normalized stage App Store (App Store Server Notifications v2) Google Play (subscriptionsv2) Stripe / web billing RevenueCat webhook
Trialing SUBSCRIBED + INITIAL_BUY with trial offer SUBSCRIPTION_STATE_ACTIVE + offerPhase.freeTrial trialing INITIAL_PURCHASE (period_type=TRIAL)
Pending payment DID_FAIL_TO_RENEW (no grace subtype) Renewal pending after expiry incomplete (23-hour window) BILLING_ISSUE
Billing recovery DID_FAIL_TO_RENEW + GRACE_PERIOD SUBSCRIPTION_STATE_IN_GRACE_PERIOD (autoRenewEnabled stays true) past_due during retries BILLING_ISSUE / CANCELLATION with cancel_reason=BILLING_ERROR
Cancelled (still active) DID_CHANGE_RENEWAL_STATUS + AUTO_RENEW_DISABLED autoRenewEnabled=false, still entitled cancel_at_period_end set, still active/trialing CANCELLATION (auto-renew off)
Converted DID_RENEW after trial (or offer conversion) SUBSCRIPTION_RENEWED into basePrice phase trialing → active on first payment RENEWAL / first paid INITIAL_PURCHASE
Expired EXPIRED + VOLUNTARY / BILLING_RETRY subtype SUBSCRIPTION_STATE_EXPIRED canceled / unpaid / incomplete_expired EXPIRATION

The v2 subtype mapping above follows the AppsOps App Store Server Notifications guide, the RevenueCat webhook event reference, the Google Play lifecycle doc, and the Stripe subscription docs. Three details in the map routinely corrupt reported rates:

  • Platform dashboards silently shrink denominators. Apple's Active Plans metric excludes subscriptions in billing retry and grace period (Apple App Store Connect analytics). A native "active subscribers" tile can understate users who still hold entitlement during payment recovery.
  • Store events have no cross-platform equivalent. RevenueCat's SUBSCRIPTION_PAUSED fires only on Google Play, and INVOICE_ISSUANCE only on RevenueCat Billing — a normalized schema needs an explicit "platform-only" bucket instead of dropping those events.
  • A lapsed resubscribe is not a new conversion. RevenueCat fires RENEWAL both when a subscription renews and when a lapsed user resubscribes (RevenueCat webhook event reference); win-back revenue must not re-enter the trial-conversion numerator.

If your stack already pipes store and web events through one billing layer, the identity work underneath the map — joining an app user to a billing subscriber — is the same plumbing described in web to app attribution for subscription apps. The map only works when the person behind app_user_id is actually one person.

Step 3: Separate cancellation from failed conversion

A cancelled trial and a failed conversion are different outcomes, and reporting them as one "did not convert" bucket hides the fixable half of the funnel. Cancellation is a decision: the user turned off auto-renew or asked to cancel while still entitled. Failed conversion is a mechanic: the trial ended and no successful first payment ever happened.

The platforms expose the difference precisely — if you read the right fields:

  • App Store: AUTO_RENEW_DISABLED is the cancellation signal while access continues; EXPIRED with subtype VOLUNTARY, BILLING_RETRY, PRICE_INCREASE, or PRODUCT_NOT_FOR_SALE is the terminal outcome (AppsOps notification guide). A user can sit "cancelled" for days inside the trial and still convert if they re-enable — Apple even reports AUTO_RENEW_ENABLED as the reverse.
  • Google Play: autoRenewEnabled=false marks the cancelled-still-active state, while grace period keeps autoRenewEnabled=true and extends expiryTime dynamically — the user is still entitled, so neither cancelled nor failed (Google Play lifecycle).
  • Stripe / web: a failed first payment parks the subscription in incomplete for up to 23 hours, then drops it to incomplete_expired — a terminal state where nothing was ever billed (Stripe subscription lifecycle). Separately, a trial ending with no payment method on file can leave the subscription paused rather than converted or cancelled, and customer.subscription.trial_will_end fires a few days early so you can verify a payment method exists (Stripe subscription webhooks).
  • RevenueCat: CANCELLATION means auto-renew was turned off or a refund occurred — the subscription may still be active; EXPIRATION is the separate event where access ends, and BILLING_ISSUE is explicitly not an expiry (RevenueCat webhook event reference).

The outcome taxonomy that falls out of this: converted, cancelled during trial (still deciding until expiry), failed — no payment, expired unpaid, billing recovery pending, and still running. Only the first goes in the numerator; the last two never belong in a finished cohort's denominator judgment at all.

The trial cohort comparison sheet

The worksheet that holds all of this together is one table per platform-cohort. Its columns force the three normalization decisions before any rate is computed:

Block Columns Rule it enforces
Eligibility cohort_id, platform, trial_start_date, card_required, trial_length_days, offer_type First trial only, deduped; card and no-card cohorts never share a row
State map native_status, event_source, normalized_stage Every incoming event must land in one of the six stages or a named "platform-only" bucket
Observation trial_end_date, maturity_cutoff (end + recovery window), still_running, incomplete_flag A cohort is reported only after maturity_cutoff; before that it is flagged, not compared
Outcome converted, cancelled_still_active, failed_no_payment, expired_unpaid, recovery_pending Numerator is converted only; cancelled and failed stay separate lines

Synthetic worked example. Same launch week, same product, 1,000 trial starts per platform, snapshot pulled 5 days after the longest trial ends — deliberately too early:

Platform row (synthetic) Starts Completed trials Converted Still running / pending Rate ÷ completed Rate ÷ starts
iOS, 7-day trial 1,000 940 282 60 trialing 30.0% 28.2%
Android, 14-day trial 1,000 905 253 60 trialing + 35 grace 28.0% 25.3%
Web, 14-day no-card 1,000 930 112 40 trialing + 30 incomplete/paused 12.0% 11.2%

Read it honestly: the "completed" column reproduces something close to Apple's own metric (which divides by completed offers), while "starts" is the denominator most internal dashboards use. At this snapshot the iOS row looks 2.7× better than web — yet the web row still carries 70 unresolved members, and the Android row carries 35 grace-period members who are still entitled and might recover. The web cohort is also a different population (no card required), which alone can explain most of the gap — Recurly's 39.0% versus 12.0% card-required split shows how large that effect can be. Recompute after maturity_cutoff and only the converted column should move; if it does not, the window was set too short.

These numbers are invented to show the arithmetic, not a benchmark. Copy the column structure into a spreadsheet or BI model and fill it from your own store exports, Stripe reports, or billing-layer events.

Decide which number goes to the dashboard

Ship the normalized rate as the headline, keep platform-native rates as diagnostic columns, and never publish an immature cohort without its incomplete_flag. Concretely:

  1. Report mature cohorts only. Dashboard tiles show cohorts past maturity_cutoff; younger cohorts display as "n of N still resolving" rather than a rate.
  2. State the denominator on the chart. "Of completed trials" and "of started trials" are both defensible — pick one, write it on the tile, and keep it identical across platforms.
  3. Keep outcome buckets visible. A falling rate split into "more failed payments" versus "more cancellations" points at billing recovery or at product value — opposite fixes for opposite causes.
  4. Treat external benchmarks as orientation, not targets. RevenueCat's 2026 dataset across 115,000+ apps put median trial-to-paid near 32.6% on the App Store and 32.5% on Google Play — for store trials that bill automatically, on that report's own definitions (Empact Partners compilation). A trial to paid conversion rate benchmark built on a different denominator is a benchmark of a different metric: those medians answer "are we in a sane range," not "what should our number be."
  5. Choose the decision, then the metric. If the real question is trial design, a controlled experiment such as monthly-to-annual subscription conversion framing beats rate-watching; one randomized 337,724-user test found 7-day and 30-day trials within a point of each other (15.36% vs 14.67%, via the same compilation).

Where this breaks

Three boundaries deserve honest exits. Promotional and code-based trials skip parts of the billing event stream entirely — RevenueCat's compatibility matrix shows Promo columns missing INITIAL_PURCHASE and RENEWAL, so those trials need their own counting rule or exclusion. Refunds can reverse a recorded conversion after the fact, so mature cohorts still need a late-adjustment pass. And the same person can hold an App Store subscription trial and a web trial at once; without identity resolution across app_user_id, email, and Stripe customer, that person lands in two denominators.

If the sheet shows three platforms, six stages, and nobody owning the definition, the problem is governance, not instrumentation. An automation audit from That'sGonnaHelp can bound which events, states, and owners the normalized metric needs before the next reporting cycle.

FAQ

What is a good trial conversion rate? There is no single good number — it depends on denominator, card requirement, and platform. For orientation only: RevenueCat's 2026 data across 115,000+ apps reports median trial-to-paid near 32.6% on the App Store and 32.5% on Google Play for auto-billing store trials, while Recurly's 2022 merchants saw 39.0% with a card required versus 12.0% without (Empact Partners compilation). Compare only against benchmarks that share your trial model.

What is trial conversion rate? The percentage of trial starts — or, in Apple's analytics, completed trial offers — that convert to a paid subscription: converted trials divided by the chosen denominator (some teams track the same thing as the trial to purchase conversion rate). The free trial conversion rate formula is simple; the denominator choice is the entire dispute this article is about.

Is a cancelled trial the same as a failed conversion? No. Cancellation is a user decision — auto-renew off while still entitled, and still reversible until expiry (AUTO_RENEW_ENABLED exists for a reason). Failed conversion is mechanical: the trial ended with no successful first payment, as in Stripe's incomplete_expired state. They need different fixes and different reporting lines.

How long should a trial cohort mature before reporting? Until every member has resolved: trial end + the platform's payment-recovery window. That means covering Play grace periods, Stripe's past_due retry cycle, and late conversions — research cited by Adapty suggests around half of eventual conversions can land after the formal trial end.

Does a free trial count as a subscription? Platform-dependent. Apple counts free trials inside its subscription-offer metrics; Stripe models a free trial as subscription status trialing; Play exposes the trial through offerPhase on an active subscription. For your own counting, a trial is a subscription in a pre-paid stage — which is exactly why the state map matters.

Answer clarity notes

  • Dates: the 2026 benchmark medians and the 2022 Recurly figures reflect their linked sources and publication contexts; platform trial rules, notification subtypes, and subscription states change — check current Apple, Google, Stripe, and RevenueCat docs before implementing.
  • Evidence: platform state and event details come from the linked Apple, Google, Stripe, RevenueCat, and AppsOps documentation; benchmark statistics are secondary compilations used for orientation only, on their original definitions.
  • Synthetic example: the 1,000-start cohort table is invented to make the denominator and maturity arithmetic reproducible; it is not a client result, a measured benchmark, or a prediction of your rates.
  • Scope: written for lifecycle, product, and subscription-ops leads at mobile or consumer subscription businesses selling via App Store, Google Play, and web billing (Stripe or a billing layer such as RevenueCat). US-market B2B context; not legal, tax, or platform-policy advice.
  • Do not infer: observation-window lengths, outcome taxonomies, and external medians are planning guidance on stated assumptions, not guarantees.

Sources

A

Alex Khvoinitskii

Founder, That'sGonnaHelp

Founder of That'sGonnaHelp. Building growth and automation systems since 2021 — GTM, traction, retention, and revenue — for SaaS, FinTech, and e-commerce clients, from early-stage brands to global exchanges.

Related articles

Analytics

Web to App Attribution for Subscription Apps

The ad platform reports installs and the billing system reports subscribers; nothing connects them. This blueprint joins web clicks to paid app subscriptions with a deferred deep link, an ID map, and explicit unmatched reporting.

August 21, 202510 min
Discuss your project