storebound/skills/store-subscriptions/SKILL.md
CRT 4032657695
Some checks failed
ci / gate (push) Successful in 2s
ci / acceptance (bun, 22, ubuntu-22.04, Linux, bun) (push) Successful in 28s
ci / acceptance (bun, 22, windows, Windows, bun) (push) Successful in 33s
ci / acceptance (node20, 20, ubuntu-22.04, Linux, node) (push) Successful in 1m20s
ci / ci (push) Successful in 2m3s
ci / acceptance (node22, 22, ubuntu-22.04, Linux, node) (push) Successful in 1m23s
ci / acceptance (node20, 20, windows, Windows, node) (push) Has been cancelled
ci / linux (push) Has been cancelled
ci / acceptance (node22, 22, windows, Windows, node) (push) Has been cancelled
ci / windows (push) Has been cancelled
ci / record (push) Has been cancelled
scan: remote config, feature flags and remote paywalls not told to review (App Review 2.3.1, Play Deceptive behavior): remote-features finding for both stores, medium, with deep links, unless the review notes mention them; check prints it as a warning, validate carries it; warn only, the templates get a commented example line; code obfuscators a low note, minify alone nothing; store-pitfalls 74
2026-10-18 01:26:32 +08:00

71 lines
6.4 KiB
Markdown

---
name: store-subscriptions
description: 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`).
- Remote paywalls (RevenueCat Paywalls, Superwall, Adapty, Qonversion, Purchasely, Apphud) or flags: say in the review
notes that paywall designs / prices shown change remotely (2.3.1, `scan` warns; store-pitfalls 74); moving features
behind the subscription after review needs a new review and a description that discloses it.
## 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.