@openreceive/node 0.4.14 → 0.4.17

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 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",
package/dist/cli.js CHANGED
@@ -2,13 +2,13 @@ import {
2
2
  createNwcReceiveClient,
3
3
  readLscConnectionsFromEnvironment,
4
4
  redactSecrets
5
- } from "./chunk-GOWL2Y6H.js";
5
+ } from "./chunk-LD7HMUY3.js";
6
6
 
7
7
  // src/cli.ts
8
- import { existsSync } from "node:fs";
8
+ import { cpSync, existsSync, mkdirSync, realpathSync, rmSync } from "node:fs";
9
9
  import { createRequire } from "node:module";
10
10
  import path2 from "node:path";
11
- import { pathToFileURL } from "node:url";
11
+ import { fileURLToPath, pathToFileURL } from "node:url";
12
12
  import {
13
13
  formatInvalidNwcMessage,
14
14
  NwcUriParseError,
@@ -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)",
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  summarizeReconcilePass,
20
20
  summarizeSwapProviderApiRequest,
21
21
  summarizeSwapProviderApiResponse
22
- } from "./chunk-GOWL2Y6H.js";
22
+ } from "./chunk-LD7HMUY3.js";
23
23
 
24
24
  // src/index.ts
25
25
  import { OpenReceiveError as OpenReceiveError2 } from "@openreceive/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openreceive/node",
3
- "version": "0.4.14",
3
+ "version": "0.4.17",
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.14"
20
+ "@openreceive/core": "0.4.17"
21
21
  },
