# Products, Prices, and Paid Access

Source: https://docs.ezoic.com/docs/subscriptions/products/


A product is the paid thing visitors can buy — a subscription, or a one-time purchase. Each product has a **product handle** and one or more **prices**, and each price has a **price handle**. Those two handles drive the onsite integration: you check the product handle with `ezsubscriptions.hasAccess(...)`, and you open checkout with either `ezsubscriptions.showPaywall({ product })` or `ezsubscriptions.openCheckout({ price })`.

## Products

You create products in the Ezoic dashboard. A product has:

- A public name, such as `Remove Ads` or `Premium`.
- An optional description shown on the paywall.
- A **product handle** your site code uses.
- One or more prices.

Suppose you create a product named `Remove Ads` with the product handle `remove-ads`. On your site, your template checks access with `ezsubscriptions.hasAccess("remove-ads")`. If the visitor has access, you show the subscriber-only experience. If they do not, you call `ezsubscriptions.showPaywall({ product: "remove-ads" })`, which opens Ezoic's pre-built paywall and checkout experience for that product's prices.

You can create more than one product on a site — for example, a `remove-ads` subscription site-wide and a separate `poll-access` product on results pages — and open whichever one fits the page.

## Product Handles

The product handle is the stable identifier your site passes to `hasAccess(...)` and `showPaywall({ product })`. Pick a handle when you create the product in your Ezoic dashboard.

Product handles are:

- Domain-scoped and unique per site.
- Case-insensitive and stored lowercase.
- Limited to letters, numbers, hyphens, and underscores.

Examples: `remove-ads`, `premium`, `poll-access`.

Use handles that describe the access level, not the current price or promotion. Avoid changing a product handle after your site is using it unless you also update the site code that references it.

## Prices

A price is a way to buy a product. Each price has:

- A label, such as `Monthly`, `Annual`, or `Lifetime`.
- An amount and billing interval.
- A **price handle** your site code can use for custom checkout buttons.

`showPaywall({ product })` presents all of a product's active prices and lets the visitor choose. When you want a button that charges one specific price directly — skipping price selection — pass its price handle to `ezsubscriptions.openCheckout({ price })`.

Price handles follow the same rules as product handles: domain-scoped, unique per site, case-insensitive, and limited to letters, numbers, hyphens, and underscores. Examples: `remove-ads-monthly`, `remove-ads-annual`.

## One-Time Per-Item Purchases

A one-time price can sell access to a specific item, such as a single article or download. Pass a publisher-chosen `item` key alongside the price:

```javascript
await ezsubscriptions.openCheckout({
  price: "article-unlock",
  item: "article-12345",
});
```

Check it later with `ezsubscriptions.hasPurchased({ item: "article-12345", price: "article-unlock" })`. The `item` value is stored verbatim and matched exactly, so keep it stable per item.

## Example Product Structures

### Simple Subscription

- Product name: `Premium`
- Product handle: `premium`
- Price: `Monthly` — `$9.99 / month` — price handle `premium-monthly`

Use this when one paid product unlocks all subscriber-only content.

### Subscription With Multiple Prices

- Product handle: `premium`
- Price: `Monthly` — `$9.99 / month` — price handle `premium-monthly`
- Price: `Annual` — `$99.00 / year` — price handle `premium-annual`

Use this when you want to offer the same product at more than one billing interval. `showPaywall({ product: "premium" })` lets the visitor choose; a custom button can call `openCheckout({ price: "premium-annual" })` to sell a specific price.

## Donations Are Separate

Donations are a separate domain-level support option. They do not grant products and do not unlock subscriber-only content. Use [Donations](/docs/subscriptions/donations/) if you want readers to contribute without buying a product.

## After Creating a Product

After your product is live:

1. Add the onsite script to your site.
2. Use the product handle in `ezsubscriptions.hasAccess(...)`.
3. Use the product handle in `ezsubscriptions.showPaywall({ product })`, or a price handle in `ezsubscriptions.openCheckout({ price })`.
4. Add paywalled-content SEO markup to gated articles.
5. Verify checkout as a new visitor.

