@openreceive/node 0.4.13 → 0.4.16
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.
- package/README.md +11 -0
- package/dist/{chunk-7WTXLEDH.js → chunk-LD7HMUY3.js} +6 -3
- package/dist/cli.js +45 -10
- package/dist/index.js +6 -6
- package/package.json +3 -3
- package/skills/debug-openreceive-payment/SKILL.md +20 -4
- package/skills/integrate-openreceive/SKILL.md +42 -21
- package/skills/integrate-openreceive/references/btcpay.md +5 -1
- package/skills/integrate-openreceive/references/django.md +27 -16
- package/skills/integrate-openreceive/references/fastapi.md +8 -4
- package/skills/integrate-openreceive/references/fastify.md +11 -5
- package/skills/integrate-openreceive/references/laravel.md +8 -4
- package/skills/integrate-openreceive/references/next.md +11 -5
- package/skills/integrate-openreceive/references/node.md +11 -5
- package/skills/integrate-openreceive/references/php.md +4 -2
- package/skills/integrate-openreceive/references/rails.md +8 -4
- package/skills/integrate-openreceive/references/woocommerce.md +107 -52
package/README.md
CHANGED
|
@@ -48,3 +48,14 @@ exposing payment instructions.
|
|
|
48
48
|
- [Optional swaps](https://github.com/openreceive/openreceive/blob/master/docs/guides/automated-swaps.md)
|
|
49
49
|
- [Test with a fake wallet](https://github.com/openreceive/openreceive/blob/master/docs/guides/host-testing.md)
|
|
50
50
|
- [Deploy and reconcile payments](https://github.com/openreceive/openreceive/blob/master/docs/guides/deploying.md)
|
|
51
|
+
|
|
52
|
+
## Agent skills
|
|
53
|
+
|
|
54
|
+
Run `npx openreceive skills install` from your application after installing an
|
|
55
|
+
OpenReceive server adapter. The offline copy ships in `@openreceive/node`, a
|
|
56
|
+
server-adapter dependency. For a frontend-only project, use
|
|
57
|
+
`npx skills add OpenReceive/openreceive`.
|
|
58
|
+
See [agent setup](https://openreceive.org/agents). The bundled installers write
|
|
59
|
+
to `.agents/skills/`; use `--dir .claude/skills` for Claude Code. They replace
|
|
60
|
+
only `integrate-openreceive` and `debug-openreceive-payment`, preserving
|
|
61
|
+
unrelated skills.
|
|
@@ -28,6 +28,7 @@ var NWC_ERROR_CODE_ALIASES = {
|
|
|
28
28
|
EXPIRED: "INVOICE_EXPIRED",
|
|
29
29
|
FETCH_ERROR: "WALLET_UNAVAILABLE",
|
|
30
30
|
FORBIDDEN: "RESTRICTED",
|
|
31
|
+
INFO_UNAVAILABLE_ERROR: "WALLET_UNAVAILABLE",
|
|
31
32
|
INVOICE_NOT_FOUND: "NOT_FOUND",
|
|
32
33
|
INVALID_PARAMETER: "INVALID_REQUEST",
|
|
33
34
|
INVALID_PARAMETERS: "INVALID_REQUEST",
|
|
@@ -37,6 +38,7 @@ var NWC_ERROR_CODE_ALIASES = {
|
|
|
37
38
|
NIP47_NETWORK_ERROR: "WALLET_UNAVAILABLE",
|
|
38
39
|
NOSTR_NETWORK_ERROR: "WALLET_UNAVAILABLE",
|
|
39
40
|
NOT_AUTHORIZED: "UNAUTHORIZED",
|
|
41
|
+
NOT_SENT_ERROR: "WALLET_UNAVAILABLE",
|
|
40
42
|
NOT_SUPPORTED: "UNSUPPORTED_METHOD",
|
|
41
43
|
NOTFOUND: "NOT_FOUND",
|
|
42
44
|
PERMISSION_DENIED: "RESTRICTED",
|
|
@@ -45,6 +47,7 @@ var NWC_ERROR_CODE_ALIASES = {
|
|
|
45
47
|
SERVICE_UNAVAILABLE: "WALLET_UNAVAILABLE",
|
|
46
48
|
TIMED_OUT: "TIMEOUT",
|
|
47
49
|
TIMEOUT_ERROR: "TIMEOUT",
|
|
50
|
+
TRANSPORT_ERROR: "WALLET_UNAVAILABLE",
|
|
48
51
|
UNKNOWN_METHOD: "UNSUPPORTED_METHOD",
|
|
49
52
|
UNSUPPORTED: "UNSUPPORTED_METHOD",
|
|
50
53
|
UNSUPPORTED_ENCRYPTION_MODE: "UNSUPPORTED_ENCRYPTION",
|
|
@@ -488,8 +491,8 @@ function unwrapNwcResult(value) {
|
|
|
488
491
|
}
|
|
489
492
|
|
|
490
493
|
// src/nwc/transport.ts
|
|
491
|
-
import { createRequire } from "module";
|
|
492
|
-
import { pathToFileURL } from "url";
|
|
494
|
+
import { createRequire } from "node:module";
|
|
495
|
+
import { pathToFileURL } from "node:url";
|
|
493
496
|
import { recordOrEmpty as recordOrEmpty2 } from "@openreceive/core";
|
|
494
497
|
|
|
495
498
|
// src/nwc/history-request.ts
|
|
@@ -1815,7 +1818,7 @@ function fixedFloatAvailabilityMessage(reason) {
|
|
|
1815
1818
|
}
|
|
1816
1819
|
|
|
1817
1820
|
// src/swap/fixedfloat-transport.ts
|
|
1818
|
-
import { createHmac } from "crypto";
|
|
1821
|
+
import { createHmac } from "node:crypto";
|
|
1819
1822
|
import { isRecord } from "@openreceive/core";
|
|
1820
1823
|
var FixedFloatApiError = class _FixedFloatApiError extends Error {
|
|
1821
1824
|
path;
|
package/dist/cli.js
CHANGED
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
createNwcReceiveClient,
|
|
3
3
|
readLscConnectionsFromEnvironment,
|
|
4
4
|
redactSecrets
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LD7HMUY3.js";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
|
-
import { existsSync } from "fs";
|
|
9
|
-
import { createRequire } from "module";
|
|
10
|
-
import path2 from "path";
|
|
11
|
-
import { pathToFileURL } from "url";
|
|
8
|
+
import { cpSync, existsSync, mkdirSync, realpathSync, rmSync } from "node:fs";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import path2 from "node:path";
|
|
11
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
12
12
|
import {
|
|
13
13
|
formatInvalidNwcMessage,
|
|
14
14
|
NwcUriParseError,
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
} from "@openreceive/core";
|
|
18
18
|
|
|
19
19
|
// src/scaffold/index.ts
|
|
20
|
-
import { createInterface } from "readline/promises";
|
|
21
|
-
import { stdin as defaultStdin, stdout as defaultStdout } from "process";
|
|
20
|
+
import { createInterface } from "node:readline/promises";
|
|
21
|
+
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
22
22
|
|
|
23
23
|
// src/scaffold/shared.ts
|
|
24
24
|
import { paymentsDdlStatements } from "@openreceive/core";
|
|
@@ -777,8 +777,8 @@ async function promptChoice(prompt, label, choices, fallback) {
|
|
|
777
777
|
}
|
|
778
778
|
|
|
779
779
|
// src/scaffold/write-files.ts
|
|
780
|
-
import { mkdir, writeFile, access } from "fs/promises";
|
|
781
|
-
import path from "path";
|
|
780
|
+
import { mkdir, writeFile, access } from "node:fs/promises";
|
|
781
|
+
import path from "node:path";
|
|
782
782
|
async function writeScaffoldFiles(input) {
|
|
783
783
|
const root = path.resolve(input.cwd, input.outDir);
|
|
784
784
|
const planned = input.files.map((file) => ({
|
|
@@ -928,6 +928,8 @@ Commands:
|
|
|
928
928
|
debug-report Print the same diagnostics as a redacted support report
|
|
929
929
|
(alias of doctor; always exits 0).
|
|
930
930
|
scaffold payments Emit the openreceive_payments + openreceive_meta migration and wiring guide for your ORM.
|
|
931
|
+
skills install Copy bundled agent skills into .agents/skills.
|
|
932
|
+
--dir <path> selects another directory (e.g. .claude/skills).
|
|
931
933
|
|
|
932
934
|
Options:
|
|
933
935
|
-h, --help Show this help.
|
|
@@ -965,6 +967,33 @@ async function runCli(options) {
|
|
|
965
967
|
walletClientFactory: options.walletClientFactory
|
|
966
968
|
});
|
|
967
969
|
}
|
|
970
|
+
if (command === "skills") {
|
|
971
|
+
if (args[0] !== "install" || !(args.length === 1 || args.length === 3 && args[1] === "--dir" && args[2] && !args[2].startsWith("--"))) {
|
|
972
|
+
throw new Error("Usage: openreceive skills install [--dir <path>]");
|
|
973
|
+
}
|
|
974
|
+
const source = realpathSync(fileURLToPath(new URL("../skills/", import.meta.url)));
|
|
975
|
+
let target = path2.resolve(cwd, args[2] ?? ".agents/skills");
|
|
976
|
+
const names = ["integrate-openreceive", "debug-openreceive-payment"];
|
|
977
|
+
for (const name of names) {
|
|
978
|
+
if (!existsSync(path2.join(source, name, "SKILL.md"))) {
|
|
979
|
+
throw new Error(`Bundled skill missing: ${name}. Reinstall @openreceive/node.`);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
mkdirSync(target, { recursive: true });
|
|
983
|
+
target = realpathSync(target);
|
|
984
|
+
if (target === source || target.startsWith(`${source}${path2.sep}`)) {
|
|
985
|
+
throw new Error("Choose a skills directory outside the installed package's bundle.");
|
|
986
|
+
}
|
|
987
|
+
for (const name of names) {
|
|
988
|
+
const destination = path2.join(target, name);
|
|
989
|
+
rmSync(destination, { recursive: true, force: true });
|
|
990
|
+
cpSync(path2.join(source, name), destination, { recursive: true });
|
|
991
|
+
stdout.write(`Wrote ${destination}
|
|
992
|
+
`);
|
|
993
|
+
}
|
|
994
|
+
stdout.write("For Claude Code: openreceive skills install --dir .claude/skills\n");
|
|
995
|
+
return 0;
|
|
996
|
+
}
|
|
968
997
|
if (command === "scaffold") {
|
|
969
998
|
const [target = "help", ...scaffoldArgs] = args;
|
|
970
999
|
if (target === "help" || target === "--help" || target === "-h") {
|
|
@@ -1047,6 +1076,7 @@ async function runDiagnostics(input) {
|
|
|
1047
1076
|
}
|
|
1048
1077
|
const lines = [
|
|
1049
1078
|
`OpenReceive ${input.command}`,
|
|
1079
|
+
"Agent skills: run `npx openreceive skills install`",
|
|
1050
1080
|
`node: ${process.version}`,
|
|
1051
1081
|
`cwd: ${input.cwd}`,
|
|
1052
1082
|
"storage: payment-attempt rows live in the host database (no separate store)",
|
|
@@ -1202,7 +1232,12 @@ async function listPresentTables(input) {
|
|
|
1202
1232
|
`no SQLite database at ${sqlitePath}. Pass the file your app opens, or a postgres:// / mysql:// URL.`
|
|
1203
1233
|
);
|
|
1204
1234
|
}
|
|
1205
|
-
const { DatabaseSync } = await import("sqlite")
|
|
1235
|
+
const { DatabaseSync } = await import("node:sqlite").catch((error) => {
|
|
1236
|
+
if (error.code !== "ERR_UNKNOWN_BUILTIN_MODULE") throw error;
|
|
1237
|
+
throw new Error(
|
|
1238
|
+
`Node ${process.version} keeps node:sqlite behind a flag. Rerun as \`NODE_OPTIONS=--experimental-sqlite npx openreceive doctor \u2026\`, or upgrade to Node 22.13 or newer.`
|
|
1239
|
+
);
|
|
1240
|
+
});
|
|
1206
1241
|
const database = new DatabaseSync(sqlitePath, { readOnly: true });
|
|
1207
1242
|
try {
|
|
1208
1243
|
const rows = database.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (?, ?)").all(...tables);
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
summarizeReconcilePass,
|
|
20
20
|
summarizeSwapProviderApiRequest,
|
|
21
21
|
summarizeSwapProviderApiResponse
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-LD7HMUY3.js";
|
|
23
23
|
|
|
24
24
|
// src/index.ts
|
|
25
25
|
import { OpenReceiveError as OpenReceiveError2 } from "@openreceive/core";
|
|
@@ -52,13 +52,13 @@ var ConfigError = class extends Error {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
// src/service/file-logger.ts
|
|
55
|
-
import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from "fs";
|
|
56
|
-
import { appendFile } from "fs/promises";
|
|
57
|
-
import path from "path";
|
|
55
|
+
import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
56
|
+
import { appendFile } from "node:fs/promises";
|
|
57
|
+
import path from "node:path";
|
|
58
58
|
import { compact as compact2 } from "@openreceive/core";
|
|
59
59
|
|
|
60
60
|
// src/console-logger.ts
|
|
61
|
-
import { inspect } from "util";
|
|
61
|
+
import { inspect } from "node:util";
|
|
62
62
|
import { compact } from "@openreceive/core";
|
|
63
63
|
|
|
64
64
|
// src/log-level.ts
|
|
@@ -789,7 +789,7 @@ var SWAP_STATE_COPY = {
|
|
|
789
789
|
},
|
|
790
790
|
awaiting_deposit: {
|
|
791
791
|
label: "Waiting for your payment",
|
|
792
|
-
detail: "Send exactly the amount shown
|
|
792
|
+
detail: "Send exactly the amount shown."
|
|
793
793
|
},
|
|
794
794
|
confirming: {
|
|
795
795
|
label: "Confirming payment",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openreceive/node",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.16",
|
|
4
4
|
"description": "Accept Bitcoin Lightning payments in Node.js with your own wallet and optional USDT, USDC, SOL and ETH swaps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@getalby/sdk": "^8.0.3",
|
|
20
|
-
"@openreceive/core": "0.4.
|
|
20
|
+
"@openreceive/core": "0.4.16"
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
23
|
"openreceive": "./bin/openreceive.mjs"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"LICENSE"
|
|
41
41
|
],
|
|
42
42
|
"scripts": {
|
|
43
|
-
"build": "tsup
|
|
43
|
+
"build": "tsup",
|
|
44
44
|
"prepack": "npm run build"
|
|
45
45
|
},
|
|
46
46
|
"license": "MIT",
|
|
@@ -19,6 +19,12 @@ URL below is raw markdown — fetch it when the step needs it.
|
|
|
19
19
|
npx openreceive doctor # Node version, NWC_URI, swap config, wallet probe
|
|
20
20
|
npx openreceive doctor --db <db> # + are openreceive_payments/openreceive_meta migrated?
|
|
21
21
|
npx openreceive doctor --url http://localhost:3000 # + are the routes actually mounted?
|
|
22
|
+
bin/rails openreceive:doctor # Rails
|
|
23
|
+
manage.py openreceive_doctor # Django
|
|
24
|
+
openreceive doctor --app main:app # FastAPI
|
|
25
|
+
php artisan openreceive:doctor # Laravel
|
|
26
|
+
wp openreceive doctor # WordPress
|
|
27
|
+
php bin/doctor # plain PHP: host script calling $engine->doctor()
|
|
22
28
|
```
|
|
23
29
|
|
|
24
30
|
Each failing line states its own fix. `npx openreceive debug-report` prints the
|
|
@@ -32,7 +38,7 @@ same diagnostics redacted, always exit 0 — safe to share.
|
|
|
32
38
|
| `INVALID_NWC` / "not a valid NWC code" | The value is malformed (must be `nostr+walletconnect://` with 64-hex pubkey and secret, ≥1 `wss` relay). Re-copy it from the wallet. |
|
|
33
39
|
| "NOT receive-only" / spend methods advertised | The wallet minted a spend-capable code; OpenReceive fails closed because a leak would drain the wallet. Mint a receive-only code. Overriding (`allowSpendCapableWallet` / `OPENRECEIVE_ALLOW_SPEND_CAPABLE_NWC`) is a last resort. |
|
|
34
40
|
| Wallet preflight failed (methods/encryption) | The wallet must advertise `make_invoice` + `list_transactions` and NIP-04 or NIP-44 v2. Use a compatible wallet. |
|
|
35
|
-
| "The openreceive_meta table does not exist" / raw `no such table: openreceive_payments` | The migration was never applied. Node: `npx openreceive scaffold payments --orm <yours>`, then run the emitted migration through the app's normal workflow. Rails: `bin/rails generate openreceive:install`, then `bin/rails db:migrate`. https://openreceive.org/guides/storage.md |
|
|
41
|
+
| "The openreceive_meta table does not exist" / raw `no such table: openreceive_payments` | The migration was never applied. Node: `npx openreceive scaffold payments --orm <yours>`, then run the emitted migration through the app's normal workflow. Rails: `bin/rails generate openreceive:install`, then `bin/rails db:migrate`. Django: `manage.py openreceive_install <app> && manage.py migrate`. FastAPI: `openreceive scaffold payments --alembic --dialect <db>` (or `--sql`), then apply through the host workflow. Laravel: `php artisan openreceive:install && php artisan migrate`. Plain PHP: apply `OpenReceive\Storage\PaymentsSchema::statements($dialect)` through the host workflow. WordPress: check plugin activation/upgrades applied the tables. https://openreceive.org/guides/storage.md |
|
|
36
42
|
| "requires amountFor / onPaid / authorize / host" | The factory is missing a required hook — see the host contract in https://openreceive.org/guides/api-reference.md |
|
|
37
43
|
|
|
38
44
|
## 3. Request-time errors from the routes
|
|
@@ -51,7 +57,12 @@ same diagnostics redacted, always exit 0 — safe to share.
|
|
|
51
57
|
- Settlement is opportunistic: any OpenReceive request runs one reconcile pass
|
|
52
58
|
through a durable gate (min 3s between wallet scans, stretched by invoice
|
|
53
59
|
age). A quiet server settles on the next request — or run the optional
|
|
54
|
-
|
|
60
|
+
notifications worker: Rails `bin/rails openreceive:notifications`, Django
|
|
61
|
+
`manage.py openreceive_notifications`, FastAPI
|
|
62
|
+
`openreceive notifications --app main:app`, Laravel
|
|
63
|
+
`php artisan openreceive:notifications`, WordPress `wp openreceive notifications`,
|
|
64
|
+
or plain PHP's host script `php bin/notifications`. Node hosts run their
|
|
65
|
+
separate worker using the notifications API. No web-process timer is missing.
|
|
55
66
|
- An unpaid attempt closes only after a successful wallet scan at/after expiry
|
|
56
67
|
plus a 900s grace constant — a local clock alone never closes one. `expired`
|
|
57
68
|
arriving "late" is correct.
|
|
@@ -83,8 +94,13 @@ same diagnostics redacted, always exit 0 — safe to share.
|
|
|
83
94
|
|
|
84
95
|
- The components require `prefix` — the exact base path the routes are mounted
|
|
85
96
|
at (`"/openreceive"` unless you changed it).
|
|
86
|
-
- Import
|
|
87
|
-
|
|
97
|
+
- Import `@openreceive/react/styles.css` or `@openreceive/elements/styles.css`
|
|
98
|
+
alongside the component registration. For standalone Django/Laravel/PHP,
|
|
99
|
+
serve both `openreceive-checkout.js` (as a module) and
|
|
100
|
+
`openreceive-checkout.css`. Django serves them from `static/openreceive/`
|
|
101
|
+
via `collectstatic`; Laravel/PHP serve the unpacked standalone assets from
|
|
102
|
+
the public directory. Check that both requests succeed and the custom
|
|
103
|
+
element is registered. Do not process the compiled stylesheet with Tailwind.
|
|
88
104
|
- "invoice must not be an NWC connection string" means a server secret leaked
|
|
89
105
|
into a browser payload — stop and fix the server response; never render it.
|
|
90
106
|
https://openreceive.org/guides/frontend-checkout.md
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: integrate-openreceive
|
|
3
3
|
description: >
|
|
4
|
-
Integrate OpenReceive
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
Integrate OpenReceive Bitcoin Lightning checkout and optional USDT, USDC,
|
|
5
|
+
SOL, and ETH swaps. Use for Node.js, Express, Fastify, Next.js, Rails,
|
|
6
|
+
Python, Django, FastAPI, Laravel, plain PHP, WordPress/WooCommerce,
|
|
7
|
+
React, Vue, Svelte, Angular, or plain HTML applications, or connecting
|
|
8
|
+
BTCPay Server to a receive-only NWC wallet. A configured swap provider
|
|
9
|
+
converts these payments to BTC over Lightning in the merchant's connected
|
|
10
|
+
wallet; asset and network availability depends on the provider.
|
|
10
11
|
license: MIT
|
|
11
12
|
---
|
|
12
13
|
|
|
@@ -19,6 +20,10 @@ settles. There is no OpenReceive account and no API key; funds land directly in
|
|
|
19
20
|
the merchant's wallet. The one required credential is a **receive-only NWC
|
|
20
21
|
code** (`NWC_URI`).
|
|
21
22
|
|
|
23
|
+
Optional swaps let customers pay with USDT, USDC, SOL, and ETH. A configured
|
|
24
|
+
swap provider converts these payments to BTC over Lightning in the merchant's
|
|
25
|
+
connected wallet; asset and network availability depends on the provider.
|
|
26
|
+
|
|
22
27
|
## Pick the stack, then follow its directions
|
|
23
28
|
|
|
24
29
|
1. Identify the server stack of the application you are in.
|
|
@@ -28,6 +33,8 @@ code** (`NWC_URI`).
|
|
|
28
33
|
- Node, Fastify: [references/fastify.md](references/fastify.md)
|
|
29
34
|
- Node, Next.js App Router: [references/next.md](references/next.md)
|
|
30
35
|
- Rails: [references/rails.md](references/rails.md)
|
|
36
|
+
- Python, FastAPI: [references/fastapi.md](references/fastapi.md)
|
|
37
|
+
- Plain PHP: [references/php.md](references/php.md)
|
|
31
38
|
- Django: [references/django.md](references/django.md)
|
|
32
39
|
- Laravel: [references/laravel.md](references/laravel.md)
|
|
33
40
|
- WordPress + WooCommerce: [references/woocommerce.md](references/woocommerce.md) — the packaged gateway and merchant settings.
|
|
@@ -44,6 +51,9 @@ Fastify: `npm install @openreceive/fastify @openreceive/react`; Next.js:
|
|
|
44
51
|
`npm install @openreceive/next @openreceive/react`. Swap the UI package (`vue`,
|
|
45
52
|
`svelte`, `angular`, `elements`) for the frontend the app already has. Install
|
|
46
53
|
(Rails): `bundle add openreceive-rails`.
|
|
54
|
+
Django: `pip install "openreceive[django]"`; FastAPI:
|
|
55
|
+
`pip install "openreceive[fastapi]"`; Laravel: `composer require openreceive/laravel`;
|
|
56
|
+
plain PHP: `composer require openreceive/openreceive nyholm/psr7 nyholm/psr7-server`.
|
|
47
57
|
|
|
48
58
|
## The three server objects
|
|
49
59
|
|
|
@@ -108,25 +118,36 @@ overriding.
|
|
|
108
118
|
|
|
109
119
|
## Database tables
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
npx openreceive scaffold payments --orm prisma # or drizzle | typeorm | sequelize | knex
|
|
113
|
-
```
|
|
121
|
+
Generate the two tables in the host's existing database:
|
|
114
122
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
| Stack | Generate and apply |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| Node | `npx openreceive scaffold payments --orm <orm>`, then the app's normal migration command |
|
|
126
|
+
| Rails | `bin/rails generate openreceive:install && bin/rails db:migrate` |
|
|
127
|
+
| Django | `manage.py openreceive_install <app> && manage.py migrate` |
|
|
128
|
+
| FastAPI | `openreceive scaffold payments --alembic --dialect <db>` (or `--sql`), then apply through the app's migration workflow |
|
|
129
|
+
| Laravel | `php artisan openreceive:install && php artisan migrate` |
|
|
130
|
+
| Plain PHP | Use `OpenReceive\Storage\PaymentsSchema::statements($dialect)` in the host's migration workflow, as in the PHP reference |
|
|
119
131
|
|
|
120
|
-
|
|
132
|
+
These emit `openreceive_payments` + `openreceive_meta`. The tables sit beside
|
|
133
|
+
your models — no relations to them, no separate database, no Redis. WordPress
|
|
134
|
+
and BTCPay manage installation through their plugins; follow their references.
|
|
121
135
|
|
|
122
|
-
|
|
136
|
+
## Verify, and test without a real wallet
|
|
123
137
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
| Stack | Doctor | Test seam |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| Node | `npx openreceive doctor` | `client` on `createOpenReceive` (`preflight`, `makeInvoice`, `listTransactions`), plus `StaticPriceProvider` |
|
|
141
|
+
| Rails | `bin/rails openreceive:doctor` | `config.nwc_client`, `config.swap_providers`, `config.price_provider` |
|
|
142
|
+
| Django | `manage.py openreceive_doctor` | `OPENRECEIVE["SERVICE"]`, a factory returning a `Service` built on `openreceive.testing` fakes |
|
|
143
|
+
| FastAPI | `openreceive doctor --app main:app` | `nwc_client`, `price_provider`, `swap_providers` on `openreceive_router`, using `openreceive.testing` fakes |
|
|
144
|
+
| Laravel | `php artisan openreceive:doctor` | Bind `ReceiveNwcClient`, `PriceProvider`, and `OpenReceiveServiceProvider::SWAP_PROVIDERS` in the container |
|
|
145
|
+
| Plain PHP | `php bin/doctor` (host script calling `$engine->doctor()`) | Build `Service` with `OpenReceive\Testing\FakeWallet`, `FakeSwapProvider`, and `OpenReceive\Rates\StaticPriceProvider` |
|
|
146
|
+
| WordPress | `wp openreceive doctor` | Repository development: the documented Docker `compose.testkit.yml` override |
|
|
147
|
+
| BTCPay | Follow the plugin reference's connection and checkout checks | Use the plugin's Docker test setup in its reference |
|
|
148
|
+
|
|
149
|
+
The routes, persistence, reconciliation, and fulfillment hooks then run the
|
|
150
|
+
production paths. Details: https://openreceive.org/guides/host-testing.md
|
|
130
151
|
|
|
131
152
|
## Deeper documentation
|
|
132
153
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (BTCPay Server)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Connect a BTCPay Server store to a receive-only NWC wallet with the OpenReceive
|
|
6
6
|
plugin, and optionally let payers pay BTCPay invoices with USDT, USDC, ETH or
|
|
@@ -164,13 +164,17 @@ them to install the plugin for you.
|
|
|
164
164
|
|
|
165
165
|
**1. Open the Plugins menu.** It is the plug icon in the top-right corner.
|
|
166
166
|
|
|
167
|
+
|
|
167
168
|
**2. Click Plugin Directory.**
|
|
168
169
|
|
|
170
|
+
|
|
169
171
|
**3. Search for `openreceive`** and click the **OpenReceive** result.
|
|
170
172
|
|
|
173
|
+
|
|
171
174
|
**4. Click Install in BTCPay Server.** Confirm when prompted, then click
|
|
172
175
|
**Restart now** and wait for BTCPay to come back.
|
|
173
176
|
|
|
177
|
+
|
|
174
178
|
At startup, BTCPay creates the plugin's two tables in its own Postgres
|
|
175
179
|
database: `openreceive_invoices` and `openreceive_swaps`, in the schema
|
|
176
180
|
`BTCPayServer.Plugins.OpenReceive`. Nothing else is created.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Django)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Django project — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the Python package is on PyPI
|
|
@@ -167,15 +167,19 @@ itself, and they hold for every integration.
|
|
|
167
167
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
168
168
|
after leaving your page to fetch an address from another wallet. Three things
|
|
169
169
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
170
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
171
|
-
|
|
170
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
171
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
172
|
+
restore the order from, and the ATTEMPT.
|
|
172
173
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
173
174
|
reference alone opens on the method grid. Re-picking the same coin
|
|
174
175
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
175
176
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
176
177
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
177
178
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
178
|
-
such window.
|
|
179
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
180
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
181
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
182
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
179
183
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
180
184
|
the price from `amount_for` and both drop-ins render it above the
|
|
181
185
|
amount. Without it the checkout is a QR and "$1.00" with no sign of what the
|
|
@@ -362,17 +366,18 @@ Then add the app, point it at a host class, and mount the routes:
|
|
|
362
366
|
INSTALLED_APPS += ["openreceive.django"]
|
|
363
367
|
|
|
364
368
|
OPENRECEIVE = {
|
|
365
|
-
"HOST": "shop.openreceive_host.Host",
|
|
369
|
+
"HOST": "shop.openreceive_host.Host", # the class with the three hooks (generated below)
|
|
366
370
|
"PRICE_CURRENCIES": ["USD"],
|
|
367
|
-
"RATE_LIMITING": False,
|
|
368
|
-
"OPPORTUNISTIC_RECONCILE": True,
|
|
369
|
-
"DATABASE": "default",
|
|
371
|
+
"RATE_LIMITING": False, # True for public web shops (see below)
|
|
372
|
+
"OPPORTUNISTIC_RECONCILE": True, # or {"min_interval_seconds": …}; False only with your own worker
|
|
373
|
+
"DATABASE": "default", # the DATABASES alias that holds the two engine tables
|
|
370
374
|
}
|
|
371
375
|
```
|
|
372
376
|
|
|
373
377
|
```python
|
|
374
378
|
# urls.py
|
|
375
379
|
from django.urls import include, path
|
|
380
|
+
|
|
376
381
|
urlpatterns += [path("openreceive/", include("openreceive.django.urls"))]
|
|
377
382
|
```
|
|
378
383
|
|
|
@@ -416,9 +421,9 @@ The generated host module explains this and shows the guarded transition:
|
|
|
416
421
|
|
|
417
422
|
```python
|
|
418
423
|
def on_paid(self, settlement: PaymentSettlement) -> None:
|
|
419
|
-
claimed = Order.objects.filter(
|
|
420
|
-
|
|
421
|
-
)
|
|
424
|
+
claimed = Order.objects.filter(pk=settlement.reference, state="awaiting_payment").update(
|
|
425
|
+
state="paid", paid_at=datetime.fromtimestamp(settlement.paid_at, tz=UTC)
|
|
426
|
+
)
|
|
422
427
|
if claimed == 0:
|
|
423
428
|
return # someone else already fulfilled it
|
|
424
429
|
|
|
@@ -446,7 +451,9 @@ instead:
|
|
|
446
451
|
|
|
447
452
|
```python
|
|
448
453
|
def on_paid(self, settlement: PaymentSettlement) -> None:
|
|
449
|
-
order =
|
|
454
|
+
order = (
|
|
455
|
+
Order.objects.select_for_update().filter(pk=settlement.reference).first()
|
|
456
|
+
) # SELECT … FOR UPDATE
|
|
450
457
|
if order is None or order.state != "awaiting_payment":
|
|
451
458
|
return
|
|
452
459
|
order.state = "paid"
|
|
@@ -529,8 +536,9 @@ from openreceive.server import HookContext
|
|
|
529
536
|
from openreceive.storage import PaymentSettlement
|
|
530
537
|
|
|
531
538
|
from shop.models import Order # YOUR model — it could be named anything. OpenReceive
|
|
532
|
-
|
|
533
|
-
|
|
539
|
+
# never sees it or touches its table; these hooks are the
|
|
540
|
+
# only bridge between the engine and your data.
|
|
541
|
+
|
|
534
542
|
|
|
535
543
|
class Host:
|
|
536
544
|
# Your policy, called before every checkout/payment/swap request. `context`
|
|
@@ -560,8 +568,11 @@ class Host:
|
|
|
560
568
|
order = Order.objects.filter(pk=reference).first()
|
|
561
569
|
if order is None:
|
|
562
570
|
return None
|
|
563
|
-
return {
|
|
564
|
-
|
|
571
|
+
return {
|
|
572
|
+
"currency": "USD",
|
|
573
|
+
"value": str(order.total),
|
|
574
|
+
"description": f"{order.items.count()} items",
|
|
575
|
+
}
|
|
565
576
|
|
|
566
577
|
# Runs inside the settlement transaction, only for the order's first settled
|
|
567
578
|
# attempt. The WHERE clause is the lock: a second fulfillment path of yours
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (FastAPI)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a FastAPI application — the app you are already working in.
|
|
6
6
|
You do not need a copy of the OpenReceive source: the engine is on PyPI
|
|
@@ -157,15 +157,19 @@ itself, and they hold for every integration.
|
|
|
157
157
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
158
158
|
after leaving your page to fetch an address from another wallet. Three things
|
|
159
159
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
160
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
161
|
-
|
|
160
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
161
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
162
|
+
restore the order from, and the ATTEMPT.
|
|
162
163
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
163
164
|
reference alone opens on the method grid. Re-picking the same coin
|
|
164
165
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
165
166
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
166
167
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
167
168
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
168
|
-
such window.
|
|
169
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
170
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
171
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
172
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
169
173
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
170
174
|
the price from `amount_for` and both drop-ins render it above the amount.
|
|
171
175
|
Without it the checkout is a QR and "$1.00" with no sign of what the dollar
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Fastify)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Fastify application — the app you are already working in.
|
|
6
6
|
You do not need a copy of the OpenReceive source: the packages are on npm, and
|
|
@@ -149,15 +149,19 @@ itself, and they hold for every integration.
|
|
|
149
149
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
150
150
|
after leaving your page to fetch an address from another wallet. Three things
|
|
151
151
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
152
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
153
|
-
|
|
152
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
153
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
154
|
+
restore the order from, and the ATTEMPT.
|
|
154
155
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
155
156
|
reference alone opens on the method grid. Re-picking the same coin
|
|
156
157
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
157
158
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
158
159
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
159
160
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
160
|
-
such window.
|
|
161
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
162
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
163
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
164
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
161
165
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
162
166
|
the price from `amountFor` and both drop-ins render it above the amount.
|
|
163
167
|
Without it the checkout is a QR and "$1.00" with no sign of what the dollar
|
|
@@ -375,7 +379,9 @@ there is nothing else to generate. Details:
|
|
|
375
379
|
No ORM? You can pass a bare driver handle (`pg`, `node:sqlite`,
|
|
376
380
|
`better-sqlite3`) as the `db` in step 4. The scaffold has no flavor for it.
|
|
377
381
|
Instead of scaffolding, run the same DDL once yourself, using
|
|
378
|
-
`paymentsSchemaSql(dialect)` from `@openreceive/http`.
|
|
382
|
+
`paymentsSchemaSql(dialect)` from `@openreceive/http`. Your adapter already
|
|
383
|
+
pulls that package in, but this import is yours, so install it too:
|
|
384
|
+
`npm install @openreceive/http`.
|
|
379
385
|
|
|
380
386
|
### 3. Add wallet credentials
|
|
381
387
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Laravel)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Laravel application — the app you are already working in.
|
|
6
6
|
You do not need a copy of the OpenReceive source: the package is on Packagist
|
|
@@ -158,15 +158,19 @@ itself, and they hold for every integration.
|
|
|
158
158
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
159
159
|
after leaving your page to fetch an address from another wallet. Three things
|
|
160
160
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
161
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
162
|
-
|
|
161
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
162
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
163
|
+
restore the order from, and the ATTEMPT.
|
|
163
164
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
164
165
|
reference alone opens on the method grid. Re-picking the same coin
|
|
165
166
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
166
167
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
167
168
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
168
169
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
169
|
-
such window.
|
|
170
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
171
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
172
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
173
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
170
174
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
171
175
|
the price from `amountFor` and both drop-ins render it above the
|
|
172
176
|
amount. Without it the checkout is a QR and "$1.00" with no sign of what the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Next.js)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Next.js App Router application — the app you are already
|
|
6
6
|
working in. You do not need a copy of the OpenReceive source: the packages are
|
|
@@ -149,15 +149,19 @@ itself, and they hold for every integration.
|
|
|
149
149
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
150
150
|
after leaving your page to fetch an address from another wallet. Three things
|
|
151
151
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
152
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
153
|
-
|
|
152
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
153
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
154
|
+
restore the order from, and the ATTEMPT.
|
|
154
155
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
155
156
|
reference alone opens on the method grid. Re-picking the same coin
|
|
156
157
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
157
158
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
158
159
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
159
160
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
160
|
-
such window.
|
|
161
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
162
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
163
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
164
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
161
165
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
162
166
|
the price from `amountFor` and both drop-ins render it above the amount.
|
|
163
167
|
Without it the checkout is a QR and "$1.00" with no sign of what the dollar
|
|
@@ -381,7 +385,9 @@ there is nothing else to generate. Details:
|
|
|
381
385
|
No ORM? You can pass a bare driver handle (`pg`, `node:sqlite`,
|
|
382
386
|
`better-sqlite3`) as the `db` in step 4. The scaffold has no flavor for it.
|
|
383
387
|
Instead of scaffolding, run the same DDL once yourself, using
|
|
384
|
-
`paymentsSchemaSql(dialect)` from `@openreceive/http`.
|
|
388
|
+
`paymentsSchemaSql(dialect)` from `@openreceive/http`. Your adapter already
|
|
389
|
+
pulls that package in, but this import is yours, so install it too:
|
|
390
|
+
`npm install @openreceive/http`.
|
|
385
391
|
|
|
386
392
|
### 3. Add wallet credentials
|
|
387
393
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Node.js)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Node application — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the packages are on npm, and the
|
|
@@ -146,15 +146,19 @@ itself, and they hold for every integration.
|
|
|
146
146
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
147
147
|
after leaving your page to fetch an address from another wallet. Three things
|
|
148
148
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
149
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
150
|
-
|
|
149
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
150
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
151
|
+
restore the order from, and the ATTEMPT.
|
|
151
152
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
152
153
|
reference alone opens on the method grid. Re-picking the same coin
|
|
153
154
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
154
155
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
155
156
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
156
157
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
157
|
-
such window.
|
|
158
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
159
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
160
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
161
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
158
162
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
159
163
|
the price from `amountFor` and both drop-ins render it above the amount.
|
|
160
164
|
Without it the checkout is a QR and "$1.00" with no sign of what the dollar
|
|
@@ -366,7 +370,9 @@ there is nothing else to generate. Details:
|
|
|
366
370
|
No ORM? You can pass a bare driver handle (`pg`, `node:sqlite`,
|
|
367
371
|
`better-sqlite3`) as the `db` in step 4. The scaffold has no flavor for it.
|
|
368
372
|
Instead of scaffolding, run the same DDL once yourself, using
|
|
369
|
-
`paymentsSchemaSql(dialect)` from `@openreceive/http`.
|
|
373
|
+
`paymentsSchemaSql(dialect)` from `@openreceive/http`. Your adapter already
|
|
374
|
+
pulls that package in, but this import is yours, so install it too:
|
|
375
|
+
`npm install @openreceive/http`.
|
|
370
376
|
|
|
371
377
|
### 3. Add wallet credentials
|
|
372
378
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (PHP)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a PHP application — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the engine is on Packagist
|
|
@@ -171,7 +171,9 @@ itself, and they hold for every integration.
|
|
|
171
171
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
172
172
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
173
173
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
174
|
-
such window.
|
|
174
|
+
such window. On the element: the `resume-payment-hash` attribute, fed from
|
|
175
|
+
the `openreceive-state` event (`event.detail.state.payment_hash`).
|
|
176
|
+
https://openreceive.org/guides/swap-refunds.md
|
|
175
177
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
176
178
|
the price from `amountFor` and the drop-in renders it above the amount.
|
|
177
179
|
Without it the checkout is a QR and "$1.00" with no sign of what the dollar
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenReceive agent directions (Rails)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Add OpenReceive to a Rails application — the app you are already working in. You
|
|
6
6
|
do not need a copy of the OpenReceive source: the gem is on RubyGems, the
|
|
@@ -153,15 +153,19 @@ itself, and they hold for every integration.
|
|
|
153
153
|
late becomes `refund_required`, and the payer claims it on a SECOND VISIT,
|
|
154
154
|
after leaving your page to fetch an address from another wallet. Three things
|
|
155
155
|
must exist or that money is unreachable through your UI: a per-order URL your
|
|
156
|
-
server serves (`/checkout/:reference` — `syncUrl` on
|
|
157
|
-
|
|
156
|
+
server serves (`/checkout/:reference` — `syncUrl` on `<Checkout>`, `sync-url`
|
|
157
|
+
or `resumable` on `<openreceive-checkout>`), your own order-summary route to
|
|
158
|
+
restore the order from, and the ATTEMPT.
|
|
158
159
|
`/checkouts/prepare` returns no attempts, so a checkout rebuilt from the
|
|
159
160
|
reference alone opens on the method grid. Re-picking the same coin
|
|
160
161
|
(`POST /swaps`) re-serves the committed attempt — but only while it is live,
|
|
161
162
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
162
163
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
163
164
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
164
|
-
such window.
|
|
165
|
+
such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
|
|
166
|
+
`<Checkout>`; the `resume-payment-hash` attribute, fed from the
|
|
167
|
+
`openreceive-state` event (`event.detail.state.payment_hash`), on
|
|
168
|
+
`<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
|
|
165
169
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
166
170
|
the price from `config.amount_for` and both drop-ins render it above the
|
|
167
171
|
amount. Without it the checkout is a QR and "$1.00" with no sign of what the
|
|
@@ -1,23 +1,53 @@
|
|
|
1
1
|
# OpenReceive agent directions (WordPress + WooCommerce)
|
|
2
2
|
|
|
3
|
-
These directions describe OpenReceive 0.4.
|
|
3
|
+
These directions describe OpenReceive 0.4.16.
|
|
4
4
|
|
|
5
5
|
Install and configure the OpenReceive gateway in the existing WooCommerce
|
|
6
6
|
store. Preserve its theme, checkout, customer accounts, order model and prices.
|
|
7
7
|
The plugin bundles the PHP engine and checkout assets; the merchant does not
|
|
8
8
|
install npm or Composer packages on the WordPress server.
|
|
9
9
|
|
|
10
|
-
## Step 0 —
|
|
10
|
+
## Step 0 — collect and save the codes, one at a time
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
For repository development, use the Docker demo's explicit testkit override.
|
|
12
|
+
Inspect WordPress, WooCommerce and PHP versions and GMP/sodium in both the web
|
|
13
|
+
and WP-CLI runtimes. If installed, use `wp openreceive doctor` to see which
|
|
14
|
+
credentials are set, without displaying their values. Skip codes already set.
|
|
15
|
+
Do not search other projects, container environments or deployment secrets.
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
the
|
|
17
|
+
Ask for the missing receive-only NWC code first, with this walkthrough:
|
|
18
|
+
|
|
19
|
+
> In Rizful, open the menu → NWC → Receive-only NWC code → Copy
|
|
20
|
+
> (https://openreceive.org/get_a_nwc_code_to_receive_payments). Alby Hub also
|
|
21
|
+
> works: Connections → Add Connection → Read Only. Paste the code here and
|
|
22
|
+
> I will save it for you.
|
|
23
|
+
|
|
24
|
+
Install the exact built plugin archive described below if needed. When the code
|
|
25
|
+
arrives, save it yourself with `wp openreceive configure --nwc-uri=-`, supplying
|
|
26
|
+
the code through the process's stdin. Never put it in shell arguments, shell
|
|
27
|
+
history, logs, source files or browser code. Do not ask the user to edit PHP or
|
|
28
|
+
an environment file. The command encrypts the code and runs wallet preflight
|
|
29
|
+
before saving; a failure preserves existing settings. Constants in wp-config.php
|
|
30
|
+
remain authoritative; if a constant must change, use the host's secret workflow.
|
|
31
|
+
|
|
32
|
+
Next ask whether customers should also pay with USDT, USDC, SOL and ETH, unless
|
|
33
|
+
the user already requested these. A configured swap provider converts payments
|
|
34
|
+
to BTC over Lightning in the merchant's connected wallet; available assets and
|
|
35
|
+
networks depend on the provider. Ask for the LSC code separately:
|
|
36
|
+
|
|
37
|
+
> Go to https://lightning-swap.com, sign in for API keys, create a key, and copy
|
|
38
|
+
> the whole URI (https://openreceive.org/set_up_swap_provider). Paste it here
|
|
39
|
+
> and I will save it, or say “Bitcoin only”.
|
|
40
|
+
|
|
41
|
+
Save it with `wp openreceive configure --lsc-uri-primary=-` through stdin.
|
|
42
|
+
Mention FixedFloat only if the merchant already uses it. Save an optional backup
|
|
43
|
+
separately with `--lsc-uri-backup=-`. Do not use generic `wp wc payment_gateway`
|
|
44
|
+
or REST settings writes for credentials: they are deliberately rejected.
|
|
45
|
+
|
|
46
|
+
Run `wp openreceive configure --enable`, then `wp openreceive doctor`. Resolve
|
|
47
|
+
failed checks before checkout testing. Create an unpaid test order and verify
|
|
48
|
+
that the order-pay page opens, lists the configured methods, and resumes its
|
|
49
|
+
Lightning invoice on reload. Ask the merchant to pay only if they want a real
|
|
50
|
+
settlement test.
|
|
21
51
|
|
|
22
52
|
The plugin owns only its payment-attempt tables in the WordPress database.
|
|
23
53
|
WooCommerce owns orders, totals, stock and email. Do not add an external
|
|
@@ -32,48 +62,13 @@ flows; a receive-only NWC wallet cannot send payments.
|
|
|
32
62
|
|
|
33
63
|
## Further reading
|
|
34
64
|
|
|
35
|
-
- [
|
|
36
|
-
- [Fastify Quickstart](https://openreceive.org/guides/quickstart-fastify.md)
|
|
37
|
-
- [FastAPI Quickstart](https://openreceive.org/guides/quickstart-fastapi.md)
|
|
38
|
-
- [Django Quickstart](https://openreceive.org/guides/quickstart-django.md)
|
|
39
|
-
- [Next.js Quickstart](https://openreceive.org/guides/quickstart-next.md)
|
|
40
|
-
- [Rails Quickstart](https://openreceive.org/guides/quickstart-rails.md)
|
|
41
|
-
- [PHP Quickstart (plain PHP)](https://openreceive.org/guides/quickstart-php.md)
|
|
42
|
-
- [Laravel Quickstart](https://openreceive.org/guides/quickstart-laravel.md)
|
|
43
|
-
- [BTCPay Server Quickstart](https://openreceive.org/guides/quickstart-btcpay.md)
|
|
44
|
-
- [BTCPay Plugin Reference](https://openreceive.org/guides/btcpay-reference.md)
|
|
45
|
-
- [Node ORM Recipes](https://openreceive.org/guides/node-orms.md)
|
|
46
|
-
- [Authorization](https://openreceive.org/guides/authorization.md)
|
|
47
|
-
- [Rate Limiting](https://openreceive.org/guides/rate-limiting.md)
|
|
48
|
-
- [Frontend Checkout](https://openreceive.org/guides/frontend-checkout.md)
|
|
49
|
-
- [Checkout UX](https://openreceive.org/guides/checkout-ux.md)
|
|
50
|
-
- [Headless Checkout](https://openreceive.org/guides/headless-checkout.md)
|
|
65
|
+
- [WordPress + WooCommerce Quickstart](https://openreceive.org/guides/quickstart-woocommerce.md)
|
|
51
66
|
- [Automated Swaps](https://openreceive.org/guides/automated-swaps.md)
|
|
52
67
|
- [Swap Refunds](https://openreceive.org/guides/swap-refunds.md)
|
|
53
68
|
- [Lightning Swap Connect URI](https://openreceive.org/guides/lightning-swap-connect.md)
|
|
54
|
-
- [Environment Variables](https://openreceive.org/guides/environment-variables.md)
|
|
55
|
-
- [Payment Storage](https://openreceive.org/guides/storage.md)
|
|
56
|
-
- [Deploying OpenReceive](https://openreceive.org/guides/deploying.md)
|
|
57
|
-
- [Testing Your OpenReceive Integration](https://openreceive.org/guides/host-testing.md)
|
|
58
|
-
- [API Reference](https://openreceive.org/guides/api-reference.md)
|
|
59
69
|
- [Security](https://openreceive.org/guides/security.md)
|
|
60
|
-
- [Provider Registry](https://openreceive.org/guides/provider-registry.md)
|
|
61
70
|
- [Price Feeds](https://openreceive.org/guides/price-feeds.md)
|
|
62
|
-
- [
|
|
63
|
-
- [Flask Recipe](https://openreceive.org/guides/flask-recipe.md)
|
|
64
|
-
- [Writing Your Own Checkout Route](https://openreceive.org/guides/custom-checkout-route.md)
|
|
65
|
-
- [Agent Directions: Node.js](https://openreceive.org/guides/agent-directions-node.md)
|
|
66
|
-
- [Agent Directions: Fastify](https://openreceive.org/guides/agent-directions-fastify.md)
|
|
67
|
-
- [Agent Directions: FastAPI](https://openreceive.org/guides/agent-directions-fastapi.md)
|
|
68
|
-
- [Agent Directions: Django](https://openreceive.org/guides/agent-directions-django.md)
|
|
69
|
-
- [Agent Directions: Next.js](https://openreceive.org/guides/agent-directions-next.md)
|
|
70
|
-
- [Agent Directions: Rails](https://openreceive.org/guides/agent-directions-rails.md)
|
|
71
|
-
- [Agent Directions: PHP](https://openreceive.org/guides/agent-directions-php.md)
|
|
72
|
-
- [Agent Directions: Laravel](https://openreceive.org/guides/agent-directions-laravel.md)
|
|
73
|
-
- [Agent Directions: BTCPay Server](https://openreceive.org/guides/agent-directions-btcpay.md)
|
|
74
|
-
- [WordPress + WooCommerce Quickstart](https://openreceive.org/guides/quickstart-woocommerce.md)
|
|
75
|
-
|
|
76
|
-
- https://openreceive.org/guides/payment-safety-upgrade.md — coordinated upgrades and reviewed repair of existing attempts
|
|
71
|
+
- [Payment Safety Upgrade](https://openreceive.org/guides/payment-safety-upgrade.md)
|
|
77
72
|
|
|
78
73
|
---
|
|
79
74
|
|
|
@@ -84,6 +79,10 @@ passes. The page it comes from is https://openreceive.org/guides/quickstart-wooc
|
|
|
84
79
|
|
|
85
80
|
## WordPress + WooCommerce quickstart
|
|
86
81
|
|
|
82
|
+
The [WordPress integration entry point](https://openreceive.org/integrations/wordpress)
|
|
83
|
+
redirects to the WooCommerce integration, which uses this same guide and agent
|
|
84
|
+
directions. OpenReceive checkout on WordPress requires WooCommerce.
|
|
85
|
+
|
|
87
86
|
Activate WooCommerce first. Then install the built OpenReceive plugin zip
|
|
88
87
|
through **Plugins → Add New → Upload Plugin**. You cannot upload the source
|
|
89
88
|
directory as-is. It needs a build first. The plugin is not yet submitted to
|
|
@@ -96,15 +95,16 @@ database or application.
|
|
|
96
95
|
|
|
97
96
|
### Get the installable archive
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
Download [openreceive-wordpress-0.4.16.zip](https://github.com/OpenReceive/openreceive/releases/download/v0.4.16/openreceive-wordpress-0.4.16.zip)
|
|
99
|
+
from the matching release. Historical releases may lack this asset. If that exact
|
|
100
|
+
URL returns 404, build the same tag below; never silently install an older ZIP.
|
|
101
|
+
The GitHub source-code ZIP is not an installable plugin. On a development machine
|
|
102
|
+
with Node 22+, PHP 8.2+ with GMP/sodium, Composer and WP-CLI:
|
|
103
103
|
|
|
104
104
|
```sh
|
|
105
105
|
git clone https://github.com/OpenReceive/openreceive.git
|
|
106
106
|
cd openreceive
|
|
107
|
-
git checkout
|
|
107
|
+
git checkout v0.4.16
|
|
108
108
|
npm ci
|
|
109
109
|
npm run build:packages
|
|
110
110
|
composer install --working-dir=packages/php/wordpress
|
|
@@ -116,6 +116,40 @@ needs WP-CLI on `PATH`. Otherwise, set `OPENRECEIVE_WP_CLI` to the absolute path
|
|
|
116
116
|
of its phar. Your WordPress server needs neither Node nor Composer. The built
|
|
117
117
|
plugin already bundles its dependencies and checkout assets.
|
|
118
118
|
|
|
119
|
+
### Enable GMP in both PHP runtimes
|
|
120
|
+
|
|
121
|
+
GMP is required by the bundled elliptic-curve dependency. Enable it for both
|
|
122
|
+
web PHP (Apache/FPM) and the PHP executable running WP-CLI. Installing it in
|
|
123
|
+
only the WordPress container does not update a separate CLI container.
|
|
124
|
+
|
|
125
|
+
For Debian-based official PHP/WordPress images, add to **each** Dockerfile:
|
|
126
|
+
|
|
127
|
+
```dockerfile
|
|
128
|
+
USER root
|
|
129
|
+
RUN apt-get update && apt-get install -y --no-install-recommends libgmp-dev \
|
|
130
|
+
&& docker-php-ext-install gmp \
|
|
131
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
For Alpine-based PHP/CLI images:
|
|
135
|
+
|
|
136
|
+
```dockerfile
|
|
137
|
+
USER root
|
|
138
|
+
RUN apk add --no-cache gmp \
|
|
139
|
+
&& apk add --no-cache --virtual .gmp-build $PHPIZE_DEPS gmp-dev \
|
|
140
|
+
&& docker-php-ext-install gmp \
|
|
141
|
+
&& apk del .gmp-build
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Restore the base image's original runtime user after installing extensions.
|
|
145
|
+
Rebuild and recreate both containers. On Debian/Ubuntu hosts, install the GMP
|
|
146
|
+
package matching the active PHP version (for example `php8.2-gmp` for PHP 8.2),
|
|
147
|
+
then restart that version's web PHP service. Verify `php --ri gmp` and
|
|
148
|
+
`wp openreceive doctor` for CLI, and the gateway Doctor panel for web PHP.
|
|
149
|
+
On managed WordPress hosting, ask the host to enable GMP and sodium in both
|
|
150
|
+
runtimes; if they cannot, this plugin cannot run there. Do not use Composer's
|
|
151
|
+
`--ignore-platform-reqs` to bypass the requirements.
|
|
152
|
+
|
|
119
153
|
### Configure the wallet
|
|
120
154
|
|
|
121
155
|
1. Open **WooCommerce → Settings → Payments → OpenReceive**.
|
|
@@ -134,6 +168,27 @@ providers, you can also set the `OPENRECEIVE_LSC_URI_PRIMARY` and
|
|
|
134
168
|
`OPENRECEIVE_LSC_URI_BACKUP` constants. Never put these values in browser code
|
|
135
169
|
or logs.
|
|
136
170
|
|
|
171
|
+
#### Configure through WP-CLI
|
|
172
|
+
|
|
173
|
+
`wp openreceive configure` accepts one credential at a time from stdin. Feed
|
|
174
|
+
stdin through your secret manager or an existing protected file, never a code
|
|
175
|
+
literal in the command line:
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
wp openreceive configure --nwc-uri=- < /secure/path/wallet-code
|
|
179
|
+
wp openreceive configure --lsc-uri-primary=- < /secure/path/swap-code
|
|
180
|
+
wp openreceive configure --enable
|
|
181
|
+
wp openreceive doctor
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Omit the swap command for Bitcoin-only checkout. `--lsc-uri-backup=-` adds a
|
|
185
|
+
backup. These commands share admin preflight and encrypted storage. Credential
|
|
186
|
+
flags accept only `-`; blank input leaves settings intact. Generic WooCommerce
|
|
187
|
+
REST and `wp wc payment_gateway` credential updates are rejected. `doctor`
|
|
188
|
+
reports the failed check with credentials redacted and exits nonzero on failure.
|
|
189
|
+
The default payment title becomes “Bitcoin & crypto (OpenReceive)” with swaps;
|
|
190
|
+
a customized title is preserved.
|
|
191
|
+
|
|
137
192
|
### Checkout and settlement
|
|
138
193
|
|
|
139
194
|
Both WooCommerce checkout blocks and classic checkout send the customer to the
|