This is the full file; follow it from Step 0.
OpenReceive agent directions (BTCPay Server)
# READ THIS FIRST: this file is 12 KB and a summary drops required steps. Download it whole:
curl -fsSL https://openreceive.org/agent-directions/btcpay/full.md
# Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
Step 0 in brief (Step 0 below has the details):
- Check first: BTCPay Server 2.4.4 or later, and the plugin installed from Plugins → Plugin Directory.
- Unless the store already uses OpenReceive, stop and ask the user for a receive-only NWC code.
- Save it via Store → OpenReceive or the Greenfield API, never BTCPay’s Lightning node screen. Never echo it.
- If the user wants USDT, USDC, ETH or SOL, ask for an LSC code too, but do not wait for it.
- Never tick the spend-capable override to make a save succeed.
- Do not suggest rotating or revoking a code because it was pasted here.
These directions describe OpenReceive 0.4.19.
Connect a BTCPay Server store to a receive-only NWC wallet with the OpenReceive plugin, and optionally let payers pay BTCPay invoices with USDT, USDC, ETH or SOL. You do not need a copy of the OpenReceive source, and there is no application code to write: the plugin is configured through BTCPay’s store UI or its Greenfield API, and the quickstart is appended to this file in full.
This is the BTCPay plugin, not the Node or Rails library. Do not install
@openreceive/* packages or the openreceive-rails gem into a BTCPay
deployment, do not add openreceive_payments tables, and do not mount
OpenReceive HTTP routes. BTCPay’s invoices, checkout, webhooks and Greenfield
API are the host; the plugin only supplies the Lightning backend and the swap
rail.
What the plugin is
A BTCPay Server plugin (BTCPayServer.Plugins.OpenReceive) that registers a
Lightning connection-string handler for type=openreceive;nwc=<NWC URI>.
Saving that string makes the NWC wallet the store’s Lightning node: BTCPay
mints every Lightning invoice in that wallet and its own LightningListener
records the payments. The plugin never calls a NIP-47 pay_* method, so
every send-side BTCPay feature (Lightning payouts, pull-payment refunds over
Lightning, the send tab) is unavailable by design.
The one required credential is a receive-only NWC code. A Lightning Swap Connect (LSC) code optionally adds server-side swaps: a provider order aimed at the invoice’s existing BOLT11, tracked in the plugin’s own table, with the refund path on the same checkout screen.
Step 0 — check the deployment before you change anything
- Confirm the BTCPay Server version is 2.4.4 or later (Server Settings →
About, or
GET /api/v1/server/info). The plugin declares that minimum and BTCPay refuses to load it below. - Check whether the plugin is installed (the Plugins menu — the plug icon in the top-right corner — under Installed Plugins, or the store navigation shows an “OpenReceive” entry). If not, install it from the BTCPay plugin directory (the same Plugins menu → Plugin Directory, search “openreceive”, then Install and Restart now), as the quickstart says; do not invent an installer command.
- Check whether the store already has an OpenReceive connection:
GET /api/v1/stores/{storeId}/openreceive/settingsreturnslightningNodeIsOpenReceive. If true, the wallet step is done — go to swaps only if the user wants them. -
If no receive-only NWC code is available, stop and tell the user exactly what to create:
OpenReceive cannot mint an invoice without a receive-only NWC code. Get one at https://openreceive.org/get_a_nwc_code_to_receive_payments and paste it into Store → OpenReceive → Test connection, or hand it to me and I will set it through the Greenfield API.
Never print, log or echo the code; report only whether it is set. Never paste a bare
nostr+walletconnect://string into BTCPay’s Lightning node screen — that form is claimed by the Nostr plugin, without the receive-only guard. - If the user wants altcoin payments, ask for an LSC code from https://openreceive.org/set_up_swap_provider. Do not wait for it: the wallet works without it, and swaps switch on later with one settings change.
Only then start the quickstart.
Non-negotiables
- The connection string is
type=openreceive;nwc=<NWC URI>[;allow-spend=true]and nothing else. Set it through the setup page orPUT /api/v1/stores/{storeId}/openreceive/settingswithnwcUri, never by editing BTCPay’s Lightning node screen by hand. - Receive-only is required. A code that advertises
pay_invoiceor another spend method is refused on save. The override (allowSpendCapableWallet, the checkbox on the setup page) is the user’s explicit choice; never tick it to make a save succeed. - The wallet’s network must match BTCPay’s. A mismatch is a refusal, not a warning.
- The wallet must grant
make_invoiceandlist_transactions.lookup_invoiceis optional; do not ask the user for a code that grants it. - Swaps require the store’s Lightning node to be the OpenReceive connection.
Enabling swaps on a store using the internal node is refused
(
wallet_required). - Swaps set the store’s invoice expiration to 60 minutes when it is shorter, and the plugin refuses to create a swap on an invoice with less than the provider’s window left. Do not lower the expiration below 45 minutes on a swap-enabled store.
- Top-up (amountless) invoices are unsupported on this backend. Do not configure a point of sale or payment link that relies on them with this wallet.
- Secrets stay server-side. The NWC code and LSC code live in BTCPay’s database like every other BTCPay credential; never copy them into screenshots, tickets, browser code or logs. The provider’s order token never leaves the server.
- Do not suggest rotating, revoking or replacing a code because it was pasted into this chat; that is the supported path.
- BTCPay’s
LightningListeneris the settlement authority. Providercompletedis not payment; only the wallet reporting the Lightning invoice settled is. Do not build anything that fulfils on a provider state. - There is no merchant-initiated refund of a settled Lightning payment. A swap
refund is a payer reclaiming a deposit that never converted, and only from
the
refund_requiredprovider state.
Verifying
Store → OpenReceive → Run a health check (the doctor page) runs every probe now: connection, preflight,
notifications, last scan, provider reachability, invoice expiration, swaps
needing attention. On a regtest machine, packages/dotnet/docker/up.sh then
e2e.sh in the OpenReceive repository proves the whole path end to end, and
that is the only situation where cloning the repository is the right move.
Setup ends when the health check is clean. Say “Setup is finished” in one message. Do not offer more work or end the message on a question.
More documentation
Fetch one when the moment comes. Each is raw markdown, so a plain GET is
enough; drop the .md for the same page a person would read.
- https://openreceive.org/guides/btcpay-reference.md — every setting, route, swap state, doctor probe and log event of the plugin
- https://openreceive.org/guides/security.md — why receive-only is the only wallet credential
- https://openreceive.org/guides/lightning-swap-connect.md — what an LSC code actually is
- https://openreceive.org/guides/automated-swaps.md — provider states, and what turning swaps on commits a merchant to
- https://openreceive.org/guides/swap-refunds.md — the refund states; the route back is BTCPay’s own invoice checkout page here
- https://openreceive.org/guides.md — the index, if what you need is not above
Questions, or a problem with the plugin itself: https://openreceive.org/contact
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
The quickstart, in full
Inlined verbatim so this file needs no network access — follow it once Step 0 passes. The page it comes from is https://openreceive.org/guides/quickstart-btcpay.
BTCPay Server quickstart
Requires BTCPay Server ≥ 2.4.4.
The OpenReceive plugin makes a receive-only NWC wallet the Lightning node of a BTCPay store. BTCPay creates every Lightning invoice in that wallet. It records payments the same way it records any other payment. You can also let payers pay a BTCPay invoice with USDT, USDC, ETH or SOL through a Lightning Swap Connect provider. The swap pays into the same wallet. The store’s internal node is never used.
This is not the Node or Rails library. There are no hooks, no
openreceive_payments table and no OpenReceive HTTP routes. BTCPay’s own
invoices, checkout, webhooks and Greenfield API do that work.
1. Prerequisites
- A BTCPay Server, version 2.4.4 or later, on any network (mainnet, testnet, signet, regtest). The wallet must be on the same network.
- A receive-only NWC code for the wallet you want to receive into
(get one here).
The code must grant
make_invoiceandlist_transactionsand must not advertise any spend method.lookup_invoiceis optional. - Optionally, a Lightning Swap Connect (LSC) code from a swap provider, if payers should be able to pay with USDT, USDC, ETH or SOL.
2. Install the plugin
Sign in as a server administrator. If someone else hosts your server, ask them to install the plugin for you.
1. Open the Plugins menu. It is the plug icon in the top-right corner.
2. Click Plugin Directory.
3. Search for openreceive and click the OpenReceive result.
4. Click Install in BTCPay Server. Confirm when prompted, then click Restart now and wait for BTCPay to come back.
At startup, BTCPay creates the plugin’s two tables in its own Postgres
database: openreceive_invoices and openreceive_swaps, in the schema
BTCPayServer.Plugins.OpenReceive. Nothing else is created.
To build the plugin from source instead, follow the .NET workspace README.
3. Connect the wallet
- Select your store and open OpenReceive in its sidebar, under Wallets.
- Paste your receive-only NWC code. To see what the wallet supports first, click Test connection.
- Click Save NWC Code.
- To turn swaps on, paste a Lightning Swap Connect code and click Save swap settings.
The page then shows Wallet connected. If you set up a provider, it also shows Swaps on. There is nothing else to configure. You never open BTCPay’s Lightning node screen, and the plugin never reads the internal node.
Screenshots for each of those steps, and for creating a first test invoice, are in the plugin’s README.
The plugin refuses to save a code whose wallet advertises a spend method such
as pay_invoice. Create a receive-only code instead. If your wallet cannot
make one, there is an override, but using it is a deliberate choice and the
plugin logs it.
Turning swaps on raises the store’s invoice expiration to 60 minutes if it is shorter. A swap needs the invoice to stay open for at least 45 minutes.
4. Check it
Click Run a health check on the OpenReceive page. It runs every check right there:
- the connection
- the wallet preflight
- payment notifications
- the last wallet scan
- the swap provider and its assets
- the invoice expiration
- swaps that need a human
Each failing check comes with a link to the fix.
The BTCPay plugin reference lists every setting,
Greenfield route, swap state, log event and check. It also lists what the
plugin does not support by design: every send-side feature, top-up invoices,
and a bare nostr+walletconnect:// string in BTCPay’s Lightning node screen.