---
title: "Stripe"
space: "BWH Payments"
url: "https://docs.bwh.tech/bwh-payments/gateways/stripe"
updated: "2026-09-21"
---

Take card and wallet payments worldwide through [Stripe Checkout](https://docs.stripe.com/payments/checkout). The shopper pays on a Stripe-hosted page. A signed webhook tells your site when the payment completes.

## Before you start

- A Stripe account. Test mode is fine.
- Your **secret key** (`sk_test_…`) and **publishable key** (`pk_test_…`) from **Developers → API keys**

## Set up Stripe

**1. Add the webhook in Stripe first.** Stripe Gateway Settings won't save without the webhook signing secret. In Stripe, go to **Developers → Webhooks → Add endpoint**:

- **URL:** `https://your.site/api/v2/method/bwh_payments.bwh_payments.webhook.handle?gateway=Stripe`
- **Event:** `checkout.session.completed`

Copy the endpoint's **signing secret** (`whsec_…`).

**2. Fill in Stripe Gateway Settings**

| Field | Value |
|---|---|
| Publishable Key | `pk_test_…` or `pk_live_…` |
| Secret Key | `sk_test_…` or `sk_live_…`. The key decides whether payments are test or live. |
| Mode | `Test` or `Live`, for your own reference |
| Success URL | Your [confirmation page](/bwh-payments/get-started/return-pages). Stripe adds `?session_id=…` to it. |
| Failure URL | Your [payment-failed page](/bwh-payments/get-started/return-pages), shown when the shopper cancels |
| Webhook Secret | The endpoint's signing secret (`whsec_…`) from step 1 |

**3. Create a Payment Gateway Profile** named `Stripe`, set **Gateway Settings** to `Stripe Gateway Settings`, and tick **Enabled**.

:::caution
The profile name must match the `?gateway=` in the webhook URL exactly. A mismatch rejects every delivery.
:::

## How it behaves

| Stripe says | Payment status |
|---|---|
| Checkout Session `payment_status` is `paid` | Paid |
| Checkout Session `status` is `expired` | Expired |
| Anything else | Pending |

- **Amounts** are sent in the currency's smallest unit (cents, fils, yen), using its correct number of decimals.
- **Webhooks** are verified with Stripe's own library, which also rejects old, replayed signatures. Stripe's event id makes sure each event is applied only once.
- **Refunds** go against the Checkout Session's payment. Partial refunds are supported.
- **Cancelling** an open checkout (`release_if_unpaid()`) expires the Stripe session.

## Good to know

- The Stripe Python SDK (version 11 or later) is installed with BWH Payments.
- A payment method that settles later, such as a bank debit, stays Pending when the checkout completes. It becomes Paid when your app next checks its status.

## Next steps

- [Webhooks and security](/bwh-payments/build/webhooks-and-security): debug a delivery that isn't arriving
- [Build a payment gateway](/bwh-payments/build/build-a-payment-gateway): add a gateway that isn't listed
