openreceive 0.4.22 → 0.4.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ab3f684223bbce9a03d09d44e0ec67529cbc2b6616d2b50fb28a42f664e1a9a
4
- data.tar.gz: 067be079ba6ccc64c251183495e7252837f596be0f244e1eceee557b08491f38
3
+ metadata.gz: dd185f08cf0e3d60b0477eb5f109df48b386e58bac6e2dc9e68438d964f5b009
4
+ data.tar.gz: bf33fa7ac49d2baff5c17a39d5f88dd29fe5f0f1a3927368be29b41cb6bb0ab1
5
5
  SHA512:
6
- metadata.gz: 913b9d2b647168a3e383199a15078eaa807496ac18fe941e25d7d1bddc0e114f654aaa9cccecdff347fd9eb578043ac397fe6cca484b0088bb7659ac5583aff0
7
- data.tar.gz: d4a72bb5286fcaaa4c58aeef127c488fcf6921f921ea3676b9115bb3138500954a5c5bcbe5c6b369ed6e5e36f759d985c080db351a83f898edc4e67d7d2a3dd8
6
+ metadata.gz: 52511e9cf27f2cf168a3a8b5c59a0d71fa7b2f8d41e1db67ddda8dda32926b291119b952edc643132368ecec87057b51092d7f2574451c7b71c916ffa0c373cc
7
+ data.tar.gz: 89caad630814757eb881d325cb728173b28ebb8ff246697a5b92c2e7f0dc21dd8392683d99a4add6366ddb67182a571753ebbe8299b551f7a6018a5f6da9b23a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.24 - 2026-10-12
4
+
5
+ Release with the complete 0.4.24 package family. The gem's registry page
6
+ now links openreceive.org's Rails page. The bundled agent skill's
7
+ Lovable directions need `@openreceive/*` 0.4.24, whose Supabase functions
8
+ no longer hang when two requests write one order, and keep the app's own
9
+ order flow. Its FastAPI directions run doctor through `dotenv run` and never
10
+ load `.env` into the shell, and the Node, Next.js, Fastify, FastAPI and Django
11
+ directions link the serverless guide. No Ruby runtime changes from 0.4.23.
12
+
13
+ ## 0.4.23 - 2026-10-11
14
+
15
+ Release with the complete 0.4.23 package family. The bundled agent skill adds
16
+ directions for Lovable's agent. Its BTCPay Server directions ask for one code
17
+ per message and never search the machine for one, its WooCommerce directions
18
+ install the plugin from WordPress.org, and its Laravel directions add GMP to
19
+ the Docker image. No Ruby runtime changes from 0.4.22.
20
+
3
21
  ## 0.4.22 - 2026-10-10
4
22
 
5
23
  Release with the complete 0.4.22 package family. The bundled agent skills'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenReceive
4
- VERSION = "0.4.22"
4
+ VERSION = "0.4.24"
5
5
  end
@@ -38,6 +38,9 @@ connected wallet; asset and network availability depends on the provider.
38
38
  - Django: [references/django.md](references/django.md)
39
39
  - Laravel: [references/laravel.md](references/laravel.md)
40
40
  - WordPress + WooCommerce: [references/woocommerce.md](references/woocommerce.md) — the packaged gateway and merchant settings.
41
+ - Lovable (TanStack Start on Cloudflare Workers, with Supabase):
42
+ [references/lovable.md](references/lovable.md) — no terminal; codes are
43
+ Lovable secrets and payments go through Supabase's HTTPS API.
41
44
  - BTCPay Server: [references/btcpay.md](references/btcpay.md) — a plugin,
42
45
  configured in BTCPay's store UI or Greenfield API; no application code,
43
46
  no npm packages, no gem. The rest of this file is about the library.
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (BTCPay Server)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 12 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 16 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/btcpay/full.md
8
8
  # Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
