Offers
Free trials and discounts on a product, through introductory offers, App Store promotional and win-back offers, and Google Play offers.
An offer is a free trial or a discounted way to buy a product, such as a free first week or 50% off the first year. It isn't a separate product: the person gets the same subscription and perks and pays a different price.
The SDKs list a subscription's offers on each product: subscription.introOffer is the free trial or
introductory price the person can have now, and offers are the other offers they can buy.
| Kind | App Store | Google Play |
|---|---|---|
| Introductory | A free trial or introductory price for people new to the subscription group. Apple decides. | An offer for new customers on the base plan, or the one you tag. See offer tags. |
| Promotional | An offer for current and former subscribers, signed by Voidhash. | Any other offer on the base plan. |
| Win-back | An offer for lapsed subscribers, from iOS 18. Apple decides who can have it. | An offer whose eligibility you set for lapsed subscribers. |
Set up an offer
- Create the offer in each store: on the subscription in App Store Connect, or on the base plan in Google Play.
- List an App Store promotional offer on the product's App Store mapping under an offer ID your
paywalls and app use, such as
win-back, beside the store's own offer identifier. Voidhash signs only the promotional offers a mapping lists. List the matching Google Play offer on the Google Play mapping under the same offer ID. See Manage your catalog for where this lives in Studio. - On Google Play, tag your free trials in Play Console; see Google Play offer tags.
Introductory offers and App Store win-back offers need no mapping, and the SDK lists a base plan's unmapped Google Play offers under their Google Play offer ID. A Google Play offer needs the mapping's Base plan ID.
Voidhash signs App Store promotional offers with the In-App Purchase key from your App Store settings. Add the key's Issuer ID there too: from iOS 26 the SDK applies the offer with Apple's signed JWS, which needs it. Without it, and below iOS 26, the SDK uses the older signature.
Google Play offer tags
Google Play returns an offer for new customers only to people eligible for it, but returns an offer you target yourself (developer-determined) to every person, without saying which is which. So on each base plan, the SDK's introductory offer is:
- the offer tagged
voidhash-introin Play Console; - otherwise the offer with the longest free trial that the mapping doesn't list and that isn't tagged
voidhash-ignore. An offer that starts with a paid discount is never picked unless it's taggedvoidhash-intro.
The product's offers are the offers the mapping lists, then every other offer Google Play returned
that isn't tagged voidhash-ignore. A plan change applies the introductory offer only when it's
tagged voidhash-intro.
An untagged developer-determined free trial becomes the introductory offer and is applied to every
purchase by default, also for people who had a trial before. Tag the trial new subscribers get
voidhash-intro, and tag offers you don't sell through Voidhash voidhash-ignore. List an offer
meant for some people, such as a win-back offer, on the mapping and show it only to them: Google
Play doesn't check who gets it.
Buy an offer
The SDK buys exactly the offer, or fails (dashed).
By default a purchase applies the introductory offer the person can have, otherwise the regular
price. To buy another offer from code, pass it, or its offer ID, as the purchase's offer; see
Purchases for Swift, Kotlin or
React Native.
Buying a chosen offer either buys exactly that offer or fails with OFFER_UNAVAILABLE; it never
falls back to the regular price. It fails when the product has no such offer on that store, when the
store doesn't offer it to this person, for example because they aren't eligible, or when Voidhash
couldn't sign an App Store promotional offer. A paywall that sells an offer no mapping lists fails to
build.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.