22
22
  "bin": {
23
23
  "openreceive": "./bin/openreceive.mjs"
@@ -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
- notification worker. No timer is missing; that is the design.
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 the stylesheet (`@openreceive/react/styles.css` or the elements
87
- sheet).
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 inbound Bitcoin Lightning payments into an application.
5
- Use when adding Bitcoin, Lightning, or crypto checkout to a Node.js, Express,
6
- Fastify, Next.js, Rails, React, Vue, Svelte, Angular, or plain-HTML
7
- application with OpenReceive (the @openreceive/* npm packages or the
8
- openreceive-rails gem), or when connecting a BTCPay Server store to a
9
- receive-only NWC wallet with the OpenReceive plugin.
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
- ```sh
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
- emits the `openreceive_payments` + `openreceive_meta` migration for THIS app's
116
- database (Rails: `bin/rails generate openreceive:install`); run it through the
117
- app's normal migration workflow. The tables sit beside your models — no
118
- relations to them, no separate database, no Redis.
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
- ## Verify, and test without a real wallet
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
- `npx openreceive doctor` checks the configuration and says what to fix.
136
+ ## Verify, and test without a real wallet
123
137
 
124
- For tests, inject a fake wallet at the stable seams — `client` on
125
- `createOpenReceive` (any object with `preflight`, `makeInvoice`,
126
- `listTransactions`) or `config.nwc_client` in Rails — plus
127
- `StaticPriceProvider` for fiat pricing without a network. Your routes,
128
- persistence, reconcile, and `onPaid` then run the production code paths.
129
- Details: https://openreceive.org/guides/host-testing.md
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`, then `wp openreceive test-invoice <order id>` for a real invoice | 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.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/btcpay.md`), not through a summarizing tool: a summary drops steps.
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.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/django.md`), not through a summarizing tool: a summary drops steps.
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
@@ -366,17 +366,18 @@ Then add the app, point it at a host class, and mount the routes:
366
366
  INSTALLED_APPS += ["openreceive.django"]
367
367
 
368
368
  OPENRECEIVE = {
369
- "HOST": "shop.openreceive_host.Host", # the class with the three hooks (generated below)
369
+ "HOST": "shop.openreceive_host.Host", # the class with the three hooks (generated below)
370
370
  "PRICE_CURRENCIES": ["USD"],
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
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
374
374
  }
375
375
  ```
376
376
 
377
377
  ```python
378
378
  # urls.py
379
379
  from django.urls import include, path
380
+
380
381
  urlpatterns += [path("openreceive/", include("openreceive.django.urls"))]
381
382
  ```
382
383
 
@@ -420,9 +421,9 @@ The generated host module explains this and shows the guarded transition:
420
421
 
421
422
  ```python
422
423
  def on_paid(self, settlement: PaymentSettlement) -> None:
423
- claimed = Order.objects.filter(
424
- pk=settlement.reference, state="awaiting_payment"
425
- ).update(state="paid", paid_at=datetime.fromtimestamp(settlement.paid_at, tz=UTC))
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
+ )
426
427
  if claimed == 0:
427
428
  return # someone else already fulfilled it
428
429
 
@@ -450,7 +451,9 @@ instead:
450
451
 
451
452
  ```python
452
453
  def on_paid(self, settlement: PaymentSettlement) -> None:
453
- order = Order.objects.select_for_update().filter(pk=settlement.reference).first() # SELECT … FOR UPDATE
454
+ order = (
455
+ Order.objects.select_for_update().filter(pk=settlement.reference).first()
456
+ ) # SELECT … FOR UPDATE
454
457
  if order is None or order.state != "awaiting_payment":
455
458
  return
456
459
  order.state = "paid"
@@ -533,8 +536,9 @@ from openreceive.server import HookContext
533
536
  from openreceive.storage import PaymentSettlement
534
537
 
535
538
  from shop.models import Order # YOUR model — it could be named anything. OpenReceive
536
- # never sees it or touches its table; these hooks are the
537
- # only bridge between the engine and your data.
539
+ # never sees it or touches its table; these hooks are the
540
+ # only bridge between the engine and your data.
541
+
538
542
 
539
543
  class Host:
540
544
  # Your policy, called before every checkout/payment/swap request. `context`
@@ -564,8 +568,11 @@ class Host:
564
568
  order = Order.objects.filter(pk=reference).first()
565
569
  if order is None:
566
570
  return None
567
- return {"currency": "USD", "value": str(order.total),
568
- "description": f"{order.items.count()} items"}
571
+ return {
572
+ "currency": "USD",
573
+ "value": str(order.total),
574
+ "description": f"{order.items.count()} items",
575
+ }
569
576
 
570
577
  # Runs inside the settlement transaction, only for the order's first settled
571
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.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/fastapi.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,6 +1,6 @@
1
1
  # OpenReceive agent directions (Fastify)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/fastify.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,6 +1,6 @@
1
1
  # OpenReceive agent directions (Laravel)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/laravel.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,6 +1,6 @@
1
1
  # OpenReceive agent directions (Next.js)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/next.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,6 +1,6 @@
1
1
  # OpenReceive agent directions (Node.js)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/node.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,6 +1,6 @@
1
1
  # OpenReceive agent directions (PHP)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/php.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,6 +1,6 @@
1
1
  # OpenReceive agent directions (Rails)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/rails.md`), not through a summarizing tool: a summary drops steps.
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
@@ -1,79 +1,165 @@
1
1
  # OpenReceive agent directions (WordPress + WooCommerce)
2
2
 
3
- These directions describe OpenReceive 0.4.14.
3
+ These directions describe OpenReceive 0.4.17. If you fetched this file, fetch it raw (`curl -fsSL https://openreceive.org/agent-directions/woocommerce.md`), not through a summarizing tool: a summary drops steps.
4
+
5
+ Install and configure the OpenReceive payment gateway in the WooCommerce store
6
+ you are working in. Preserve its theme, checkout, customer accounts, order
7
+ model and prices. The plugin bundles the PHP engine and checkout assets: do not
8
+ install npm or Composer packages on the WordPress server, and do not clone the
9
+ OpenReceive repository unless the release download in Step 1 fails.
10
+
11
+ The one required credential is a receive-only NWC code (Nostr Wallet Connect):
12
+ a string from the merchant's wallet that can create invoices and read their
13
+ status, and cannot spend. A swap provider (an "LSC" code) optionally lets
14
+ customers pay with USDT, USDC, ETH or SOL instead; the provider converts the
15
+ payment to BTC over Lightning in the merchant's connected wallet. Available
16
+ assets and networks depend on the provider.
17
+
18
+ Run every `wp` command below where this store's WP-CLI runs. When WordPress
19
+ runs in Docker Compose, prefix it with the service that has WP-CLI, for
20
+ example `docker compose run --rm -T cli wp …` or
21
+ `docker compose exec -T wordpress wp …`. `-T` passes stdin through. If the
22
+ store has no WP-CLI at all (managed hosting without a shell), say so and walk
23
+ the user through the quickstart's admin screens instead.
24
+
25
+ ## Step 0 — ask for the two codes, one question at a time
26
+
27
+ Before anything else, check one thing: whether OpenReceive is already
28
+ installed (`wp plugin is-active openreceive`). If it is, run
29
+ `wp openreceive doctor`. It prints `NWC_URI: set` or `unset` (likewise
30
+ `LSC_URI_PRIMARY`), never the values. A code that is already set is not asked
31
+ for again; if both are set, skip to `wp openreceive configure --enable` at
32
+ the end of Step 2.
33
+
34
+ Otherwise your next action is a question to the user. Do not install the
35
+ plugin, edit Docker files or search anywhere else before asking it. Do not
36
+ read wp-config.php, deploy config, container environments or other projects
37
+ looking for a code: a new store has neither code yet.
38
+
39
+ The user never runs a command and never edits a file. They paste each code
40
+ into this chat; you store it. That is the supported path: do not ask them to
41
+ run the save command themselves, and do not tell them to revoke or replace a
42
+ code because it was pasted here. Ask one question per message.
43
+
44
+ 1. **First message — the NWC code, and nothing else.**
45
+
46
+ > To receive payments I need a receive-only wallet code. In Rizful: open
47
+ > the menu, tap NWC, choose Receive-only NWC code, and tap Copy
48
+ > (https://openreceive.org/get_a_nwc_code_to_receive_payments). If you would
49
+ > rather run your own wallet, Alby Hub works too: Connections → Add
50
+ > Connection → Read Only. Paste the code here and I will store it.
51
+
52
+ 2. **When they paste it.** If it does not start with `nostr+walletconnect://`,
53
+ ask them to copy the receive-only code again. Otherwise do not repeat it:
54
+ reply only that you have it, then ask the next question. You store it in
55
+ Step 2.
56
+ 3. **Second message — swaps.** If the user asked for stablecoins, USDT, USDC,
57
+ ETH, SOL, altcoins or "crypto" (as in "Bitcoin and stablecoin payments"),
58
+ this message IS the walkthrough below: do not skip it, and do not ask yes
59
+ or no first. Otherwise ask whether customers should also be able to pay
60
+ with USDT, USDC, ETH or SOL, then give the walkthrough. The walkthrough:
61
+
62
+ > Go to https://lightning-swap.com, sign in for API keys, create a key, and
63
+ > copy the whole URI (https://openreceive.org/set_up_swap_provider). Paste
64
+ > it here and I will store it — or say "Bitcoin only" and I will continue
65
+ > without it.
66
+
67
+ Mention FixedFloat only if they already use it.
68
+ 4. **When they paste it.** If it does not start with
69
+ `lightning+swapconnect://`, ask them to copy it again. Swaps are now on,
70
+ so keep the route back (the swap non-negotiable below).
71
+
72
+ Do not report setup as complete until the NWC code is saved, and the LSC code
73
+ is saved or the user said "Bitcoin only". Never invent a placeholder code.
74
+
75
+ ## Step 1 — install the plugin
76
+
77
+ Install the plugin built for this release. Never install the GitHub
78
+ source-code ZIP or a ZIP from an older release:
4
79
 
5
- Install and configure the OpenReceive gateway in the existing WooCommerce
6
- store. Preserve its theme, checkout, customer accounts, order model and prices.
7
- The plugin bundles the PHP engine and checkout assets; the merchant does not
8
- install npm or Composer packages on the WordPress server.
80
+ ```sh
81
+ wp plugin install https://github.com/OpenReceive/openreceive/releases/download/v0.4.17/openreceive-wordpress-0.4.17.zip --activate
82
+ ```
83
+
84
+ It needs WooCommerce active, and PHP 8.2+ with GMP and sodium in BOTH the web
85
+ PHP and the WP-CLI PHP. On the official `wordpress` and `wordpress:cli` Docker
86
+ images, activation fails with "OpenReceive requires the PHP sodium and GMP
87
+ extensions": add GMP to both images as "Enable GMP in both PHP runtimes" below
88
+ says, rebuild both, then install again. If the URL answers 404, build the same
89
+ tag as "Get the installable archive" below says.
90
+
91
+ ## Step 2 — store the codes, then enable the gateway
92
+
93
+ Store each code yourself, one per command, and never as a shell argument:
9
94
 
10
- ## Step 0 — inspect configuration
95
+ 1. Write the code with your file-editing tool, not a shell command (no
96
+ `echo`, `printf` or heredoc), to a new file outside the repository, such
97
+ as `/tmp/openreceive-code`.
98
+ 2. Run `wp openreceive configure --nwc-uri=- < /tmp/openreceive-code`. In
99
+ Docker: `docker compose run --rm -T cli wp openreceive configure --nwc-uri=- < /tmp/openreceive-code`.
100
+ For the LSC code, use `--lsc-uri-primary=-`.
101
+ 3. Delete the file (`rm /tmp/openreceive-code`), whether the command passed
102
+ or not.
11
103
 
12
- Check WordPress, WooCommerce and PHP versions, GMP and sodium availability,
13
- whether the plugin is installed, and whether the Doctor panel reports the
14
- receive-only NWC credential as set. Never display its value. For a real store,
15
- ask the merchant to configure a receive-only wallet if none is available.
16
- For repository development, use the Docker demo's explicit testkit override.
104
+ The command runs the receive-only wallet preflight, encrypts the code and
105
+ prints only "Settings saved; wallet preflight passed."; a failure keeps the
106
+ previous settings. If it reports spend methods such as `pay_invoice`, ask the
107
+ user for a receive-only code again. Never turn on the spend-capable override.
108
+ `wp wc payment_gateway` and WooCommerce REST writes of these fields are
109
+ rejected on purpose; do not use them. A code set as a constant in
110
+ wp-config.php wins over the stored one and changes only through the host's
111
+ secret workflow.
17
112
 
18
- Upload a built plugin archive, not a zip of the source directory. The plugin
19
- has not yet been accepted into the WordPress.org directory. Configuration and
20
- the complete quickstart follow below.
113
+ Then run `wp openreceive configure --enable` and `wp openreceive doctor`.
114
+ Doctor names any failed check and exits nonzero; fix it before going on.
21
115
 
22
- The plugin owns only its payment-attempt tables in the WordPress database.
23
- WooCommerce owns orders, totals, stock and email. Do not add an external
24
- idempotency store, payment database, browser wallet credentials or custom
25
- fulfillment implementation. Guest return links use WooCommerce's order key;
26
- the plugin verifies it before issuing an expiring order-bound cookie.
116
+ ## Step 3 — mint a test invoice
117
+
118
+ Create a pending test order that pays with OpenReceive, then mint its
119
+ Lightning invoice from the terminal:
120
+
121
+ ```sh
122
+ wp wc shop_order create --user=<admin user id> --payment_method=openreceive \
123
+ --line_items='[{"product_id":<product id>,"quantity":1}]' --porcelain
124
+ wp openreceive test-invoice <order id>
125
+ ```
27
126
 
28
- Run `wp openreceive doctor` after configuration. Use the documented scheduled
29
- reconciliation or optional notifications command for offline settlement.
30
- Manual merchant refunds and provider-managed payer swap refunds are separate
31
- flows; a receive-only NWC wallet cannot send payments.
127
+ `test-invoice` goes through the same checkout route as the order-pay page. It
128
+ prints the amount in sats, the BOLT11 invoice and the order-pay link. Give the
129
+ user that link: it opens the checkout with the configured methods and resumes
130
+ this same invoice. Ask them to pay only if they want a real settlement test,
131
+ and tell them the test order is theirs to delete.
132
+
133
+ ## Non-negotiables
134
+
135
+ - Never print, log or commit a code, never put one in a shell argument, and
136
+ never write one into source files, wp-config.php or browser code. Doctor's
137
+ set/unset is all you report.
138
+ - Receive-only NWC is required. Never turn on the spend-capable override to
139
+ get past the preflight.
140
+ - The plugin owns only its payment-attempt tables in the WordPress database.
141
+ WooCommerce owns orders, totals, stock and email. Do not add an external
142
+ idempotency store, payment database or custom fulfillment code.
143
+ - IF SWAPS ARE ON, KEEP THE ROUTE BACK. A deposit that arrives short or late
144
+ becomes refundable, and the customer claims it later on the same order-pay
145
+ link (guests return with the order key in it). Keep order-pay links
146
+ reachable, and keep the plugin installed while swap orders may still need a
147
+ refund. https://openreceive.org/guides/swap-refunds.md
148
+ - A receive-only wallet cannot send merchant refunds. Refund a settled
149
+ payment manually from the wallet.
150
+ - Settlement runs on checkout requests and an every-minute scheduled job. On a
151
+ low-traffic store, run WordPress scheduled work from a system cron;
152
+ `wp openreceive notifications` is an optional long-running worker.
32
153
 
33
154
  ## Further reading
34
155
 
35
- - [Express Quickstart (Node)](https://openreceive.org/guides/quickstart-node.md)
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)
156
+ - [WordPress + WooCommerce Quickstart](https://openreceive.org/guides/quickstart-woocommerce.md)
51
157
  - [Automated Swaps](https://openreceive.org/guides/automated-swaps.md)
52
158
  - [Swap Refunds](https://openreceive.org/guides/swap-refunds.md)
53
159
  - [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
160
  - [Security](https://openreceive.org/guides/security.md)
60
- - [Provider Registry](https://openreceive.org/guides/provider-registry.md)
61
161
  - [Price Feeds](https://openreceive.org/guides/price-feeds.md)
62
- - [React Material UI Recipe](https://openreceive.org/guides/react-material-ui-recipe.md)
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
162
+ - [Payment Safety Upgrade](https://openreceive.org/guides/payment-safety-upgrade.md)
77
163
 
78
164
  ---
79
165
 
@@ -84,6 +170,10 @@ passes. The page it comes from is https://openreceive.org/guides/quickstart-wooc
84
170
 
85
171
  ## WordPress + WooCommerce quickstart
86
172
 
173
+ The [WordPress integration entry point](https://openreceive.org/integrations/wordpress)
174
+ redirects to the WooCommerce integration, which uses this same guide and agent
175
+ directions. OpenReceive checkout on WordPress requires WooCommerce.
176
+
87
177
  Activate WooCommerce first. Then install the built OpenReceive plugin zip
88
178
  through **Plugins → Add New → Upload Plugin**. You cannot upload the source
89
179
  directory as-is. It needs a build first. The plugin is not yet submitted to
@@ -96,15 +186,16 @@ database or application.
96
186
 
97
187
  ### Get the installable archive
98
188
 
99
- If the [OpenReceive GitHub release](https://github.com/OpenReceive/openreceive/releases)
100
- you picked lists `openreceive-wordpress-<version>.zip`, use that file. The GitHub
101
- source-code zip is not the plugin archive. If the release has no built zip yet,
102
- build one on a development machine with Node 22+, PHP 8.2+, Composer and WP-CLI:
189
+ Download [openreceive-wordpress-0.4.17.zip](https://github.com/OpenReceive/openreceive/releases/download/v0.4.17/openreceive-wordpress-0.4.17.zip)
190
+ from the matching release. Historical releases may lack this asset. If that exact
191
+ URL returns 404, build the same tag below; never silently install an older ZIP.
192
+ The GitHub source-code ZIP is not an installable plugin. On a development machine
193
+ with Node 22+, PHP 8.2+ with GMP/sodium, Composer and WP-CLI:
103
194
 
104
195
  ```sh
105
196
  git clone https://github.com/OpenReceive/openreceive.git
106
197
  cd openreceive
107
- git checkout <release-tag>
198
+ git checkout v0.4.17
108
199
  npm ci
109
200
  npm run build:packages
110
201
  composer install --working-dir=packages/php/wordpress
@@ -116,6 +207,40 @@ needs WP-CLI on `PATH`. Otherwise, set `OPENRECEIVE_WP_CLI` to the absolute path
116
207
  of its phar. Your WordPress server needs neither Node nor Composer. The built
117
208
  plugin already bundles its dependencies and checkout assets.
118
209
 
210
+ ### Enable GMP in both PHP runtimes
211
+
212
+ GMP is required by the bundled elliptic-curve dependency. Enable it for both
213
+ web PHP (Apache/FPM) and the PHP executable running WP-CLI. Installing it in
214
+ only the WordPress container does not update a separate CLI container.
215
+
216
+ For Debian-based official PHP/WordPress images, add to **each** Dockerfile:
217
+
218
+ ```dockerfile
219
+ USER root
220
+ RUN apt-get update && apt-get install -y --no-install-recommends libgmp-dev \
221
+ && docker-php-ext-install gmp \
222
+ && rm -rf /var/lib/apt/lists/*
223
+ ```
224
+
225
+ For Alpine-based PHP/CLI images:
226
+
227
+ ```dockerfile
228
+ USER root
229
+ RUN apk add --no-cache gmp \
230
+ && apk add --no-cache --virtual .gmp-build $PHPIZE_DEPS gmp-dev \
231
+ && docker-php-ext-install gmp \
232
+ && apk del .gmp-build
233
+ ```
234
+
235
+ Restore the base image's original runtime user after installing extensions.
236
+ Rebuild and recreate both containers. On Debian/Ubuntu hosts, install the GMP
237
+ package matching the active PHP version (for example `php8.2-gmp` for PHP 8.2),
238
+ then restart that version's web PHP service. Verify `php --ri gmp` and
239
+ `wp openreceive doctor` for CLI, and the gateway Doctor panel for web PHP.
240
+ On managed WordPress hosting, ask the host to enable GMP and sodium in both
241
+ runtimes; if they cannot, this plugin cannot run there. Do not use Composer's
242
+ `--ignore-platform-reqs` to bypass the requirements.
243
+
119
244
  ### Configure the wallet
120
245
 
121
246
  1. Open **WooCommerce → Settings → Payments → OpenReceive**.
@@ -134,6 +259,40 @@ providers, you can also set the `OPENRECEIVE_LSC_URI_PRIMARY` and
134
259
  `OPENRECEIVE_LSC_URI_BACKUP` constants. Never put these values in browser code
135
260
  or logs.
136
261
 
262
+ #### Configure through WP-CLI
263
+
264
+ `wp openreceive configure` accepts one credential at a time from stdin. Feed
265
+ stdin through your secret manager or an existing protected file, never a code
266
+ literal in the command line:
267
+
268
+ ```sh
269
+ wp openreceive configure --nwc-uri=- < /secure/path/wallet-code
270
+ wp openreceive configure --lsc-uri-primary=- < /secure/path/swap-code
271
+ wp openreceive configure --enable
272
+ wp openreceive doctor
273
+ ```
274
+
275
+ Omit the swap command for Bitcoin-only checkout. `--lsc-uri-backup=-` adds a
276
+ backup. These commands share admin preflight and encrypted storage. Credential
277
+ flags accept only `-`; blank input leaves settings intact. Generic WooCommerce
278
+ REST and `wp wc payment_gateway` credential updates are rejected. `doctor`
279
+ reports the failed check with credentials redacted and exits nonzero on failure.
280
+ The default payment title becomes “Bitcoin & crypto (OpenReceive)” with swaps;
281
+ a customized title is preserved.
282
+
283
+ To check checkout from the terminal, mint an invoice for an unpaid order whose
284
+ payment method is OpenReceive:
285
+
286
+ ```sh
287
+ wp wc shop_order create --user=<admin user id> --payment_method=openreceive \
288
+ --line_items='[{"product_id":<product id>,"quantity":1}]' --porcelain
289
+ wp openreceive test-invoice <order id>
290
+ ```
291
+
292
+ `test-invoice` uses the same checkout route as the order-pay page. It prints the
293
+ amount in sats, the Lightning invoice and the order-pay link, which opens the
294
+ checkout on that invoice. Delete the test order when you are done.
295
+
137
296
  ### Checkout and settlement
138
297
 
139
298
  Both WooCommerce checkout blocks and classic checkout send the customer to the