Skip to main content
tutorial Featured

Designing a Freemium Upgrade Flow: Entitlements, Prompts, and Stripe

A practical architecture for premium entitlements, contextual upgrade prompts, hosted checkout, webhook fulfilment, and measurable recovery.

BY Group
February 11, 2026
14 min read

A freemium upgrade flow is an entitlement system with a user interface attached. The interface explains why an action is unavailable and offers a route to buy access. The server decides whether the person has access, creates checkout, and fulfils the purchase.

Treating the modal as the whole system creates predictable failures: controls that look available but do nothing, pricing that disagrees with checkout, client-side flags that can be bypassed, and success pages that grant access before payment is confirmed.

Define the commercial contract first

Write one entitlement table before building components:

CapabilityFree statePaid stateEnforcement pointRecovery
Feature accessAvailable or unavailableAvailableServer and interfaceUpgrade or return
Usage quotaCurrent limit and reset rulePaid limitServer mutation routeUpgrade or wait for reset
Export or saveExact free formats or limitsExact paid formats or limitsServer operationUpgrade or choose free output
CollaborationExact free role or seat limitExact paid allowanceServer authorizationUpgrade or remove members

Every phrase shown to a user must come from this contract. If the product does not support a trial, do not say “Start free trial.” If cancellation takes effect at the end of a billing period, explain that instead of promising immediate cancellation. If tax is calculated in checkout, do not imply the displayed subtotal is the final amount in every jurisdiction.

The price identifier, currency, billing interval, and entitlement result should be configured once and reused. A marketing page, upgrade dialog, account page, and checkout route must not maintain separate commercial truths.

Choose the gate according to the action

There are three useful interface patterns:

Visible premium capability

Show the control with a concise “Premium” label when discovering the capability is useful. Activating it opens an explanation instead of failing silently.

Usage limit

Allow the action while quota remains. When it is exhausted, show the measured state, the reset rule, and the paid allowance. The server must calculate and enforce the quota.

Result or export boundary

Let the person complete the free work only when the product has explicitly chosen that experience. Before they invest substantial effort, disclose any paid export or save limitation. Surprise gates damage comprehension and make it hard to interpret abandonment.

Do not blur a premium gate with authorization. A paid customer may still lack permission to edit another user’s resource. Subscription status and resource authorization are separate server checks.

Make the prompt answer five questions

An upgrade prompt should tell the person:

  1. Which action requires a paid plan.
  2. What the plan unlocks in this context.
  3. The current price and billing interval, or where to verify them.
  4. What happens after selecting the upgrade action.
  5. How to dismiss the prompt and continue using the free product.

Use the feature name in the heading. A generic “Unlock your potential” message forces the person to reconstruct why the modal appeared. Keep the primary button specific, such as “Upgrade to export SVG,” and keep a visible close action.

An icon can support the label but cannot replace “Premium.” Colour alone cannot communicate access state. The W3C use-of-colour guidance requires another visual means when colour conveys information.

Keep entitlement logic out of presentation components

A small React surface can support a large product when responsibilities remain separate:

  • a subscription hook loads the current entitlement;
  • a feature-gate helper decides whether to continue or open the prompt;
  • a presentation component renders the premium label;
  • a server route creates the checkout session;
  • webhook handlers update the durable entitlement record;
  • protected server operations verify that record again.

The shared BY Group implementation follows this separation. PremiumBadge renders the visual label. FeatureGate chooses between accessible and fallback content. useFeatureGate opens a product-owned upgrade modal when access is absent. The shared billing package owns checkout, portal, status, session verification, and webhook behaviour.

The client helper is a convenience, not a security boundary:

const { gate } = useFeatureGate({
  isPremium,
  showUpgradeModal,
});

function handleExport() {
  if (!gate()) return;
  startExport();
}

The export route must still verify the authenticated user’s entitlement.

Create Checkout on the server

Stripe’s subscription Checkout guide places session creation on the server. A typical flow is:

  1. Authenticate the request.
  2. Resolve the price from server configuration.
  3. create a Checkout Session in subscription mode;
  4. attach an internal user identifier in metadata;
  5. return the hosted Checkout URL;
  6. redirect the browser.

Never accept an arbitrary price identifier, entitlement, product name, or user identifier from the browser. Allow-list any optional return paths or derive them from trusted configuration.

Hosted Checkout owns payment data collection. Your application owns the product context before checkout and the entitlement after Stripe reports the payment state.

Fulfil from webhooks, not the success page

Stripe explicitly warns that a success page is not a reliable fulfilment mechanism. A customer can pay and close the tab before the redirect. Use verified webhook events to update entitlement state, and make event handling idempotent because Stripe can retry delivery.

The durable flow is:

Authenticated request
  -> server creates Checkout Session
  -> Stripe hosts payment
  -> verified webhook records subscription state
  -> application reads entitlement

The success page may poll or verify the Checkout Session to improve feedback, but it should not grant access on its own. Show a processing state if the webhook has not yet updated the product.

Handle at least subscription creation, updates, deletion, and payment failure according to the product’s access policy. Expose Stripe’s customer portal for billing changes when it fits the product, rather than building a second billing interface without need.

Design cancellation and failure recovery

The upgrade button is not the only commercial state. Test:

  • checkout cancellation;
  • a returned customer whose webhook is still processing;
  • a payment that requires another action;
  • a subscription scheduled to cancel;
  • a failed renewal;
  • a customer with an active subscription but a stale browser cache;
  • a duplicate webhook event;
  • a webhook received out of order.

The account page should display the server-derived state and provide a refresh or portal action. Do not trap a customer in an upgrade loop because the interface cached isPremium=false.

Measure the whole funnel

Track a small set of events with non-sensitive properties:

  • premium capability viewed;
  • premium action attempted;
  • upgrade prompt shown;
  • prompt dismissed;
  • checkout started;
  • checkout returned;
  • entitlement activated;
  • payment recovery opened.

The useful denominator depends on the question. Prompt-to-checkout rate diagnoses the offer and prompt. Attempt-to-entitlement rate includes checkout and fulfilment. Product revenue and retention determine whether the plan creates lasting value.

Do not claim that a modal pattern or button label “converts” based on another company’s case study. Measure the actual product, price, audience, and trigger. Change one material element at a time when volume supports a comparison.

Release checklist

Before enabling a paid gate:

  1. Approve the entitlement table and exact product copy.
  2. Verify server-side enforcement for every protected mutation.
  3. Confirm displayed price, interval, tax wording, and checkout price agree.
  4. Test keyboard operation, focus return, close actions, and text access-state labels.
  5. Confirm checkout creation requires authentication and allow-lists browser input.
  6. Verify webhook signatures and idempotency.
  7. Exercise success, cancellation, processing, renewal failure, and cancellation states.
  8. Inspect analytics for payment data, email addresses, Stripe identifiers, or other personal data.

A good freemium flow is legible even when payment fails. It tells the person what is available, why the action is gated, what the plan costs, and what will happen next, while keeping the actual access decision on the server.

Sources and implementation references

B

BY Group

Software engineering studio building high-quality products with minimal overhead.

See the Implementation Context

Explore the products and shared infrastructure behind the engineering guides.

View the Portfolio

No credit card required • Free forever plan available