storebound/skills/store-subscriptions/SKILL.md

6 KiB

name description
store-subscriptions Set up auto-renewable subscriptions on the App Store and Google Play from one config - Apple subscription groups, levels, localizations, equalized prices with overrides and a price CSV, introductory offers, promotional offers, offer codes and custom codes; Play subscriptions with base plans, regional prices, intro and developer-chosen offers; first submission with a version, the disclosure Apple 3.1.2 and Play's subscription policy require, Terms of Use (EULA) links, price changes and the catalog parity report. Use when the user sells subscriptions, adds a free trial, changes subscription prices, or gets a 3.1.2 / subscription policy rejection.

Subscriptions on both stores

CLI: storebound apple subscriptions, storebound play subscriptions (both --dry first; they print the plan and ask before writing), storebound catalog diff (read-only parity of the whole catalog), storebound validate. Config: subscriptions: [{ name, listings, subscriptions: [{ id, period, level, usd, overrides, listings, intro, offers, offerCodes, reviewScreenshot, basePlanId, gracePeriod, platforms }] }], app.termsUrl.

Model

  • Apple: a subscription group per product line (people hold one subscription per group); level 1 = highest service (upgrades and downgrades follow it). Prices: the USA price point for usd, its equalizations for every territory, then overrides and rows from --csv store/prices.csv (same CSV as storebound prices, rows with a future start_date become scheduled price changes). preserveCurrentPrice is always true: existing subscribers keep their price; raising it for them is a separate, consent-bound step in App Store Connect.
  • Google Play: no groups. Each entry is a Play subscription with one auto-renewing base plan (basePlanId default from the period: monthly, yearly, ...), prices from convertRegionPrices + overrides + CSV. A base plan price change applies to new subscribers; existing ones keep their price until you run a price migration in the Console.
  • Intro offer (intro): free trial, pay as you go (periods at usd), or pay up front. Apple: per territory, first-time subscribers of the group. Play: an offer for people who never had this subscription (pay as you go is a relative discount, so it follows every regional price).
  • Promotional offers (offers, Apple + Play): Apple promotional offers (your server signs them at purchase time); Play offers with an offer tag and no acquisition targeting (your app decides who sees them).
  • Offer codes (offerCodes, Apple): a campaign (eligibility NEW / EXISTING / EXPIRED, replace or stack with the intro offer). Custom codes are created only with --codes from a terminal that confirms (anyone with the code can redeem it). One-time-use codes: the owner generates them in App Store Connect. Play promo codes have no API: Play Console > Monetize > Promo codes.

Steps

  1. Prerequisites: Apple Paid Applications agreement, banking and tax; Play payments profile and a build with the billing library on a track.
  2. storebound check (limits: Apple name 30 / description 45; Play description 200, up to 4 benefits).
  3. storebound apple subscriptions --dry and storebound play subscriptions --dry; show the plan, then run them yourself (--yes): new subscriptions, first prices, listings, base plans and offers are written. A price change of an existing subscription is a live price and --yes never applies it: the run writes everything else, prints price change on <id> (<from> -> <to>) needs the owner: re-run with --allow-price-changes and exits 3. After the owner's yes in chat for that diff, run it again with --yes --allow-price-changes. Flows never pass that flag.
  4. Review screenshot of the paywall per subscription (reviewScreenshot), uploaded by apple subscriptions.
  5. First subscriptions go with an app version (and a new group with at least one of its subscriptions): storebound apple iap-submit --with-version --dry plans one review submission with the app version, the subscription versions and the group version; the owner runs it without --dry (it asks, and submitting it submits the app), or adds --no-submit and presses "Submit for Review" in App Store Connect. Details and the exact limits: store-iap-setup skill, step 3. 5b. Win-back offers for lapsed subscribers (winBack on a subscription, storebound apple win-back) once the subscription is approved: app-store-promotion skill.
  6. storebound validate (states, localizations, group names, review screenshots, active Play base plans, terms URL) and storebound catalog diff (both stores against the config, prices per region).

Disclosure (review checks it)

  • Purchase screen: plan name, length, full renewal price per period as the most prominent price, what is included, trial length and what is charged after it, how to cancel, Restore Purchases, links to the Terms of Use and the Privacy Policy (Apple 3.1.2 and "Clearly describing subscriptions", https://developer.apple.com/app-store/subscriptions/; Google Play subscriptions policy, https://support.google.com/googleplay/android-developer/answer/9900533).
  • Metadata: the Terms of Use link in the App Store description, or a custom EULA in App Information > License Agreement; the privacy policy URL field (storebound apple listing). validate warns when a description has no Terms link. 3.1.2(a): a subscription must give ongoing value and last at least 7 days.
  • Review notes: where the paywall is, that the demo account can buy in the sandbox (review-access).

Gotchas

  • A product id, a billing period or a subscription's group cannot change; make a new id.
  • Apple applies one introductory offer per customer per group; offers cannot be edited, only replaced.
  • Account deletion does not cancel a subscription: say so (account-deletion).
  • Unverified (no live account during development): free promotional offers and offer codes are sent with territories but no price point; check the first run in App Store Connect.