Checkout

A checkout charges one-time items and starts one or more subscriptions in a single payment. Your customer enters a card once, confirms once, and sees one charge. Use it when a plan comes with something else on the first invoice, such as a setup fee, a hardware kit, or a second plan.

When to use a checkout

You want to…Use
Charge for one-time items onlycreatePayment
Start one subscription with nothing else on the first chargecreateSubscription
Charge one-time items and start one or more subscriptions togethercreateCheckout

A checkout with no subscriptions behaves like a payment, and one with a single subscription and no one-time items behaves like createSubscription. Each subscription still renews on its own schedule afterward.

Prerequisites

  • Your project owner has completed compliance registration (live projects must be APPROVED). Sandbox projects bypass this.

Limits

  • At most 10 subscriptions per checkout.
  • At most 50 line items across the whole checkout, one-time and subscription lines together.
  • At least one one-time line or one subscription.
  • Card only. A checkout that includes a subscription can't be paid with ACH direct debit, for the same reasons given in Subscriptions.
  • Every subscription starts now. A checkout has no startTime. To schedule a future start, use createSubscription.
  • The first-charge subtotal and each subscription's subtotal must be at least the smallest charge Forte accepts, because each subscription renews as its own charge.
  • lineItemId and subscriptionId on the lines you send are ignored. Forte sets them.

Creating a checkout

Call createCheckout with a currency, any one-time lineItems, and a list of subscriptions. Each subscription has its own interval, lineItems, and optional description, metadata, and endTime. Use the client-side surface when a signed-in user checks out. Use the server-side surface from your backend with an API key. See API Surfaces.

Client-side (signed-in user session):

  • POST /api/v1/{projectId}/users/me/checkouts
  • POST /api/v1/{projectId}/users/me/checkouts/preview

Server-side (API key):

  • POST /api/v1/projects/{projectId}/users/{userId}/checkouts
  • POST /api/v1/projects/{projectId}/users/{userId}/checkouts/preview
typescript
import { ForteClient } from "@forteplatforms/sdk";
 
const forte = new ForteClient();
 
const result = await forte.users.createCheckout({
  projectId,
  createCheckoutRequest: {
    currency: "usd",
    description: "Pro plan with onboarding",
    lineItems: [
      { description: "Onboarding", unitAmountCents: 9900, quantity: 1, taxCode: "txcd_20030000" },
      { description: "Starter kit", unitAmountCents: 2500, quantity: 1, taxCode: "txcd_99999999" },
    ],
    subscriptions: [
      {
        interval: "MONTH",
        description: "Pro plan",
        lineItems: [
          { description: "Pro plan", unitAmountCents: 4900, quantity: 1, taxCode: "txcd_10000000" },
        ],
      },
    ],
    customerAddress: { line1: "...", city: "...", state: "...", postalCode: "...", country: "US" },
    shippingAddress: { line1: "...", city: "...", state: "...", postalCode: "...", country: "US" },
    // paymentMethodId is optional: omit it to let the customer enter a card now (it's saved for renewals).
  },
});
 
const { payment, subscriptions, stripeClientSecret, stripePublishableKey, stripeConnectedAccountId } = result;

The response contains:

  • payment: the single Payment for the first charge. Each line on it has a lineItemId, which you use to refund an item.
  • subscriptions: one subscription per entry in your request, in the same order. Each is INCOMPLETE until the payment is confirmed. The list is empty when you sent no subscriptions.
  • stripeClientSecret, stripePublishableKey, and stripeConnectedAccountId, as with createPayment.

metadata on the checkout is stored on the Payment. Each subscription keeps its own metadata.

Previewing the first charge and each renewal

createCheckoutPreview takes the same body as createCheckout, minus paymentMethodId. The call is read-only: nothing is charged or saved.

