> ## Documentation Index
> Fetch the complete documentation index at: https://docs.numeral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Numeral for Stripe Checkout

> Calculate tax, validate customer location, and create Stripe Checkout Sessions with one Numeral API call.

Numeral for Stripe Checkout is a server-side replacement for creating a Stripe Checkout Session directly. Send Numeral the same Stripe Price IDs and checkout URLs you already use. Numeral determines the customer's tax location, calculates tax, prepares Stripe Checkout, and returns the next URL for the buyer.

<CardGroup cols={2}>
  <Card title="Keep Stripe Checkout" icon="stripe">
    Stripe still collects payment details and completes the payment. Numeral prepares the cart with the correct tax first.
  </Card>

  <Card title="Use the best location signal" icon="location-dot">
    Send a complete address, a customer IP address, or let Numeral collect only the missing location fields.
  </Card>

  <Card title="Choose the buyer experience" icon="window-maximize">
    Redirect through a Numeral-hosted address step or embed the secure collector directly in your checkout.
  </Card>

  <Card title="Validate before payment" icon="circle-check">
    Numeral checks location data before tax is finalized, including mismatched state or province and postal code combinations.
  </Card>
</CardGroup>

## A small change to your Stripe integration

Instead of calling `stripe.checkout.sessions.create(...)`, call `numeral.tax.bridge.sessions.create(...)`. The checkout object remains familiar: it uses your connected Stripe account, Stripe Price IDs, line-item quantities, and success and cancel URLs.

<CodeGroup>
  ```ts Stripe (Node.js) theme={null}
  const session = await stripe.checkout.sessions.create({
    mode: "payment",
    line_items: [{ price: "price_123", quantity: 1 }],
    success_url: "https://store.example/success",
    cancel_url: "https://store.example/cart",
  });
  ```

  ```ts Numeral (Node.js) theme={null}
  const session = await numeral.tax.bridge.sessions.create({
    config_id: "brcfg_123",
    collection_mode: "hosted",
    confirmation_method: "automatic",
    checkout: {
      mode: "payment",
      line_items: [{ price: "price_123", quantity: 1 }],
      success_url: "https://store.example/success",
      cancel_url: "https://store.example/cart",
    },
    tax_context: {
      location: {
        basis: "billing_address",
        address: { country: "US" },
      },
    },
    "X-API-Version": "2026-03-01",
    "Idempotency-Key": crypto.randomUUID(),
  });
  ```

  ```go Numeral (Go) theme={null}
  client := numeraltax.NewClient(
      option.WithAPIKey(os.Getenv("NUMERAL_API_KEY")),
  )

  session, err := client.Tax.Bridge.Sessions.New(ctx, numeraltax.TaxBridgeSessionNewParams{
      XAPIVersion:        numeraltax.TaxBridgeSessionNewParamsXAPIVersion2026_03_01,
      IdempotencyKey:     numeraltax.String("checkout_request_8421"),
      ConfigID:           os.Getenv("NUMERAL_STRIPE_CHECKOUT_CONFIG_ID"),
      CollectionMode:     numeraltax.TaxBridgeSessionNewParamsCollectionModeHosted,
      ConfirmationMethod: numeraltax.TaxBridgeSessionNewParamsConfirmationMethodAutomatic,
      Checkout: numeraltax.TaxBridgeSessionNewParamsCheckout{
          Mode: "payment",
          LineItems: []numeraltax.TaxBridgeSessionNewParamsCheckoutLineItem{{
              Price: "price_123", Quantity: 1,
          }},
          SuccessURL: numeraltax.String("https://store.example/success"),
          CancelURL:  numeraltax.String("https://store.example/cart"),
      },
      TaxContext: numeraltax.TaxBridgeSessionNewParamsTaxContext{
          Location: numeraltax.TaxBridgeSessionNewParamsTaxContextLocationUnion{
              OfTaxBridgeSessionNewsTaxContextLocationObject: &numeraltax.TaxBridgeSessionNewParamsTaxContextLocationObject{
                  Basis: "billing_address",
                  Address: numeraltax.TaxBridgeSessionNewParamsTaxContextLocationObjectAddress{
                      Country: "US",
                  },
              },
          },
      },
  })
  if err != nil {
      return err
  }
  ```
