View as Markdown

Products, Prices, and Paid Access

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 Purchases

A one-time price is a single payment rather than a recurring subscription. When you create one in the dashboard, two extra fields decide exactly what the payment buys:

  • What this unlocks — one of three modes (see below).
  • Access duration (days, optional) — leave it blank for lifetime access, or enter a number of days for a time-limited pass (for example a 1-day article rental or a 7-day pass). When the window lapses, access stops and the check returns expired, so always treat a fresh check as the source of truth.

What this unlocks

Mode What it grants Gate the content with
The whole product The entire product, exactly like a subscription. hasAccess({ product })
A single item your site provides One item you name at checkout (an article id, a download slug). hasAccess({ product }) || hasPurchased({ item })
The current article automatically The article the paywall is shown on — Ezoic uses the page URL as the item, so one price covers every article. hasPurchased({ page: true })

Either item mode grants access to that one item only: it never satisfies hasAccess({ product }) and never appears in getProducts().

A single item your site provides

Pass a publisher-chosen item key alongside the price at checkout:

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

You can also let Ezoic's paywall sell it: ezsubscriptions.showPaywall({ product: "premium", item: "article-12345" }). Check it later with ezsubscriptions.hasPurchased({ item: "article-12345" }). The item is a site-wide key — stored verbatim and matched exactly on its own, no price needed — so keep it stable per item.

The current article automatically

Pick this mode when every article sells the same way and you don't want to maintain a per-article id. Put ezsubscriptions.showPaywall({ product: "premium" }) on the article — the paywall sells access to that page — and reveal an already-bought article with hasPurchased({ page: true }):

const access = await ezsubscriptions.hasPurchased({ page: true });
if (access.decision === "allowed") {
  revealArticle();
}

{ page: true } tells Ezoic to check the current page using the same normalized page key it stamps at checkout, so your check always matches what was sold — you never compute or pass the item yourself.

Example Product Structures

Ad-Free Subscription

  • Product name: Remove Ads
  • Product handle: remove-ads
  • Price: Monthly$4.99 / month — price handle remove-ads-monthly

The most common setup: one product that removes ads for subscribers.

Premium Content 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.

Appearance and Styling

Each product has its own Appearance settings — template, color mode, and accent color — plus a curated set of CSS variables for finer brand matching. See Paywall Appearance and Styling.

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 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.