No confusing trial/purchase toggle patterns
Why it matters
A "Free Trial" toggle that flips the CTA between trial and full-price products treats trial eligibility as a user choice — but Apple and Google determine eligibility server-side based on prior introductory offer usage. Non-eligible users tapping "Start Free Trial" get charged immediately with no warning, which generates refund requests, chargebacks, and 1-star reviews citing deceptive billing. This also puts the paywall in the crosshairs of App Store Review Guideline 3.1.2(a) on subscription clarity and equivalent Play Store policies on misleading pricing.
Severity rationale
Medium because deceptive trial UI drives chargebacks and store rejections but does not halt shipping outright.
Remediation
Drop the trial/non-trial toggle entirely and let the platform's eligibility check drive CTA copy. Query eligibility before rendering, then render one button whose label matches the user's actual purchase terms:
const eligibility = await Purchases.checkIntroductoryPriceEligibility([productId]);
const isEligible = eligibility[productId] === IntroEligibilityStatus.INTRO_ELIGIBILITY_STATUS_ELIGIBLE;
<SubscribeButton
title={isEligible ? 'Start 7-Day Free Trial' : 'Subscribe — $9.99/month'}
/>
Never hide recurring price in small gray text beneath a large "Free" headline — Apple and Google both reject this pattern.
Detection
- ID:
no-trial-toggle - Severity:
medium - What to look for: Count all relevant instances and enumerate each. Look for UI patterns on the paywall that make it unclear whether the user is subscribing with a trial or subscribing at full price. Common problematic patterns: (1) A toggle switch labeled "Free Trial" that, when switched on, changes the CTA button label and hides the trial terms — confusing because trial eligibility is determined by Apple/Google, not the user. (2) Presenting "Monthly" and "Annual" options alongside "Free Trial" as three separate purchasable options where the trial is presented as a distinct product rather than an introductory offer on a subscription. (3) A paywall that shows "7 Days Free" in large text and the regular price in very small, gray text that could be considered deceptive. (4) "Try for free" language that actually starts a paid subscription immediately because trial eligibility has expired, with no clear messaging about non-eligibility. Check if the paywall checks introductory offer eligibility before showing trial CTA. In RevenueCat:
package.product.introductoryPrice != null— but this does not confirm user eligibility; usecheckIntroductoryPriceEligibility([productId]). - Pass criteria: The paywall presents a single clear CTA (with or without trial terms), does not use a toggle to switch between trial and non-trial purchase modes, and correctly communicates the terms for the user's actual eligibility state. At least 1 implementation must be verified.
- Fail criteria: Trial/non-trial toggle that changes purchase terms depending on user selection; "Try free" CTA shown to users who are no longer eligible for a trial without a fallback; deceptive visual hierarchy where trial messaging is prominent and price is visually suppressed.
- Skip (N/A) when: No free trial configured for any IAP products.
- Detail on fail:
"PaywallScreen.tsx renders a 'Free Trial' toggle that switches between TRIAL_PRODUCT_ID and REGULAR_PRODUCT_ID purchases — trial eligibility should be determined by platform, not user toggle"or"Trial CTA shown to all users without checking introductory offer eligibility — non-eligible users see 'Start Free Trial' but are immediately charged" - Remediation: Simplify paywall CTAs. Let the platform determine trial eligibility:
- Check trial eligibility before rendering trial CTA:
// RevenueCat const eligibility = await Purchases.checkIntroductoryPriceEligibility([productId]); const isEligible = eligibility[productId] === IntroEligibilityStatus.INTRO_ELIGIBILITY_STATUS_ELIGIBLE; - Show different CTA copy based on eligibility:
<SubscribeButton title={isEligible ? 'Start 7-Day Free Trial' : 'Subscribe — $9.99/month'} /> - Never use a toggle to switch between trial and non-trial purchase flows
- Check trial eligibility before rendering trial CTA:
Taxons
History
- 2026-04-18·v1.0.0·Initial import from app-store-iap-subscriptions·automated