---
title: Stripe
sort: 1
tags: [payments]
---

# Stripe

Stripe is the default payment provider for new tenants. The integration covers:

- Card payments via Stripe Payment Intents
- Stored payment methods on the customer record
- Stripe Tax handoff for jurisdictions where we don't run our own engine
- Refunds, partial refunds, and disputes

## Set up your Stripe account

Two keys, in your tenant's **Settings → Payments → Stripe** page:

| Field                  | Where to find it                                                     |
|------------------------|----------------------------------------------------------------------|
| Publishable key        | Stripe Dashboard → Developers → API keys → "Publishable key"         |
| Secret key             | Stripe Dashboard → Developers → API keys → "Secret key" (test or live) |
| Webhook signing secret | We register the webhook with Stripe on save and store the secret      |

When you save, ZephyrCart calls Stripe to register a webhook endpoint at
`https://api.zephyrcart.io/v1/internal/stripe/webhook` scoped to your account.

## Stripe Tax handoff

Inside the EU, ZephyrCart's tax engine handles VAT. In the US sales-tax jurisdictions and a few
others, Stripe Tax is the smoother experience. Flip per-tenant:

```bash
zephyr settings set payments.stripe.use_stripe_tax true
```

Effect:

- Tax is computed on the Stripe side during Payment Intent creation
- The order's `tax_calculation_source` is `stripe_tax` (was `zephyr`)
- The line-item-level breakdown is preserved verbatim from Stripe

## Code sample — TypeScript SDK

```typescript
import { Zephyr } from "@zephyrcart/sdk";

const client = new Zephyr({ apiKey: process.env.ZEPHYR_API_KEY! });

const checkout = await client.checkouts.create({
  cart: cartId,
  payment: {
    method: "stripe",
    return_url: "https://shop.example.com/return",
  },
});

// checkout.checkout_url is the hosted page; redirect the customer there
```

## Refund flow

A refund triggered through ZephyrCart's API also refunds through Stripe automatically. There is no
"refund only in Stripe" mode — refunds must always originate in ZephyrCart so the order record
stays the source of truth.

```bash
curl -sS https://api.zephyrcart.io/v1/orders/order_…/refunds \
  -H "Authorization: Bearer $ZEPHYR_API_KEY" \
  -d '{ "amount_cents": 1490, "reason": "requested_by_customer" }'
```

## Disputes

When a chargeback is opened in Stripe, we emit `order.disputed`. The order's status is unchanged;
the dispute is a sidecar object you query via `GET /v1/orders/{id}/disputes`.
