Skip to content

Choose a runtime fact

Use the narrowest operation that returns the fact you need.

Question Operation For every request
Does this customer have any subscription that gives access? customers.hasActiveSubscription Yes
Does the customer have an active item on this product? activeItems.hasProduct Yes
Which active products and prices does the customer have? activeItems.list Yes
Does an active product grant this boolean entitlement? entitlements.has Yes
What does the customer’s plan grant, including limits? entitlements.retrieve Yes
What detailed billing and scheduled state should a billing page explain? customers.retrieveBillingState No

The HTTP API has one operation meant for every request, active_items/list. Every SDK check reads that list: hasActiveSubscription, hasProduct, and the entitlement operations. When one request checks more than one product or entitlement, read the list or the entitlements once and resolve in memory; each boolean check whose value is missing from the saved list sends its own request.

Use customers.hasActiveSubscription when every paid plan grants the same access, or when you only need to know whether the customer pays at all.

It returns true when the customer has at least one active item. Active items are the prices charged by subscriptions in active, trialing, or past_due (Stripe calls them subscription items), and Stripe never leaves a subscription without items.

Use activeItems.list when Stripe products or prices represent plans, billing intervals, seats, or add-ons.

Each item includes its subscription ID, item ID, price ID, price lookup_key, product ID, and quantity. Derive product and price membership from that set in your application.

Use entitlements.retrieve or entitlements.has when you want plans to grant named capabilities or numeric limits. You declare what each product grants when creating the SDK client; the SDK resolves it from the active-item list, so it needs only customer runtime state. See Check entitlements.

Use customers.retrieveBillingState for billing pages, support inspection, and schedule-aware explanation. It returns last_refreshed_at (null when Strent has never refreshed the customer) and each retained subscription, with its items and the facts Strent already derived from Stripe:

Field Meaning
grants_access false for unpaid, paused, and incomplete subscriptions. They grant nothing, but your billing page should ask for payment instead of offering a new subscription.
cancellation.effective_at When Stripe will end the subscription: an explicit cancel_at, the earliest item period end for cancel_at_period_end, or the final phase of a schedule that ends in cancellation. canceled_at is never used. null means Stripe scheduled the end without a time.
pending_change The next schedule phase that has not started yet: its start and the prices and quantities it switches to.
trial_end Stripe’s trial end, or null.
current_period_end The earliest item period end, which is when Stripe bills the subscription next.
price.currency, price.unit_amount, price.recurring Stripe’s price facts. unit_amount is in the currency’s minor unit, for arithmetic, and is null for tiered or decimal-only prices.
price.unit_amount_display The same amount as an exact decimal string in whole currency units, such as "19.99" for usd 1999 or "2000" for jpy 2000. It has no symbol or separators; null exactly when unit_amount is null.

Show a price with unit_amount_display rather than dividing unit_amount yourself: Stripe uses zero, two, or three minor-unit digits depending on the currency. When it is not null, format it in your user’s language:

const label = new Intl.NumberFormat(locale, {
style: "currency",
currency: price.currency,
}).format(Number(price.unit_amount_display));

Read these facts instead of recomputing them from raw Stripe fields. Before you offer Checkout, check that the customer has no retained subscriptions, so an owner with an unpaid subscription settles it instead of paying twice. Build a billing page puts these rules into a complete page.

It is slower than the checks above, so do not call it on every request to decide access.