This is the full file; follow it from Step 0.
OpenReceive agent directions (WordPress + WooCommerce)
# READ THIS FIRST: this file is 22 KB and a summary drops required steps. Download it whole:
curl -fsSL https://openreceive.org/agent-directions/woocommerce/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):
- Before installing or editing anything, ask the user for a receive-only NWC code. One question per message.
- Next ask for the LSC code. If the user wants stablecoins, USDT, USDC, ETH, SOL or crypto, do not ask yes/no first.
- The user only pastes codes into this chat. Never ask them to run a command, edit a file or use an admin page.
- Store each code yourself as Step 2 says. Never put a code in a shell command.
- Do not suggest rotating or revoking a code because it was pasted here.
- Setup is done only when the NWC code is saved, and the LSC code is saved or the user said “Bitcoin only”.
These directions describe OpenReceive 0.4.19.
Install and configure the OpenReceive payment gateway in the WooCommerce store you are working in. Preserve its theme, checkout, customer accounts, order model and prices. The plugin bundles the PHP engine and checkout assets: do not install npm or Composer packages on the WordPress server, and do not clone the OpenReceive repository unless the release download in Step 1 fails.
The one required credential is a receive-only NWC code (Nostr Wallet Connect): a string from the merchant’s wallet that can create invoices and read their status, and cannot spend. A swap provider (an “LSC” code) optionally lets customers pay with USDT, USDC, ETH or SOL instead; the provider converts the payment to BTC over Lightning in the merchant’s connected wallet. Available assets and networks depend on the provider.
Run every wp command below where this store’s WP-CLI runs. When WordPress
runs in Docker Compose, prefix it with the service that has WP-CLI, for
example docker compose run --rm -T cli wp … or
docker compose exec -T wordpress wp …. -T passes stdin through. If the
store has no WP-CLI at all (managed hosting without a shell), say so and walk
the user through the quickstart’s admin screens instead.
Step 0 — ask for the two codes, one question at a time
Before anything else, check one thing: whether OpenReceive is already
installed (wp plugin is-active openreceive). If it is, run
wp openreceive doctor. It prints NWC_URI: set or unset (likewise
LSC_URI_PRIMARY), never the values. A code that is already set is not asked
for again; if both are set, skip to wp openreceive configure --enable at
the end of Step 2.
Otherwise your next action is a question to the user. Do not install the plugin, edit Docker files or search anywhere else before asking it. PHP extensions, Docker images and the database wait until both codes are in this chat, or the user said “Bitcoin only”; Step 1 covers them. Do not read wp-config.php, deploy config, container environments or other projects looking for a code: a new store has neither code yet.
The user never runs a command and never edits a file. They paste each code into this chat; you store it. That is the supported path: do not ask them to run the save command themselves, and do not tell them to revoke or replace a code because it was pasted here. Ask one question per message.
-
First message — the NWC code, and nothing else.
To receive payments I need a receive-only wallet code. In Rizful: open the menu, tap NWC, choose Receive-only NWC code, and tap Copy (https://openreceive.org/get_a_nwc_code_to_receive_payments). If you would rather run your own wallet, Alby Hub works too: Connections → Add Connection → Read Only. Paste the code here and I will store it.
- When they paste it. If it does not start with
nostr+walletconnect://, ask them to copy the receive-only code again. Otherwise do not repeat it: reply only that you have it, then ask the next question. You store it in Step 2. -
Second message — swaps. If the user asked for stablecoins, USDT, USDC, ETH, SOL, altcoins or “crypto” (as in “Bitcoin and stablecoin payments”), this message IS the walkthrough below: do not skip it, and do not ask yes or no first. Otherwise ask whether customers should also be able to pay with USDT, USDC, ETH or SOL, then give the walkthrough. The walkthrough:
Go to https://lightning-swap.com, sign in for API keys, create a key, and copy the whole URI (https://openreceive.org/set_up_swap_provider). Paste it here and I will store it — or say “Bitcoin only” and I will continue without it.
Mention FixedFloat only if they already use it.
- When they paste it. If it does not start with
lightning+swapconnect://, ask them to copy it again. Swaps are now on, so keep the route back (the swap non-negotiable below).
Do not report setup as complete until the NWC code is saved, and the LSC code is saved or the user said “Bitcoin only”. Never invent a placeholder code.
Step 1 — install the plugin
Install the plugin built for this release. Never install the GitHub source-code ZIP or a ZIP from an older release:
wp plugin install https://github.com/OpenReceive/openreceive/releases/download/v0.4.19/openreceive-wordpress-0.4.19.zip --activate
It needs WooCommerce active, and PHP 8.2+ with GMP and sodium in BOTH the web
PHP and the WP-CLI PHP. On the official wordpress and wordpress:cli Docker
images, activation fails with “OpenReceive requires the PHP sodium and GMP
extensions”: add GMP to both images as “Enable GMP in both PHP runtimes” below
says, rebuild both, then install again. If the Compose file has only image:
lines, use the two Dockerfiles and build: keys under “Compose files with only
image: lines” below, and add no other service. If the URL answers 404, build the same
tag as “Get the installable archive” below says.
Step 2 — store the codes, then enable the gateway
Store each code yourself, one per command, and never as a shell argument:
- Write the code with your file-editing tool, not a shell command (no
echo,printfor heredoc), to a new file outside the repository, such as/tmp/openreceive-code. - Run
wp openreceive configure --nwc-uri=- < /tmp/openreceive-code. In Docker:docker compose run --rm -T cli wp openreceive configure --nwc-uri=- < /tmp/openreceive-code. For the LSC code, use--lsc-uri-primary=-. - Delete the file (
rm /tmp/openreceive-code), whether the command passed or not.
The command runs the receive-only wallet preflight, encrypts the code and
prints only “Settings saved; wallet preflight passed.”; a failure keeps the
previous settings. If it reports spend methods such as pay_invoice, ask the
user for a receive-only code again. Never turn on the spend-capable override.
wp wc payment_gateway and WooCommerce REST writes of these fields are
rejected on purpose; do not use them. A code set as a constant in
wp-config.php wins over the stored one and changes only through the host’s
secret workflow.
Then run wp openreceive configure --enable and wp openreceive doctor.
Doctor names any failed check and exits nonzero; fix it before going on.
Step 3 — mint a test invoice, then stop
Create a pending test order that pays with OpenReceive, then mint its Lightning invoice from the terminal:
wp wc shop_order create --user=<admin user id> --payment_method=openreceive \
--line_items='[{"product_id":<product id>,"quantity":1}]' --porcelain
wp openreceive test-invoice <order id>
test-invoice goes through the same checkout route as the order-pay page. It
prints the amount in sats, the BOLT11 invoice, the order-pay link and the
methods that page offers, each swap asset marked available or followed by the
reason it is not. That reason is the answer; report it. “Below the provider
minimum” or “above the provider maximum” is about this order’s amount, not a
fault: a small test order is often under a swap minimum. Only when the reason
says the provider is unreachable does doctor’s “Swap provider” line have more
detail. test-invoice and doctor are the whole checkout check.
Give the user the order-pay link, which opens the checkout on this same invoice, and the list of methods. Tell them the test order is theirs to delete.
You cannot pay the invoice: the code is receive-only. Do not pay, settle or
mark the order paid, and do not look for a way to (a wallet control port, a
test endpoint, another wallet). If the user wants a real settlement test, they
pay on the order-pay link from their own wallet; afterwards
wp wc shop_order get <order id> --user=<admin user id> --field=status is no
longer pending.
Setup ends here. Once doctor is clean and the user has the link, say that setup is finished, in one message. Do not install mail software, add containers or services, or set up cron. If doctor’s “Reconcile scheduled” check fails, fix that. On a store with little traffic, add one sentence to that message: a system cron for WordPress scheduled work settles orders sooner, and this setup does not add one. State it as a recommendation. Do not offer to set it up or end the message on a question.
Non-negotiables
- Never print, log or commit a code, never put one in a shell argument, and never write one into source files, wp-config.php or browser code. Doctor’s set/unset is all you report.
- Do not suggest rotating, revoking or replacing a code because it was pasted into this chat; that is the supported path.
- Work only in this store. Never read or run anything from another project or
directory on this machine (its
node_modules, tools or source), for any reason. A browser, Playwright, hand-made calls to the checkout’s REST routes and reading the plugin’s source are not part of setup: when doctor ortest-invoicefails, report its output. - Receive-only NWC is required. Never turn on the spend-capable override to get past the preflight.
- The plugin owns only its payment-attempt tables in the WordPress database. WooCommerce owns orders, totals, stock and email. Do not add an external idempotency store, payment database or custom fulfillment code.
- IF SWAPS ARE ON, KEEP THE ROUTE BACK. A deposit that arrives short or late becomes refundable, and the customer claims it later on the same order-pay link (guests return with the order key in it). Keep order-pay links reachable, and keep the plugin installed while swap orders may still need a refund. https://openreceive.org/guides/swap-refunds.md
- A receive-only wallet cannot send merchant refunds. Refund a settled payment manually from the wallet.
- Settlement runs on checkout requests and an every-minute scheduled job. A
system cron for WordPress scheduled work helps a low-traffic store, and
wp openreceive notificationsis an optional long-running worker: recommend them, and set one up only when the user asks for it by name. “Go ahead” is not that request.
Further reading
- WordPress + WooCommerce Quickstart
- Automated Swaps
- Swap Refunds
- Lightning Swap Connect URI
- Security
- Price Feeds
- Payment Safety Upgrade
The quickstart, in full
Inlined verbatim so this file needs no network access. Steps 0–3 above are the setup and this is their reference: where the two differ, the steps win, and its wp-admin screens are only for a store with no WP-CLI. The page it comes from is https://openreceive.org/guides/quickstart-woocommerce.
WordPress + WooCommerce quickstart
The WordPress integration entry point redirects to the WooCommerce integration, which uses this same guide and agent directions. OpenReceive checkout on WordPress requires WooCommerce.
Activate WooCommerce first. Then install the built OpenReceive plugin zip through Plugins → Add New → Upload Plugin. You cannot upload the source directory as-is. It needs a build first. The plugin is not yet submitted to WordPress.org.
Requirements: WordPress 6.6+, WooCommerce 9+, 64-bit PHP 8.2+ with GMP and sodium, and MySQL 8 or MariaDB 10.5+. When you activate the plugin, it creates tables for payment attempts in your existing WordPress database. You do not need a separate database or application.
Get the installable archive
Download openreceive-wordpress-0.4.19.zip from the matching release. Historical releases may lack this asset. If that exact URL returns 404, build the same tag below; never silently install an older ZIP. The GitHub source-code ZIP is not an installable plugin. On a development machine with Node 22+, PHP 8.2+ with GMP/sodium, Composer and WP-CLI:
git clone https://github.com/OpenReceive/openreceive.git
cd openreceive
git checkout v0.4.19
npm ci
npm run build:packages
composer install --working-dir=packages/php/wordpress
npm run release:wordpress:build
Upload the resulting dist/openreceive-wordpress-<version>.zip. The build
needs WP-CLI on PATH. Otherwise, set OPENRECEIVE_WP_CLI to the absolute path
of its phar. Your WordPress server needs neither Node nor Composer. The built
plugin already bundles its dependencies and checkout assets.
Enable GMP in both PHP runtimes
GMP is required by the bundled elliptic-curve dependency. Enable it for both web PHP (Apache/FPM) and the PHP executable running WP-CLI. Installing it in only the WordPress container does not update a separate CLI container.
For Debian-based official PHP/WordPress images, add to each Dockerfile:
USER root
RUN apt-get update && apt-get install -y --no-install-recommends libgmp-dev \
&& docker-php-ext-install gmp \
&& rm -rf /var/lib/apt/lists/*
For Alpine-based PHP/CLI images:
USER root
RUN apk add --no-cache gmp \
&& apk add --no-cache --virtual .gmp-build $PHPIZE_DEPS gmp-dev \
&& docker-php-ext-install gmp \
&& apk del .gmp-build
Restore the base image’s original runtime user after installing extensions.
Rebuild and recreate both containers. On Debian/Ubuntu hosts, install the GMP
package matching the active PHP version (for example php8.2-gmp for PHP 8.2),
then restart that version’s web PHP service. Verify php --ri gmp and
wp openreceive doctor for CLI, and the gateway Doctor panel for web PHP.
On managed WordPress hosting, ask the host to enable GMP and sodium in both
runtimes; if they cannot, this plugin cannot run there.
WordPress hosting requirements lists what common hosts
offer and how to check your site. Do not use Composer’s
--ignore-platform-reqs to bypass the requirements.
Compose files with only image: lines
Many stores run the official images straight from Compose, for example
image: wordpress:php8.2-apache and image: wordpress:cli-php8.2, with no
Dockerfile. Add two Dockerfiles next to compose.yml, keeping the tags your
image: lines had. The web image is Debian and runs as root:
# wordpress.Dockerfile
FROM wordpress:php8.2-apache
RUN apt-get update && apt-get install -y --no-install-recommends libgmp-dev \
&& docker-php-ext-install gmp \
&& rm -rf /var/lib/apt/lists/*
The CLI image is Alpine and runs as www-data:
# wp-cli.Dockerfile
FROM wordpress:cli-php8.2
USER root
RUN apk add --no-cache gmp \
&& apk add --no-cache --virtual .gmp-build $PHPIZE_DEPS gmp-dev \
&& docker-php-ext-install gmp \
&& apk del .gmp-build
USER www-data
In compose.yml, replace each of those two image: lines with a build: key
and leave the rest of both services as they are:
services:
wordpress:
build: { context: ., dockerfile: wordpress.Dockerfile }
cli:
build: { context: ., dockerfile: wp-cli.Dockerfile }
Then run docker compose build wordpress cli and docker compose up -d wordpress.
Use your own service names. Do not add any other service for this.
Configure the wallet
On managed hosting with no shell or WP-CLI, use these admin screens. With WP-CLI, use Configure through WP-CLI below instead.
- Open WooCommerce → Settings → Payments → OpenReceive.
- Enter a receive-only NWC code and save.
- Enable the gateway.
When you save, the plugin checks that the wallet can receive. It refuses to save a wallet that can spend, unless you set the explicit override. The password fields never show saved credentials. The plugin encrypts these values with keys derived from WordPress’s authentication keys. If you change those keys, enter the values again.
For managed deployments, set OPENRECEIVE_NWC_URI in wp-config.php from your
server’s secret environment. It overrides the settings field. To configure swap
providers, you can also set the OPENRECEIVE_LSC_URI_PRIMARY and
OPENRECEIVE_LSC_URI_BACKUP constants. Never put these values in browser code
or logs.
Configure through WP-CLI
wp openreceive configure accepts one credential at a time from stdin. Feed
stdin through your secret manager or an existing protected file, never a code
literal in the command line:
wp openreceive configure --nwc-uri=- < /secure/path/wallet-code
wp openreceive configure --lsc-uri-primary=- < /secure/path/swap-code
wp openreceive configure --enable
wp openreceive doctor
Omit the swap command for Bitcoin-only checkout. --lsc-uri-backup=- adds a
backup. These commands share admin preflight and encrypted storage. Credential
flags accept only -; blank input leaves settings intact. Generic WooCommerce
REST and wp wc payment_gateway credential updates are rejected. doctor
reports the failed check with credentials redacted and exits nonzero on failure.
It also asks each configured swap provider for its asset list, and fails when a
provider does not answer or offers no assets. The default payment title becomes “Bitcoin & stablecoins (OpenReceive)” with swaps;
a customized title is preserved.
To check checkout from the terminal, mint an invoice for an unpaid order whose payment method is OpenReceive:
wp wc shop_order create --user=<admin user id> --payment_method=openreceive \
--line_items='[{"product_id":<product id>,"quantity":1}]' --porcelain
wp openreceive test-invoice <order id>
test-invoice uses the same checkout route as the order-pay page. It prints the
amount in sats, the Lightning invoice and the order-pay link, which opens the
checkout on that invoice. It then lists the methods that page offers: Bitcoin
Lightning, plus each swap asset with its network and whether it is available
for this amount, with the reason when it is not. A small test order is often
below a provider’s minimum; that is the order’s amount, not a fault. Delete the
test order when you are done.
Checkout and settlement
Both WooCommerce checkout blocks and classic checkout send the customer to the
order-pay page. There, the plugin reads the amount from WC_Order and serves
the bundled checkout. It lets the customer in through one of:
- their account
- their checkout session
- an expiring signed cookie, issued after it verifies the order-pay key
Keep that order-pay URL available. Customers use it to return to a pending payment or a swap refund. The plugin checks that each requested payment hash belongs to the order.
The plugin saves each payment attempt before it shows invoice instructions. It
records settlement exactly once, inside the payment’s database transaction.
WooCommerce’s payment_complete then handles order status, stock and emails.
The plugin also keeps a durable marker on the order. If something interrupts the
step between settlement and order completion, later requests and scheduled runs
use that marker to finish it.
While the checkout polls for status, it also asks the PHP engine to check the wallet for payments. The engine’s shared database gate keeps these checks from running too often. Action Scheduler adds a safety net that runs every minute. WP-Cron only runs on page visits, so on a store with little traffic that safety net waits for the next visitor. A system cron that runs WordPress scheduled work settles those orders sooner. It is a recommendation for the store owner, not a setup step.
wp openreceive reconcile runs one settlement pass and exits.
wp openreceive notifications is an optional long-running worker that settles
a payment as soon as the wallet reports it; run it under a process manager only
if you want that. Setup needs neither.
The Doctor panel in the gateway settings reports on the schema, whether credentials are present, whether each swap provider answers, scheduling, and orders that need attention. If the store currency has no usable price feed, the gateway is unavailable.
Refunds and removal
The receive-only wallet cannot send merchant refunds. Send those yourself from your wallet. Payer swap refunds go through the configured provider, on the same authorized order-pay page. If you turn on LSC payments, you commit to keeping that recovery path available. See swap refunds.
Deactivating the plugin keeps payment records. Deleting the plugin drops its two tables only if Remove data on uninstall was enabled. WooCommerce orders are always kept.
Local example
The repository’s examples/wordpress Docker stack builds the plugin. It fills
WooCommerce with products from the shared button catalog. Run
npm run demo wordpress to use a real wallet. For a throwaway shop with a fake
wallet, use the stack’s documented compose.testkit.yml override. No testkit
routes are registered by default.