Skip to content

Payments ​

A checkout starts with a published offer and ends with a verified payment. This guide covers the current Payments V2 API for Stripe and Solidgate.

Building funnel UI? Start with @funnelsgrove/payments: its shared checkout components and helpers handle provider UI, confirmation, and payment analytics. Use funnelSdkService for the underlying Stripe operations. Direct HTTP details below are for custom integrations.

Choose a checkout flow ​

FlowProviderUse
Embedded subscriptionStripe or SolidgateSell recurring access inside your funnel.
Embedded one-time paymentStripe or SolidgateSell a one-time offer inside your funnel.
Hosted subscriptionStripeRedirect to hosted checkout and return to your funnel.
One-click upsellStripeCharge the saved method from an earlier paid checkout, with extra authentication when required.

The Stripe runtime methods are createSubscriptionCheckout, createOneTimeCheckout, and createHostedCheckoutSession. Use the shared Solidgate checkout helpers for Solidgate; the Stripe methods do not accept a Solidgate selection.

1. Select a published offer ​

Choose a plan from the funnel's published offer catalog. Send the complete selection; the server resolves the price and payment account.

FieldValue
funnelIdThe funnel running checkout. The runtime supplies its configured ID.
offerSetIdThe published offer set containing the plan.
planKeyThe plan's key within that offer set.
paymentProfileIdThe payment profile assigned to the offer.
providerstripe or solidgate.
providerPlanIdThe provider price or product selected by the published offer.
environmentExplicitly test or live, matching the request scope.

runtimeConfigRevisionId is optional. A new deployment alone does not invalidate an unchanged offer in an open tab. A removed, archived, or inconsistent selection returns 409; refresh the offer selection rather than falling back to another price or payment route.

Prices stay on the server

Direct HTTP requests select an offer; they do not set an amount or currency. Some runtime input types retain amountCents for compatibility, but it does not override the published price.

2. Create checkout ​

All checkout flows below use:

http
POST /sdk/public/v2/payments/checkout-sessions
X-SDK-Publishable-Key: <project-sdk-publishable-key>
Content-Type: application/json

Base URL: https://sdk-api.funnelsgrove.com.

Stripe embedded checkout ​

Use checkoutType: "subscription" or "one_time" with uiMode: "custom". Provide an HTTP(S) returnUrl for any required redirect.

Example request body — replace the placeholders with your published selection and the identity returned by the funnel runtime:

json
{
  "funnelId": "<funnel-id>",
  "offerSetId": "<offer-set-id>",
  "planKey": "annual",
  "paymentProfileId": "<payment-profile-id>",
  "provider": "stripe",
  "providerPlanId": "<provider-price-id>",
  "environment": "test",
  "checkoutType": "subscription",
  "uiMode": "custom",
  "returnUrl": "https://your-funnel.example/checkout-return",
  "userId": "<current-funnel-user-id>",
  "checkoutIdentityToken": "<proof-from-bootstrap>",
  "idempotencyKey": "<stable-id-for-this-checkout-attempt>"
}

The response supplies clientSecret and checkoutSessionId for the shared checkout UI. Preserve any returned checkoutAccessToken for status reads and plan updates; the runtime stores it for you.

Stripe hosted checkout ​

Use the same published selection with checkoutType: "subscription", uiMode: "hosted", and HTTP(S) successUrl and cancelUrl. The response contains url and checkoutSessionId. Redirect to url; hosted checkout does not return a client secret. The runtime wrapper also returns the legacy sessionId alias.

Solidgate embedded checkout ​

Use the same endpoint with provider: "solidgate", uiMode: "custom", and checkoutType: "subscription" or "one_time". Include the published selection, funnelEndUserId, a stable idempotencyKey, and HTTP(S) returnUrl, successUrl, and failUrl. Use the shared Solidgate helper to initialize its checkout UI. The helper forwards the bootstrap checkoutIdentityToken on create, status and one-click requests and keeps any returned checkoutAccessToken for status reads.

Identity and retries ​

An arbitrary user ID is not proof of ownership. Use the runtime's current user and bootstrap identity proof. Treat identity and checkout access tokens as opaque; keep them out of URLs and logs.

Use one stable idempotencyKey per logical attempt (up to 200 characters). For Stripe, a supplied key requires a stable userId or anonymousUserId. The runtime handles supported same-attempt recovery and expired identity proof refresh. Do not start a new purchase automatically after an uncertain response.

3. Verify the result ​

A return URL or provider UI callback is not proof of payment.

For Stripe, call verifySubscriptionCheckout({ checkoutSessionId }) through the configured runtime. Despite its name, this reads the checkout session. Direct HTTP callers use:

http
GET /sdk/public/v2/payments/checkout-sessions/<checkout-session-id>?funnelId=<funnel-id>
X-SDK-Publishable-Key: <project-sdk-publishable-key>
X-FunnelsGrove-Checkout-Access-Token: <token-returned-by-create>

Supply the access-token header when creation returned a token. Use the verified paid flag for completion; status: "complete" alone is insufficient.

Solidgate status uses GET /sdk/public/v2/payments/checkout-sessions/:attemptId with query parameters provider=solidgate, funnelId, and funnelEndUserId, plus the publishable-key header. Let the shared helper poll canonical status before advancing; an iframe success message alone is insufficient.

To unlock content in your app, follow the separate verified app access flow.

Change a plan or add an upsell ​

Change the selected plan ​

Use updateSubscriptionCheckoutPlan with the existing checkoutSessionId and the new published selection. The runtime uses the V2 session update route and supplies a retained checkout access token when available.

Charge a one-click upsell ​

Use chargeOneClickPayment and the completeStripeOneClickPayment helper from @funnelsgrove/payments to handle requires_action / 3DS confirmation.

The underlying POST /sdk/public/v2/payments/one-click request requires:

  • the upsell's complete published Stripe selection;
  • the original paid checkoutSessionId and its userId;
  • a stable idempotencyKey for this upsell attempt (up to 200 characters).

The server uses the original checkout's account, customer, and saved payment method. Do not substitute a client-selected Stripe customer or payment method. Continue only after successful confirmation.

Manage subscriptions ​

Use listSubscriptions to read the user's subscriptions and updateSubscription for a supported action: cancel, renew, or apply-discount.

Mutations require a provider-neutral subscriptionRef:

ts
{
  provider: 'stripe', // or 'solidgate'
  paymentProfileId: '<payment-profile-id>',
  providerSubscriptionId: '<provider-subscription-id>',
  environment: 'live',
}

The runtime owns route selection. For trusted backend cancellation and renewal, use the Subscriptions API.

Handle checkout errors ​

ResponseWhat to do
409, stale or inconsistent offerReload the published selection and ask the user to confirm the current offer.
401, checkout_identity_owner_unavailableStop checkout and retain checkoutRequestId for support. Do not reassign the user or blindly retry.
409, subscription_ownership_unavailableOwnership is missing. Stop the operation; waiting does not repair it.
404, subscription_not_foundCheck the selected user and subscription. Do not switch to another owner.
Timeout or lost responseRecover the same attempt using its documented idempotency contract. Do not assume no charge occurred.

See Errors & retries for diagnostics.

Build. Launch. Grow.