openreceive 0.4.29 → 0.4.30
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 +6 -0
- data/lib/openreceive/version.rb +1 -1
- data/skills/integrate-openreceive/SKILL.md +10 -1
- data/skills/integrate-openreceive/references/astro.md +8 -9
- data/skills/integrate-openreceive/references/bolt.md +9 -7
- data/skills/integrate-openreceive/references/btcpay.md +3 -4
- data/skills/integrate-openreceive/references/django.md +2 -3
- data/skills/integrate-openreceive/references/edd.md +4 -5
- data/skills/integrate-openreceive/references/fastapi.md +4 -5
- data/skills/integrate-openreceive/references/fastify.md +4 -5
- data/skills/integrate-openreceive/references/flask.md +2 -3
- data/skills/integrate-openreceive/references/fossbilling.md +7 -8
- data/skills/integrate-openreceive/references/laravel.md +3 -4
- data/skills/integrate-openreceive/references/lovable.md +16 -12
- data/skills/integrate-openreceive/references/medusa.md +12 -5
- data/skills/integrate-openreceive/references/mobile.md +1 -1
- data/skills/integrate-openreceive/references/nestjs.md +2 -3
- data/skills/integrate-openreceive/references/next.md +4 -5
- data/skills/integrate-openreceive/references/node.md +4 -5
- data/skills/integrate-openreceive/references/nuxt.md +9 -10
- data/skills/integrate-openreceive/references/odoo.md +446 -0
- data/skills/integrate-openreceive/references/paymenter.md +5 -6
- data/skills/integrate-openreceive/references/php.md +2 -3
- data/skills/integrate-openreceive/references/prestashop.md +476 -0
- data/skills/integrate-openreceive/references/rails.md +2 -3
- data/skills/integrate-openreceive/references/react-router.md +8 -9
- data/skills/integrate-openreceive/references/sveltekit.md +9 -10
- data/skills/integrate-openreceive/references/tanstack-start.md +14 -11
- data/skills/integrate-openreceive/references/vendure.md +544 -0
- data/skills/integrate-openreceive/references/woocommerce.md +5 -6
- metadata +4 -1
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
This is the full file; follow it from Step 0.
|
|
2
|
+
|
|
3
|
+
# OpenReceive agent directions (Vendure)
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
# READ THIS FIRST: this file is 25 KB and a summary drops required steps. Download it whole:
|
|
7
|
+
curl -fsSL https://openreceive.org/agent-directions/vendure/full.md
|
|
8
|
+
# Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Step 0 in brief** (Step 0 below has the details):
|
|
12
|
+
|
|
13
|
+
- Before installing 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
|
+
- The user only pastes codes into this chat. Never ask them to edit .env or run a command to save one.
|
|
16
|
+
- Write each code into .env yourself, as Step 0 says. Never echo it or put it in a shell command.
|
|
17
|
+
- Do not suggest rotating or revoking a code because it was pasted here.
|
|
18
|
+
- Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
|
|
19
|
+
|
|
20
|
+
These directions describe OpenReceive 0.4.30.
|
|
21
|
+
|
|
22
|
+
Add OpenReceive to the Vendure shop you are working in: a Vendure server with
|
|
23
|
+
its worker, and usually a storefront in its own project (Vendure's Next.js
|
|
24
|
+
starter or another). OpenReceive is a Vendure plugin on npm,
|
|
25
|
+
`@openreceive/vendure`, with a payment method handler. The quickstart
|
|
26
|
+
appended to this file has all the code: do not clone the OpenReceive
|
|
27
|
+
repository, and do not write your own payment handler, routes or settlement.
|
|
28
|
+
|
|
29
|
+
The one required credential is a receive-only NWC code (Nostr Wallet Connect):
|
|
30
|
+
a string from the merchant's wallet that can create invoices and read their
|
|
31
|
+
status, and cannot spend. A swap provider (an "LSC" code) optionally lets
|
|
32
|
+
buyers pay with USDT, USDC, ETH or SOL instead; the provider converts the
|
|
33
|
+
payment to BTC over Lightning in the merchant's connected wallet. Available
|
|
34
|
+
assets and networks depend on the provider.
|
|
35
|
+
|
|
36
|
+
Vendure keeps orders, prices, customers and fulfillment. The plugin keeps its
|
|
37
|
+
payment attempts in two tables of Vendure's database and adds the settled
|
|
38
|
+
payment to the order itself once the payment settles, so Vendure places it.
|
|
39
|
+
You write no hooks.
|
|
40
|
+
|
|
41
|
+
## Step 0 — ask for the two codes, one question at a time
|
|
42
|
+
|
|
43
|
+
Before anything else, check one file: the Vendure server's `.env`.
|
|
44
|
+
`grep -E '^(NWC_URI|LSC_URI_PRIMARY)=.' .env | cut -d= -f1` names the codes it
|
|
45
|
+
sets without printing them. Check with that, here and after each write, not
|
|
46
|
+
by reading the file, and never print the values. A code that is already set is
|
|
47
|
+
not asked for again; if both are set, go to Step 1.
|
|
48
|
+
|
|
49
|
+
Otherwise your next action is a question to the user. Do not install packages,
|
|
50
|
+
edit the shop or search anywhere else before asking it. Do not read deploy
|
|
51
|
+
config, Docker containers or process environments looking for a code, and
|
|
52
|
+
never run `printenv`: a new shop has neither code yet.
|
|
53
|
+
|
|
54
|
+
The user never edits an environment file. They paste each code
|
|
55
|
+
into this chat; you store it. That is the supported path: do not ask them to
|
|
56
|
+
save it themselves, and do not tell them to revoke or replace a
|
|
57
|
+
code because it was pasted here. Ask one question per message.
|
|
58
|
+
|
|
59
|
+
1. **First message — the NWC code, and nothing else.**
|
|
60
|
+
|
|
61
|
+
> To receive payments I need a receive-only wallet code. In Rizful: open
|
|
62
|
+
> the menu, tap NWC, choose Receive-only NWC code, and tap Copy
|
|
63
|
+
> (https://openreceive.org/get_a_nwc_code_to_receive_payments). If you would
|
|
64
|
+
> rather run your own wallet, Alby Hub works too: Connections → Add
|
|
65
|
+
> Connection → Read Only. Paste the code here and I will store it.
|
|
66
|
+
|
|
67
|
+
2. **When they paste it.** If it does not start with `nostr+walletconnect://`,
|
|
68
|
+
ask them to copy the receive-only code again. Otherwise write
|
|
69
|
+
`NWC_URI=<paste>` into the Vendure server's `.env` with your file-editing
|
|
70
|
+
tool. Never write it with a shell command (`echo`, `printf`, a heredoc): the
|
|
71
|
+
command line shows the code. Never put it in the storefront, in a
|
|
72
|
+
`NEXT_PUBLIC_` variable or in a commit. Reply only that it is saved, then
|
|
73
|
+
ask the next question.
|
|
74
|
+
3. **Second message — swaps.** If the user asked for stablecoins, USDT, USDC,
|
|
75
|
+
ETH, SOL, altcoins or "crypto" (as in "Bitcoin and stablecoin payments"),
|
|
76
|
+
this message IS the walkthrough below: do not skip it, and do not ask yes
|
|
77
|
+
or no first. Otherwise ask whether buyers should also be able to pay with
|
|
78
|
+
USDT, USDC, ETH or SOL, then give the walkthrough. The walkthrough:
|
|
79
|
+
|
|
80
|
+
> Go to https://lightning-swap.com, sign in for API keys, create a key, and
|
|
81
|
+
> copy the whole URI (https://openreceive.org/set_up_swap_provider). Paste
|
|
82
|
+
> it here and I will store it — or say "Bitcoin only" and I will continue
|
|
83
|
+
> without it.
|
|
84
|
+
|
|
85
|
+
Mention FixedFloat only if they already use it.
|
|
86
|
+
4. **When they paste it.** If it does not start with
|
|
87
|
+
`lightning+swapconnect://`, ask them to copy it again. Otherwise add
|
|
88
|
+
`LSC_URI_PRIMARY=<paste>` to the same `.env` with the file-editing tool.
|
|
89
|
+
Swaps are now on, so keep the route back (the swap non-negotiable below).
|
|
90
|
+
|
|
91
|
+
A new Vendure project loads `.env` itself. Under Docker Compose, give the
|
|
92
|
+
server, the worker and every other service that runs the Vendure config
|
|
93
|
+
`env_file` pointing at that `.env`, and recreate them with
|
|
94
|
+
`docker compose up -d`. Do not report setup as complete until the NWC code is
|
|
95
|
+
saved, and the LSC code is saved or the user said "Bitcoin only". Never
|
|
96
|
+
invent a placeholder code.
|
|
97
|
+
|
|
98
|
+
## Step 1 — install the plugin in the Vendure server
|
|
99
|
+
|
|
100
|
+
In the Vendure server's project, run `npm install @openreceive/vendure`, then
|
|
101
|
+
add `OpenReceivePlugin` from `@openreceive/vendure/plugin` to the `plugins` in
|
|
102
|
+
`src/vendure-config.ts` exactly as the quickstart's "Install the plugin"
|
|
103
|
+
shows, keeping every setting already there. The plugin needs Vendure 3.3 or
|
|
104
|
+
later, Node 22.12 or later, and PostgreSQL, MySQL or MariaDB; it refuses
|
|
105
|
+
SQLite. Restart the server and the worker: the server's startup migrations
|
|
106
|
+
create the plugin's two tables, and the worker runs its scheduled task.
|
|
107
|
+
|
|
108
|
+
Restart them and the storefront the way this project already runs them: in
|
|
109
|
+
their own terminal, under Docker Compose with `docker compose up -d --build`,
|
|
110
|
+
or by the pid of the process you started. Never `pkill` or `killall` by name:
|
|
111
|
+
on a shared machine that stops other people's servers.
|
|
112
|
+
|
|
113
|
+
## Step 2 — create the payment method and check the wallet
|
|
114
|
+
|
|
115
|
+
In the Vendure server's project, with the server up (under Docker Compose,
|
|
116
|
+
run both inside the server's container with `docker compose exec -T`):
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
node node_modules/@openreceive/vendure/dist/enable-openreceive.js
|
|
120
|
+
npx openreceive doctor --url http://localhost:3000
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The first creates the payment method, code `openreceive`, in the default
|
|
124
|
+
channel, signing in with `SUPERADMIN_USERNAME` and `SUPERADMIN_PASSWORD` from
|
|
125
|
+
the environment; when those are missing, report that and ask the user to
|
|
126
|
+
create it in the dashboard (Settings → Payment methods, code `openreceive`,
|
|
127
|
+
handler OpenReceive). `doctor` reports each code as set or unset, asks the
|
|
128
|
+
wallet whether it is receive-only, and with `--url` (the server's own
|
|
129
|
+
address) checks that the plugin's routes answer. It names any failed check
|
|
130
|
+
and exits nonzero; fix it before going on. If it reports spend methods such
|
|
131
|
+
as `pay_invoice`, remove `NWC_URI` from `.env` and ask the user for a
|
|
132
|
+
receive-only code again. Never set the spend-capable override to get past it.
|
|
133
|
+
|
|
134
|
+
## Step 3 — add the checkout to the storefront
|
|
135
|
+
|
|
136
|
+
For Vendure's Next.js starter, make the quickstart's storefront changes:
|
|
137
|
+
`npm install @openreceive/react`, the route file
|
|
138
|
+
`src/app/api/openreceive/[...path]/route.ts`, the server actions in
|
|
139
|
+
`src/features/checkout/openreceive.ts`, the checkout in the review step, and
|
|
140
|
+
the one-line change to `transitionToArrangingPayment`. Keep the rest of the
|
|
141
|
+
storefront as it is. Do not import `@openreceive/react/styles.css` from a
|
|
142
|
+
component or a CSS file: the route serves it, outside the starter's Tailwind
|
|
143
|
+
build.
|
|
144
|
+
|
|
145
|
+
For another storefront, follow the quickstart's "Another storefront": `POST
|
|
146
|
+
/openreceive/order` with the buyer's session where the buyer confirms, the
|
|
147
|
+
checkout with the `reference` it answers, and the checkout's calls sent to
|
|
148
|
+
`/openreceive` with the same session.
|
|
149
|
+
|
|
150
|
+
Rebuild and restart the storefront. Then prove the routes answer through it:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
curl -s -X POST <storefront url>/api/openreceive/checkouts \
|
|
154
|
+
-H 'content-type: application/json' \
|
|
155
|
+
-d '{"reference":"CHECK:USD:100"}'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The answer is OpenReceive's JSON refusal, `"code":"FORBIDDEN"`: no session
|
|
159
|
+
owns such an order, and the plugin's routes are mounted behind the
|
|
160
|
+
storefront's route.
|
|
161
|
+
|
|
162
|
+
## Step 4 — hand over, then stop
|
|
163
|
+
|
|
164
|
+
Setup is done when `doctor` is clean and the curl above answers FORBIDDEN.
|
|
165
|
+
The browser check is the user's: name it in one line of your closing message,
|
|
166
|
+
and do not run it yourself. You cannot pay an invoice: the code is
|
|
167
|
+
receive-only. Do not pay, settle or mark an order paid, and do not look for a
|
|
168
|
+
way to (a wallet control port, a test endpoint, another wallet, a payment
|
|
169
|
+
added through the Admin API).
|
|
170
|
+
|
|
171
|
+
Your last message starts "Setup is finished" and has at most five short
|
|
172
|
+
lines: the storefront's address, "add a product to the cart, check out, pick
|
|
173
|
+
Bitcoin & stablecoins at the payment step and place the order: the checkout
|
|
174
|
+
appears", and the methods it offers (Bitcoin, plus USDT, USDC, ETH and SOL
|
|
175
|
+
when the swap code is saved). Do not mention minimums, never say a coin will
|
|
176
|
+
not work, do not list what you changed, and send nothing after it. Do not add
|
|
177
|
+
containers, services, workers or cron jobs: the plugin's task runs in
|
|
178
|
+
Vendure's own worker.
|
|
179
|
+
|
|
180
|
+
## Non-negotiables
|
|
181
|
+
|
|
182
|
+
- Never print, log or commit a code, never put one in a shell argument, and
|
|
183
|
+
never write one into the storefront, source files or browser code.
|
|
184
|
+
`doctor`'s set/unset is all you report.
|
|
185
|
+
- Do not suggest rotating, revoking or replacing a code because it was pasted
|
|
186
|
+
into this chat; that is the supported path.
|
|
187
|
+
- Work only in this shop's projects. Never read or run anything from another
|
|
188
|
+
project or directory on this machine (its `node_modules`, tools or source),
|
|
189
|
+
for any reason. A browser, Playwright and reading the plugin's source are
|
|
190
|
+
not part of setup: when `doctor`, the enable script or the curl fails,
|
|
191
|
+
report its output.
|
|
192
|
+
- Receive-only NWC is required. Never get past the preflight with a code that
|
|
193
|
+
can spend.
|
|
194
|
+
- The plugin owns only its two payment tables. Vendure owns orders, prices,
|
|
195
|
+
customers and email. Do not write your own payment handler, routes,
|
|
196
|
+
event subscribers or fulfillment code, and do not change Vendure's own
|
|
197
|
+
packages.
|
|
198
|
+
- IF SWAPS ARE ON, KEEP THE ROUTE BACK. A deposit that arrives short or late
|
|
199
|
+
becomes refundable, and the buyer claims it later from the same checkout.
|
|
200
|
+
Keep the plugin installed while swap payments may still need a refund.
|
|
201
|
+
https://openreceive.org/guides/swap-refunds.md
|
|
202
|
+
- A receive-only wallet cannot send refunds. A refund made in the Vendure
|
|
203
|
+
dashboard stays pending; the merchant sends it from their own wallet.
|
|
204
|
+
|
|
205
|
+
## Further reading
|
|
206
|
+
|
|
207
|
+
- [Vendure Quickstart](https://openreceive.org/guides/quickstart-vendure.md)
|
|
208
|
+
- [Automated Swaps](https://openreceive.org/guides/automated-swaps.md)
|
|
209
|
+
- [Swap Refunds](https://openreceive.org/guides/swap-refunds.md)
|
|
210
|
+
- [Lightning Swap Connect URI](https://openreceive.org/guides/lightning-swap-connect.md)
|
|
211
|
+
- [Security](https://openreceive.org/guides/security.md)
|
|
212
|
+
- [Price Feeds](https://openreceive.org/guides/price-feeds.md)
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## The quickstart, in full
|
|
217
|
+
|
|
218
|
+
Inlined verbatim so this file needs no network access. Steps 0–4 above are the setup and this is their reference: it has the code they name. Where the two differ, the steps win, and its dashboard screens are only for a shop whose Vendure server has no shell.
|
|
219
|
+
The page it comes from is https://openreceive.org/guides/quickstart-vendure.
|
|
220
|
+
|
|
221
|
+
## Vendure quickstart
|
|
222
|
+
|
|
223
|
+
[Vendure](https://vendure.io) is an open-source headless commerce platform on
|
|
224
|
+
Node. The OpenReceive plugin adds a payment method to it: the buyer pays at
|
|
225
|
+
the storefront's checkout with Bitcoin over Lightning, straight into your own
|
|
226
|
+
wallet, and Vendure places the order once the payment settles. With a swap
|
|
227
|
+
provider, the same checkout also takes USDT, USDC, SOL and ETH, which the
|
|
228
|
+
provider converts to BTC over Lightning into the same wallet.
|
|
229
|
+
|
|
230
|
+
Vendure keeps owning orders, prices, customers and fulfillment. The plugin
|
|
231
|
+
keeps its payment attempts in two tables of Vendure's own database, over
|
|
232
|
+
Vendure's own connection. When a payment settles, the plugin adds a settled
|
|
233
|
+
payment to the order, with the Lightning payment hash as its transaction id,
|
|
234
|
+
and Vendure places the order as it does for any payment method: the order
|
|
235
|
+
reads PaymentSettled, stock is allocated and Vendure's emails go out.
|
|
236
|
+
|
|
237
|
+
Requirements: Vendure 3.3 or later on Node 22.12 or later, with PostgreSQL,
|
|
238
|
+
MySQL or MariaDB. SQLite and sql.js are not supported: Vendure shares their
|
|
239
|
+
one connection with the plugin. The worker must run, with
|
|
240
|
+
`DefaultSchedulerPlugin`, as a new Vendure project's does: the plugin's
|
|
241
|
+
scheduled task runs there.
|
|
242
|
+
|
|
243
|
+
### Install the plugin
|
|
244
|
+
|
|
245
|
+
In the Vendure server's project:
|
|
246
|
+
|
|
247
|
+
```sh
|
|
248
|
+
npm install @openreceive/vendure
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Add the plugin to `src/vendure-config.ts`, next to what is already there:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
import { OpenReceivePlugin } from "@openreceive/vendure/plugin";
|
|
255
|
+
|
|
256
|
+
export const config: VendureConfig = {
|
|
257
|
+
// …your existing settings
|
|
258
|
+
plugins: [
|
|
259
|
+
// …your existing plugins
|
|
260
|
+
OpenReceivePlugin,
|
|
261
|
+
],
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The plugin registers the payment method handler `openreceive`, the
|
|
266
|
+
checkout's routes under `/openreceive` on the Vendure server, a scheduled
|
|
267
|
+
task that runs every minute on the worker, and a migration for its two
|
|
268
|
+
tables. Restart the server and the worker. A new Vendure project runs its
|
|
269
|
+
migrations when the server starts (`runMigrations` in `src/index.ts`), which
|
|
270
|
+
creates the tables; if yours does not, run its migrations the way it already
|
|
271
|
+
does.
|
|
272
|
+
|
|
273
|
+
### Configure the wallet
|
|
274
|
+
|
|
275
|
+
Put the codes in the Vendure server's `.env`, never in the storefront:
|
|
276
|
+
|
|
277
|
+
```sh
|
|
278
|
+
NWC_URI=nostr+walletconnect://…
|
|
279
|
+
LSC_URI_PRIMARY=lightning+swapconnect://…
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`NWC_URI` is a receive-only Nostr Wallet Connect code: it can create invoices
|
|
283
|
+
and read payments but cannot spend, and the plugin refuses a code that can
|
|
284
|
+
spend. `LSC_URI_PRIMARY` is optional; leave it out for Bitcoin-only checkout.
|
|
285
|
+
A new Vendure project loads `.env` itself (`import 'dotenv/config'` in
|
|
286
|
+
`vendure-config.ts`). Under Docker Compose, give the server and the worker
|
|
287
|
+
`env_file: .env`. Restart both after changing it.
|
|
288
|
+
|
|
289
|
+
Then create the payment method. In the dashboard, open **Settings → Payment
|
|
290
|
+
methods**, create one with the code `openreceive` and the handler
|
|
291
|
+
**OpenReceive**, and enable it. Or run, in the server's project while the
|
|
292
|
+
server is up:
|
|
293
|
+
|
|
294
|
+
```sh
|
|
295
|
+
node node_modules/@openreceive/vendure/dist/enable-openreceive.js
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
It signs in to the Admin API with `SUPERADMIN_USERNAME` and
|
|
299
|
+
`SUPERADMIN_PASSWORD` from `.env`, as a new Vendure project has them, and
|
|
300
|
+
creates the method in the default channel: "Bitcoin & stablecoins" with a
|
|
301
|
+
swap code, "Bitcoin Lightning" without. Run again, it enables the method.
|
|
302
|
+
`--admin-api <url>` names another Admin API and `--name "…"` another name.
|
|
303
|
+
Check the codes and the wallet with:
|
|
304
|
+
|
|
305
|
+
```sh
|
|
306
|
+
npx openreceive doctor
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`doctor` reads `.env`, prints each code as set or unset (never its value)
|
|
310
|
+
and asks the wallet whether it is receive-only. With
|
|
311
|
+
`--url http://localhost:3000` it also checks that the plugin's routes answer.
|
|
312
|
+
It exits nonzero when a check fails.
|
|
313
|
+
|
|
314
|
+
### Add the checkout to the storefront
|
|
315
|
+
|
|
316
|
+
The buyer pays at the checkout's review step, before the order is placed.
|
|
317
|
+
For Vendure's [Next.js starter](https://github.com/vendurehq/nextjs-starter-vendure),
|
|
318
|
+
install the React checkout:
|
|
319
|
+
|
|
320
|
+
```sh
|
|
321
|
+
npm install @openreceive/react
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Add `src/app/api/openreceive/[...path]/route.ts`. It forwards the checkout's
|
|
325
|
+
calls to the Vendure server with the buyer's session token, which the starter
|
|
326
|
+
keeps in its `vendure-auth-token` cookie, and serves the checkout's
|
|
327
|
+
stylesheet as it is, outside the starter's Tailwind build:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
import { readFile } from "node:fs/promises";
|
|
331
|
+
import { createRequire } from "node:module";
|
|
332
|
+
import path from "node:path";
|
|
333
|
+
import { NextRequest } from "next/server";
|
|
334
|
+
import { getAuthToken } from "@/platform/vendure/auth-token";
|
|
335
|
+
|
|
336
|
+
const SHOP_API = process.env.VENDURE_SHOP_API_URL || process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL!;
|
|
337
|
+
const CHANNEL =
|
|
338
|
+
process.env.VENDURE_CHANNEL_TOKEN || process.env.NEXT_PUBLIC_VENDURE_CHANNEL_TOKEN || "__default_channel__";
|
|
339
|
+
|
|
340
|
+
async function stylesheet() {
|
|
341
|
+
const resolve = createRequire(path.join(process.cwd(), "package.json")).resolve;
|
|
342
|
+
const css = await readFile(resolve("@openreceive/react/styles.css"));
|
|
343
|
+
return new Response(css, {
|
|
344
|
+
headers: { "content-type": "text/css; charset=utf-8", "cache-control": "public, max-age=3600" },
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function forward(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
|
349
|
+
const { path: segments } = await params;
|
|
350
|
+
if (request.method === "GET" && segments.join("/") === "styles.css") return stylesheet();
|
|
351
|
+
const url = new URL(`/openreceive/${segments.join("/")}`, SHOP_API);
|
|
352
|
+
url.search = request.nextUrl.search;
|
|
353
|
+
const token = await getAuthToken();
|
|
354
|
+
const response = await fetch(url, {
|
|
355
|
+
method: request.method,
|
|
356
|
+
headers: {
|
|
357
|
+
"content-type": request.headers.get("content-type") ?? "application/json",
|
|
358
|
+
accept: "application/json",
|
|
359
|
+
"vendure-token": CHANNEL,
|
|
360
|
+
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
361
|
+
},
|
|
362
|
+
body: request.method === "GET" ? undefined : await request.text(),
|
|
363
|
+
cache: "no-store",
|
|
364
|
+
});
|
|
365
|
+
const headers = new Headers({ "cache-control": "no-store" });
|
|
366
|
+
for (const name of ["content-type", "retry-after", "x-request-id"]) {
|
|
367
|
+
const value = response.headers.get(name);
|
|
368
|
+
if (value) headers.set(name, value);
|
|
369
|
+
}
|
|
370
|
+
return new Response(await response.text(), { status: response.status, headers });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export { forward as GET, forward as POST };
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Add `src/features/checkout/openreceive.ts`, two server actions. The first
|
|
377
|
+
asks the plugin to start paying the active order: the plugin moves it to
|
|
378
|
+
ArrangingPayment, as Vendure's own checkout does before a payment, and
|
|
379
|
+
answers the reference the checkout pays. The second runs once the payment
|
|
380
|
+
settled, when the plugin has placed the order:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
"use server";
|
|
384
|
+
|
|
385
|
+
import { updateTag } from "next/cache";
|
|
386
|
+
import { getLocale } from "next-intl/server";
|
|
387
|
+
import { redirect } from "@/platform/i18n/navigation";
|
|
388
|
+
import { getAuthToken } from "@/platform/vendure/auth-token";
|
|
389
|
+
|
|
390
|
+
const SHOP_API = process.env.VENDURE_SHOP_API_URL || process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL!;
|
|
391
|
+
const CHANNEL =
|
|
392
|
+
process.env.VENDURE_CHANNEL_TOKEN || process.env.NEXT_PUBLIC_VENDURE_CHANNEL_TOKEN || "__default_channel__";
|
|
393
|
+
|
|
394
|
+
export async function startOpenReceivePayment(): Promise<{ reference: string; orderCode: string }> {
|
|
395
|
+
const token = await getAuthToken();
|
|
396
|
+
const response = await fetch(new URL("/openreceive/order", SHOP_API), {
|
|
397
|
+
method: "POST",
|
|
398
|
+
headers: {
|
|
399
|
+
"content-type": "application/json",
|
|
400
|
+
"vendure-token": CHANNEL,
|
|
401
|
+
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
402
|
+
},
|
|
403
|
+
body: "{}",
|
|
404
|
+
cache: "no-store",
|
|
405
|
+
});
|
|
406
|
+
const body = await response.json();
|
|
407
|
+
if (!response.ok) throw new Error(body.message ?? `OpenReceive answered ${response.status}`);
|
|
408
|
+
return { reference: body.reference, orderCode: body.order_code };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export async function finishOpenReceivePayment(orderCode: string) {
|
|
412
|
+
updateTag("cart");
|
|
413
|
+
updateTag("active-order");
|
|
414
|
+
const locale = await getLocale();
|
|
415
|
+
redirect({ href: `/order-confirmation/${orderCode}`, locale });
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
In `src/features/checkout/routes/steps/review-step.tsx`, show the checkout
|
|
420
|
+
when the buyer places an order with OpenReceive:
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
import { finishOpenReceivePayment, startOpenReceivePayment } from "../../openreceive";
|
|
424
|
+
import { Checkout } from "@openreceive/react";
|
|
425
|
+
|
|
426
|
+
// The code of the OpenReceive payment method (enable-openreceive.js creates it).
|
|
427
|
+
const OPENRECEIVE_METHOD = "openreceive";
|
|
428
|
+
|
|
429
|
+
// in ReviewStep, beside the existing state:
|
|
430
|
+
const [openReceive, setOpenReceive] = useState<{ reference: string; orderCode: string } | null>(null);
|
|
431
|
+
const [paymentError, setPaymentError] = useState<string | null>(null);
|
|
432
|
+
|
|
433
|
+
// first in handlePlaceOrder, after setLoading(true):
|
|
434
|
+
if (selectedPaymentMethodCode === OPENRECEIVE_METHOD) {
|
|
435
|
+
// The buyer pays here; the plugin places the order once the payment settles.
|
|
436
|
+
try {
|
|
437
|
+
setOpenReceive(await startOpenReceivePayment());
|
|
438
|
+
} catch (error) {
|
|
439
|
+
setPaymentError(error instanceof Error ? error.message : String(error));
|
|
440
|
+
} finally {
|
|
441
|
+
setLoading(false);
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// in place of the Place Order <Button>:
|
|
447
|
+
{openReceive ? (
|
|
448
|
+
<>
|
|
449
|
+
<link rel="stylesheet" href="/api/openreceive/styles.css" precedence="default" />
|
|
450
|
+
<Checkout
|
|
451
|
+
reference={openReceive.reference}
|
|
452
|
+
prefix="/api/openreceive"
|
|
453
|
+
resumable
|
|
454
|
+
onSettled={() => {
|
|
455
|
+
void finishOpenReceivePayment(openReceive.orderCode);
|
|
456
|
+
}}
|
|
457
|
+
/>
|
|
458
|
+
</>
|
|
459
|
+
) : (
|
|
460
|
+
<Button onClick={handlePlaceOrder} /* …as it was */>…</Button>
|
|
461
|
+
)}
|
|
462
|
+
{paymentError && <p className="text-sm text-destructive text-center">{paymentError}</p>}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The order now waits in ArrangingPayment while the buyer pays. In
|
|
466
|
+
`src/features/checkout/routes/actions.ts`, let `transitionToArrangingPayment`
|
|
467
|
+
accept an order that is already there, so a buyer who opened the Bitcoin
|
|
468
|
+
checkout can still pay with another method:
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
if (result.data.transitionOrderToState?.__typename === "OrderStateTransitionError") {
|
|
472
|
+
const errorResult = result.data.transitionOrderToState;
|
|
473
|
+
// The OpenReceive checkout already moved it: it is ready to pay another way.
|
|
474
|
+
if (errorResult.fromState === "ArrangingPayment") return;
|
|
475
|
+
throw new Error(/* …as it was */);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### Another storefront
|
|
480
|
+
|
|
481
|
+
Where the buyer confirms the order, `POST /openreceive/order` on the Vendure
|
|
482
|
+
server with the buyer's session (the cookie or bearer token the storefront
|
|
483
|
+
already sends Vendure, plus `vendure-token` on a channel other than the
|
|
484
|
+
default) and an empty JSON body. Render React `<Checkout>`, or the
|
|
485
|
+
`<openreceive-checkout>` element from `@openreceive/elements`, with the
|
|
486
|
+
`reference` it answers, and send the checkout's calls to `/openreceive` on
|
|
487
|
+
the Vendure server with the same session: forward them from your
|
|
488
|
+
storefront's server as above, or call Vendure from the browser with a `fetch`
|
|
489
|
+
that sends the session. A browser call from another site needs the
|
|
490
|
+
storefront's exact origin in Vendure's `apiOptions.cors.origin`; a pattern or
|
|
491
|
+
`true` is not enough. When the checkout reports the payment settled, the
|
|
492
|
+
order is placed: show its confirmation.
|
|
493
|
+
|
|
494
|
+
### How a payment works
|
|
495
|
+
|
|
496
|
+
1. The buyer picks "Bitcoin & stablecoins" at the payment step and places
|
|
497
|
+
the order. The plugin moves the order to ArrangingPayment and answers its
|
|
498
|
+
reference: the order's code, currency and amount due.
|
|
499
|
+
2. The checkout charges that amount in the order's currency, converted to
|
|
500
|
+
sats at the current BTC price. Only the buyer's session, or the signed-in
|
|
501
|
+
customer the order belongs to, may pay it. A Shop API call cannot add an
|
|
502
|
+
OpenReceive payment, so a buyer cannot mark an order paid.
|
|
503
|
+
3. Once the wallet reports the payment, the plugin adds a settled payment to
|
|
504
|
+
the order, and Vendure places it.
|
|
505
|
+
4. The checkout takes the buyer to the order's confirmation.
|
|
506
|
+
|
|
507
|
+
If the buyer closes the page after paying, the scheduled task finds the
|
|
508
|
+
payment within a minute or so and places the order anyway. If the cart
|
|
509
|
+
changes before the buyer pays, the amount due changes and so does the
|
|
510
|
+
reference: the checkout asks for a new invoice for the new total. Vendure
|
|
511
|
+
keeps an order in ArrangingPayment from changing; a storefront that lets the
|
|
512
|
+
buyer go back to the cart moves it back with
|
|
513
|
+
`transitionOrderToState(state: "AddingItems")`.
|
|
514
|
+
|
|
515
|
+
### Refunds and removal
|
|
516
|
+
|
|
517
|
+
The receive-only wallet cannot send refunds. A refund made in the dashboard
|
|
518
|
+
stays pending in Vendure, and the plugin logs a warning naming the payment:
|
|
519
|
+
send the refund from your own wallet, then settle the refund in the
|
|
520
|
+
dashboard. A buyer whose swap deposit never converted reclaims it from the
|
|
521
|
+
same checkout, with the same session, while the order is still unpaid. If you
|
|
522
|
+
turn on swaps, keep that path available; see
|
|
523
|
+
[swap refunds](https://openreceive.org/guides/swap-refunds.md).
|
|
524
|
+
|
|
525
|
+
Removing the plugin keeps its two tables: they are a record of payments
|
|
526
|
+
received.
|
|
527
|
+
|
|
528
|
+
### Tested, and not tested
|
|
529
|
+
|
|
530
|
+
Tested on 2026-10-19 with Vendure 3.8.0 on PostgreSQL 17 in Docker, its
|
|
531
|
+
worker and the Next.js starter, a test wallet and a test swap provider: the
|
|
532
|
+
migration, the enable script, `doctor`, and a guest paying an order by
|
|
533
|
+
Lightning and by USDT on Solana in Chromium, after which the order reads
|
|
534
|
+
PaymentSettled with the payment hash as its transaction id. A payment whose
|
|
535
|
+
buyer left was placed by the scheduled task. Another guest, a visitor with no
|
|
536
|
+
session, a changed amount and the Shop API's `addPaymentToOrder` were
|
|
537
|
+
refused, a reload resumed the same invoice, and a buyer who opened the
|
|
538
|
+
Bitcoin checkout then paid with Vendure's test method. CI runs the plugin
|
|
539
|
+
against a Vendure 3.8 server and PostgreSQL.
|
|
540
|
+
|
|
541
|
+
Not tested: MySQL and MariaDB, more than one channel, Vendure's other
|
|
542
|
+
storefront starters, and a storefront calling Vendure from the browser on
|
|
543
|
+
another site. Lightning cannot pull a payment, so OpenReceive saves no
|
|
544
|
+
payment methods.
|
|
@@ -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.30.
|
|
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
|
|
@@ -95,7 +95,7 @@ is saved or the user said "Bitcoin only". Never invent a placeholder code.
|
|
|
95
95
|
Install this release from the WordPress.org plugin directory:
|
|
96
96
|
|
|
97
97
|
```sh
|
|
98
|
-
wp plugin install openreceive --version=0.4.
|
|
98
|
+
wp plugin install openreceive --version=0.4.30 --activate
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
If that fails because WordPress.org does not have this version yet, install
|
|
@@ -103,7 +103,7 @@ the ZIP attached to the same GitHub release. Never install the GitHub
|
|
|
103
103
|
source-code ZIP or a ZIP from an older release:
|
|
104
104
|
|
|
105
105
|
```sh
|
|
106
|
-
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.30/openreceive-wordpress-0.4.30.zip --activate
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
It needs WooCommerce active, and PHP 8.2+ with GMP and sodium in BOTH the web
|
|
@@ -222,7 +222,6 @@ question.
|
|
|
222
222
|
- [Lightning Swap Connect URI](https://openreceive.org/guides/lightning-swap-connect.md)
|
|
223
223
|
- [Security](https://openreceive.org/guides/security.md)
|
|
224
224
|
- [Price Feeds](https://openreceive.org/guides/price-feeds.md)
|
|
225
|
-
- [Payment Safety Upgrade](https://openreceive.org/guides/payment-safety-upgrade.md)
|
|
226
225
|
|
|
227
226
|
---
|
|
228
227
|
|
|
@@ -254,7 +253,7 @@ database or application.
|
|
|
254
253
|
|
|
255
254
|
Each GitHub release also attaches the same plugin as a ZIP, for a site that
|
|
256
255
|
cannot reach WordPress.org or when WordPress.org does not list a release yet.
|
|
257
|
-
Download [openreceive-wordpress-0.4.
|
|
256
|
+
Download [openreceive-wordpress-0.4.30.zip](https://github.com/OpenReceive/openreceive/releases/download/v0.4.30/openreceive-wordpress-0.4.30.zip)
|
|
258
257
|
and install it through **Plugins → Add New → Upload Plugin**. Historical
|
|
259
258
|
releases may lack this asset. If that exact URL returns 404, build the same tag
|
|
260
259
|
below; never silently install an older ZIP. The GitHub source-code ZIP is not
|
|
@@ -264,7 +263,7 @@ GMP/sodium, Composer and WP-CLI:
|
|
|
264
263
|
```sh
|
|
265
264
|
git clone https://github.com/OpenReceive/openreceive.git
|
|
266
265
|
cd openreceive
|
|
267
|
-
git checkout v0.4.
|
|
266
|
+
git checkout v0.4.30
|
|
268
267
|
npm ci
|
|
269
268
|
npm run build:packages
|
|
270
269
|
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.30
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenReceive
|
|
@@ -73,12 +73,15 @@ files:
|
|
|
73
73
|
- skills/integrate-openreceive/references/next.md
|
|
74
74
|
- skills/integrate-openreceive/references/node.md
|
|
75
75
|
- skills/integrate-openreceive/references/nuxt.md
|
|
76
|
+
- skills/integrate-openreceive/references/odoo.md
|
|
76
77
|
- skills/integrate-openreceive/references/paymenter.md
|
|
77
78
|
- skills/integrate-openreceive/references/php.md
|
|
79
|
+
- skills/integrate-openreceive/references/prestashop.md
|
|
78
80
|
- skills/integrate-openreceive/references/rails.md
|
|
79
81
|
- skills/integrate-openreceive/references/react-router.md
|
|
80
82
|
- skills/integrate-openreceive/references/sveltekit.md
|
|
81
83
|
- skills/integrate-openreceive/references/tanstack-start.md
|
|
84
|
+
- skills/integrate-openreceive/references/vendure.md
|
|
82
85
|
- skills/integrate-openreceive/references/woocommerce.md
|
|
83
86
|
homepage: https://openreceive.org/integrations/rails
|
|
84
87
|
licenses:
|