9
9
  ```
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/btcpay/full.md
17
17
  - Never tick the spend-capable override to make a save succeed.
18
18
  - Do not suggest rotating or revoking a code because it was pasted here.
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Connect a BTCPay Server store to a receive-only NWC wallet with the OpenReceive
23
23
  plugin, and optionally let payers pay BTCPay invoices with USDT, USDC, ETH or
@@ -54,31 +54,84 @@ refund path on the same checkout screen.
54
54
  BTCPay refuses to load it below.
55
55
  2. Check whether the plugin is installed (the Plugins menu — the plug icon in
56
56
  the top-right corner — under Installed Plugins, or the store navigation
57
- shows an "OpenReceive" entry). If not, install it from the BTCPay plugin
58
- directory (the same Plugins menu → Plugin Directory, search "openreceive",
59
- then Install and Restart now), as the quickstart says; do not invent an
60
- installer command.
57
+ shows an "OpenReceive" entry; through Greenfield,
58
+ `GET /api/v1/stores/{storeId}/openreceive/settings` answers 404 until it
59
+ is). If not, install it from the BTCPay plugin directory (the same Plugins
60
+ menu → Plugin Directory, search "openreceive", then Install and Restart
61
+ now), as the quickstart says; do not invent an installer command. BTCPay
62
+ installs plugins only from its UI. If you cannot use it, ask the user to:
63
+ name those clicks, and wait until they say it is installed.
61
64
  3. Check whether the store already has an OpenReceive connection:
62
65
  `GET /api/v1/stores/{storeId}/openreceive/settings` returns
63
- `lightningNodeIsOpenReceive`. If true, the wallet step is done — go to
64
- swaps only if the user wants them.
65
- 4. If no receive-only NWC code is available, stop and tell the user exactly
66
- what to create:
67
-
68
- > OpenReceive cannot mint an invoice without a receive-only NWC code. Get
69
- > one at https://openreceive.org/get_a_nwc_code_to_receive_payments and
70
- > paste it into Store → OpenReceive → Test connection, or hand it to me and
71
- > I will set it through the Greenfield API.
72
-
73
- Never print, log or echo the code; report only whether it is set. Never
74
- paste a bare `nostr+walletconnect://` string into BTCPay's Lightning node
75
- screen — that form is claimed by the Nostr plugin, without the receive-only
76
- guard.
77
- 5. If the user wants altcoin payments, ask for an LSC code from
78
- https://openreceive.org/set_up_swap_provider. Do not wait for it: the
79
- wallet works without it, and swaps switch on later with one settings change.
80
-
81
- Only then start the quickstart.
66
+ `lightningNodeIsOpenReceive` and `lscPrimaryConfigured`. A code that is
67
+ already saved is not asked for again; if both are, skip to Verifying.
68
+
69
+ Otherwise your next action is a question to the user. A store without a
70
+ connection has no code anywhere on this machine. Do not search for one: do not
71
+ read other files, Docker containers, compose files beyond this deployment's,
72
+ or process environments, and never run `printenv`. Hunting for a code burns
73
+ the turn and can leak other secrets into the transcript.
74
+
75
+ The user never edits BTCPay's settings for you. They paste each code
76
+ into the chat; you save it (Step 1). Ask one question per message:
77
+
78
+ 1. **First message — the NWC code, and nothing else.** Ask for it and walk them
79
+ through getting it:
80
+
81
+ > To receive payments I need a receive-only wallet code. In Rizful: open
82
+ > the menu, tap NWC, choose Receive-only NWC code, and tap Copy
83
+ > (https://openreceive.org/get_a_nwc_code_to_receive_payments). If you would
84
+ > rather run your own wallet, Alby Hub works too: Connections → Add
85
+ > Connection → Read Only. Paste the code here and I will save it in
86
+ > BTCPay.
87
+
88
+ If the plugin still needs installing, ask for that in the same message.
89
+ 2. **When they paste it.** If it does not start with `nostr+walletconnect://`,
90
+ ask them to copy the receive-only code again. Otherwise save it (Step 1),
91
+ reply only that it is saved, then ask the next question. Never print, log
92
+ or echo the code; report only whether it is set. Never paste a bare
93
+ `nostr+walletconnect://` string into BTCPay's Lightning node screen — that
94
+ form is claimed by the Nostr plugin, without the receive-only guard.
95
+ 3. **Second message — swaps.** If the user asked for stablecoins, USDT, USDC,
96
+ ETH, SOL, altcoins or "crypto" (as in "Bitcoin and stablecoin payments"),
97
+ this message IS the walkthrough below: send it as it is, and do not ask yes
98
+ or no first. Otherwise ask whether payers should also be able to pay with
99
+ USDT, USDC, ETH or SOL, then give the walkthrough. The walkthrough:
100
+
101
+ > Go to https://lightning-swap.com, sign in for API keys, create a key, and
102
+ > copy the whole URI (https://openreceive.org/set_up_swap_provider). Paste
103
+ > it here and I will save it — or say "Bitcoin only" and I will continue
104
+ > without it.
105
+
106
+ Mention FixedFloat only if they already use it.
107
+ 4. **When they paste it.** If it does not start with
108
+ `lightning+swapconnect://`, ask them to copy it again. Otherwise save it
109
+ (Step 1); saving it turns swaps on.
110
+
111
+ The quickstart below shows the same steps in BTCPay's UI.
112
+
113
+ ## Step 1 — save the codes through the Greenfield API
114
+
115
+ Use a Greenfield API key the user gives you (Account → API Keys) that can
116
+ modify the store's settings. A swap provider on a local network needs a server
117
+ admin's key. Save each code yourself, one request each, and never in a
118
+ command line:
119
+
120
+ 1. Write the request body with your file-editing tool, not a shell command (no
121
+ `echo`, `printf` or heredoc), to a new file outside the deployment
122
+ directory, such as `/tmp/openreceive-settings.json`:
123
+ `{"nwcUri": "<the NWC code>"}`. For the LSC code the body is
124
+ `{"lscPrimary": "<the LSC code>"}`; saving it turns swaps on.
125
+ 2. Send it, with your server's address, the key and the store id:
126
+ `curl -fsS -X PUT -H "Authorization: token $BTCPAY_API_KEY" -H "Content-Type: application/json" --data @/tmp/openreceive-settings.json "$BTCPAY_URL/api/v1/stores/$BTCPAY_STORE_ID/openreceive/settings"`.
127
+ 3. Delete the file (`rm /tmp/openreceive-settings.json`), whether the request
128
+ passed or not.
129
+
130
+ The response never contains a code. `lightningNodeIsOpenReceive: true` means
131
+ the wallet is saved; `swapsEnabled` and `lscPrimaryConfigured` say the same
132
+ for swaps. A refusal answers 422 with a `code` and a `message`: a code that
133
+ can spend is refused on purpose, so ask the user for a receive-only code
134
+ instead of setting the override.
82
135
 
83
136
  ## Non-negotiables
84
137
 
@@ -121,11 +174,16 @@ Only then start the quickstart.
121
174
 
122
175
  Store → OpenReceive → **Run a health check** (the doctor page) runs every probe now: connection, preflight,
123
176
  notifications, last scan, provider reachability, invoice expiration, swaps
124
- needing attention. On a regtest machine, `packages/dotnet/docker/up.sh` then
177
+ needing attention. Without the UI, check through Greenfield instead:
178
+ `GET /api/v1/stores/{storeId}/openreceive/settings` shows the wallet and
179
+ swaps, `POST /api/v1/stores/{storeId}/openreceive/wallet/test` with `{}`
180
+ runs the wallet preflight again, and a test invoice
181
+ (`POST /api/v1/stores/{storeId}/invoices` with an amount) must list a
182
+ `BTC-LN` payment method whose `destination` is a BOLT11. On a regtest machine, `packages/dotnet/docker/up.sh` then
125
183
  `e2e.sh` in the OpenReceive repository proves the whole path end to end, and
126
184
  that is the only situation where cloning the repository is the right move.
127
185
 
128
- Setup ends when the health check is clean. Say "Setup is finished" in one
186
+ Setup ends when the health check, or those Greenfield checks, are clean. Say "Setup is finished" in one
129
187
  message. Do not offer more work or end the message on a question.
130
188
 
131
189
  ## More documentation
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/django/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a Django project — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the Python package is on PyPI
@@ -389,6 +389,7 @@ enough; drop the `.md` for the same page a person would read.
389
389
  - https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
390
390
  - https://openreceive.org/guides/storage.md — the engine tables and the attempt state machine
391
391
  - https://openreceive.org/guides/supabase.md — when the database is Supabase: its pooler, its certificate, and locking the tables away from its Data API
392
+ - https://openreceive.org/guides/serverless.md — when the app runs serverless (Netlify, AWS Lambda, Cloud Run, Google AI Studio): pooled database URLs, timeouts, and why it needs no cron or worker
392
393
  - https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, attributes and slots
393
394
  - https://openreceive.org/guides/checkout-ux.md — read before building any custom UI
394
395
  - https://openreceive.org/guides/headless-checkout.md — the controller, the display models, refunds
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (FastAPI)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 39 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 40 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/fastapi/full.md
8
8
  # Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
9
9
  ```
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/fastapi/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a FastAPI application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the engine is on PyPI
@@ -126,6 +126,11 @@ you store it. Ask one question per message.
126
126
  with `uvicorn --env-file .env` (update the app's run script or Procfile, not
127
127
  just your own command). When it is started with Docker Compose, give the
128
128
  service `env_file: .env`. Restart the server after writing the file.
129
+ Doctor needs the file too: run it as
130
+ `dotenv -f .env run -- openreceive doctor …`. The `dotenv` command comes
131
+ with python-dotenv, which `uvicorn --env-file` already needs; if it is
132
+ missing, `pip install "python-dotenv[cli]"`. Never `source`, `export` or
133
+ `cat` the file: the codes contain `&`, and the shell prints the pieces.
129
134
 
130
135
  Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
131
136
  saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
@@ -133,8 +138,9 @@ runs the receive-only preflight: if it reports spend methods such as
133
138
  `pay_invoice`, remove `NWC_URI` from `.env` and ask for a receive-only code
134
139
  again. Never set the spend-capable override to get past it.
135
140
 
136
- After the router is wired, run `openreceive doctor --app <module>:app`. It
137
- reports `NWC_URI` presence without printing it, probes the wallet, and names
141
+ After the router is wired, run
142
+ `dotenv -f .env run -- openreceive doctor --app <module>:app`. It reports
143
+ `NWC_URI` presence without printing it, probes the wallet, and names
138
144
  any hook still on a placeholder and whether the migration ran. Exit code 1 is a
139
145
  finding to fix, not a reason to skip the check.
140
146
 
@@ -152,7 +158,8 @@ Only then start the quickstart.
152
158
  ## After the quickstart — hand over, then stop
153
159
 
154
160
  The quickstart is done when
155
- `openreceive doctor --app <module>:app --url <app url>` is clean and this app
161
+ `dotenv -f .env run -- openreceive doctor --app <module>:app --url <app url>`
162
+ is clean and this app
156
163
  serves the checkout page for one of its orders. Doctor names any failed check
157
164
  and exits nonzero; fix it before going on.
158
165
 
@@ -383,6 +390,7 @@ enough; drop the `.md` for the same page a person would read.
383
390
  - https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
384
391
  - https://openreceive.org/guides/storage.md — the payment tables and the attempt state machine
385
392
  - https://openreceive.org/guides/supabase.md — when the database is Supabase: its pooler, its certificate, and locking the tables away from its Data API
393
+ - https://openreceive.org/guides/serverless.md — when the app runs serverless (Netlify, AWS Lambda, Cloud Run, Google AI Studio): pooled database URLs, timeouts, and why it needs no cron or worker
386
394
  - https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, attributes and slots
387
395
  - https://openreceive.org/guides/checkout-ux.md — read before building any custom UI
388
396
  - https://openreceive.org/guides/headless-checkout.md — the controller, the display models, refunds
@@ -658,8 +666,13 @@ send it after commit.
658
666
 
659
667
  A page without a bundler renders the same checkout as a custom element:
660
668
  `<openreceive-checkout reference="…" prefix="/openreceive">` from
661
- `@openreceive/elements`. You can also serve its standalone build from your
662
- static directory.
669
+ `@openreceive/elements`. For Jinja templates without a JavaScript build, serve
670
+ its standalone build from your static directory: copy
671
+ `openreceive-checkout.js` and `openreceive-checkout.css` from
672
+ `node_modules/@openreceive/elements/dist/standalone/` to `static/openreceive/`,
673
+ and load both on the checkout page. The `openreceive` package from PyPI carries
674
+ the same two files in `openreceive/django/static/openreceive/`, so you can copy
675
+ them from there without npm.
663
676
 
664
677
  Buy a Button is a runnable illustration of the boundary
665
678
  (`examples/buttons/server/fastapi`).
@@ -671,9 +684,16 @@ shape onto the models in THIS app.
671
684
  ### 6. Verify
672
685
 
673
686
  ```sh
674
- openreceive doctor --app main:app
687
+ dotenv -f .env run -- openreceive doctor --app main:app
675
688
  ```
676
689
 
690
+ Doctor reads the process environment. `dotenv -f .env run --` loads `.env`
691
+ into it, as `uvicorn --env-file .env` does for the server. The `dotenv`
692
+ command comes with python-dotenv, which `uvicorn --env-file` needs anyway; if
693
+ it is missing, run `pip install "python-dotenv[cli]"`. Never `source` or
694
+ `export` `.env` in a shell: the codes contain `&`, and the shell splits the
695
+ line and prints the pieces.
696
+
677
697
  `openreceive doctor` checks Python, `NWC_URI`, and swap-provider
678
698
  configuration. It also probes the wallet relay to confirm the code is
679
699
  receive-only. With `--app module:attr` (your FastAPI app, the router, or an
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/fastify/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a Fastify application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the packages are on npm, and
@@ -372,6 +372,7 @@ enough; drop the `.md` for the same page a person would read.
372
372
  - https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
373
373
  - https://openreceive.org/guides/storage.md — the payment tables and the attempt state machine
374
374
  - https://openreceive.org/guides/supabase.md — when the database is Supabase: its pooler, its certificate, and locking the tables away from its Data API
375
+ - https://openreceive.org/guides/serverless.md — when the app runs serverless (Netlify, AWS Lambda, Cloud Run, Google AI Studio): pooled database URLs, timeouts, and why it needs no cron or worker
375
376
  - https://openreceive.org/guides/node-orms.md — recipes for Prisma, Drizzle, Knex, TypeORM, Sequelize
376
377
  - https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, attributes and slots
377
378
  - https://openreceive.org/guides/checkout-ux.md — read before building any custom UI
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (Laravel)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 44 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 45 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/laravel/full.md
8
8
  # Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
9
9
  ```
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/laravel/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a Laravel application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the package is on Packagist
@@ -417,8 +417,24 @@ PHP extensions:
417
417
  `pdo_sqlite`).
418
418
 
419
419
  `php -m` lists what your build has. To add gmp on Debian/Ubuntu, run
420
- `apt-get install php8.2-gmp`. In the official Docker image, run
421
- `docker-php-ext-install gmp`.
420
+ `apt-get install php8.2-gmp`. The official `php` Docker images do not ship
421
+ it, and `docker-php-ext-install gmp` alone fails without the GMP headers. Add
422
+ both to the Dockerfile, before the step that installs the app's Composer
423
+ packages, and rebuild the image:
424
+
425
+ ```dockerfile
426
+ RUN apt-get update && apt-get install -y --no-install-recommends libgmp-dev \
427
+ && docker-php-ext-install gmp \
428
+ && rm -rf /var/lib/apt/lists/*
429
+ ```
430
+
431
+ Run `composer require` with a PHP that has gmp. On a machine without one,
432
+ use the rebuilt image with the project mounted, then rebuild again so the
433
+ image installs the new lock:
434
+ `docker compose run --rm --no-deps -v "$PWD":/app web composer require openreceive/laravel -W`.
435
+ Never pass `--ignore-platform-req=ext-gmp` or `--ignore-platform-reqs`:
436
+ Composer then installs a package that fails as soon as it talks to the
437
+ wallet.
422
438
 
423
439
  Add the Laravel package:
424
440
 
@@ -0,0 +1,534 @@
1
+ This is the full file; follow it from Step 0.
2
+
3
+ # OpenReceive agent directions (Lovable)
4
+
5
+ ```text
6
+ READ THIS FIRST: this file is 26 KB and a summary drops required steps. Read all of it:
7
+ https://openreceive.org/agent-directions/lovable/full.md
8
+ If your fetch tool gave you a summary, fetch that URL again for the full text.
9
+ ```
10
+
11
+ **Step 0 in brief** (Step 0 below has the details):
12
+
13
+ - Before adding packages or editing anything, ask the user for a receive-only NWC code. One question per message.
14
+ - Next ask for the LSC code. If the user wants stablecoins, USDT, USDC, ETH, SOL or crypto, do not ask yes/no first.
15
+ - Ask for NWC_URI and LSC_URI_PRIMARY with Lovable's secret input. Never put a code in .env, code or a VITE_ name.
16
+ - You have no terminal: never ask the user to run a command. Every step is a file, a package, a migration or a secret.
17
+ - Do not suggest rotating or revoking a code because it was pasted here.
18
+ - Start Step 1 only once NWC_URI is saved, and LSC_URI_PRIMARY is saved or the user said "Bitcoin only".
19
+
20
+ These directions describe OpenReceive 0.4.24.
21
+
22
+ Add OpenReceive to this Lovable app: a TanStack Start app on Cloudflare
23
+ Workers, with Lovable Cloud or a connected Supabase project as its database.
24
+ These directions are written for Lovable's agent. You have no terminal, so
25
+ every step is a file edit, a package, a Supabase migration or a secret. Never
26
+ ask the user to run a command. The TanStack Start recipe is appended to this
27
+ file in full; its "On Supabase (Lovable)" section is the code to follow.
28
+
29
+ Do not clone the OpenReceive repository, and do not copy a demo's models over
30
+ tables that already exist. Find this app's orders and products, whatever they
31
+ are named, and map the hooks onto those. Keep this app's UI, sign-in and
32
+ database.
33
+
34
+ ## What OpenReceive is
35
+
36
+ A payment library that runs inside THIS app's server code. It mounts as one
37
+ TanStack Start server route (`src/routes/openreceive.$.ts`), issues Lightning
38
+ invoices against a wallet the merchant already controls, and settles each
39
+ payment into this app's Supabase database. There is no OpenReceive account
40
+ and no API key, and OpenReceive never holds the funds: the sats land in the
41
+ merchant's wallet.
42
+
43
+ The one required credential is a receive-only NWC code (Nostr Wallet
44
+ Connect): a string from the merchant's wallet that can create invoices and
45
+ read their status, and cannot spend. A swap provider (an "LSC" code)
46
+ optionally lets the payer send USDT, USDC, ETH or SOL instead, converted into
47
+ that same Lightning payment. Step 0 collects those credentials. You write two
48
+ hooks in TypeScript, `amountFor` and `authorize`, and one SQL function,
49
+ `openreceive_on_paid`, that marks the order paid. OpenReceive supplies
50
+ invoices, polling, settlement and the checkout UI. It never owns orders,
51
+ users, prices, or fulfillment.
52
+
53
+ On Lovable, OpenReceive keeps its payment rows in this app's Supabase
54
+ database and reaches it over Supabase's HTTPS API with the project's secret
55
+ key, because a Worker cannot open a Postgres connection to Supabase.
56
+
57
+ ## Step 0 — ask for the two codes, one secret at a time
58
+
59
+ If the user says the codes are already saved as this project's secrets
60
+ (`NWC_URI`, and `LSC_URI_PRIMARY` unless they want Bitcoin only), believe
61
+ them: do not ask for them, and go to Step 1. You cannot read a secret's value,
62
+ and you do not need to.
63
+
64
+ Otherwise, before anything else, your next action is a question to the user.
65
+ Do not add packages, write migrations or edit files before asking it. A new
66
+ shop has neither code yet.
67
+
68
+ Two server-only secrets are needed before the integration:
69
+
70
+ - `NWC_URI` — a receive-only Nostr Wallet Connect code,
71
+ `nostr+walletconnect://…`. Required for Bitcoin.
72
+ - `LSC_URI_PRIMARY` — a Lightning Swap Connect URI,
73
+ `lightning+swapconnect://…`. Required for USDT, USDC, ETH and SOL. Skip it
74
+ only when the user says they want Bitcoin alone.
75
+
76
+ Ask for each one with Lovable's secure secret input, named exactly as above.
77
+ The user never edits a file: they paste each code into this chat or into
78
+ that secret input, and Lovable stores it as a project secret. A code pasted
79
+ into the chat is saved as a secret too. Never repeat a code back. Ask one
80
+ question per message.
81
+
82
+ 1. **First message — the NWC code, and nothing else.** Ask for the secret
83
+ `NWC_URI` and walk them through getting it:
84
+
85
+ > To receive payments I need a receive-only wallet code. In Rizful: open
86
+ > the menu, tap NWC, choose Receive-only NWC code, and tap Copy
87
+ > (https://openreceive.org/get_a_nwc_code_to_receive_payments). If you would
88
+ > rather run your own wallet, Alby Hub works too: Connections → Add
89
+ > Connection → Read Only. Paste it into the secret input for NWC_URI.
90
+
91
+ 2. **When it is saved.** If the user pasted it into the chat and it does not
92
+ start with `nostr+walletconnect://`, ask them to copy the receive-only code
93
+ again. Reply only that it is saved, then ask the next question.
94
+ 3. **Second message — swaps.** If the user asked for stablecoins, USDT, USDC,
95
+ ETH, SOL, altcoins or "crypto" (as in "Bitcoin and stablecoin payments"),
96
+ this message IS the walkthrough below: send it as it is, and do not ask yes
97
+ or no first. Otherwise ask whether payers should also be able to pay with
98
+ USDT, USDC, ETH or SOL, then give the walkthrough. The walkthrough:
99
+
100
+ > Go to https://lightning-swap.com, sign in for API keys, create a key, and
101
+ > copy the whole URI (https://openreceive.org/set_up_swap_provider). Paste
102
+ > it into the secret input for LSC_URI_PRIMARY — or say "Bitcoin only" and
103
+ > I will continue without it.
104
+
105
+ Mention FixedFloat only if they already use it. Swaps on means the refund
106
+ route back is part of this integration (the swap non-negotiable below).
107
+ 4. **Supabase.** Lovable sets `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY`
108
+ for server code when Lovable Cloud, or a connected Supabase project, is on.
109
+ Do not ask for them or try to create them: Lovable reserves the
110
+ `SUPABASE_` prefix. If this project has no backend yet, ask the user to
111
+ turn on Lovable Cloud, and wait until they have.
112
+
113
+ Never give a code a `VITE_` prefix, never put one in `.env` (Lovable commits
114
+ that file), and never write one into code or a log. Do not invent placeholder
115
+ codes. Start Step 1 only once `NWC_URI` is saved and `LSC_URI_PRIMARY` is
116
+ saved or explicitly declined.
117
+
118
+ ## Step 1 — the packages
119
+
120
+ Add `@openreceive/http` and `@openreceive/react` at 0.4.24 or newer.
121
+ Nothing else: no `pg`, no Postgres driver, no OpenReceive scaffold. Do not
122
+ change the Vite or Wrangler configuration.
123
+
124
+ ## Step 2 — the migration
125
+
126
+ Fetch https://openreceive.org/guides/supabase-migration.md and copy its SQL
127
+ block, unchanged, into one new Supabase migration, then apply it. Do not edit
128
+ it, split it, rename anything in it, or drop its `revoke` lines. It creates
129
+ `openreceive_payments` and `openreceive_meta` with row level security on and
130
+ every grant revoked from `anon` and `authenticated`, the functions the server
131
+ calls to write them, and `openreceive_on_paid` as a placeholder that refuses
132
+ every settlement.
133
+
134
+ ## Step 3 — the order and its buyer
135
+
136
+ - **The order's id is the `reference`.** Create the order before checkout,
137
+ keep it across retries, never reuse it. A fresh id per page load lets one
138
+ order be paid twice.
139
+ - **The order row is the price.** Order creation copies live prices into the
140
+ order. `amountFor` reads only that order and returns
141
+ `{ currency, value, description }`, the value a decimal string such as
142
+ `"12.50"`, and `null` for an order that is not payable.
143
+ - **The buyer is a cookie.** The checkout calls the payment routes from the
144
+ browser with the page's cookies and nothing else, and a Lovable app keeps
145
+ its Supabase sign-in in the browser, not in a cookie. So in a migration add
146
+ `buyer_token text` to the orders table, and create orders in a server
147
+ function (`createServerFn`) that reads the `buyer` cookie, or sets one to
148
+ `crypto.randomUUID()` (HttpOnly, Secure, SameSite=Lax, path `/`, one
149
+ year) with `getCookie` and `setCookie` from `@tanstack/react-start/server`,
150
+ then inserts the order with `buyer_token` set to it through `supabaseAdmin`
151
+ from `@/integrations/supabase/client.server`, and returns the id. Keep the
152
+ app's order flow as it is: where it creates orders on the server today (a
153
+ server route a form posts to, or another server function), call this
154
+ function there, and keep those routes, forms and redirects working. If the
155
+ app creates orders in the browser today, move that insert into this
156
+ function. Keep the order's user column too, if the app has sign-in.
157
+ - **The order is unpaid or paid.** Do not copy attempt statuses (`pending`,
158
+ `expired`, `failed`, `attention`) onto it, and do not add a relation to
159
+ `openreceive_payments`.
160
+
161
+ ## Step 4 — `openreceive_on_paid`
162
+
163
+ In a second new migration, replace the placeholder with the SQL that marks
164
+ this app's order paid. Use `create or replace`, and this app's own table,
165
+ column and status names:
166
+
167
+ ```sql
168
+ create or replace function public.openreceive_on_paid(
169
+ p_reference text, p_payment_hash text, p_paid_at bigint
170
+ ) returns void language plpgsql as $$
171
+ begin
172
+ update public.orders
173
+ set status = 'paid'
174
+ where id = p_reference::uuid -- or just p_reference, if the ids are text
175
+ and status = 'pending';
176
+ end
177
+ $$;
178
+ ```
179
+
180
+ It runs inside the transaction that records the payment, for the order's
181
+ first settled payment only. If it raises, nothing is recorded, and the next
182
+ request retries it. Its `WHERE` on the unpaid status is the guard against a
183
+ second fulfillment. Keep it to database writes.
184
+
185
+ ## Step 5 — the payment route
186
+
187
+ Write `src/routes/openreceive.$.ts` and `src/lib/openreceive.server.ts`
188
+ exactly as the recipe's "On Supabase (Lovable)" section shows:
189
+
190
+ - `storage: { supabase: { url: process.env.SUPABASE_URL, key: process.env.SUPABASE_SERVICE_ROLE_KEY } }`,
191
+ read inside the handler, never at module scope.
192
+ - No `onPaid` and no `db`: `openreceive_on_paid` is the fulfillment.
193
+ - `findOrder` reads the order with `supabaseAdmin`; `authorize` compares the
194
+ request's `buyer` cookie with the order's `buyer_token`.
195
+ - Build the stack inside each request and close it in `finally`.
196
+ - Import the server module only inside the route's handlers, so it never
197
+ reaches the browser bundle.
198
+
199
+ ## Step 6 — the checkout page
200
+
201
+ Add `src/routes/checkout.$reference.tsx` with `<Checkout reference={reference}
202
+ prefix="/openreceive" />` from `@openreceive/react`, and import
203
+ `@openreceive/react/styles.css` there. After the order is created, navigate
204
+ to `/checkout/<id>`. Use the drop-in; do not build your own checkout UI.
205
+
206
+ ## After setup — hand over, then stop
207
+
208
+ You cannot pay the invoice: the code is receive-only. Do not pay, settle or
209
+ mark an order paid, and do not look for a way to.
210
+
211
+ If the checkout answers 503, the server log names the fix: usually the
212
+ migration is not applied, or `openreceive_on_paid` is still the placeholder.
213
+
214
+ Setup ends here. Your last message starts "Setup is finished" and has at most
215
+ five short lines: where to place an order in the preview, the methods the
216
+ checkout offers, and that a small real payment from their own wallet marks
217
+ the order paid. Send nothing after it. Do not list what changed, offer more
218
+ work, or end the message on a question. Name the methods: Bitcoin, plus
219
+ USDT, USDC, ETH and SOL when `LSC_URI_PRIMARY` is saved. Do not mention
220
+ minimums, and never say a coin will not work or will not be offered.
221
+
222
+ ## Non-negotiables
223
+
224
+ The recipe below has the code. These are the rules it cannot state for
225
+ itself.
226
+
227
+ - OpenReceive never owns orders, users, prices, or fulfillment.
228
+ - Keep `NWC_URI` / `LSC_URI_*` server-only: secrets read with `process.env`
229
+ in `.server.ts` modules. Never a `VITE_` variable, `.env`, browser code or
230
+ a log.
231
+ - Do not suggest rotating, revoking or replacing a code because it was pasted
232
+ into this chat; that is the supported path.
233
+ - `SUPABASE_SERVICE_ROLE_KEY` reads and writes every table. Read it only in
234
+ server modules.
235
+ - Never grant `anon` or `authenticated` anything on an `openreceive_*` table
236
+ or function, and never add a row level security policy to those tables.
237
+ The server refuses to serve while they can reach them.
238
+ - The host owns the price. `amountFor` reads it from the order; reject
239
+ payer-supplied amounts.
240
+ - `authorize` runs on every request, and the `resource` it receives is a CLAIM
241
+ the payer made, not proof. Check the `buyer` cookie against the order.
242
+ - Receive-only NWC is required; a spend-capable code fails closed unless
243
+ explicitly overridden. Never set that override.
244
+ - There is NO merchant-initiated refund of a settled Lightning payment, because
245
+ the wallet cannot spend. Swap refunds — a payer reclaiming a deposit that
246
+ never converted — are the only refund OpenReceive performs. Do not build,
247
+ promise, or imply a Lightning refund path.
248
+ - IF YOU TURN SWAPS ON, BUILD THE ROUTE BACK. A deposit that arrives short or
249
+ late is refunded on a SECOND visit, so the order needs its own URL
250
+ (`/checkout/$reference`, with `syncUrl` on `<Checkout>`), and the attempt
251
+ must come back with it: keep the `payment_hash` from `onState` and pass it
252
+ as `resumePaymentHash`. https://openreceive.org/guides/swap-refunds.md
253
+ - Show the payer what they are buying: return a `description` beside the
254
+ price from `amountFor`.
255
+ - HTTP JSON is snake_case; TypeScript APIs are camelCase.
256
+ - Money is integers or decimal strings — never binary floats.
257
+
258
+ ## More documentation
259
+
260
+ Fetch one when the moment comes. Each is raw markdown, so a plain GET is
261
+ enough.
262
+
263
+ - https://openreceive.org/guides/lovable.md — what the user does on Lovable around these steps
264
+ - https://openreceive.org/guides/supabase.md — Supabase over HTTPS: how the storage works, and what the server checks before it serves
265
+ - https://openreceive.org/guides/supabase-migration.md — the migration for Step 2
266
+ - https://openreceive.org/guides/authorization.md — before you write `authorize`
267
+ - https://openreceive.org/guides/storage.md — the payment tables and the attempt state machine
268
+ - https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, including `syncUrl` and `resumePaymentHash`
269
+ - https://openreceive.org/guides/checkout-ux.md — the rules the drop-in already follows
270
+ - https://openreceive.org/guides/provider-registry.md — where the wallet logos and pay tutorials come from: inside the JavaScript, nothing to serve
271
+ - https://openreceive.org/guides/automated-swaps.md — only if `LSC_URI_PRIMARY` is set
272
+ - https://openreceive.org/guides/swap-refunds.md — the refund flow, and the route back to it
273
+ - https://openreceive.org/guides/lightning-swap-connect.md — what an `LSC_URI_*` code actually is
274
+ - https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
275
+ - https://openreceive.org/guides/price-feeds.md — where the fiat→sats rate comes from
276
+ - https://openreceive.org/guides/rate-limiting.md — before a public shop goes live
277
+ - https://openreceive.org/guides/security.md and https://openreceive.org/guides/deploying.md — before this goes anywhere real
278
+ - https://openreceive.org/guides/api-reference.md — every route, option and error code
279
+ - https://openreceive.org/guides.md — the index, if what you need is not above
280
+
281
+ Questions, or a problem with the library itself:
282
+ https://openreceive.org/contact
283
+
284
+ ---
285
+
286
+ ## The quickstart, in full
287
+
288
+ Inlined verbatim so this file needs no network access. Steps 0–6 above are the setup on Lovable and this recipe is their reference: follow its On Supabase (Lovable) section, not its pg pool, and skip its npm commands. Where the two differ, the steps win.
289
+ The page it comes from is https://openreceive.org/guides/tanstack-start-recipe.
290
+
291
+ ## TanStack Start recipe
292
+
293
+ OpenReceive ships no TanStack Start package. A TanStack Start server route
294
+ takes a Web-standard `Request` and returns a `Response`, and so does
295
+ `@openreceive/http`, so the integration is the route below. Your server code
296
+ creates each Lightning invoice, the payment goes straight to your wallet, and
297
+ payment attempts are stored in your app's Postgres database. There is no
298
+ OpenReceive account and no background worker.
299
+
300
+ Optional swaps let customers pay with **USDT, USDC, SOL, and ETH**. A swap
301
+ provider you configure converts the payment to **BTC over Lightning**, and it
302
+ settles into the same wallet. Available assets and networks depend on the
303
+ provider.
304
+
305
+ This recipe was checked with `@tanstack/react-start` 1.168 and
306
+ `@openreceive/*` 0.4.19. The app was built with Lovable's Vite config for
307
+ Cloudflare Workers and run under workerd: a buyer's checkout showed a real
308
+ Lightning invoice, and a stranger was refused. The Supabase route below runs
309
+ in OpenReceive's CI as a Worker, against Supabase's own Postgres image and
310
+ API server: a buyer's invoice, a stranger refused, one live attempt under
311
+ concurrent requests, and a payment that `openreceive_on_paid` fulfills once.
312
+
313
+ ```sh
314
+ npm install @openreceive/http @openreceive/react pg
315
+ ```
316
+
317
+ ### Where it runs
318
+
319
+ - **Node** (the `node-server` preset, or any Node host): any Postgres.
320
+ - **Cloudflare Workers** (the `cloudflare-module` preset, Lovable's default):
321
+ with Node compatibility, and a Postgres whose certificate a public
322
+ authority signed, such as Neon.
323
+ - **Cloudflare Workers with Supabase**, which is every Lovable app: a Worker
324
+ cannot open a Postgres connection to Supabase, whose certificate comes from
325
+ a private authority. Use [On Supabase](#on-supabase-lovable) below, which
326
+ stores payments through Supabase's HTTPS API instead.
327
+
328
+ Workers ties every socket to the request that opened it, and forbids network
329
+ calls at import. So the route builds its database pool and its OpenReceive
330
+ stack inside each request, and closes both before it responds. Each request
331
+ then opens its own wallet connection; in our test a checkout request took one
332
+ to four seconds. The same code works on Node.
333
+
334
+ ### The payment route
335
+
336
+ Keep the handler in a `.server.ts` module. Route files are also bundled for
337
+ the browser, so the route loads the module only when a request arrives:
338
+
339
+ ```ts
340
+ // src/routes/openreceive.$.ts — every route under /openreceive/
341
+ import { createFileRoute } from "@tanstack/react-router";
342
+
343
+ async function openReceive({ request }: { request: Request }): Promise<Response> {
344
+ const { handleOpenReceive } = await import("@/lib/openreceive.server");
345
+ return handleOpenReceive(request);
346
+ }
347
+
348
+ export const Route = createFileRoute("/openreceive/$")({
349
+ server: { handlers: { GET: openReceive, POST: openReceive } },
350
+ });
351
+ ```
352
+
353
+ ```ts
354
+ // src/lib/openreceive.server.ts
355
+ import { createStack } from "@openreceive/http";
356
+ import pg from "pg";
357
+ import { findOrder, currentVisitor } from "./orders.server"; // your own orders
358
+
359
+ export async function handleOpenReceive(request: Request): Promise<Response> {
360
+ // Read process.env inside the handler: on Workers it is empty at import.
361
+ const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 1 });
362
+ const stack = createStack({
363
+ wallet: { nwc: process.env.NWC_URI ?? "" },
364
+ storage: {
365
+ db: pool,
366
+ // Runs once, inside the transaction that records the payment.
367
+ onPaid: async ({ reference, paidAt, query }) => {
368
+ await query(
369
+ "UPDATE orders SET state = 'paid', paid_at = $1 WHERE id = $2 AND state = 'awaiting_payment'",
370
+ [paidAt, reference],
371
+ );
372
+ },
373
+ },
374
+ // The price comes from the order row, never from the request.
375
+ amountFor: async (reference) => {
376
+ const order = await findOrder(pool, reference);
377
+ return order?.state === "awaiting_payment"
378
+ ? { currency: order.currency, value: order.amount, description: order.title }
379
+ : null;
380
+ },
381
+ // Only the buyer may pay for their order.
382
+ authorize: async ({ request: incoming, resource }) => {
383
+ const order = resource.reference ? await findOrder(pool, resource.reference) : undefined;
384
+ const visitor = currentVisitor(incoming);
385
+ return Boolean(order && visitor && order.visitor === visitor);
386
+ },
387
+ // Cloudflare sets cf-connecting-ip on every request, and a client cannot.
388
+ // On Node behind your own proxy, read the header that proxy sets.
389
+ rateLimiting: {
390
+ ip: ({ request: incoming }) => incoming.headers.get("cf-connecting-ip") ?? undefined,
391
+ },
392
+ });
393
+ try {
394
+ return await stack.handler(request, { native: request });
395
+ } finally {
396
+ await stack.close();
397
+ await pool.end();
398
+ }
399
+ }
400
+ ```
401
+
402
+ `authorize` sees the incoming request with its cookies, so check it with
403
+ the session your app already has. The Next.js quickstart
404
+ explains each hook. They are the same here.
405
+
406
+ Create OpenReceive's two tables with your migrations, or run
407
+ `paymentsSchemaSql("postgres")` from `@openreceive/http` once. It is
408
+ idempotent. See Payment storage.
409
+
410
+ ### On Supabase (Lovable)
411
+
412
+ On Supabase the route keeps payments in your Supabase database through its
413
+ HTTPS API, with the project's secret key. There is no `pg` pool. Use
414
+ `@openreceive/*` 0.4.24 or newer:
415
+
416
+ ```sh
417
+ npm install @openreceive/http @openreceive/react
418
+ ```
419
+
420
+ 1. Apply the migration from `npx openreceive scaffold payments --supabase`.
421
+ It creates OpenReceive's tables, locked away from the browser's Supabase
422
+ key, and the functions that write them.
423
+ 2. Replace its placeholder `openreceive_on_paid` with the SQL that marks your
424
+ order paid. It runs inside the transaction that records the payment, for
425
+ the order's first payment only, so a failure there records nothing:
426
+
427
+ ```sql
428
+ create or replace function public.openreceive_on_paid(
429
+ p_reference text, p_payment_hash text, p_paid_at bigint
430
+ ) returns void language plpgsql as $$
431
+ begin
432
+ update public.orders set status = 'paid'
433
+ where id = p_reference::uuid and status = 'pending';
434
+ end
435
+ $$;
436
+ ```
437
+
438
+ 3. Write the route's server module:
439
+
440
+ ```ts
441
+ // src/lib/openreceive.server.ts
442
+ import { createStack } from "@openreceive/http";
443
+ import { findOrder, currentBuyer } from "./orders.server"; // your own orders
444
+
445
+ export async function handleOpenReceive(request: Request): Promise<Response> {
446
+ // Read process.env inside the handler: on Workers it is empty at import.
447
+ // Lovable sets SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY for server code.
448
+ const stack = createStack({
449
+ wallet: { nwc: process.env.NWC_URI ?? "" },
450
+ storage: {
451
+ supabase: {
452
+ url: process.env.SUPABASE_URL ?? "",
453
+ key: process.env.SUPABASE_SERVICE_ROLE_KEY ?? "",
454
+ },
455
+ },
456
+ // The price comes from the order row, never from the request.
457
+ amountFor: async (reference) => {
458
+ const order = await findOrder(reference);
459
+ return order?.status === "pending"
460
+ ? { currency: order.currency, value: order.total, description: order.title }
461
+ : null;
462
+ },
463
+ // Only the buyer may pay for their order.
464
+ authorize: async ({ request: incoming, resource }) => {
465
+ const order = resource.reference ? await findOrder(resource.reference) : null;
466
+ const buyer = currentBuyer(incoming);
467
+ return Boolean(order && buyer && order.buyer_token === buyer);
468
+ },
469
+ rateLimiting: {
470
+ ip: ({ request: incoming }) => incoming.headers.get("cf-connecting-ip") ?? undefined,
471
+ },
472
+ });
473
+ try {
474
+ return await stack.handler(request, { native: request });
475
+ } finally {
476
+ await stack.close();
477
+ }
478
+ }
479
+ ```
480
+
481
+ There is no `onPaid`: `openreceive_on_paid` is the fulfillment. `findOrder`
482
+ reads the order with your server-side Supabase client; in a Lovable app that
483
+ is `supabaseAdmin` from `@/integrations/supabase/client.server`.
484
+
485
+ The checkout calls the payment routes from the browser with the page's
486
+ cookies, and nothing else. A Lovable app keeps its Supabase sign-in in the
487
+ browser, not in a cookie, so `authorize` cannot see who is signed in. Give
488
+ the buyer a cookie of their own instead: create orders in a server function
489
+ that sets an HttpOnly `buyer` cookie (a random value, kept across orders) and
490
+ stores the same value on the order as `buyer_token`. `currentBuyer` reads it
491
+ back from the request's `cookie` header.
492
+
493
+ Before it serves, the server checks the database, and the payment routes
494
+ answer 503 until the migration is applied and `openreceive_on_paid` is
495
+ yours; the log names the fix. More: Supabase over HTTPS.
496
+
497
+ ### The checkout page
498
+
499
+ ```tsx
500
+ // src/routes/checkout.$reference.tsx
501
+ import { Checkout } from "@openreceive/react";
502
+ import "@openreceive/react/styles.css";
503
+ import { createFileRoute } from "@tanstack/react-router";
504
+
505
+ export const Route = createFileRoute("/checkout/$reference")({
506
+ component: OrderCheckout,
507
+ });
508
+
509
+ function OrderCheckout() {
510
+ const { reference } = Route.useParams();
511
+ return <Checkout reference={reference} prefix="/openreceive" />;
512
+ }
513
+ ```
514
+
515
+ It renders on the server and starts the checkout in the browser. The payer
516
+ picks Bitcoin and gets the invoice.
517
+
518
+ ### Settings
519
+
520
+ - `NWC_URI`: a receive-only code from your wallet
521
+ ([get one](https://openreceive.org/get_a_nwc_code_to_receive_payments)).
522
+ Optional: `LSC_URI_PRIMARY` for swaps
523
+ ([set one up](https://openreceive.org/set_up_swap_provider)).
524
+ - `DATABASE_URL`: your Postgres. On serverless hosts use the pooled URL. The
525
+ storage is tested through a transaction pooler.
526
+ - On Supabase over HTTPS, instead of `DATABASE_URL`: `SUPABASE_URL` and the
527
+ secret key in `SUPABASE_SERVICE_ROLE_KEY`. Lovable sets both itself.
528
+
529
+ Set them as server secrets, never with a `VITE_` prefix, which would put them
530
+ in the browser bundle.
531
+
532
+ No worker or cron job is needed. Each request to the payment routes also
533
+ checks the wallet for settled invoices, through a lock in your database. A
534
+ payer who closes the tab is settled on the next request.
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/next/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a Next.js App Router application — the app you are already
23
23
  working in. You do not need a copy of the OpenReceive source: the packages are
@@ -378,6 +378,7 @@ enough; drop the `.md` for the same page a person would read.
378
378
  - https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
379
379
  - https://openreceive.org/guides/storage.md — the payment tables and the attempt state machine
380
380
  - https://openreceive.org/guides/supabase.md — when the database is Supabase: its pooler, its certificate, and locking the tables away from its Data API
381
+ - https://openreceive.org/guides/serverless.md — when the app runs serverless (Netlify, AWS Lambda, Cloud Run, Google AI Studio): pooled database URLs, timeouts, and why it needs no cron or worker
381
382
  - https://openreceive.org/guides/node-orms.md — recipes for Prisma, Drizzle, Knex, TypeORM, Sequelize
382
383
  - https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, attributes and slots
383
384
  - https://openreceive.org/guides/checkout-ux.md — read before building any custom UI
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/node/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a Node application — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the packages are on npm, and the
@@ -368,6 +368,7 @@ enough; drop the `.md` for the same page a person would read.
368
368
  - https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
369
369
  - https://openreceive.org/guides/storage.md — the payment tables and the attempt state machine
370
370
  - https://openreceive.org/guides/supabase.md — when the database is Supabase: its pooler, its certificate, and locking the tables away from its Data API
371
+ - https://openreceive.org/guides/serverless.md — when the app runs serverless (Netlify, AWS Lambda, Cloud Run, Google AI Studio): pooled database URLs, timeouts, and why it needs no cron or worker
371
372
  - https://openreceive.org/guides/node-orms.md — recipes for Prisma, Drizzle, Knex, TypeORM, Sequelize
372
373
  - https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, attributes and slots
373
374
  - https://openreceive.org/guides/checkout-ux.md — read before building any custom UI
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/php/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a PHP application — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the engine is on Packagist
@@ -133,8 +133,10 @@ again. Never set the spend-capable override to get past it.
133
133
 
134
134
  Then check the PHP runtime: `php -m` must list `gmp`, `sodium`, `mbstring`,
135
135
  `json`, `pdo` and one PDO driver. `ext-gmp` is REQUIRED — the NWC transport
136
- signs every request with it — and `php:*-cli`/`-fpm` images do not ship it
137
- (`docker-php-ext-install gmp`). PHP must be ≥ 8.2 and 64-bit.
136
+ signs every request with it — and `php:*-cli`/`-fpm` images do not ship it:
137
+ install `libgmp-dev` first, then `docker-php-ext-install gmp`, in the
138
+ Dockerfile. Never pass `--ignore-platform-req=ext-gmp` to Composer. PHP must
139
+ be ≥ 8.2 and 64-bit.
138
140
 
139
141
  ### Upgrading an existing install
140
142
 
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/rails/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Add OpenReceive to a Rails application — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the gem is on RubyGems, the
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/woocommerce/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Setup is done only when the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.22.
20
+ These directions describe OpenReceive 0.4.24.
21
21
 
22
22
  Install and configure the OpenReceive payment gateway in the WooCommerce store
23
23
  you are working in. Preserve its theme, checkout, customer accounts, order
@@ -92,11 +92,18 @@ is saved or the user said "Bitcoin only". Never invent a placeholder code.
92
92
 
93
93
  ## Step 1 — install the plugin
94
94
 
95
- Install the plugin built for this release. Never install the GitHub
95
+ Install this release from the WordPress.org plugin directory:
96
+
97
+ ```sh
98
+ wp plugin install openreceive --version=0.4.24 --activate
99
+ ```
100
+
101
+ If that fails because WordPress.org does not have this version yet, install
102
+ the ZIP attached to the same GitHub release. Never install the GitHub
96
103
  source-code ZIP or a ZIP from an older release:
97
104
 
98
105
  ```sh
99
- wp plugin install https://github.com/OpenReceive/openreceive/releases/download/v0.4.22/openreceive-wordpress-0.4.22.zip --activate
106
+ wp plugin install https://github.com/OpenReceive/openreceive/releases/download/v0.4.24/openreceive-wordpress-0.4.24.zip --activate
100
107
  ```
101
108
 
102
109
  It needs WooCommerce active, and PHP 8.2+ with GMP and sodium in BOTH the web
@@ -105,7 +112,7 @@ images, activation warns "OpenReceive is not running: this site needs the PHP
105
112
  GMP extension" and the plugin stays idle: add GMP to both images as "Enable GMP in both PHP runtimes" below
106
113
  says, rebuild both, then install again. If the Compose file has only `image:`
107
114
  lines, use the two Dockerfiles and `build:` keys under "Compose files with only
108
- `image:` lines" below, and add no other service. If the URL answers 404, build the same
115
+ `image:` lines" below, and add no other service. If the ZIP URL answers 404, build the same
109
116
  tag as "Get the installable archive" below says.
110
117
 
111
118
  ## Step 2 — store the codes, then enable the gateway
@@ -230,10 +237,11 @@ The [WordPress integration entry point](https://openreceive.org/integrations/wor
230
237
  redirects to the WooCommerce integration, which uses this same guide and agent
231
238
  directions. OpenReceive checkout on WordPress requires WooCommerce.
232
239
 
233
- Activate WooCommerce first. Then install the built OpenReceive plugin zip
234
- through **Plugins → Add New → Upload Plugin**. You cannot upload the source
235
- directory as-is. It needs a build first. The plugin is not yet submitted to
236
- WordPress.org.
240
+ Activate WooCommerce first. Then install OpenReceive from the
241
+ [WordPress.org plugin directory](https://wordpress.org/plugins/openreceive/):
242
+ in **Plugins → Add New**, search for **OpenReceive**, then install and
243
+ activate it. With WP-CLI, run `wp plugin install openreceive --activate`.
244
+ WordPress offers each new release as an ordinary plugin update.
237
245
 
238
246
  Requirements: WordPress 6.6+, WooCommerce 9+, 64-bit PHP 8.2+ with GMP and sodium,
239
247
  and MySQL 8 or MariaDB 10.5+. When you activate the plugin, it creates tables for
@@ -242,16 +250,19 @@ database or application.
242
250
 
243
251
  ### Get the installable archive
244
252
 
245
- Download [openreceive-wordpress-0.4.22.zip](https://github.com/OpenReceive/openreceive/releases/download/v0.4.22/openreceive-wordpress-0.4.22.zip)
246
- from the matching release. Historical releases may lack this asset. If that exact
247
- URL returns 404, build the same tag below; never silently install an older ZIP.
248
- The GitHub source-code ZIP is not an installable plugin. On a development machine
249
- with Node 22+, PHP 8.2+ with GMP/sodium, Composer and WP-CLI:
253
+ Each GitHub release also attaches the same plugin as a ZIP, for a site that
254
+ cannot reach WordPress.org or when WordPress.org does not list a release yet.
255
+ Download [openreceive-wordpress-0.4.24.zip](https://github.com/OpenReceive/openreceive/releases/download/v0.4.24/openreceive-wordpress-0.4.24.zip)
256
+ and install it through **Plugins → Add New → Upload Plugin**. Historical
257
+ releases may lack this asset. If that exact URL returns 404, build the same tag
258
+ below; never silently install an older ZIP. The GitHub source-code ZIP is not
259
+ an installable plugin. On a development machine with Node 22+, PHP 8.2+ with
260
+ GMP/sodium, Composer and WP-CLI:
250
261
 
251
262
  ```sh
252
263
  git clone https://github.com/OpenReceive/openreceive.git
253
264
  cd openreceive
254
- git checkout v0.4.22
265
+ git checkout v0.4.24
255
266
  npm ci
256
267
  npm run build:packages
257
268
  composer install --working-dir=packages/php/wordpress
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openreceive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.22
4
+ version: 0.4.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenReceive
@@ -61,16 +61,17 @@ files:
61
61
  - skills/integrate-openreceive/references/fastapi.md
62
62
  - skills/integrate-openreceive/references/fastify.md
63
63
  - skills/integrate-openreceive/references/laravel.md
64
+ - skills/integrate-openreceive/references/lovable.md
64
65
  - skills/integrate-openreceive/references/next.md
65
66
  - skills/integrate-openreceive/references/node.md
66
67
  - skills/integrate-openreceive/references/php.md
67
68
  - skills/integrate-openreceive/references/rails.md
68
69
  - skills/integrate-openreceive/references/woocommerce.md
69
- homepage: https://openreceive.org
70
+ homepage: https://openreceive.org/integrations/rails
70
71
  licenses:
71
72
  - MIT
72
73
  metadata:
73
- homepage_uri: https://openreceive.org
74
+ homepage_uri: https://openreceive.org/integrations/rails
74
75
  source_code_uri: https://github.com/openreceive/openreceive
75
76
  changelog_uri: https://github.com/openreceive/openreceive/blob/master/packages/ruby/openreceive/CHANGELOG.md
76
77
  bug_tracker_uri: https://github.com/openreceive/openreceive/issues