Runtime

Create checkout and grant access after payment

Use Licenzy to start Stripe checkout, then grant access only after webhook-confirmed payment updates entitlement state.

How to create a Stripe Checkout Session without granting access too early

This page is for the purchase start of the flow: how to open Stripe Checkout through Licenzy and make sure access is granted only after payment is actually finalized.

If you are trying to sell subscriptions, credits, or usage-based products with Stripe, creating checkout is the easy part. The harder part is tying payment to reliable access control. Stripe does not decide access for your product. Licenzy does.

Licenzy solves that gap by creating Stripe Checkout for you, processing the billing webhook, and updating entitlement state before your app makes runtime access decisions. That keeps checkout creation, payment confirmation, and access control in one consistent flow. See the full integration example if you want the entire purchase-to-access sequence.

When you should use this

This flow is useful when payment should activate or extend product access, including:

  • SaaS products with subscriptions.
  • Pay-per-use APIs.
  • Credit-based systems.
  • Gated products such as tools, features, or courses.

In all of these cases, creating checkout is not enough. You also need reliable access control after payment completes.

How to create a Stripe Checkout Session from your backend

Call POST /v1/checkout/session from your backend. Send the subject you are selling to and the Licenzy product code. Licenzy creates the Stripe checkout session internally and returns the redirect URL.

Checkout request
HTTP
POST /v1/checkout/session
Authorization: Bearer lz_test_...
Idempotency-Key: checkout-user_123-pro_monthly-001
Content-Type: application/json

{
  "subject_ref": "user_123",
  "product_code": "PRO_MONTHLY"
}
Checkout response
JSON
{
  "attempt_id": "att_123",
  "status": "checkout_created",
  "checkout_url": "https://checkout.stripe.com/...",
  "mode": "test"
}

Common mistake: granting access too early

A common integration bug is treating checkout creation or a browser success page as proof that access should be granted. That is too early. Checkout starts payment, but access must follow confirmed billing state.

Stripe may still need to deliver the event and Licenzy may still need to process it. Your app should not assume that a redirect means entitlement state is already active.

Checkout is not access
Create checkout first, then wait for webhook-processed entitlement state before enabling paid behavior.

How to redirect to Stripe Checkout and handle idempotency

Keep the responsibilities separate. Your backend talks to Licenzy. Your frontend talks to your backend and performs the browser redirect using the returned checkout_url.

Backend: create checkout session
TypeScript
const response = await fetch("https://api.licenzy.app/v1/checkout/session", {
  method: "POST",
  headers: {
    Authorization: "Bearer lz_test_***",
    "Idempotency-Key": "checkout-user_123-pro_monthly-001",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    subject_ref: "user_123",
    product_code: "PRO_MONTHLY",
  }),
});

if (!response.ok) {
  throw new Error("Failed to create checkout session.");
}

return response.json();
Frontend: redirect to checkout_url
TypeScript
const { checkout_url } = await response.json();
window.location.assign(checkout_url);

Reuse the same Idempotency-Key when retrying the exact same checkout creation attempt. Do not generate a new key for transport retries.

Critical behavior
Checkout only initiates payment. Access is granted only after webhook processing completes inside Licenzy. The full integration example shows that end-to-end sequence.

What happens after payment

After the customer completes Stripe Checkout, Stripe sends the billing event to Licenzy. Licenzy validates and processes that webhook, then creates or updates the relevant entitlement state for the subject you sold to.

This is the critical difference between payment processing and access control.

Your app should not grant access manually at this stage. Instead, read the resulting state through Licenzy runtime endpoints and let entitlement-backed access checks drive the final decision.

  • Stripe sends the payment outcome through a billing webhook.
  • Licenzy processes the event and updates entitlement state.
  • Your app reads access from Licenzy after that state exists.
Operational rule
Payment completion and access enablement are connected through webhook-processed entitlement state. Do not add a separate manual access grant on top of that flow.

Next steps

Continue with the runtime surfaces that turn payment state into actual product access: