Freedom technology for inbound payments.
Accept Bitcoin payments on your website, app, or point of sale, straight into a wallet you control.
See it in action:
basic-rails-demo-compressed.mp4
Bitcoin by default. Use the internet's neutral settlement currency. Your server issues a QR code. The payer pays the QR code, and your server approves delivery of the purchase.
Deposit-only by design. OpenReceive exposes no payment-sending API and does not need your wallet seed phrase: it connects with a receive-only NWC code. Choose an existing NWC service to receive payments, or build your own NWC Service.
To run the wallet on your own hardware, use an NWC service you host yourself, like Alby Hub.
Optionally accept USDT, USDC, SOL, and ETH through swaps. Customers pay with a supported asset; you receive BTC over Lightning in your connected wallet. Available assets and networks depend on your configured provider. Configure a swap provider to receive altcoins. Use any swap provider that implements the FixedFloat / Lightning-Swap API, or build your own.
Optional Inbound Currencies:
| Pay with | On network | Settles in |
|---|---|---|
# Node: an HTTP adapter for your framework, plus the checkout UI for yours
npm install @openreceive/express @openreceive/reactSwap @openreceive/express for @openreceive/fastify or @openreceive/next,
and @openreceive/react for @openreceive/vue, @openreceive/svelte,
@openreceive/angular, or @openreceive/elements (framework-free custom
element). On Rails:
# Gemfile
gem "openreceive-rails"Pick your stack:
| Stack | Quickstart |
|---|---|
| Express / Node.js | Express quickstart |
| Fastify | Fastify quickstart |
| Next.js | Next.js quickstart |
| Ruby on Rails | Rails quickstart |
| Django | Django quickstart |
| FastAPI | FastAPI quickstart |
| Plain PHP | PHP quickstart |
| Laravel | Laravel quickstart |
| WordPress + WooCommerce | WooCommerce quickstart |
| BTCPay Server | BTCPay quickstart |
Your application supplies authorization, the order amount, and the payment hook. OpenReceive tracks payment attempts in your existing database and verifies receipt in your wallet. WooCommerce and BTCPay integrations connect that settlement to the platform's existing order or invoice lifecycle.
- Receive-only wallet access. OpenReceive creates invoices and reads payments through NWC. It exposes no send-payment API and rejects spend-capable wallet connections by default.
- Credentials stay on your server. Your browser receives checkout instructions, never your wallet connection or swap-provider credentials.
- You choose the wallet and provider. OpenReceive does not hold your funds. Your wallet determines custody, and an optional swap provider handles the customer's deposit until payout or refund. Receive-only access limits wallet permissions; it does not remove the need to secure your app. See the security guide.
- Your app owns business state. Your application owns orders; the library
owns the
openreceive_paymentsrows (they live in your database) — see Payment storage. OpenReceive never owns orders, users, prices, or fulfillment, and never requires a separate database, Redis, or migration runner: you pass a database handle, and the library owns the schema, locking, settlement write-once, and reconciliation.
In Node.js, OpenReceive is three server objects plus an optional browser package. Each one talks to a different side of your app, and each has an obvious home:
| Piece | You build it with | It talks to | It lives |
|---|---|---|---|
| Wallet client | createOpenReceive() |
your wallet — mints invoices, reads settlement, holds the NWC code | server-only, one per process |
| Host | createHost() |
your database — your hooks, plus the openreceive_payments table |
server-only, next to your models |
| HTTP routes | openReceiveExpress() (or Fastify / Next / Rails) |
the browser — the endpoints the checkout UI calls | mounted on your app by default at /openreceive |
| Checkout UI (optional) | @openreceive/react (or vue/svelte/angular/elements) |
the HTTP routes above — creates the checkout, polls until paid | your browser bundle |
Only that last row is genuinely optional. Take the drop-in components, build
your own on the semver-stable @openreceive/browser/headless engine, or skip
our browser packages altogether and call the routes yourself — the server side
is identical either way. See
Headless checkout.
The three server pieces:
import { openReceiveExpress } from "@openreceive/express";
import { createHost } from "@openreceive/http";
import { createOpenReceive } from "@openreceive/node";
// 1. The wallet client. Reads NWC_URI; never let this reach client code.
const service = await createOpenReceive();
// 2. The host: your database and your price.
const host = createHost({
db, // pg Pool/Client, node:sqlite, better-sqlite3, or a custom adapter
// The authoritative price for a reference (here, your order id) — never
// taken from payer input.
// OpenReceive converts this exact decimal into the invoice amount; null
// means there is nothing to pay for (404).
amountFor: async (reference) => {
const order = await orders.find(reference);
return order
? { currency: order.currency, value: order.total.toString() }
: null;
},
onPaid: async ({ reference, query }) => {
// Runs inside the settlement transaction, only for the order's first
// settled attempt. Update the order or insert an outbox row here.
await query("UPDATE orders SET state = 'paid' WHERE id = ?", [reference]);
},
});
// 3. The HTTP routes. `authorize` is your own access check: it runs on every
// reference-scoped request, because a reference alone does not prove ownership.
app.use(
openReceiveExpress({
service,
host,
authorize: async ({ action, request, resource }) =>
orders.authorize({ request, reference: resource.reference, action }),
}),
);The host is the server object between your app and OpenReceive's payment
attempts: it calls the two hooks you hand it — amountFor, onPaid — and
owns one table, openreceive_payments, inside your database. You run that
table's migration (npx openreceive scaffold payments emits it
for your ORM); the library owns everything else: schema, per-reference locking,
write-once settlement, reconciliation.
The reference is a string you choose, and it is the fulfillment identity:
your order id — one per thing you fulfill, created before checkout, kept
across retries, never reused. OpenReceive never looks inside it, but onPaid
runs once per reference, a new checkout under a reference that already
settled is refused with 409, and a fresh id per page load lets one order be
paid twice. Each row is one invoice or swap attempt under a reference. A row
commits before the payer sees an invoice, settles once, and fulfills at most
once per reference; to your app an order is simply unpaid or paid.
Schema, the attempt state machine, live-attempt rules, and the custom-repository escape hatch: Payment storage.
createOpenReceive() reads the receive-only wallet
code from NWC_URI; optional swap providers come from LSC_URI_PRIMARY and
LSC_URI_BACKUP. Those are OpenReceive's only secret environment variables.
See Environment variables.
The routes never inspect your session. You write one callback,
authorize (step 3 above), and it runs on every reference-scoped
request — a reference identifies a row but does not prove the caller owns it.
The context carries the action (checkout.create, payment.check, …), the
Web-standard request, and the untrusted resource selectors the payer sent;
return false for 403.
A create request carries a reference, never a price: your amountFor hook
resolves the amount. A refused attempt (order already paid, competing
live attempt) is a 409 with no invoice attached.
Authorization covers the context object,
framework sessions, and guest orders.
OpenReceive checks your wallet for payment while serving the checkout routes,
so no background process is required. An order is marked paid only when the
wallet itself reports the payment final — never from a preimage the payer
presents, never from a swap provider reporting "complete" — and an unpaid
invoice is closed only once the wallet confirms it went unpaid, not on your
server's clock. Optional notification workers (Rails:
rake openreceive:notifications) settle faster
under the same rule.
How settlement is driven, multi-instance behavior, and workers:
Deploying. Swap recovery, swap_data, and refunds:
Automated swaps.
Most applications should not — mounting the adapter gives you the routes, and the shipped checkout components work against them with no glue. If you need a flow the routes do not offer: Writing your own checkout route.
Try a working shop: add items to a cart, create an order, and pay with Lightning or an optional swap. The download unlocks after wallet settlement. Examples cover Node.js, Ruby, Python, PHP, and WordPress, with a shared product catalog and each framework's own database integration.
Run a demo in Docker from the repository root:
cp -n .env.example .env # configure your receive-only NWC_URI
npm run demo node # Express, :3000
npm run demo django # Django, :3006
npm run demo php # plain PHP, :3008
npm run demo wordpress # WooCommerce, :3009The examples directory lists every stack, its launch command, and ways to run against fake wallets and swap providers. The Buy a Button README explains the shared shop, order persistence, and checkout integration.
npm test # the JS suite
npm run check # contracts and secret-safety checks
npm run test:ci # the full gate across engines, packages, docs, and demo buildsCONTRIBUTING has setup, ground rules, and the repository layout; the test command map lists every command.
Integrating with a coding agent? OpenReceive ships installable
agent skills — npx skills add OpenReceive/openreceive, or
/plugin marketplace add OpenReceive/openreceive in Claude Code — plus
self-contained per-stack agent directions, /llms.txt, and the OpenAPI
contract at openreceive.org/agents. Working
on OpenReceive itself? That is AGENTS.md.
Start with the developer guides:
- Node quickstart
- Node ORM recipes
- Rails quickstart
- BTCPay Server quickstart
- Frontend checkout
- Headless checkout
- Writing your own checkout route
- Price feeds
- Automated swaps
- Lightning Swap Connect
- Environment variables
- Provider registry
- Authorization
- Rate limiting
- Payment storage
- Deploying OpenReceive
- Testing your OpenReceive integration
- Security
- API reference
- React + Material UI recipe
- Normative HTTP contract
- Contributor and operator docs
