openreceive 0.4.22 → 0.4.23
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/openreceive/version.rb +1 -1
- data/skills/integrate-openreceive/SKILL.md +3 -0
- data/skills/integrate-openreceive/references/btcpay.md +85 -27
- data/skills/integrate-openreceive/references/django.md +1 -1
- data/skills/integrate-openreceive/references/fastapi.md +1 -1
- data/skills/integrate-openreceive/references/fastify.md +1 -1
- data/skills/integrate-openreceive/references/laravel.md +20 -4
- data/skills/integrate-openreceive/references/lovable.md +531 -0
- data/skills/integrate-openreceive/references/next.md +1 -1
- data/skills/integrate-openreceive/references/node.md +1 -1
- data/skills/integrate-openreceive/references/php.md +5 -3
- data/skills/integrate-openreceive/references/rails.md +1 -1
- data/skills/integrate-openreceive/references/woocommerce.md +25 -14
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb5542a9163a02e90baa6f8938172785600029980002c119fde4631b452da20c
|
|
4
|
+
data.tar.gz: 7f71eb77da79968df633485d01ca9e7a89bbd839186a1daf1d137323f889daba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75465da88449b28ff097b5b4a4d7eadb141e2ba8be2a1e4797ae021fd23ba876df3b50cbef86e7afb0b05df4f718e957dd78a5c4979cda2558e79bb9642c20c4
|
|
7
|
+
data.tar.gz: 90884d4fef6027acb5919a5a9d36c0e7d1ba066d13d21db1793c940dad7c2c6653288a26efb0540eccb7b7baf37a4817e99d3826fb58af034aa594bc303ff260
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.23 - 2026-10-11
|
|
4
|
+
|
|
5
|
+
Release with the complete 0.4.23 package family. The bundled agent skill adds
|
|
6
|
+
directions for Lovable's agent. Its BTCPay Server directions ask for one code
|
|
7
|
+
per message and never search the machine for one, its WooCommerce directions
|
|
8
|
+
install the plugin from WordPress.org, and its Laravel directions add GMP to
|
|
9
|
+
the Docker image. No Ruby runtime changes from 0.4.22.
|
|
10
|
+
|
|
3
11
|
## 0.4.22 - 2026-10-10
|
|
4
12
|
|
|
5
13
|
Release with the complete 0.4.22 package family. The bundled agent skills'
|
data/lib/openreceive/version.rb
CHANGED
|
@@ -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
|
|
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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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.
|
|
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
|
|
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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
@@ -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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
@@ -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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
@@ -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
|
|
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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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`.
|
|
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,531 @@
|
|
|
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 25 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.23.
|
|
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.23 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. If the app
|
|
152
|
+
creates orders in the browser today, move that insert into this function.
|
|
153
|
+
Keep the order's user column too, if the app has sign-in.
|
|
154
|
+
- **The order is unpaid or paid.** Do not copy attempt statuses (`pending`,
|
|
155
|
+
`expired`, `failed`, `attention`) onto it, and do not add a relation to
|
|
156
|
+
`openreceive_payments`.
|
|
157
|
+
|
|
158
|
+
## Step 4 — `openreceive_on_paid`
|
|
159
|
+
|
|
160
|
+
In a second new migration, replace the placeholder with the SQL that marks
|
|
161
|
+
this app's order paid. Use `create or replace`, and this app's own table,
|
|
162
|
+
column and status names:
|
|
163
|
+
|
|
164
|
+
```sql
|
|
165
|
+
create or replace function public.openreceive_on_paid(
|
|
166
|
+
p_reference text, p_payment_hash text, p_paid_at bigint
|
|
167
|
+
) returns void language plpgsql as $$
|
|
168
|
+
begin
|
|
169
|
+
update public.orders
|
|
170
|
+
set status = 'paid'
|
|
171
|
+
where id = p_reference::uuid -- or just p_reference, if the ids are text
|
|
172
|
+
and status = 'pending';
|
|
173
|
+
end
|
|
174
|
+
$$;
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
It runs inside the transaction that records the payment, for the order's
|
|
178
|
+
first settled payment only. If it raises, nothing is recorded, and the next
|
|
179
|
+
request retries it. Its `WHERE` on the unpaid status is the guard against a
|
|
180
|
+
second fulfillment. Keep it to database writes.
|
|
181
|
+
|
|
182
|
+
## Step 5 — the payment route
|
|
183
|
+
|
|
184
|
+
Write `src/routes/openreceive.$.ts` and `src/lib/openreceive.server.ts`
|
|
185
|
+
exactly as the recipe's "On Supabase (Lovable)" section shows:
|
|
186
|
+
|
|
187
|
+
- `storage: { supabase: { url: process.env.SUPABASE_URL, key: process.env.SUPABASE_SERVICE_ROLE_KEY } }`,
|
|
188
|
+
read inside the handler, never at module scope.
|
|
189
|
+
- No `onPaid` and no `db`: `openreceive_on_paid` is the fulfillment.
|
|
190
|
+
- `findOrder` reads the order with `supabaseAdmin`; `authorize` compares the
|
|
191
|
+
request's `buyer` cookie with the order's `buyer_token`.
|
|
192
|
+
- Build the stack inside each request and close it in `finally`.
|
|
193
|
+
- Import the server module only inside the route's handlers, so it never
|
|
194
|
+
reaches the browser bundle.
|
|
195
|
+
|
|
196
|
+
## Step 6 — the checkout page
|
|
197
|
+
|
|
198
|
+
Add `src/routes/checkout.$reference.tsx` with `<Checkout reference={reference}
|
|
199
|
+
prefix="/openreceive" />` from `@openreceive/react`, and import
|
|
200
|
+
`@openreceive/react/styles.css` there. After the order is created, navigate
|
|
201
|
+
to `/checkout/<id>`. Use the drop-in; do not build your own checkout UI.
|
|
202
|
+
|
|
203
|
+
## After setup — hand over, then stop
|
|
204
|
+
|
|
205
|
+
You cannot pay the invoice: the code is receive-only. Do not pay, settle or
|
|
206
|
+
mark an order paid, and do not look for a way to.
|
|
207
|
+
|
|
208
|
+
If the checkout answers 503, the server log names the fix: usually the
|
|
209
|
+
migration is not applied, or `openreceive_on_paid` is still the placeholder.
|
|
210
|
+
|
|
211
|
+
Setup ends here. Your last message starts "Setup is finished" and has at most
|
|
212
|
+
five short lines: where to place an order in the preview, the methods the
|
|
213
|
+
checkout offers, and that a small real payment from their own wallet marks
|
|
214
|
+
the order paid. Send nothing after it. Do not list what changed, offer more
|
|
215
|
+
work, or end the message on a question. Name the methods: Bitcoin, plus
|
|
216
|
+
USDT, USDC, ETH and SOL when `LSC_URI_PRIMARY` is saved. Do not mention
|
|
217
|
+
minimums, and never say a coin will not work or will not be offered.
|
|
218
|
+
|
|
219
|
+
## Non-negotiables
|
|
220
|
+
|
|
221
|
+
The recipe below has the code. These are the rules it cannot state for
|
|
222
|
+
itself.
|
|
223
|
+
|
|
224
|
+
- OpenReceive never owns orders, users, prices, or fulfillment.
|
|
225
|
+
- Keep `NWC_URI` / `LSC_URI_*` server-only: secrets read with `process.env`
|
|
226
|
+
in `.server.ts` modules. Never a `VITE_` variable, `.env`, browser code or
|
|
227
|
+
a log.
|
|
228
|
+
- Do not suggest rotating, revoking or replacing a code because it was pasted
|
|
229
|
+
into this chat; that is the supported path.
|
|
230
|
+
- `SUPABASE_SERVICE_ROLE_KEY` reads and writes every table. Read it only in
|
|
231
|
+
server modules.
|
|
232
|
+
- Never grant `anon` or `authenticated` anything on an `openreceive_*` table
|
|
233
|
+
or function, and never add a row level security policy to those tables.
|
|
234
|
+
The server refuses to serve while they can reach them.
|
|
235
|
+
- The host owns the price. `amountFor` reads it from the order; reject
|
|
236
|
+
payer-supplied amounts.
|
|
237
|
+
- `authorize` runs on every request, and the `resource` it receives is a CLAIM
|
|
238
|
+
the payer made, not proof. Check the `buyer` cookie against the order.
|
|
239
|
+
- Receive-only NWC is required; a spend-capable code fails closed unless
|
|
240
|
+
explicitly overridden. Never set that override.
|
|
241
|
+
- There is NO merchant-initiated refund of a settled Lightning payment, because
|
|
242
|
+
the wallet cannot spend. Swap refunds — a payer reclaiming a deposit that
|
|
243
|
+
never converted — are the only refund OpenReceive performs. Do not build,
|
|
244
|
+
promise, or imply a Lightning refund path.
|
|
245
|
+
- IF YOU TURN SWAPS ON, BUILD THE ROUTE BACK. A deposit that arrives short or
|
|
246
|
+
late is refunded on a SECOND visit, so the order needs its own URL
|
|
247
|
+
(`/checkout/$reference`, with `syncUrl` on `<Checkout>`), and the attempt
|
|
248
|
+
must come back with it: keep the `payment_hash` from `onState` and pass it
|
|
249
|
+
as `resumePaymentHash`. https://openreceive.org/guides/swap-refunds.md
|
|
250
|
+
- Show the payer what they are buying: return a `description` beside the
|
|
251
|
+
price from `amountFor`.
|
|
252
|
+
- HTTP JSON is snake_case; TypeScript APIs are camelCase.
|
|
253
|
+
- Money is integers or decimal strings — never binary floats.
|
|
254
|
+
|
|
255
|
+
## More documentation
|
|
256
|
+
|
|
257
|
+
Fetch one when the moment comes. Each is raw markdown, so a plain GET is
|
|
258
|
+
enough.
|
|
259
|
+
|
|
260
|
+
- https://openreceive.org/guides/lovable.md — what the user does on Lovable around these steps
|
|
261
|
+
- https://openreceive.org/guides/supabase.md — Supabase over HTTPS: how the storage works, and what the server checks before it serves
|
|
262
|
+
- https://openreceive.org/guides/supabase-migration.md — the migration for Step 2
|
|
263
|
+
- https://openreceive.org/guides/authorization.md — before you write `authorize`
|
|
264
|
+
- https://openreceive.org/guides/storage.md — the payment tables and the attempt state machine
|
|
265
|
+
- https://openreceive.org/guides/frontend-checkout.md — the drop-in's props, including `syncUrl` and `resumePaymentHash`
|
|
266
|
+
- https://openreceive.org/guides/checkout-ux.md — the rules the drop-in already follows
|
|
267
|
+
- https://openreceive.org/guides/provider-registry.md — where the wallet logos and pay tutorials come from: inside the JavaScript, nothing to serve
|
|
268
|
+
- https://openreceive.org/guides/automated-swaps.md — only if `LSC_URI_PRIMARY` is set
|
|
269
|
+
- https://openreceive.org/guides/swap-refunds.md — the refund flow, and the route back to it
|
|
270
|
+
- https://openreceive.org/guides/lightning-swap-connect.md — what an `LSC_URI_*` code actually is
|
|
271
|
+
- https://openreceive.org/guides/environment-variables.md — every variable, and what is deliberately not one
|
|
272
|
+
- https://openreceive.org/guides/price-feeds.md — where the fiat→sats rate comes from
|
|
273
|
+
- https://openreceive.org/guides/rate-limiting.md — before a public shop goes live
|
|
274
|
+
- https://openreceive.org/guides/security.md and https://openreceive.org/guides/deploying.md — before this goes anywhere real
|
|
275
|
+
- https://openreceive.org/guides/api-reference.md — every route, option and error code
|
|
276
|
+
- https://openreceive.org/guides.md — the index, if what you need is not above
|
|
277
|
+
|
|
278
|
+
Questions, or a problem with the library itself:
|
|
279
|
+
https://openreceive.org/contact
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## The quickstart, in full
|
|
284
|
+
|
|
285
|
+
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.
|
|
286
|
+
The page it comes from is https://openreceive.org/guides/tanstack-start-recipe.
|
|
287
|
+
|
|
288
|
+
## TanStack Start recipe
|
|
289
|
+
|
|
290
|
+
OpenReceive ships no TanStack Start package. A TanStack Start server route
|
|
291
|
+
takes a Web-standard `Request` and returns a `Response`, and so does
|
|
292
|
+
`@openreceive/http`, so the integration is the route below. Your server code
|
|
293
|
+
creates each Lightning invoice, the payment goes straight to your wallet, and
|
|
294
|
+
payment attempts are stored in your app's Postgres database. There is no
|
|
295
|
+
OpenReceive account and no background worker.
|
|
296
|
+
|
|
297
|
+
Optional swaps let customers pay with **USDT, USDC, SOL, and ETH**. A swap
|
|
298
|
+
provider you configure converts the payment to **BTC over Lightning**, and it
|
|
299
|
+
settles into the same wallet. Available assets and networks depend on the
|
|
300
|
+
provider.
|
|
301
|
+
|
|
302
|
+
This recipe was checked with `@tanstack/react-start` 1.168 and
|
|
303
|
+
`@openreceive/*` 0.4.19. The app was built with Lovable's Vite config for
|
|
304
|
+
Cloudflare Workers and run under workerd: a buyer's checkout showed a real
|
|
305
|
+
Lightning invoice, and a stranger was refused. The Supabase route below runs
|
|
306
|
+
in OpenReceive's CI as a Worker, against Supabase's own Postgres image and
|
|
307
|
+
API server: a buyer's invoice, a stranger refused, one live attempt under
|
|
308
|
+
concurrent requests, and a payment that `openreceive_on_paid` fulfills once.
|
|
309
|
+
|
|
310
|
+
```sh
|
|
311
|
+
npm install @openreceive/http @openreceive/react pg
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Where it runs
|
|
315
|
+
|
|
316
|
+
- **Node** (the `node-server` preset, or any Node host): any Postgres.
|
|
317
|
+
- **Cloudflare Workers** (the `cloudflare-module` preset, Lovable's default):
|
|
318
|
+
with Node compatibility, and a Postgres whose certificate a public
|
|
319
|
+
authority signed, such as Neon.
|
|
320
|
+
- **Cloudflare Workers with Supabase**, which is every Lovable app: a Worker
|
|
321
|
+
cannot open a Postgres connection to Supabase, whose certificate comes from
|
|
322
|
+
a private authority. Use [On Supabase](#on-supabase-lovable) below, which
|
|
323
|
+
stores payments through Supabase's HTTPS API instead.
|
|
324
|
+
|
|
325
|
+
Workers ties every socket to the request that opened it, and forbids network
|
|
326
|
+
calls at import. So the route builds its database pool and its OpenReceive
|
|
327
|
+
stack inside each request, and closes both before it responds. Each request
|
|
328
|
+
then opens its own wallet connection; in our test a checkout request took one
|
|
329
|
+
to four seconds. The same code works on Node.
|
|
330
|
+
|
|
331
|
+
### The payment route
|
|
332
|
+
|
|
333
|
+
Keep the handler in a `.server.ts` module. Route files are also bundled for
|
|
334
|
+
the browser, so the route loads the module only when a request arrives:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
// src/routes/openreceive.$.ts — every route under /openreceive/
|
|
338
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
339
|
+
|
|
340
|
+
async function openReceive({ request }: { request: Request }): Promise<Response> {
|
|
341
|
+
const { handleOpenReceive } = await import("@/lib/openreceive.server");
|
|
342
|
+
return handleOpenReceive(request);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export const Route = createFileRoute("/openreceive/$")({
|
|
346
|
+
server: { handlers: { GET: openReceive, POST: openReceive } },
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
// src/lib/openreceive.server.ts
|
|
352
|
+
import { createStack } from "@openreceive/http";
|
|
353
|
+
import pg from "pg";
|
|
354
|
+
import { findOrder, currentVisitor } from "./orders.server"; // your own orders
|
|
355
|
+
|
|
356
|
+
export async function handleOpenReceive(request: Request): Promise<Response> {
|
|
357
|
+
// Read process.env inside the handler: on Workers it is empty at import.
|
|
358
|
+
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 1 });
|
|
359
|
+
const stack = createStack({
|
|
360
|
+
wallet: { nwc: process.env.NWC_URI ?? "" },
|
|
361
|
+
storage: {
|
|
362
|
+
db: pool,
|
|
363
|
+
// Runs once, inside the transaction that records the payment.
|
|
364
|
+
onPaid: async ({ reference, paidAt, query }) => {
|
|
365
|
+
await query(
|
|
366
|
+
"UPDATE orders SET state = 'paid', paid_at = $1 WHERE id = $2 AND state = 'awaiting_payment'",
|
|
367
|
+
[paidAt, reference],
|
|
368
|
+
);
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
// The price comes from the order row, never from the request.
|
|
372
|
+
amountFor: async (reference) => {
|
|
373
|
+
const order = await findOrder(pool, reference);
|
|
374
|
+
return order?.state === "awaiting_payment"
|
|
375
|
+
? { currency: order.currency, value: order.amount, description: order.title }
|
|
376
|
+
: null;
|
|
377
|
+
},
|
|
378
|
+
// Only the buyer may pay for their order.
|
|
379
|
+
authorize: async ({ request: incoming, resource }) => {
|
|
380
|
+
const order = resource.reference ? await findOrder(pool, resource.reference) : undefined;
|
|
381
|
+
const visitor = currentVisitor(incoming);
|
|
382
|
+
return Boolean(order && visitor && order.visitor === visitor);
|
|
383
|
+
},
|
|
384
|
+
// Cloudflare sets cf-connecting-ip on every request, and a client cannot.
|
|
385
|
+
// On Node behind your own proxy, read the header that proxy sets.
|
|
386
|
+
rateLimiting: {
|
|
387
|
+
ip: ({ request: incoming }) => incoming.headers.get("cf-connecting-ip") ?? undefined,
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
try {
|
|
391
|
+
return await stack.handler(request, { native: request });
|
|
392
|
+
} finally {
|
|
393
|
+
await stack.close();
|
|
394
|
+
await pool.end();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
`authorize` sees the incoming request with its cookies, so check it with
|
|
400
|
+
the session your app already has. The Next.js quickstart
|
|
401
|
+
explains each hook. They are the same here.
|
|
402
|
+
|
|
403
|
+
Create OpenReceive's two tables with your migrations, or run
|
|
404
|
+
`paymentsSchemaSql("postgres")` from `@openreceive/http` once. It is
|
|
405
|
+
idempotent. See Payment storage.
|
|
406
|
+
|
|
407
|
+
### On Supabase (Lovable)
|
|
408
|
+
|
|
409
|
+
On Supabase the route keeps payments in your Supabase database through its
|
|
410
|
+
HTTPS API, with the project's secret key. There is no `pg` pool. Use
|
|
411
|
+
`@openreceive/*` 0.4.23 or newer:
|
|
412
|
+
|
|
413
|
+
```sh
|
|
414
|
+
npm install @openreceive/http @openreceive/react
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
1. Apply the migration from `npx openreceive scaffold payments --supabase`.
|
|
418
|
+
It creates OpenReceive's tables, locked away from the browser's Supabase
|
|
419
|
+
key, and the functions that write them.
|
|
420
|
+
2. Replace its placeholder `openreceive_on_paid` with the SQL that marks your
|
|
421
|
+
order paid. It runs inside the transaction that records the payment, for
|
|
422
|
+
the order's first payment only, so a failure there records nothing:
|
|
423
|
+
|
|
424
|
+
```sql
|
|
425
|
+
create or replace function public.openreceive_on_paid(
|
|
426
|
+
p_reference text, p_payment_hash text, p_paid_at bigint
|
|
427
|
+
) returns void language plpgsql as $$
|
|
428
|
+
begin
|
|
429
|
+
update public.orders set status = 'paid'
|
|
430
|
+
where id = p_reference::uuid and status = 'pending';
|
|
431
|
+
end
|
|
432
|
+
$$;
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
3. Write the route's server module:
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
// src/lib/openreceive.server.ts
|
|
439
|
+
import { createStack } from "@openreceive/http";
|
|
440
|
+
import { findOrder, currentBuyer } from "./orders.server"; // your own orders
|
|
441
|
+
|
|
442
|
+
export async function handleOpenReceive(request: Request): Promise<Response> {
|
|
443
|
+
// Read process.env inside the handler: on Workers it is empty at import.
|
|
444
|
+
// Lovable sets SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY for server code.
|
|
445
|
+
const stack = createStack({
|
|
446
|
+
wallet: { nwc: process.env.NWC_URI ?? "" },
|
|
447
|
+
storage: {
|
|
448
|
+
supabase: {
|
|
449
|
+
url: process.env.SUPABASE_URL ?? "",
|
|
450
|
+
key: process.env.SUPABASE_SERVICE_ROLE_KEY ?? "",
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
// The price comes from the order row, never from the request.
|
|
454
|
+
amountFor: async (reference) => {
|
|
455
|
+
const order = await findOrder(reference);
|
|
456
|
+
return order?.status === "pending"
|
|
457
|
+
? { currency: order.currency, value: order.total, description: order.title }
|
|
458
|
+
: null;
|
|
459
|
+
},
|
|
460
|
+
// Only the buyer may pay for their order.
|
|
461
|
+
authorize: async ({ request: incoming, resource }) => {
|
|
462
|
+
const order = resource.reference ? await findOrder(resource.reference) : null;
|
|
463
|
+
const buyer = currentBuyer(incoming);
|
|
464
|
+
return Boolean(order && buyer && order.buyer_token === buyer);
|
|
465
|
+
},
|
|
466
|
+
rateLimiting: {
|
|
467
|
+
ip: ({ request: incoming }) => incoming.headers.get("cf-connecting-ip") ?? undefined,
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
try {
|
|
471
|
+
return await stack.handler(request, { native: request });
|
|
472
|
+
} finally {
|
|
473
|
+
await stack.close();
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
There is no `onPaid`: `openreceive_on_paid` is the fulfillment. `findOrder`
|
|
479
|
+
reads the order with your server-side Supabase client; in a Lovable app that
|
|
480
|
+
is `supabaseAdmin` from `@/integrations/supabase/client.server`.
|
|
481
|
+
|
|
482
|
+
The checkout calls the payment routes from the browser with the page's
|
|
483
|
+
cookies, and nothing else. A Lovable app keeps its Supabase sign-in in the
|
|
484
|
+
browser, not in a cookie, so `authorize` cannot see who is signed in. Give
|
|
485
|
+
the buyer a cookie of their own instead: create orders in a server function
|
|
486
|
+
that sets an HttpOnly `buyer` cookie (a random value, kept across orders) and
|
|
487
|
+
stores the same value on the order as `buyer_token`. `currentBuyer` reads it
|
|
488
|
+
back from the request's `cookie` header.
|
|
489
|
+
|
|
490
|
+
Before it serves, the server checks the database, and the payment routes
|
|
491
|
+
answer 503 until the migration is applied and `openreceive_on_paid` is
|
|
492
|
+
yours; the log names the fix. More: Supabase over HTTPS.
|
|
493
|
+
|
|
494
|
+
### The checkout page
|
|
495
|
+
|
|
496
|
+
```tsx
|
|
497
|
+
// src/routes/checkout.$reference.tsx
|
|
498
|
+
import { Checkout } from "@openreceive/react";
|
|
499
|
+
import "@openreceive/react/styles.css";
|
|
500
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
501
|
+
|
|
502
|
+
export const Route = createFileRoute("/checkout/$reference")({
|
|
503
|
+
component: OrderCheckout,
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
function OrderCheckout() {
|
|
507
|
+
const { reference } = Route.useParams();
|
|
508
|
+
return <Checkout reference={reference} prefix="/openreceive" />;
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
It renders on the server and starts the checkout in the browser. The payer
|
|
513
|
+
picks Bitcoin and gets the invoice.
|
|
514
|
+
|
|
515
|
+
### Settings
|
|
516
|
+
|
|
517
|
+
- `NWC_URI`: a receive-only code from your wallet
|
|
518
|
+
([get one](https://openreceive.org/get_a_nwc_code_to_receive_payments)).
|
|
519
|
+
Optional: `LSC_URI_PRIMARY` for swaps
|
|
520
|
+
([set one up](https://openreceive.org/set_up_swap_provider)).
|
|
521
|
+
- `DATABASE_URL`: your Postgres. On serverless hosts use the pooled URL. The
|
|
522
|
+
storage is tested through a transaction pooler.
|
|
523
|
+
- On Supabase over HTTPS, instead of `DATABASE_URL`: `SUPABASE_URL` and the
|
|
524
|
+
secret key in `SUPABASE_SERVICE_ROLE_KEY`. Lovable sets both itself.
|
|
525
|
+
|
|
526
|
+
Set them as server secrets, never with a `VITE_` prefix, which would put them
|
|
527
|
+
in the browser bundle.
|
|
528
|
+
|
|
529
|
+
No worker or cron job is needed. Each request to the payment routes also
|
|
530
|
+
checks the wallet for settled invoices, through a lock in your database. A
|
|
531
|
+
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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
@@ -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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
@@ -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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
-
|
|
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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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.
|
|
20
|
+
These directions describe OpenReceive 0.4.23.
|
|
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
|
|
95
|
+
Install this release from the WordPress.org plugin directory:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
wp plugin install openreceive --version=0.4.23 --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.
|
|
106
|
+
wp plugin install https://github.com/OpenReceive/openreceive/releases/download/v0.4.23/openreceive-wordpress-0.4.23.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
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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.23.zip](https://github.com/OpenReceive/openreceive/releases/download/v0.4.23/openreceive-wordpress-0.4.23.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.
|
|
265
|
+
git checkout v0.4.23
|
|
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.
|
|
4
|
+
version: 0.4.23
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenReceive
|
|
@@ -61,6 +61,7 @@ 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
|