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 — weekly, monthly, or yearly, including every-N intervals such as every 3 months.
  • 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.

Additional Currencies

Every price has a default currency (USD unless you change it). Without any further setup, checkout automatically presents a local-currency equivalent to international visitors, converted at the payment provider's exchange rate. Two things to know about the automatic conversion: the amounts follow the rate, so they are rarely round numbers (a $9.99 price might present as €9.21), and the buyer pays a small conversion fee on top. Visitors can always switch back to your listed currency at checkout if they prefer it. Automatic currency conversion is on by default; you can turn it off in the Ezoic dashboard's Subscriptions area under Settings → Ways buyers pay → Adaptive Pricing.

Recommended: set your own amounts per market with additional currencies on the price — for example $9.99 for US visitors and ₹499 for visitors in India. A visitor whose region matches one of your currencies sees and pays that exact amount in that currency, with no conversion fee. This works for recurring and one-time prices, and for donations (where each currency sets its own suggested amounts and minimum — see Donation Integration).

No onsite code changes are needed either way — the paywall and checkout present the visitor's currency automatically.

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. One price sells every article with no per-article setup; each purchase unlocks only the page it was bought on. 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.

Free Trials

A recurring price can start with a free trial. Set Free trial (days) on the price in the dashboard (1–365 days) to give new subscribers a no-charge trial before their first payment.

With a trial configured:

  • The paywall shows a Start {N}-day free trial call to action (for example, "Start 7-day free trial") and a trial badge. The badge label is customizable and substitutes the trial length — the default is {days}-day free trial.
  • The visitor enters a payment method but is not charged during the trial. They get full access immediately, so hasAccess(...) returns allowed while the trial is active.
  • The subscription converts to paid automatically when the trial ends, unless the subscriber cancels first from the subscriber portal.

Trials are limited to one per product per subscriber. A visitor who has already used the trial (or whose card was used for one) cannot start it again: when they submit the trial signup, checkout shows an "already redeemed a free trial" notice with the option to subscribe at the regular price instead (an active promo code still applies to that purchase). Until then the paywall can still show the trial call to action, so a returning visitor may see the trial offer briefly before checkout flags it at signup.

A promo code redeemed on a trial starts its discount window when the trial ends, so the free days never count against it.

No onsite code changes are needed for trials — you check and sell the product exactly as you would without one.

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 CSS variables for finer brand matching. See Paywall Appearance and Styling.

Discounts

To offer a percentage or fixed-amount discount at checkout, create a promo code. When a domain has an active promo code, the checkout automatically shows a code entry field — no onsite code required.

Donations Are Separate

Donations are a separate domain-level support option. See Donations.

After Creating a Product

After your product is live:

  1. Add the onsite script to your site — see Onsite Script Integration.
  2. Use the product handle in ezsubscriptions.hasAccess(...) — see Publisher-Managed Access API.
  3. Use the product handle in ezsubscriptions.showPaywall({ product }), or a price handle in ezsubscriptions.openCheckout({ price }).
  4. If you gate article content, add paywalled-content SEO markup.
  5. Verify checkout as a new visitor.