Complete Customer integration, connect Stripe for App/Sandbox and prepare a fixed-Price Product with the required permissions. Start with independent purchase; no Funnel is required.
Purchases & Stripe Checkout
Preview, confirm explicitly and read verified payment results from the original operation.
SDK examples target the current API. Check matching SDK versions and HTTP alternatives
Before you start
Preview and confirm a purchase
Configure Stripe for the current App/Sandbox and a Product with fixed Prices. Use a matching SDK with billing:write, plus billing:read/entitlements:read for result reads. This example runs on the trusted backend; derive customerId from verified host login.
previewAttach quotes and reserves a Coupon; attach is explicit acceptance. In autumn mode Indiana creates Stripe Checkout with the merchant's verified restricted Key and returns checkoutUrl. In record_only mode Indiana returns the original execution instructions for merchant-backend execution. Both record final purchase facts only from signature-verified webhooks.
Cancel an unpaid quote with attach({ customerId, operationId, intent: 'cancel' }) to release its Coupon. Refresh/read does not establish payment. After a lost response retain requestId/operationId, show pending and read or retry the original request.
// Persist the complete input and requestId before previewing.const { quote } = await indiana.previewAttach({ customerId: verifiedCustomer.id, requestId: retainedRequestId, plans: [{ productVersionId: selectedProductId }], provider: "stripe", currency: "USD", // offerId: heldCouponGrantId,});// Show quote and obtain explicit customer confirmation before attach.const { operation } = await indiana.attach({ customerId: verifiedCustomer.id, operationId: quote.operationId,});if (operation.status === "complete") { await refreshCustomerHoldings();} else if (operation.checkoutUrl) { return { checkoutUrl: operation.checkoutUrl };} else if (operation.executor === "merchant" && operation.execution) { // Execute only on your backend using the original operation instructions. return await executeMerchantCheckout(operation);}// After returning from checkout, read the original operation; do not create another.await indiana.readSubscriptionOperation({ customerId: verifiedCustomer.id, operationId: quote.operationId,});record_only: Stripe Checkout
Preserve execution.metadata unchanged on the session and subscription_data.metadata; for one-time payment enable invoice_creation and copy it to invoice_data.metadata. Your backend validates the Stripe account, Prices, Coupon terms, trial/payment-method behavior, tax and expiry. Keep server Keys and executable parameters off the browser trust boundary. The webhook signing secret must belong to the same account/environment.
record_only does not sell usage Prices billed at period end. Indiana adds that usage to the renewal invoice in Stripe, which only autumn mode can do. A Product with such a Price returns NOT_CONFIGURED from the quote, and Offering/Paywall resolution marks it unavailable with reason commercial_runtime_pending. Prepaid packs are charged at purchase and can be sold in record_only once each Price is bound to your Stripe Price.
// Re-read this operation on your backend for the authenticated Customer.// stripe is your server-only Stripe client for execution.accountId.const execution = operation.execution;if (operation.executor !== "merchant" || !execution) throw new Error("Wrong executor");if (execution.lineItems.some((item) => !item.price)) throw new Error("Price binding missing");const cardless = quote.lines.some((line) => line.trial && !line.trial.cardRequired);const session = await stripe.checkout.sessions.create({ mode: execution.mode, ...(quote.automaticTax ? { automatic_tax: { enabled: true } } : {}), line_items: execution.lineItems.map(({ price, quantity }) => ({ price: price!, quantity })), discounts: execution.discounts.map(({ coupon }) => ({ coupon })), metadata: execution.metadata, client_reference_id: operation.operationId, expires_at: Math.floor(Date.parse(execution.expiresAt) / 1000), success_url: configuredReturnUrl, cancel_url: configuredReturnUrl, ...(execution.mode === "subscription" ? { subscription_data: { metadata: execution.metadata, ...(execution.trialPeriodDays ? { trial_period_days: execution.trialPeriodDays } : {}), ...(cardless ? { trial_settings: { end_behavior: { missing_payment_method: "cancel" } } } : {}), }, ...(cardless ? { payment_method_collection: "if_required" } : {}) } : { invoice_creation: { enabled: true, invoice_data: { metadata: execution.metadata } }, payment_intent_data: { metadata: execution.metadata } }),}, { idempotencyKey: `checkout:${operation.operationId}` });return { checkoutUrl: session.url };Availability and results
The current purchase path targets Sandbox Stripe fixed Prices. Live, native purchase, Entity/append, variable seat/prepaid quantities, usage Prices and existing-subscription changes can return explicit NOT_CONFIGURED/SERVICE_UNAVAILABLE. Do not silently choose another Product or assume success. An SDK method does not establish runtime availability.
A trial is not a paid Invoice; a one-time purchase does not create an auto-renewing Subscription. Read verified operation state and refresh readHoldings. Redirects, onComplete and browser success messages do not replace backend authorization.
HTTP operations
Use these operations with your HTTP integration. Each reference includes complete request/response fields and credential requirements; SDK snippets are an alternative when a matching SDK is available.
| Operation | HTTP | Permission |
|---|---|---|
| previewAttach | POST /api/v1/subscriptions/preview-attach | billing:read |
| attach | POST /api/v1/subscriptions/attach | billing:write |
| readSubscriptionOperation | POST /api/v1/subscriptions/operations/read | billing:read |
Operation and permission
previewAttach
- HTTP
- POST /api/v1/subscriptions/preview-attach
- Permission
- billing:read
attach
- HTTP
- POST /api/v1/subscriptions/attach
- Permission
- billing:write
readSubscriptionOperation
- HTTP
- POST /api/v1/subscriptions/operations/read
- Permission
- billing:read
Check your result
Expected outcome
Show the server quote and obtain explicit confirmation. Retain operationId and refresh access from the verified operation result; the payment redirect alone is not success.