Packages, prices, and Play ownership
Marketing copy belongs to you. The price belongs to Google Play. Packages are where those two meet, and the spec draws a hard line between them.
What a package is
A package is one purchasable row on the paywall: a label such as "Yearly", a price, a sub-label such as "billed yearly", an optional badge, an enabled toggle, and a link to a store product through its productId. Each package has a type — subscription, one_time, or pay_what_you_want — and a source of either play or draft. One package is marked recommended and is pre-highlighted.
Who owns which field
The spec's package table is the clearest statement of ownership. Marketing fields are yours to edit at any time; price is the exception.
| Field | Editable? | Owner |
|---|---|---|
| Label, sub-label, badge | Yes | You (marketing copy) |
| Enabled toggle, recommended | Yes | You (what the paywall shows) |
| Price on a draft package | Yes, until it is pushed to Play | You, temporarily |
| Price on a Play-synced package | No, read-only | Google Play |
Source: package configuration table in spec/PAYWALL_SPEC.md.
The reason is compliance and correctness. Google owns the transaction, so the amount a user is charged is whatever the Play product says. A paywall that displayed a different number would be wrong at best and misleading at worst. Making the synced price read-only removes that failure mode by construction.
Draft packages and the design-first flow
Packages start as drafts when you design the paywall before connecting Play. Preset cards — monthly, quarterly, yearly, and lifetime — create draft products with an editable price, product id, and package type. Pushing them creates the products on Google Play through the Monetization API and marks them source: 'play'. After every fetch or push, the builder rebuilds the package list from the Play-sourced products while preserving the marketing copy you already wrote. Incomplete drafts with no id are flagged and skipped rather than blocking the flow.
If you already sell on Play, the other direction works too: fetch the existing catalog and its packages arrive as play-sourced, prices read-only from the start.
Pay-what-you-want, snapped to real products
A pay_what_you_want package renders a slider instead of a fixed price row. The slider does not accept arbitrary amounts, because Google Play does not: the package carries a tiers list of pre-registered price points, and the SDK always maps the chosen amount to the nearest tier and purchases that real Play product. The button reads "Pay" followed by the snapped tier. A pwywDefault pre-selects a tier, defaulting to the middle one. As with price, the tier list is editable only on draft packages and becomes read-only once synced.
This is a Google Play capability. The platform support matrix lists pay-what-you-want as implemented on Google Play and not implemented on the Apple App Store, so do not present it as available to iOS users.
The call-to-action label
You do not author the button text per package. The label is derived at render time: if the selected package is a one-time purchase — by its type, by the type on the synced product, or, as a fallback for unsynced drafts, by a "one-time" sub-label — the button uses the one-time label, which defaults to "Buy". Otherwise it uses the subscription label, which defaults to "Subscribe". Both labels can be overridden per locale.
// simplified from spec/PAYWALL_SPEC.md const isOneTime = selPkg.type === "one_time" || selProduct?.type === "one_time" || selPkg.sub?.toLowerCase().includes("one-time"); const ctaLabel = isOneTime ? ctaOneTime : ctaSub;
Locales change words, not money
A locale override can change the badge, headline, subtitle, both call-to-action labels, and the feature list. Packages, prices, theme, and accent stay global. That keeps the ownership rule intact across languages: translation is copy, and copy never touches price. The matrix lists 15 preset locales plus custom BCP-47 codes on Google Play; AI translation is available on the Pro and Scale plans.
Removing a product removes its package
Deleting a product also removes its matching paywall package, including that package's win-back offer, and repairs the recommended and selected package. The builder states this consequence before and after the action. It is worth remembering when you read the next chapter: a win-back offer is attached to a package, so it goes with it.
Glossary: Paywall
The screen these packages are rendered on.
Glossary: Entitlement
What a purchased package ultimately grants.
Feature: Entitlement mapping
How store products map to entitlement ids on the server.
Guide: Mapping store products to entitlements
Why a product id and an entitlement id are different things.
Platform support matrix
Where pay-what-you-want and translation are implemented per store.
Keep marketing copy yours and prices Play’s
Free tier — unlimited apps, 1 paywall. No credit card, no revenue share.
Start free