Skip to main content
Create a subscription order from a pre-built checkout session. Returns the PSP checkout URL — redirect the buyer there to complete payment and start the subscription.
POST /v1/actions/subscription-order/create-order
Authentication: Session Token — see Customer Endpoints (customer or shopper role)

Behavior

  • Price snapshot — The subscription price is frozen at order creation (price_snapshot); two-phase pricing (regularPhase + optional specialPhase for trials) is supported.
  • Billing period mappingweekly -> week/1, monthly -> month/1, quarterly -> month/3, yearly -> year/1.
  • PSP fault tolerance — If the PSP call fails, the local order is canceled and the error is surfaced.
  • Session pre-fillbillingDetail and buyerEmail from the request body override any pre-filled values on the session; if both are missing the request is rejected with 400.
  • Email normalizationbuyerEmail is normalized with trim().toLowerCase() before use.
The following fields are sourced from the session and must NOT be sent in the request body: storeId, productId, billingPeriod, currency. They are ignored if provided.

Request Body

FieldTypeRequiredDescription
checkoutSessionIdstringYesCheckout session ID returned by create-checkout-session
billingDetailobjectConditionalBuyer billing info; required if not pre-filled on the session
billingDetail.countrystringYes (if billingDetail present)ISO 3166-1 alpha-2 country code
billingDetail.isBusinessbooleanYes (if billingDetail present)Whether the buyer is a business
billingDetail.postcodestringNoPostal code
billingDetail.statestringRequired for US/CAState / province code
billingDetail.businessNamestringRequired if isBusiness=trueLegal business name
billingDetail.taxIdstringRequired for EU business buyersVAT / tax ID (e.g. DE123456789)
buyerEmailstringConditionalBuyer email; required if not pre-filled on the session
buyerIpstringNoBuyer IP address (used for tax calculation)
successUrlstringNoPost-checkout redirect URL; overrides the session-level value

Example Request

import { WaffoPancake } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  sessionToken: window.WAFFO_SESSION_TOKEN, // injected by the merchant's portal
  environment: "prod",
});

const result = await client.orders.createSubscriptionOrder({
  checkoutSessionId: "cs_550e8400-e29b-41d4-a716-446655440000",
  billingDetail: {
    country: "DE",
    isBusiness: true,
    businessName: "Acme GmbH",
    taxId: "DE123456789",
  },
  buyerEmail: "customer@example.com",
  successUrl: "https://myapp.com/subscription/success",
});

console.log(result.checkoutUrl);
const result = await fetch("https://api.waffo.ai/v1/actions/subscription-order/create-order", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${SESSION_TOKEN}`,
    "Content-Type": "application/json",
    "X-Environment": "prod",
  },
  body: JSON.stringify({
    checkoutSessionId: "cs_550e8400-e29b-41d4-a716-446655440000",
    billingDetail: {
      country: "DE",
      isBusiness: true,
      businessName: "Acme GmbH",
      taxId: "DE123456789",
    },
    buyerEmail: "customer@example.com",
    successUrl: "https://myapp.com/subscription/success",
  }),
}).then(r => r.json());
curl -X POST "https://api.waffo.ai/v1/actions/subscription-order/create-order" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Environment: prod" \
  -d '{"checkoutSessionId":"cs_550e8400-e29b-41d4-a716-446655440000","billingDetail":{"country":"DE","isBusiness":true,"businessName":"Acme GmbH","taxId":"DE123456789"},"buyerEmail":"customer@example.com","successUrl":"https://myapp.com/subscription/success"}'
wget -qO- \
  --header="Authorization: Bearer $SESSION_TOKEN" \
  --header="Content-Type: application/json" \
  --header="X-Environment: prod" \
  --post-data='{"checkoutSessionId":"cs_550e8400-e29b-41d4-a716-446655440000","billingDetail":{"country":"DE","isBusiness":true,"businessName":"Acme GmbH","taxId":"DE123456789"},"buyerEmail":"customer@example.com","successUrl":"https://myapp.com/subscription/success"}' \
  "https://api.waffo.ai/v1/actions/subscription-order/create-order"

Success Response (200)

{
  "data": {
    "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_xxx"
  }
}

Response Fields

FieldTypeDescription
checkoutUrlstringPSP checkout URL — redirect the buyer here to complete payment

Errors

Retry policy: Never retry 4xx — fix the request and resubmit. Retry 5xx with exponential backoff (start 5s, max 3 attempts).
Statuserrors[0].messageWhat it meansRecommended handling
400Invalid JSON bodyRequest body is not valid JSONFix the request body, then resubmit
400Missing required field: checkoutSessionIdcheckoutSessionId was not providedFix the request body, then resubmit
400Session product type mismatch: expected subscriptionThe session was created for a non-subscription productUse a session created for a subscription product
400Environment mismatch between request and sessionRequest X-Environment does not match the session environmentAlign the request environment with the session
400Session missing subscription information (productVersionId or billingPeriod)Session is malformed — missing required subscription fieldsRecreate the checkout session
400Missing billingDetail: provide in request body or pre-fill in checkout sessionNeither the body nor the session contains billingDetailProvide billingDetail in the body, then resubmit
400State is required for US/CAbillingDetail.state is required for US / CA buyersAdd billingDetail.state, then resubmit
401Authentication failedSession token invalid, expired, or malformedRe-mint the session token via Issue Session Token
403Session does not belong to this storeThe session belongs to a different storeVerify the session’s store ownership
409Checkout session invalid, please re-enter checkoutSession does not exist or has expiredRe-create the checkout session
500Internal server errorUnexpected server-side failureRetry with exponential backoff (start 5s, max 3 attempts)