BWH Payments (Next)

BWH Payments (Next)

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

Overview

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.

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.

Built-in gateways

Gateway Best for Page
Stripe Cards and wallets, worldwide Stripe
Razorpay Cards, UPI, netbanking and wallets in India Razorpay
Telr Cards and local methods across the GCC Telr
Tabby Buy now, pay later in the Middle East Tabby

How a payment works

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

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.

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 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 explains why. Build a payment gateway walks through one.

Install

You need a site on Frappe 16 or later.

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.

Next steps

Last updated 17 hours ago
Was this helpful?
Thanks!