getpaid-bitpay
BitPay payment gateway plugin for the
python-getpaid ecosystem. Provides a
fully async HTTP client (BitPayClient) and a payment processor
(BitPayProcessor) implementing the
getpaid-core
BaseProcessor interface. Communicates with BitPay via their REST API v2
using POS and merchant facade authentication.
Status: Alpha — under active development.
Architecture
getpaid-bitpay is composed of three layers:
- Signing — EC key (secp256k1) generation, compressed public key derivation, and ECDSA-SHA256 message signing used by the merchant facade.
- BitPayClient — a low-level async HTTP client (built on
httpx) that wraps the BitPay REST API v2 endpoints. Supports POS facade (token-only) and merchant facade (EC key signed requests). Can be used as an async context manager for connection reuse. - BitPayProcessor — a high-level processor that implements
BaseProcessorfrom getpaid-core. Translates between the core payment protocol and BitPay's API, handles IPN webhooks, PULL status polling, and FSM transitions.
Why not the official SDK?
This plugin uses its own native async HTTP client instead of wrapping the official BitPay Python SDK. The reasons:
- Sync-only — the official SDK uses
requests, requiring thread offloading for every call in an async context. - Pydantic version pinning — the SDK pins
pydantic==2.11.9exactly, causing dependency conflicts in projects using different Pydantic versions. - Testing opacity — mocking
requestsinside third-party code is fragile; withhttpxwe userespxfor clean, reliable test doubles. - Minimal surface — we only need invoices and refunds (6 endpoints), not the SDK's 40+ methods.
- Small footprint — the custom client is ~275 lines, a net reduction in complexity compared to wrapping the full SDK.
Key Features
- Invoice creation via POS facade (token-only authentication)
- Invoice retrieval, cancellation via merchant facade (EC key signing)
- Refund creation, retrieval, cancellation via merchant facade
- Callback authenticity verification via
X-SignatureHMAC - IPN webhook handling for both invoice and refund status changes
- PULL status polling via
fetch_payment_status - Two-step payment confirmation (confirm_payment + mark_as_paid)
- Automatic refund completion (confirm_refund + mark_as_refunded)
- Sandbox and production environment support
Quick Usage
BitPayClient can be used standalone as an async context manager:
from getpaid_bitpay.client import BitPayClient
async with BitPayClient(
base_url="https://test.bitpay.com",
pos_token="your-pos-token",
) as client:
invoice = await client.create_invoice(
price=29.99,
currency="USD",
order_id="order-123",
notification_url="https://example.com/webhooks/bitpay",
)
checkout_url = invoice["url"]
For merchant facade operations (invoice retrieval, refunds), provide an EC private key and merchant token:
from getpaid_bitpay.client import BitPayClient
from getpaid_bitpay.signing import generate_pem
# Generate a key pair (do this once, store the PEM securely)
private_key_pem = generate_pem()
async with BitPayClient(
base_url="https://test.bitpay.com",
pos_token="your-pos-token",
merchant_token="your-merchant-token",
private_key_pem=private_key_pem,
) as client:
invoice = await client.get_invoice("invoice-id-123")
refund = await client.create_refund(
invoice_id="invoice-id-123",
amount=10.0,
)
Key Generation
BitPay merchant facade requires an EC key pair (secp256k1). The signing module provides a helper:
from getpaid_bitpay.signing import generate_pem, get_compressed_public_key
# Generate a new private key in PEM format
pem = generate_pem()
# Derive the compressed public key (hex) — needed for BitPay dashboard pairing
public_key_hex = get_compressed_public_key(pem)
print(public_key_hex) # e.g. "02a1b2c3d4..."
Store the PEM string securely (e.g. in environment variables or a secrets manager). The compressed public key hex is used when pairing with the BitPay merchant dashboard to obtain your merchant token.
Configuration
When used via a framework adapter (e.g. django-getpaid, litestar-getpaid), configuration is provided as a dictionary:
| Key | Type | Required | Description |
|---|---|---|---|
pos_token |
str |
Yes | POS facade API token from BitPay dashboard |
merchant_token |
str |
No | Merchant facade API token (needed for refunds, invoice retrieval) |
private_key_pem |
str |
No | EC private key in PEM format (needed for merchant facade signing) |
sandbox |
bool |
No | Use sandbox (test.bitpay.com) or production (bitpay.com). Default: True |
notification_url |
str |
No | IPN webhook URL template, e.g. https://example.com/payments/{payment_id}/notify |
redirect_url |
str |
No | Redirect URL template after payment, e.g. https://example.com/payments/{payment_id}/done |
Example configuration dict:
GETPAID_BACKENDS = {
"bitpay": {
"pos_token": "your-pos-token",
"merchant_token": "your-merchant-token",
"private_key_pem": "-----BEGIN EC PRIVATE KEY-----\n...",
"sandbox": True,
"notification_url": "https://example.com/payments/{payment_id}/notify",
"redirect_url": "https://example.com/payments/{payment_id}/done",
}
}
Note:
merchant_tokenandprivate_key_pemare only required if you need merchant facade operations (invoice retrieval, cancellation, refunds). Basic invoice creation works with justpos_token.
Status Mapping
Invoice Statuses
| BitPay Status | FSM Trigger | Description |
|---|---|---|
new |
confirm_prepared |
Invoice created, awaiting payment |
paid |
confirm_payment |
Payment detected, awaiting confirmation |
confirmed |
mark_as_paid |
Payment confirmed on blockchain |
complete |
(none) | Invoice complete, no further action |
expired |
fail |
Invoice expired without payment |
invalid |
fail |
Payment invalid |
declined |
fail |
Payment declined |
Refund Statuses
| BitPay Status | FSM Trigger | Description |
|---|---|---|
pending |
(none) | Refund pending processing |
created |
(none) | Refund created |
preview |
(none) | Refund in preview |
success |
confirm_refund |
Refund completed successfully |
failure |
cancel_refund |
Refund failed |
cancelled |
cancel_refund |
Refund cancelled |
Supported Currencies
BTC, BCH, DOGE, ETH, LTC, MATIC, SHIB, USDC, USDP, BUSD, PAX, XRP, APE, EUR, GBP, USD, CAD, AUD
Requirements
- Python 3.12+
python-getpaid-core >= 0.1.0httpx >= 0.27.0ecdsa >= 0.19.0anyio >= 4.0
Related Projects
- getpaid-core — framework-agnostic payment processing library
- django-getpaid — Django framework adapter
- litestar-getpaid — Litestar framework adapter
License
MIT
Disclaimer
This project has nothing in common with the getpaid plone project.
Credits
Created by Dominik Kozaczko.
Metadata
Release files for python-getpaid-bitpay 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| python_getpaid_bitpay-0.1.1.tar.gz | 59.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| python_getpaid_bitpay-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.0 kB
Release files / python_getpaid_bitpay-0.1.1.tar.gz
| Download URL | python_getpaid_bitpay-0.1.1.tar.gz |
|---|---|
| Size | 59.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
21b666ba66421dbe9e2abc67de8560f7da38466f4c78c99c07116ab87b2c6268
|
|
BLAKE2b-256 checksum How to use checksums |
f3eb59a63bd51f77eb98de84071f11ca127b56018a39cd153d935d632cc7de82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Manjaro Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / python_getpaid_bitpay-0.1.1-py3-none-any.whl
| Download URL | python_getpaid_bitpay-0.1.1-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b61dbe089ed8302468c4351f7d998408b594d6b6f60166f9e9390101d81951a3
|
|
BLAKE2b-256 checksum How to use checksums |
7dacb0259b02acf78b9c4c868253ad39f327c8833b4c14ae7802a1b78a2f25f8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Manjaro Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|