It returns:

  • The first charge: subtotalCents, taxCents, amountCents, and lineItems (one-time lines first, then each subscription's lines in request order). taxCents is null when you send no address.
  • Each renewal: a subscriptions list, in request order, where each entry has interval, subtotalCents, taxCents, amountCents, lineItems, and nextRenewalAt.

Use it to show "Due today: $X. Then $49.00 per month starting …" before the customer commits. Renewal figures are estimates, because each real renewal recalculates tax.

Confirming with one client secret

Confirm stripeClientSecret with Stripe Elements exactly as you do for a one-off payment (Confirming the payment in the browser). That one confirmation pays for every line and saves the card for renewals. When it succeeds, every subscription on the checkout becomes ACTIVE.

If you omit paymentMethodId, the card the customer enters is saved. If you pass a saved card, Forte uses it for this charge and for renewals. The customer may be asked for an extra card authentication step (3D Secure) during confirmation. Elements handles the prompt.

If a saved card can't be reused for renewals, each subscription is cancelled, and the one-time items stay paid.

What renews

Each subscription renews on its own, on its own interval, and only its own lines are charged. One-time items never renew. Renewals, failed-payment retries, and cancellations work as described in Subscriptions.

A subscription's price is set from its own lines, not from the checkout total. For example, a checkout of a $99 setup fee and a $49 monthly plan starts a subscription that renews at $49 plus tax.

Tax addresses

  • Subscription lines are always taxed at customerAddress, on the first charge and on every renewal.
  • One-time lines are taxed at shippingAddress when you send one, and at customerAddress otherwise.
  • shippingAddress requires customerAddress. Sending only a shippingAddress returns a 400.
  • With no customerAddress, no tax is computed, as with createPayment.

Triggers

A checkout fires one PAYMENT_COMPLETED trigger, not one per item. The body includes subscriptionIds, an array with the ID of every subscription the payment started.

json
{
  "event": "PAYMENT_COMPLETED",
  "userId": "usr_...",
  "paymentId": "pay_...",
  "subscriptionIds": ["sub_...", "sub_..."],
  "paymentTime": "2026-11-02T18:42:11.123Z",
  "state": "COMPLETED"
}

subscriptionId is also sent when the payment started exactly one subscription. For a checkout with no subscriptions, the trigger omits subscriptionIds. Read subscriptionIds in new handlers. Each subscription's later renewals fire their own PAYMENT_COMPLETED with that subscription's subscriptionId.

Refunding one item

A checkout has no refund route of its own. It creates a normal Payment, so you refund it with refundPayment. Pass lineItems in the request body to refund individual lines, using the lineItemId values on the payment.

For each line, amountCents is the pre-tax amount to refund. Omit it to refund the rest of that line. Forte works out the tax share and returns it with the refund. Refunding the last of a line returns exactly the tax that remains, so the totals always match what you charged.

typescript
// Refund the whole "Onboarding" line, tax included.
const payment = await forte.projects.refundPayment({
  projectId,
  userId,
  paymentId,
  createPaymentRefundRequest: {
    lineItems: [{ lineItemId: "li_..." }],
  },
});
 
// Refund $20.00 (before tax) of the "Starter kit" line.
const partial = await forte.projects.refundPayment({
  projectId,
  userId,
  paymentId,
  createPaymentRefundRequest: {
    lineItems: [{ lineItemId: "li_...", amountCents: 2000 }],
  },
});

Rules for line refunds:

  • Don't combine lineItems with refundAmountCents. That returns a 400.
  • A lineItemId that isn't on the payment returns a 400. Payments created before line refunds existed have no line IDs, so refund those by amount.
  • An amountCents larger than what's left on a line returns INVALID_REFUND_AMOUNT.

Cancellation rules

A refund can cancel the subscriptions on a checkout. What happens depends on how you refund.

RefundDefault effectWith keepSubscriptionActive=true
Lines through the APICancels only the subscriptions whose lines you refundCancels none
Amount only through the APICancels all subscriptions on the paymentCancels none
Partial refund from the Stripe dashboardCancels noneNot applicable
Full refund from the Stripe dashboard, or a lost disputeCancels allNot applicable

keepSubscriptionActive applies to every refund you make through the API, including a full refund. Refunds made in the Stripe dashboard and disputes don't go through it, so the last two rows are fixed.

Refunding a one-time line leaves the subscriptions running. Refunding a subscription's line cancels that subscription and no others.

The Forte console refund dialog also lets you pick lines and amounts, and shows the tax and total before you confirm.

Search

Search your resources, console pages, and documentation