---
title: "Overview"
space: "BWH Payments (Next)"
url: "https://docs.bwh.tech/bwh-payments-next/get-started/overview"
updated: "2026-10-12"
---

BWH Payments is a Frappe app for taking payments through hosted checkout pages. Your app asks for a payment, the shopper pays on the gateway's own page, and BWH Payments tells you when the money has arrived. You can also refund all or part of a payment later.

It ships with four gateways. You can add your own with one settings DocType and a small service client.

<picture>
  <source srcset="/files/bwhp-settle-flow-dark.svg" media="(prefers-color-scheme: dark)">
  <img src="/files/bwhp-settle-flow-light.svg" alt="The shopper pays at the gateway. A signed webhook marks the payment request Paid and the order is notified. A later status check finds it already Paid and changes nothing.">
</picture>

## Built-in gateways


| Gateway      | Best for                                    | Page                                             |
| ------------ | ------------------------------------------- | ------------------------------------------------ |
| **Stripe**   | Cards and wallets, worldwide                | [Stripe](/bwh-payments-next/gateways/stripe)     |
| **Razorpay** | Cards, UPI, netbanking and wallets in India | [Razorpay](/bwh-payments-next/gateways/razorpay) |
| **Telr**     | Cards and local methods across the GCC      | [Telr](/bwh-payments-next/gateways/telr)         |
| **Tabby**    | Buy now, pay later in the Middle East       | [Tabby](/bwh-payments-next/gateways/tabby)       |


## How a payment works

Every payment is a **Gateway Payment Request** document. Its status moves the same way whichever gateway is behind it:

```mermaid
stateDiagram-v2
    [*] --> Pending: checkout opened
    Pending --> Paid
    Pending --> NotPaid
    Pending --> Cancelled
    Pending --> Expired
    Paid --> PartiallyRefunded: refund
    Paid --> Refunded: full refund
    PartiallyRefunded --> Refunded: refund the rest
    NotPaid: Not Paid
    PartiallyRefunded: Partially Refunded
```



1. **Your app calls** `create_payment`**.** BWH Payments asks the gateway for a checkout session and stores the checkout URL on the request.
2. **You redirect the shopper to that URL.** They pay on the gateway's own page.
3. **The request moves out of Pending.** This happens when the gateway sends a signed webhook, when the shopper comes back through BWH Payments, or when your app asks for the latest status.
4. **Your document hears about it.** When the request becomes Paid, BWH Payments calls `on_payment_authorized` on the document being paid for.

Refunds work the same way. Each refund is a **Gateway Refund**, and your document hears each change through `on_refund_update`, including refunds raised on the gateway's own dashboard.

```python
from bwh_payments.api import create_payment

payment_request = create_payment(
	order.doctype,  # any DocType: the thing being paid for
	order.name,
	49.99,
	"USD",
	"Stripe",  # a Payment Gateway Profile
	customer_email="shopper@example.com",
)

# Send the shopper here:
payment_request.order_url
```

[Accept a payment](/bwh-payments-next/get-started/accept-a-payment) covers each step in detail.

## Two ways a gateway is built

A gateway is a Frappe Single DocType that holds the gateway's credentials. Its controller extends `PaymentGatewayBase` and makes every payment decision: amounts, status mapping, capture and refund rules, and webhook checks.

The controller doesn't call the gateway's API itself. It calls a **service client**, and the client does the HTTP or SDK call. Each service has three clients:


| Client    | Used               | Does                                                                  |
| --------- | ------------------ | --------------------------------------------------------------------- |
| `base.py` | Never directly     | Lists the calls, and models the gateway's JSON                        |
| `live.py` | On every real site | Makes the call, logs it, and turns a refusal into an error            |
| `stub.py` | Only in tests      | Keeps the gateway's state in memory, so tests never touch the network |


[Service clients](/bwh-payments-next/build/service-clients) explains why. [Build a payment gateway](/bwh-payments-next/build/build-a-payment-gateway) walks through one.

## Install

You need a site on Frappe 16 or later.

```bash
bench get-app https://github.com/bwhtech/bwh_payments
bench --site your.site install-app bwh_payments
```

BWH Payments installs the Stripe SDK (12.5 or later) and pydantic 2.

:::note[Using ERPNext?]
BWH Payments doesn't need ERPNext. If ERPNext is installed, submitting a **Payment Entry** also refunds the payment at the gateway, when all of these are true:

- its **Payment Type** is *Pay*
- its **Mode of Payment** has the same name as the gateway
- its **Reference No** is the request's gateway session id
- the request is *Paid* or *Partially Refunded*
:::

## Next steps

- [Accept a payment](/bwh-payments-next/get-started/accept-a-payment): create a request, and react when it's paid
- [Return pages](/bwh-payments-next/get-started/return-pages): the confirmation and payment-failed pages every gateway needs
- [Move from frappe/payments](/bwh-payments-next/get-started/move-from-frappe-payments): keep your gateways and payment history
- [Build a payment gateway](/bwh-payments-next/build/build-a-payment-gateway): add your own gateway, step by step
- [Gateway contract](/bwh-payments-next/reference/gateway-contract): every method, model and return value