Products and perks

Model what customers buy and what each purchase unlocks.

Voidhash separates the item a store sells from the access it grants. The two concepts have different names:

  • A product is something a customer buys, such as a monthly subscription.
  • A perk is a capability your app can unlock, such as premium or remove-ads.

A product can grant several perks, and the same perk can be attached to several products. Your app checks perks instead of hard-coding product IDs, so your access logic stays stable when pricing changes.

Create a perk

In Studio, open Perks and choose Add perk. Give it a readable name and a stable slug. The name is for people, and the slug is what you use in code. For example, a perk named "Premium access" might use the slug premium, and "Remove ads" might use remove-ads.

A slug can contain lowercase letters, numbers, underscores, and hyphens. Treat a slug as permanent once it ships.

Create a product

Open Products and choose Add product. Choose a subscription, one-time purchase, or consumable product. After you create one, finish it in three steps:

  1. Attach every perk the product should unlock.
  2. Connect the matching product from each enabled payment provider.
  3. Add the product to a paywall.

The Voidhash slug is your cross-platform identifier. The App Store and Google Play product IDs may be different from it. The provider mappings connect both store products to the same Voidhash product.

Offer a discount

A discount on a subscription, such as 50% off the first year, is an offer of the product, not a separate product. The customer keeps the same subscription and perks; only the price they pay changes. Create the offer in each store first:

  • App Store: a promotional offer on the subscription in App Store Connect. Voidhash signs promotional offers with the In-App Purchase key you added to the App Store configuration.
  • Google Play: a developer offer on the base plan the product is connected to.

Then open the product, edit its App Store and Google Play products, and add the offer under Promotional offers (App Store) or Offers (Google Play). Give it the same offer ID on both stores, such as win-back, and enter each store's own identifier next to it. Paywalls and apps buy the offer by that offer ID.

A purchase of an offer buys exactly that offer or fails with OFFER_UNAVAILABLE; it never buys the product at its regular price instead. A purchase fails this way when the store does not offer it to this customer (for example, a customer who is not eligible for it), or when the product's mapping on that store has no such offer. A paywall that sells an offer no mapping lists cannot be published.

Paywalls that sell an offer need a current SDK, and are not shown by app versions that predate offers. Revenue analytics record the discounted price the App Store reports for each transaction. For Google Play, a purchase that starts with an offer is recorded like a trial, and its renewals at the base plan's price.

Import store products

On Products, choose the import icon beside Add product. Its badge shows how many unassigned store products are available. Connect Google Play or App Store in project settings first; Apple catalog access requires an App Store Connect API private key.

Products are grouped by provider. Google Play subscriptions appear separately for each base plan. Select a store product, then choose an existing Voidhash product and Assign product, or choose Create new product to create and assign one directly. Choose the matching product type and billing duration when creating a product. Products already configured for that provider are excluded from the selection so an import does not replace their mapping.

Previously assigned products, including historical mappings, are excluded from the import list. Store catalogs are cached for one minute. After making changes in the stores, allow up to a minute and use Refresh products. If one provider cannot load, you can still import from the other provider. If creating a product succeeds but assignment fails, retry assignment using the product you just created.

Keep generated types current

Studio is the source of truth for the schema. React Native projects can pull its slugs into TypeScript after each catalog change. See Keep types in sync.

Next steps