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.
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"
}{
"attempt_id": "att_123",
"status": "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.
Your app should not unlock features when Stripe Checkout is created, when the browser is redirected, or when a user lands on a success URL. The authoritative signal comes after webhook processing updates Licenzy entitlement state.
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 returnedcheckout_url.
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();async function startCheckout() {
const response = await fetch("/api/billing/checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
productCode: "PRO_MONTHLY",
}),
});
if (!response.ok) {
throw new Error("Unable to start checkout.");
}
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.
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.
Next steps
Continue with the runtime surfaces that turn payment state into actual product access:
- Access checks for the smallest allow or deny decision at runtime.
- Entitlements for the underlying commercial state that powers those checks.
- Full integration example for the complete purchase, webhook, and access sequence.