OpenReceive for Python
OpenReceive adds Bitcoin and crypto checkout to your Python application. Accept BTC over Lightning directly into your wallet, and optionally let customers pay with USDT, USDC, ETH or SOL through a configured swap provider. You receive BTC over Lightning in either case.
Your application keeps its orders, prices, customers and fulfillment. OpenReceive connects checkout to your wallet, records payment attempts in your existing database, and tells your application when a payment has settled. There is no OpenReceive account to create and no separate database to operate.
Website · Django quickstart · FastAPI quickstart · All guides
How payments work
- Your server sets the price. OpenReceive creates a Lightning invoice in your connected wallet for the amount your application supplies.
- The customer chooses how to pay. They can pay the invoice directly with a Lightning wallet. With swaps enabled, they can instead send a supported asset, such as USDT or USDC, to the swap provider. The provider converts it and pays your Lightning invoice.
- Your wallet confirms receipt. OpenReceive records settlement and runs your application's payment hook. A swap provider saying it has finished does not count as payment: your wallet must confirm the invoice settled.
Available swap assets, networks, limits and fees depend on the configured provider. Swaps are optional; Lightning checkout works without them. See how swaps work, provider setup, and payer swap refunds.
Designed for receive-only wallet access
OpenReceive connects through Nostr Wallet Connect (NWC), using a connection code issued by your wallet. It does not need your wallet's seed phrase.
- No spending permission required. A receive-only connection can create invoices and read payments, but cannot send funds. By default, OpenReceive refuses a connection that advertises spending methods. Bypassing that check requires an explicit override.
- Credentials stay on your server. Neither the NWC code nor swap-provider credentials belong in browser code, logs or tests. A receive-only code is still sensitive: it can expose payment history and allow invoice creation.
- Your application controls access and prices. Order authorization and amounts come from your server, not from the payer's browser.
- Payments survive retries. Persistent attempts, per-order locking and write-once settlement prevent repeated payment checks from running the payment hook again for the same order.
OpenReceive does not hold your funds. Your chosen wallet determines custody; when using swaps, the provider handles the customer's deposit until payout or refund. Receive-only access limits what the integration can do, while your wallet, provider and server remain part of the trust model. Read the security guide.
Install
Requires Python 3.10 or newer. Choose the extra for your application:
# Django 5.2 or newer
pip install "openreceive[django]"
# FastAPI 0.115 or newer, with SQLAlchemy 2
pip install "openreceive[fastapi]"
# Other Python applications using SQLAlchemy 2
pip install "openreceive[sqlalchemy]"
Start with the Django quickstart or FastAPI quickstart. There is also a Flask recipe. Django includes the checkout's browser assets; FastAPI and other hosts can use OpenReceive's frontend components. The SQLAlchemy integration uses a synchronous engine for the payment tables.
Connect your application
Get a receive-only wallet connection
and set NWC_URI in your server's process environment. To enable swaps, also
configure LSC_URI_PRIMARY and optionally LSC_URI_BACKUP using the
provider setup guide.
Your host supplies authorization, the order amount, a payment hook and its
existing database connection. OpenReceive manages openreceive_payments and
openreceive_meta in that database; your application applies their migrations
through its normal workflow. Checkout requests drive reconciliation through a
shared database gate, with an optional separate notifications worker.
- Payment storage and migrations
- Authorization and host responsibilities
- Environment variables
- Testing with fake wallets and swap providers
- Deployment
OpenReceive is open source under the MIT license. Source code · Report an issue
Settlement transactions and recovery
on_paid runs inside the settlement transaction. Write the entitlement or a host
outbox there; rolled-back callbacks may run again on retry. External jobs need
host-owned durable idempotency. Optional after_paid is best effort after the
actual commit, receives no live connection, and may be lost if the process dies
between commit and delivery. Django uses transaction.on_commit on the configured
database alias, including outer transactions and savepoints. MySQL explicitly
rejects ambient transactions around repository reference operations because its
connection-scoped lock cannot be released safely at an inner savepoint.
SQLAlchemy repositories own their transaction and connection. Advanced custom
repositories must implement record_settlement(..., after_commit=...) and declare
supports_after_commit = True when after_paid is configured.
Explicit workers, notification fallback and request reconciliation share the same
durable gate and bounded scheduler. opportunistic_reconcile=False disables only
request triggers. Custom worker repositories need lease-bearing
claim_reconcile_gate, checkpoint_reconcile_gate, and keyset
list_reconcilable_attempts(after=..., limit=...), plus
find_by_payment_hash to confirm durable settlement outcomes. Deploy application and workers
together; stop old processes before upgrading. Resumed wallet history proves
positive finality, never absence; a dense history can therefore leave an unpaid
attempt pending until a fresh complete sweep fits its budget.
Use repository.maintenance_candidates(after=..., limit=100) for a dry-run report
(candidates, next_cursor, scanned). After operator review, pass one unchanged
candidate to repository.requeue_reviewed_attempt(candidate, decision_id="ticket-42").
This audits the decision and restores pending status; it does not settle or invoke
fulfillment. Run the ordinary gated reconciler afterward. Reports distinguish
historical early swap-deadline closure from operator-attention cases, preserve
provider credentials, and never reopen settled rows. See the
coordinated upgrade guide.
Storage-free RequestHandler hooks are a separate advanced API: settled polls can
call on_paid repeatedly, so the host owns the conditional write/outbox. A raw
on_checkout_created refusal returns 409 and withholds instructions; repository
infrastructure failures return retryable 503. Public error projections omit
internal details; FixedFloat diagnostic hooks expose metadata and presence flags,
never raw request bodies or provider responses.
Metadata
Release files for openreceive 0.4.13
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| openreceive-0.4.13.tar.gz | 942.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openreceive-0.4.13-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.9 MB
Release files / openreceive-0.4.13.tar.gz
| Download URL | openreceive-0.4.13.tar.gz |
|---|---|
| Size | 942.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e4fabd721925e6903de6f18b7fbb2560fa2791fb797a15ec1cba35e39567dffa
|
|
BLAKE2b-256 checksum How to use checksums |
ed53b1500e04de045c31da92c1ffd64eb24c695faed2b970133d2d59179ac538
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / openreceive-0.4.13-py3-none-any.whl
| Download URL | openreceive-0.4.13-py3-none-any.whl |
|---|---|
| Size | 983.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2e3ed9d20e7cfc5a84634bcbd66f03f4a9e6b99441460689b3348946bfc26d2a
|
|
BLAKE2b-256 checksum How to use checksums |
f102141aa98a3d1210dd42d7b99f5cefdf291a53aeb27342bd4795cb5c97e859
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for openreceive 0.4.13
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.