</CodeGroup>

The Go SDK is available as
[`github.com/NumeralHQ/numeral-tax-go`](https://pkg.go.dev/github.com/NumeralHQ/numeral-tax-go).
Use a unique idempotency key for each logical checkout attempt and reuse it
only when retrying that same request.

## Payments and subscriptions

Set `checkout.mode` on every session to match the Stripe Prices in the cart.

| `checkout.mode` | Stripe Prices                                                                | What Numeral prepares                                      |
| --------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `payment`       | One-time Prices                                                              | A one-time Stripe Checkout payment                         |
| `subscription`  | At least one fixed recurring Price; fixed one-time setup Prices are optional | A new Stripe Checkout subscription and its initial invoice |

<CodeGroup>
  ```ts One-time payment theme={null}
  checkout: {
    mode: "payment",
    line_items: [{ price: "price_one_time", quantity: 1 }],
    success_url: "https://store.example/success",
    cancel_url: "https://store.example/cart",
  }
  ```

  ```ts Subscription theme={null}
  checkout: {
    mode: "subscription",
    line_items: [
      { price: "price_monthly", quantity: 1 },
      { price: "price_setup_fee", quantity: 1 },
    ],
    success_url: "https://store.example/success",
    cancel_url: "https://store.example/cart",
  }
  ```

  ```go Subscription (Go) theme={null}
  Checkout: numeraltax.TaxBridgeSessionNewParamsCheckout{
      Mode: "subscription",
      LineItems: []numeraltax.TaxBridgeSessionNewParamsCheckoutLineItem{
          {Price: "price_monthly", Quantity: 1},
          {Price: "price_setup_fee", Quantity: 1},
      },
      SuccessURL: numeraltax.String("https://store.example/success"),
      CancelURL:  numeraltax.String("https://store.example/cart"),
  },
  ```
</CodeGroup>

All recurring Prices in one subscription checkout must use the same currency,
billing interval, and interval count. Subscription checkout currently supports
up to 20 fixed recurring items and up to 20 fixed one-time setup items.
[Discounts and promotion codes](/integrations/stripe/stripe-checkout-discounts)
are supported. Trials, custom billing anchors, metered or tiered pricing,
and mixed recurring cadences are not supported.

Numeral calculates, verifies, and commits tax for the initial subscription
payment. Later renewal invoices continue through Numeral's existing Stripe
invoice integration and do not create new Bridge sessions.

[Branding](/integrations/stripe/stripe-checkout-branding) on the hosted address page applies identically to both modes.

<Warning>
  A recurring Stripe Price cannot be used with `mode: "payment"`. A
  subscription request must contain at least one recurring Price from the
  Stripe account connected to the selected `brcfg_...` configuration.
</Warning>

## Discounts, metadata, and saved cards

Add these optional fields inside `checkout` when creating a session from your server:

| Field                                    | Use                                                                            |
| ---------------------------------------- | ------------------------------------------------------------------------------ |
| `allow_promotion_codes`                  | Let the buyer enter a Stripe promotion code on eligible carts                  |
| `discounts`                              | Apply one existing Stripe coupon or promotion-code ID before quoting tax       |
| `metadata`                               | Attach application identifiers to the Stripe Checkout Session                  |
| `payment_intent_data.metadata`           | Attach separate identifiers to the PaymentIntent in payment mode               |
| `payment_intent_data.setup_future_usage` | Prepare a payment method for `on_session` or `off_session` use in payment mode |
| `customer_creation`                      | Control Stripe Customer creation in payment mode                               |

Omitting these fields preserves existing checkout behavior. The API version remains `2026-03-01`.

<CardGroup cols={2}>
  <Card title="Discounts and promotion codes" href="/integrations/stripe/stripe-checkout-discounts" icon="tag">
    Choose buyer-entered codes or pre-applied discounts, and read the final verified totals.
  </Card>

  <Card title="Metadata and saved cards" href="/integrations/stripe/stripe-checkout-metadata" icon="credit-card">
    Track Session and PaymentIntent data separately and prepare cards for future payments.
  </Card>
</CardGroup>

## How it works

```mermaid theme={null}
flowchart LR
  A["Your server creates a Bridge session"] --> B{"Enough customer location data?"}
  B -->|"Yes: address or resolved IP"| C["Numeral calculates tax"]
  B -->|"No"| D["Hosted or embedded address collection"]
  D --> C
  C --> E["Numeral prepares Stripe Checkout"]
  E --> F["Buyer pays with Stripe"]
```

When the address or IP resolves to a tax location immediately, the returned `url` normally points directly to Stripe Checkout. When more information is required:

* `collection_mode: "hosted"` returns a Numeral-hosted URL that collects the missing fields and then continues to Stripe.
* `collection_mode: "embedded"` returns a session-scoped `client_secret` for the `<numeral-checkout>` element on your page.

Always treat the returned `url` as opaque and redirect the buyer to it without checking its hostname.

## Before you start

<Steps>
  <Step title="Connect your Stripe account">
    In the Numeral dashboard, open **Connections**, add Stripe, and complete the connection. Numeral uses the stored connection; you do not enter the Stripe secret key again when configuring checkout.
  </Step>

  <Step title="Create a checkout configuration">
    Open **Developers → Numeral for Stripe Checkout**. Use the dashboard's **Test Mode** switch to configure test and live environments separately.

    Select the Stripe connection and provide:

    * Your seller origin address
    * The billing, shipping, or service address used for tax
    * Success and cancel URLs
    * Allowed redirect origins
    * Origins allowed to embed the collector
    * A default Numeral product category
    * Optionally, a business name, logo, and primary color in the **Branding** section. See [Branding and appearance](/integrations/stripe/stripe-checkout-branding).

    Click **Save and publish**, then copy the resulting `brcfg_...` configuration ID.
  </Step>

  <Step title="Install the SDK">
    <CodeGroup>
      ```bash Node.js theme={null}
      npm install numeral-tax
      ```

      ```bash Go theme={null}
      go get github.com/NumeralHQ/numeral-tax-go@v0.1.0
      ```
    </CodeGroup>
  </Step>

  <Step title="Create sessions from your server">
    Keep the Numeral secret key on your server. Never expose an `sk_test_...` or live Numeral API key in browser JavaScript.
  </Step>
</Steps>

## Create a session with a complete address

If your checkout already collects the customer address, include it in the initial request. Numeral validates the location, calculates tax, and can return Stripe Checkout without an additional address step.

<CodeGroup>
  ```ts Node.js theme={null}
  import NumeralAPI from "numeral-tax";

  const numeral = new NumeralAPI({
    apiKey: process.env.NUMERAL_API_KEY!,
  });

  const session = await numeral.tax.bridge.sessions.create({
    config_id: process.env.NUMERAL_STRIPE_CHECKOUT_CONFIG_ID!,
    collection_mode: "hosted",
    confirmation_method: "automatic",
    external_reference: "order_8421",
    checkout: {
      mode: "payment",
      line_items: [
        {
          price: "price_123",
          quantity: 1,
          product_category: "GENERAL_MERCHANDISE",
        },
      ],
      success_url: "https://store.example/success",
      cancel_url: "https://store.example/cart",
    },
    tax_context: {
      location: {
        basis: "billing_address",
        assurance: "self_attested",
        address: {
          country: "US",
          line_1: "123 W 31st St",
          city: "New York",
          province: "NY",
          postal_code: "10001",
        },
      },
    },
    "X-API-Version": "2026-03-01",
    "Idempotency-Key": crypto.randomUUID(),
  });

  if (!session.url) {
    throw new Error("Checkout requires additional handling");
  }

  // Express example
  response.redirect(303, session.url);
  ```

  ```go Go theme={null}
  client := numeraltax.NewClient(
      option.WithAPIKey(os.Getenv("NUMERAL_API_KEY")),
  )

  session, err := client.Tax.Bridge.Sessions.New(ctx, numeraltax.TaxBridgeSessionNewParams{
      XAPIVersion:        numeraltax.TaxBridgeSessionNewParamsXAPIVersion2026_03_01,
      IdempotencyKey:     numeraltax.String("checkout_request_8421"),
      ConfigID:           os.Getenv("NUMERAL_STRIPE_CHECKOUT_CONFIG_ID"),
      CollectionMode:     numeraltax.TaxBridgeSessionNewParamsCollectionModeHosted,
      ConfirmationMethod: numeraltax.TaxBridgeSessionNewParamsConfirmationMethodAutomatic,
      ExternalReference:  numeraltax.String("order_8421"),
      Checkout: numeraltax.TaxBridgeSessionNewParamsCheckout{
          Mode: "payment",
          LineItems: []numeraltax.TaxBridgeSessionNewParamsCheckoutLineItem{{
              Price:           "price_123",
              Quantity:        1,
              ProductCategory: numeraltax.String("GENERAL_MERCHANDISE"),
          }},
          SuccessURL: numeraltax.String("https://store.example/success"),
          CancelURL:  numeraltax.String("https://store.example/cart"),
      },
      TaxContext: numeraltax.TaxBridgeSessionNewParamsTaxContext{
          Location: numeraltax.TaxBridgeSessionNewParamsTaxContextLocationUnion{
              OfTaxBridgeSessionNewsTaxContextLocationObject: &numeraltax.TaxBridgeSessionNewParamsTaxContextLocationObject{
                  Basis:     "billing_address",
                  Assurance: "self_attested",
                  Address: numeraltax.TaxBridgeSessionNewParamsTaxContextLocationObjectAddress{
                      Country:    "US",
                      Line1:      numeraltax.String("123 W 31st St"),
                      City:       numeraltax.String("New York"),
                      Province:   numeraltax.String("NY"),
                      PostalCode: numeraltax.String("10001"),
                  },
              },
          },
      },
  })
  if err != nil {
      return err
  }

  http.Redirect(response, request, session.URL, http.StatusSeeOther)
  ```
</CodeGroup>

<Tip>
  `collection_mode` is always required. Even when you send an address or IP, it tells Numeral how to recover if the location is incomplete or cannot be resolved confidently.
</Tip>

## Choose how Numeral gets the customer location

<CardGroup cols={2}>
  <Card title="Send an address" href="/integrations/stripe/stripe-checkout-location#send-a-complete-address" icon="address-card">
    Best when your checkout already collects billing, shipping, or service address information.
  </Card>

  <Card title="Send the customer IP" href="/integrations/stripe/stripe-checkout-location#send-the-customer-ip" icon="globe">
    Avoid an address form when the customer's public IP resolves with enough confidence.
  </Card>

  <Card title="Use hosted collection" href="/integrations/stripe/stripe-checkout-location#numeral-hosted-collection" icon="arrow-up-right-from-square">
    Redirect through a polished Numeral address step only when more location data is required.
  </Card>

  <Card title="Embed collection" href="/integrations/stripe/stripe-checkout-embedded" icon="code">
    Keep the address experience inside your checkout with the secure `<numeral-checkout>` element.
  </Card>
</CardGroup>

## Working examples

<CardGroup cols={2}>
  <Card title="Node.js checkout example" href="https://github.com/NumeralHQ/numeral-for-stripe-checkout-example" icon="node-js">
    Compare complete address, customer IP, hosted collection, and embedded collection in a one-time payment checkout.
  </Card>

  <Card title="Go subscription example" href="https://github.com/NumeralHQ/numeral-for-stripe-checkout-go-example" icon="code">
    Run the same location and collection paths with `checkout.mode: "subscription"` and a recurring Stripe Price.
  </Card>
</CardGroup>

## Test before going live

Use a test-mode Numeral API key, a test-mode Stripe connection, and a test configuration ID together. A successful non-zero payment progresses through these public states:

```text theme={null}
status: complete
payment_status: paid
tax_status: committed
```

Use Stripe test card `4242 4242 4242 4242`, any future expiration date, and any CVC. For 100% discounts, also handle the [zero-dollar completion path](/integrations/stripe/stripe-checkout-discounts#test-discounted-and-zero-dollar-orders). A zero-dollar checkout does not collect a card for future use.

<Warning>
  Test and live configurations are separate. A test Numeral API key cannot use a live Stripe connection or live `brcfg_...` configuration.
</Warning>
