# Products

A product is a purchasable item defined by Apple or Google. CherrySub registers the store identifier and connects observed purchases to access rules and product presentations. It does not replace App Store Connect or Play Console as the source of store product configuration.

## Create the store product first

Define the subscription or one-time product in the store, including its product ID, pricing, duration, offers, and availability. Google base plans and offers remain storefront configuration. Use the exact store product ID in CherrySub; matching by display name is not enough.

Store products can be registered in CherrySub before their first purchase. Create an access level first, then use **Products → Register product**. Choose the platform, the exact product ID, the access level it grants, and its grant mode. This form registers the product through an explicit product access mapping. If no access level exists, the dialog takes you to Access levels first. Where store search is available, select the intended result; otherwise enter the exact ID.

## Import products from a store

Use **Products → Import from store** to load the App Store or Google Play catalog. Apple needs a separate App Store Connect API key; Google uses the connected Play service account. Select products, optionally choose an access level, and preview the changes before applying them. Subscriptions and one-time products are supported, including Google's modern one-time-product API.

Imported products can remain unmapped and can exist before any purchase. Their names, types, and Google base plan IDs appear as catalog metadata. Existing matching entries are kept; imports never overwrite store prices or synthesize transaction history. Configure mappings and purchase presentation as separate next steps.

The CLI supports `cherrysub migrate store google APP_ID --product premium,credits`, with `--apply` after review. RevenueCat and CSV/JSON catalog imports use the same preview and conflict rules. See the [migration guide](/docs/migration) for limits, credentials, templates, and purchase migration.

## Connect the two catalog relationships

A useful product participates in two independent relationships:

1. A [product access mapping](/docs/access-levels) determines which access level it grants after verification.
2. A [package in a product set](/docs/product-sets) determines whether and where your app presents it for purchase.

A SKU can be in a product set without granting any access. It can also grant access without appearing in the current product set, such as a historical product you continue to honor. Configure both relationships deliberately.

## Read product details

Open a product to inspect its mapped access levels, store facts, transactions, and subscription lineages. Monetary values remain separated by store currency. Missing accounting information is not a zero-priced sale.

A product identifier is scoped by both app and platform. The same ID can exist in another app, so detail links carry the app context. An unscoped ambiguous product request is rejected rather than returning another app's record.

## Subscription versus one-time products

Store vocabulary matters. Apple consumables, non-consumables, and non-renewing subscriptions are non-subscription purchases. Google uses `one_time_product` for in-app products; its prepaid subscription plans remain subscriptions even though they do not auto-renew.

For Google receipt submission, choose the explicit API `product_type`: `subscription` or `one_time_product`. The SDK's Billing adapter translates store library values into that contract. Do not classify every non-auto-renewing purchase as a one-time product.

## Change a catalog without rewriting history

Update store pricing and availability in the store. Update CherrySub presentation in Product sets and access rules in Product access mappings. Access mapping versions start at a ledger boundary; replay uses the mapping version that existed for the original source event.

Removing a product from the current product set does not cancel an existing subscription, revoke its access mapping, or delete its transactions. Removing a mapping does not refund a purchase. For a migration, first confirm how existing customers will retain access on their next authoritative observation. [Migration guide](/docs/migration).

## Verify a new product

Make a sandbox purchase, verify the exact app/platform/product in Transactions, open the customer, and check the intended access level. Then fetch the current product set from the SDK and confirm its package resolves to the expected storefront product and localized price.

If the transaction exists but the feature remains locked, inspect the mapping, grant mode, expiry/revocation, and customer ownership before changing the purchase UI.
