openreceive 0.4.11 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/lib/openreceive/version.rb +1 -1
- data/skills/debug-openreceive-payment/SKILL.md +1 -1
- data/skills/integrate-openreceive/SKILL.md +4 -3
- data/skills/integrate-openreceive/references/btcpay.md +65 -36
- data/skills/integrate-openreceive/references/django.md +295 -246
- data/skills/integrate-openreceive/references/fastapi.md +210 -157
- data/skills/integrate-openreceive/references/fastify.md +206 -149
- data/skills/integrate-openreceive/references/laravel.md +298 -236
- data/skills/integrate-openreceive/references/next.md +216 -164
- data/skills/integrate-openreceive/references/node.md +194 -140
- data/skills/integrate-openreceive/references/php.md +248 -193
- data/skills/integrate-openreceive/references/rails.md +266 -220
- data/skills/integrate-openreceive/references/woocommerce.md +66 -51
- metadata +1 -1
|
@@ -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.14.
|
|
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
|
|
@@ -35,75 +35,99 @@ The one required credential is a receive-only NWC code (Nostr Wallet Connect):
|
|
|
35
35
|
a string from the merchant's wallet that can create invoices and read their
|
|
36
36
|
status, and cannot spend. A swap provider (an "LSC" code) optionally lets the
|
|
37
37
|
payer send USDT, USDC, ETH or SOL instead, converted into that same
|
|
38
|
-
Lightning payment.
|
|
38
|
+
Lightning payment. Step 0 collects those credentials from the user; you write three hooks — `authorize`,
|
|
39
39
|
`amountFor`, `onPaid` on one `OpenReceive\Host`;
|
|
40
40
|
OpenReceive supplies invoices, polling, settlement and the checkout UI. It never
|
|
41
41
|
owns orders, users, prices, or fulfillment.
|
|
42
42
|
|
|
43
|
-
## Step 0 —
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
>
|
|
72
|
-
>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
`
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
43
|
+
## Step 0 — ask for the two codes, one question at a time
|
|
44
|
+
|
|
45
|
+
Before anything else, look at one file: the project's `.env`, if it exists.
|
|
46
|
+
Read it only far enough to see whether `NWC_URI` and `LSC_URI_PRIMARY` are
|
|
47
|
+
there and non-empty, and never print the values. A code that is already
|
|
48
|
+
set is not asked for again; if both are set, skip to step 5
|
|
49
|
+
(making the server load the file), then the quickstart.
|
|
50
|
+
|
|
51
|
+
Otherwise your next action is a question to the user. Do not run `composer require`,
|
|
52
|
+
edit the app, write `.env.example`, or search anywhere else before asking
|
|
53
|
+
it. Do not read deploy config (compose files, `SetEnv`/`fastcgi_param` blocks, platform secrets), Docker containers, or process environments, and never run
|
|
54
|
+
`printenv`. A new shop has neither code yet: hunting for one burns the turn and
|
|
55
|
+
can leak other secrets into the transcript.
|
|
56
|
+
|
|
57
|
+
Two server-only URIs are needed before the quickstart:
|
|
58
|
+
|
|
59
|
+
- `NWC_URI` — a receive-only Nostr Wallet Connect code,
|
|
60
|
+
`nostr+walletconnect://…`. Required for Bitcoin.
|
|
61
|
+
- `LSC_URI_PRIMARY` — a Lightning Swap Connect URI,
|
|
62
|
+
`lightning+swapconnect://…`. Required for USDT, USDC, ETH and SOL. Skip it
|
|
63
|
+
only when the user says they want Bitcoin alone.
|
|
64
|
+
|
|
65
|
+
The user never edits an environment file. They paste each code into the chat;
|
|
66
|
+
you store it. Ask one question per message.
|
|
67
|
+
|
|
68
|
+
1. **First message — the NWC code, and nothing else.** Ask for it and walk them
|
|
69
|
+
through getting it:
|
|
70
|
+
|
|
71
|
+
> To receive payments I need a receive-only wallet code. In Rizful: open
|
|
72
|
+
> the menu, tap NWC, choose Receive-only NWC code, and tap Copy
|
|
73
|
+
> (https://openreceive.org/get_a_nwc_code_to_receive_payments). If you would
|
|
74
|
+
> rather run your own wallet, Alby Hub works too: Connections → Add
|
|
75
|
+
> Connection → Read Only. Paste the code here and I will store it.
|
|
76
|
+
|
|
77
|
+
Do not mention `.env`, exports, or "tell me when it's set".
|
|
78
|
+
2. **When they paste it.** If it does not start with `nostr+walletconnect://`,
|
|
79
|
+
ask them to copy the receive-only code again. Otherwise write
|
|
80
|
+
`NWC_URI=<paste>` into the project's `.env`, creating the file if needed.
|
|
81
|
+
Make sure `.gitignore` covers `.env` (and `.dockerignore`, if the app has
|
|
82
|
+
one). Never echo the value, commit it, or put it in client code. Reply only
|
|
83
|
+
that it is saved, then ask the next question.
|
|
84
|
+
3. **Second message — swaps.** If the user already asked for stablecoins or
|
|
85
|
+
altcoins, skip the yes/no and go straight to the walkthrough. Otherwise ask
|
|
86
|
+
whether payers should also be able to pay with USDT, USDC, ETH or SOL. The
|
|
87
|
+
walkthrough:
|
|
88
|
+
|
|
89
|
+
> Go to https://lightning-swap.com, sign in for API keys, create a key, and
|
|
90
|
+
> copy the whole URI (https://openreceive.org/set_up_swap_provider). Paste
|
|
91
|
+
> it here and I will store it — or say "Bitcoin only" and I will continue
|
|
92
|
+
> without it.
|
|
93
|
+
|
|
94
|
+
Mention FixedFloat only if they already use it.
|
|
95
|
+
4. **When they paste it.** If it does not start with
|
|
96
|
+
`lightning+swapconnect://`, ask them to copy it again. Otherwise add
|
|
97
|
+
`LSC_URI_PRIMARY=<paste>` to the same `.env`, without echoing it. Swaps
|
|
98
|
+
are now on, so build the refund route back (the swap non-negotiable below) as
|
|
99
|
+
part of this integration. If they chose Bitcoin only, leave
|
|
100
|
+
`LSC_URI_PRIMARY` unset and skip that route.
|
|
101
|
+
5. **Make the server load the file — yourself.** Nothing in PHP
|
|
102
|
+
reads `.env` on its own. Add `vlucas/phpdotenv` and load the file in the
|
|
103
|
+
front controller before `Service::fromEnvironment()`. When the app is started
|
|
104
|
+
with Docker Compose, give the service `env_file: .env`. Restart PHP-FPM or
|
|
105
|
+
the server after writing the file.
|
|
106
|
+
|
|
107
|
+
Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
|
|
108
|
+
saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
|
|
109
|
+
runs the receive-only preflight: if it reports spend methods such as
|
|
110
|
+
`pay_invoice`, remove `NWC_URI` from `.env` and ask for a receive-only code
|
|
111
|
+
again. Never set the spend-capable override to get past it.
|
|
112
|
+
|
|
113
|
+
Then check the PHP runtime: `php -m` must list `gmp`, `sodium`, `mbstring`,
|
|
114
|
+
`json`, `pdo` and one PDO driver. `ext-gmp` is REQUIRED — the NWC transport
|
|
115
|
+
signs every request with it — and `php:*-cli`/`-fpm` images do not ship it
|
|
116
|
+
(`docker-php-ext-install gmp`). PHP must be ≥ 8.2 and 64-bit.
|
|
117
|
+
|
|
118
|
+
### Upgrading an existing install
|
|
119
|
+
|
|
120
|
+
This is not the opening move of a new integration. When OpenReceive is ALREADY
|
|
121
|
+
installed here, `OpenReceive\Server\Doctor::report()` (a plain host runs it
|
|
122
|
+
from the quickstart's `bin/doctor` script) reports every credential as
|
|
123
|
+
set/unset (never the value), the host class and any placeholder hooks, the
|
|
124
|
+
mount, and the wallet preflight. Check the installed `openreceive/openreceive`
|
|
125
|
+
(`composer show openreceive/openreceive`) and the unpacked checkout's
|
|
126
|
+
`MANIFEST.json` against the release named at the top of this file. The two
|
|
127
|
+
must match: the browser build and the engine are one release. Upgrade first —
|
|
128
|
+
and if this app runs in containers, rebuild the images: `vendor/` is baked into
|
|
129
|
+
the image, so an in-place `composer update` is undone by the next
|
|
130
|
+
`compose up`.
|
|
107
131
|
|
|
108
132
|
Only then start the quickstart.
|
|
109
133
|
|
|
@@ -147,7 +171,9 @@ itself, and they hold for every integration.
|
|
|
147
171
|
and the shadow invoice behind a swap lasts about half an hour, after which the
|
|
148
172
|
same click mints a NEW deposit address and the refund is off-screen. Keep the
|
|
149
173
|
`payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
|
|
150
|
-
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
|
|
151
177
|
- Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
|
|
152
178
|
the price from `amountFor` and the drop-in renders it above the amount.
|
|
153
179
|
Without it the checkout is a QR and "$1.00" with no sign of what the dollar
|
|
@@ -312,13 +338,14 @@ passes. The page it comes from is https://openreceive.org/guides/quickstart-php.
|
|
|
312
338
|
|
|
313
339
|
## PHP quickstart (plain PHP)
|
|
314
340
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
341
|
+
This page is for plain PHP, with no framework: a front controller, a PDO handle
|
|
342
|
+
and three methods. Laravel has its own quickstart (`openreceive/laravel`, the
|
|
343
|
+
thin adapter over this engine).
|
|
344
|
+
|
|
345
|
+
Requires PHP ≥ 8.2 (64-bit) with `ext-gmp`, `ext-sodium`, `ext-mbstring`,
|
|
346
|
+
`ext-json`, `ext-pdo` and one PDO driver (`pdo_pgsql`, `pdo_sqlite` or
|
|
347
|
+
`pdo_mysql`). `ext-gmp` is **required**, not optional. The NWC transport signs
|
|
348
|
+
every wallet request with it.
|
|
322
349
|
|
|
323
350
|
### 1. Install
|
|
324
351
|
|
|
@@ -326,26 +353,27 @@ three methods.
|
|
|
326
353
|
composer require openreceive/openreceive nyholm/psr7 nyholm/psr7-server
|
|
327
354
|
```
|
|
328
355
|
|
|
329
|
-
`openreceive/openreceive` is the whole engine
|
|
330
|
-
exact money, settlement, the `openreceive_payments` repository
|
|
331
|
-
rates and a PSR-15 handler. It depends on the PSR
|
|
332
|
-
PSR-7/PSR-17 implementation your app already has
|
|
333
|
-
`nyholm/psr7-server` is the smallest pair and
|
|
356
|
+
`openreceive/openreceive` is the whole engine. It includes the receive-only
|
|
357
|
+
wallet client, exact money, settlement, the `openreceive_payments` repository
|
|
358
|
+
over PDO, swaps, rates and a PSR-15 handler. It depends only on the PSR
|
|
359
|
+
interfaces, so bring the PSR-7/PSR-17 implementation your app already has.
|
|
360
|
+
`nyholm/psr7` + `nyholm/psr7-server` is the smallest pair, and this page uses
|
|
361
|
+
it.
|
|
334
362
|
|
|
335
363
|
The **checkout UI is not in the Composer package.** Packagist installs from git
|
|
336
|
-
and cannot run a JS build
|
|
364
|
+
and cannot run a JS build. So the browser side ships separately, as
|
|
337
365
|
`standalone-checkout-<version>.tar.gz` on every
|
|
338
|
-
[GitHub release](https://github.com/openreceive/openreceive/releases)
|
|
339
|
-
self-contained ES module, its stylesheet, a source map and a
|
|
340
|
-
`MANIFEST.json`. Unpack it somewhere your web server serves
|
|
341
|
-
(step 5).
|
|
342
|
-
instead
|
|
366
|
+
[GitHub release](https://github.com/openreceive/openreceive/releases). It holds
|
|
367
|
+
one self-contained ES module, its stylesheet, a source map and a
|
|
368
|
+
`MANIFEST.json`. Unpack it somewhere your web server serves static files
|
|
369
|
+
(step 5). If your app has a JS bundler, you can `npm install @openreceive/elements`
|
|
370
|
+
instead. The tarball is the same build.
|
|
343
371
|
|
|
344
372
|
### 2. Migrate the payment tables
|
|
345
373
|
|
|
346
|
-
The engine owns two tables in **your** database and renders their DDL
|
|
347
|
-
dialect. Run
|
|
348
|
-
Phinx, Doctrine Migrations, a plain SQL file, a `bin/migrate` script
|
|
374
|
+
The engine owns two tables in **your** database and renders their DDL for each
|
|
375
|
+
SQL dialect. Run that DDL through whatever your app uses for schema changes:
|
|
376
|
+
Phinx, Doctrine Migrations, a plain SQL file, or a `bin/migrate` script.
|
|
349
377
|
|
|
350
378
|
```php
|
|
351
379
|
use OpenReceive\Storage\PaymentsSchema;
|
|
@@ -357,16 +385,18 @@ foreach (PaymentsSchema::statements($dialect) as $sql) {
|
|
|
357
385
|
// down(): PaymentsSchema::dropStatements()
|
|
358
386
|
```
|
|
359
387
|
|
|
360
|
-
`PaymentsSchema::migrate(new PdoConnection($pdo))`
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
388
|
+
If your script has no migration tool, `PaymentsSchema::migrate(new PdoConnection($pdo))`
|
|
389
|
+
does the same in one call. It creates two tables. Leave both to the library:
|
|
390
|
+
|
|
391
|
+
- `openreceive_payments`: one row per payment attempt.
|
|
392
|
+
- `openreceive_meta`: the reconcile gate and the schema version.
|
|
393
|
+
|
|
394
|
+
Details: [Payment storage](https://openreceive.org/guides/storage.md).
|
|
365
395
|
|
|
366
396
|
### 3. Add wallet credentials
|
|
367
397
|
|
|
368
|
-
Create a server-only `.env
|
|
369
|
-
|
|
398
|
+
Create a server-only `.env`, or export the variables from your process manager.
|
|
399
|
+
The engine reads `getenv()` and `$_ENV`.
|
|
370
400
|
|
|
371
401
|
```dotenv
|
|
372
402
|
NWC_URI=
|
|
@@ -375,27 +405,28 @@ LSC_URI_BACKUP=
|
|
|
375
405
|
```
|
|
376
406
|
|
|
377
407
|
1. Get a receive-only NWC code from a compatible wallet
|
|
378
|
-
([get one here](https://openreceive.org/get_a_nwc_code_to_receive_payments))
|
|
379
|
-
|
|
380
|
-
2.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
408
|
+
([get one here](https://openreceive.org/get_a_nwc_code_to_receive_payments)).
|
|
409
|
+
Put it in `NWC_URI`.
|
|
410
|
+
2. Optional: set up a [swap provider](https://openreceive.org/set_up_swap_provider).
|
|
411
|
+
Put its connection string in `LSC_URI_PRIMARY`, and a second one in
|
|
412
|
+
`LSC_URI_BACKUP` if you have one.
|
|
413
|
+
|
|
414
|
+
Never put these values in browser code. Your app refuses to start if the NWC
|
|
415
|
+
code also advertises spend methods such as `pay_invoice`. Create a
|
|
416
|
+
receive-only code instead ([Security](https://openreceive.org/guides/security.md)).
|
|
417
|
+
|
|
418
|
+
Nothing in PHP loads a `.env` file on its own. Something has to put the values
|
|
419
|
+
in the process environment first: `vlucas/phpdotenv`, your web server's
|
|
420
|
+
`SetEnv`/`fastcgi_param`, or the container runtime
|
|
390
421
|
([Environment variables](https://openreceive.org/guides/environment-variables.md)).
|
|
391
422
|
|
|
392
423
|
### 4. Wire OpenReceive
|
|
393
424
|
|
|
394
425
|
Three methods on one object are the entire bridge between the engine and your
|
|
395
|
-
data
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
426
|
+
data. The engine never sees an order, a user or a price except through them.
|
|
427
|
+
`Engine` combines the wallet, the repository over your PDO, and that object
|
|
428
|
+
into a PSR-15 handler. Your front controller sends requests under one path
|
|
429
|
+
prefix to that handler:
|
|
399
430
|
|
|
400
431
|
```php
|
|
401
432
|
<?php
|
|
@@ -481,57 +512,73 @@ if (str_starts_with($path, '/openreceive')) {
|
|
|
481
512
|
```
|
|
482
513
|
|
|
483
514
|
`Service::fromEnvironment()` builds the wallet client from `NWC_URI` and runs
|
|
484
|
-
the receive-only preflight
|
|
515
|
+
the receive-only preflight. A missing, invalid or spend-capable code throws
|
|
485
516
|
before any route is served. PHP starts every request from nothing, so that
|
|
486
|
-
check runs
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
517
|
+
check runs on every request that reaches the engine.
|
|
518
|
+
|
|
519
|
+
The settlement gate the engine relies on lives in `openreceive_meta`, not in
|
|
520
|
+
memory. That is why a fleet of PHP-FPM workers shares one wallet-scan budget,
|
|
521
|
+
with no worker process of its own. Later OpenReceive requests also settle
|
|
522
|
+
pending invoices, so a payer who closes the tab is still covered. `authorize`
|
|
523
|
+
runs on every request.
|
|
491
524
|
→ [Engine](https://openreceive.org/guides/api-reference.md#openreceiveserverengine) ·
|
|
492
525
|
[Host](https://openreceive.org/guides/api-reference.md#openreceivehost) ·
|
|
493
526
|
[the authorize context](https://openreceive.org/guides/api-reference.md#the-authorize-context-php)
|
|
494
527
|
|
|
495
|
-
**Cross-site requests.** Plain PHP has no CSRF layer,
|
|
496
|
-
|
|
497
|
-
`Sec-Fetch-Site` header says `cross-site
|
|
498
|
-
origin cannot
|
|
499
|
-
|
|
500
|
-
`X-CSRF-Token` (or the header named by `csrf-header`) for your own layer
|
|
501
|
-
check.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
528
|
+
**Cross-site requests.** Plain PHP has no CSRF layer, just like Express, and the
|
|
529
|
+
engine does not need one. Every mounted route refuses a request whose
|
|
530
|
+
`Sec-Fetch-Site` header says `cross-site`. So a form or script on another
|
|
531
|
+
origin cannot create invoices using a payer's cookie. That makes
|
|
532
|
+
`<meta name="csrf-token">` optional. If you set it, the checkout sends the value
|
|
533
|
+
back as `X-CSRF-Token` (or the header named by `csrf-header`) for your own layer
|
|
534
|
+
to check.
|
|
535
|
+
|
|
536
|
+
The engine's check does NOT cover two cases:
|
|
537
|
+
|
|
538
|
+
- A browser too old to send `Sec-Fetch-Site`. The header is absent, and an
|
|
539
|
+
absent header passes.
|
|
540
|
+
- Anything that is not a browser at all. A script holding a stolen cookie is a
|
|
541
|
+
session problem, not a forgery problem.
|
|
542
|
+
|
|
543
|
+
`authorize` is still the boundary that decides whether *this caller* may act on
|
|
544
|
+
*this order* ([Security](https://openreceive.org/guides/security.md)).
|
|
545
|
+
|
|
546
|
+
For public web shops, turn on the per-IP invoice cap with `rateLimiting: true`
|
|
547
|
+
on `Engine`. Leave it off (the default) when many payers share one IP. Behind a
|
|
548
|
+
proxy, pass `clientIp: fn ($request) => …` so the cap counts the payer, not the
|
|
549
|
+
proxy. → [Rate limiting](https://openreceive.org/guides/rate-limiting.md)
|
|
550
|
+
|
|
551
|
+
Your app also needs an ordinary order-creation route. It validates the cart,
|
|
552
|
+
prices with exact decimal math, and returns the order id. The page then passes
|
|
553
|
+
that id as the `reference`. OpenReceive never prices from payer input.
|
|
554
|
+
|
|
555
|
+
The `reference` is a string you choose, and it is the fulfillment identity. Use
|
|
556
|
+
your order id:
|
|
557
|
+
|
|
558
|
+
- one per thing you fulfill,
|
|
559
|
+
- created before checkout,
|
|
560
|
+
- kept across retries,
|
|
561
|
+
- never reused.
|
|
562
|
+
|
|
563
|
+
`onPaid` commits fulfillment once per reference, and a new checkout under a
|
|
564
|
+
reference that already settled is refused with 409. A fresh id per page load
|
|
565
|
+
would let one order be paid twice.
|
|
566
|
+
|
|
567
|
+
Naming: PHP APIs use camelCase methods and snake_case array keys
|
|
568
|
+
(`amount_msats`, `payment_hash`). The keys match the wire format. The mounted
|
|
569
|
+
HTTP routes and the browser snapshots are snake_case throughout.
|
|
524
570
|
|
|
525
571
|
### 5. Render checkout
|
|
526
572
|
|
|
527
|
-
Serve the compiled `styles.css` without Tailwind processing
|
|
528
|
-
JavaScript (with a CSS-capable bundler) or use a plain
|
|
529
|
-
Do not `@import` it into
|
|
530
|
-
|
|
573
|
+
Serve the compiled `styles.css` without Tailwind processing. Either import it
|
|
574
|
+
from JavaScript (with a CSS-capable bundler) or use a plain
|
|
575
|
+
`<link rel="stylesheet">`. Do not `@import` it into your Tailwind entry. Its
|
|
576
|
+
rules have zero specificity, so your own styles can override checkout styles.
|
|
577
|
+
Scoping does not prevent that.
|
|
531
578
|
|
|
532
579
|
Unpack the release's `standalone-checkout-<version>.tar.gz` into a directory
|
|
533
|
-
your web server serves
|
|
534
|
-
the element:
|
|
580
|
+
your web server serves. This page uses `public/openreceive/`. Then add two tags
|
|
581
|
+
and the element:
|
|
535
582
|
|
|
536
583
|
```html
|
|
537
584
|
<link rel="stylesheet" href="/openreceive/openreceive-checkout.css" />
|
|
@@ -543,32 +590,33 @@ the element:
|
|
|
543
590
|
></openreceive-checkout>
|
|
544
591
|
```
|
|
545
592
|
|
|
546
|
-
The module registers `<openreceive-checkout>` as it loads
|
|
593
|
+
The module registers `<openreceive-checkout>` as it loads. The element creates
|
|
547
594
|
the checkout for `reference`, then renders, polls and settles itself. The
|
|
548
|
-
stylesheet is scoped to what OpenReceive renders. The checkout follows the
|
|
549
|
-
always one theme, lock it with
|
|
550
|
-
|
|
551
|
-
|
|
595
|
+
stylesheet is scoped to what OpenReceive renders. The checkout follows the
|
|
596
|
+
payer's theme. On a page that always uses one theme, lock it with
|
|
597
|
+
`theme="dark"`. React, Vue, Svelte, and Angular apps use the matching wrapper
|
|
598
|
+
package instead, with the same attributes
|
|
599
|
+
([Frontend checkout](https://openreceive.org/guides/frontend-checkout.md)). A custom UI builds on
|
|
552
600
|
`@openreceive/browser/headless` ([Headless checkout](https://openreceive.org/guides/headless-checkout.md)).
|
|
553
601
|
|
|
554
602
|
Everything the checkout draws ships inside the JavaScript: the payment-method
|
|
555
603
|
icons, the wallet logos and the pay tutorials. There is no image file to copy
|
|
556
604
|
or serve and no asset option to set. Deploy your normal JavaScript and CSS
|
|
557
605
|
build output, including any generated JavaScript chunks. Bundlers with code
|
|
558
|
-
splitting can
|
|
559
|
-
|
|
560
|
-
Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
606
|
+
splitting can load tutorial screenshots only when a tutorial is first opened.
|
|
607
|
+
Single-file builds, including the standalone checkout, include them upfront. If
|
|
608
|
+
your Content-Security-Policy has a strict `img-src`, allow `data:`
|
|
561
609
|
([Provider registry](https://openreceive.org/guides/provider-registry.md#assets)).
|
|
562
610
|
|
|
563
|
-
`MANIFEST.json` in the tarball carries the version and a SHA-256 per file
|
|
564
|
-
|
|
565
|
-
version in step with the Composer package.
|
|
611
|
+
`MANIFEST.json` in the tarball carries the version and a SHA-256 per file. You
|
|
612
|
+
can use it to check a copied tree against the release it came from. Keep the
|
|
613
|
+
tarball version in step with the Composer package.
|
|
566
614
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
It has products, visitors, and orders,
|
|
571
|
-
Map that shape onto the models in THIS app.
|
|
615
|
+
Buy a Button
|
|
616
|
+
(`examples/buttons/server/php-plain`)
|
|
617
|
+
is a runnable illustration of this boundary. It is not a template to copy
|
|
618
|
+
models from. It has products, visitors, and orders, and the three hooks are the
|
|
619
|
+
only bridge. Map that shape onto the models in THIS app.
|
|
572
620
|
|
|
573
621
|
### 6. Verify
|
|
574
622
|
|
|
@@ -581,28 +629,33 @@ foreach (\OpenReceive\Server\Doctor::report(
|
|
|
581
629
|
) as $line) echo $line, PHP_EOL;
|
|
582
630
|
```
|
|
583
631
|
|
|
584
|
-
`Doctor::report()` prints
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
632
|
+
`Doctor::report()` prints:
|
|
633
|
+
|
|
634
|
+
- every credential as set or unset, never its value;
|
|
635
|
+
- the host class, and which of the three methods are still the scaffolded
|
|
636
|
+
placeholders (`Hosts\AllowAllAuthorize`, `Hosts\LoggingOnPaid`). The engine
|
|
637
|
+
also warns at boot while either is in use;
|
|
638
|
+
- where the handler is mounted;
|
|
639
|
+
- the receive-only wallet preflight.
|
|
640
|
+
|
|
641
|
+
`$engine->doctor()` is the same report for an engine you already built. Put it
|
|
642
|
+
behind a `bin/doctor` script. The demo's is twelve lines.
|
|
643
|
+
→ [Doctor](https://openreceive.org/guides/api-reference.md#openreceiveserverdoctor)
|
|
591
644
|
|
|
592
|
-
Then open the checkout in a browser
|
|
645
|
+
Then open the checkout in a browser. Confirm the payment-method icons and
|
|
593
646
|
wallet logos render, and open a wallet's pay tutorial to check its screenshots.
|
|
594
|
-
If an image is missing,
|
|
647
|
+
If an image is missing, check the console for CSP violations and the Network
|
|
595
648
|
panel for failed JavaScript chunks. Allow `data:` in `img-src` and deploy the
|
|
596
649
|
complete build output. Do not add image routes, copy package source images, or
|
|
597
650
|
use registry `icon_path` / tutorial `path` keys as browser URLs.
|
|
598
651
|
|
|
599
652
|
### Reconciliation
|
|
600
653
|
|
|
601
|
-
Settlement runs on the request path
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
need a cron job. Tune or disable it with `Engine`'s
|
|
605
|
-
(`false`, or `['min_interval_seconds' => …]`).
|
|
654
|
+
Settlement runs on the request path. Every payment route first runs one bounded
|
|
655
|
+
reconcile pass through the durable `openreceive_meta` gate. The gate allows at
|
|
656
|
+
most one real wallet scan every 3 seconds, shared by every PHP process. You do
|
|
657
|
+
not need a cron job. Tune or disable it with `Engine`'s
|
|
658
|
+
`opportunisticReconcile` (`false`, or `['min_interval_seconds' => …]`).
|
|
606
659
|
|
|
607
660
|
Optionally, run one worker so settlement does not wait for the next page load:
|
|
608
661
|
|
|
@@ -610,18 +663,20 @@ Optionally, run one worker so settlement does not wait for the next page load:
|
|
|
610
663
|
$engine->notificationsWorker()->run(); // blocks: an NWC-02 listener plus a periodic pass
|
|
611
664
|
```
|
|
612
665
|
|
|
613
|
-
as its own long-lived process (`php bin/notifications`).
|
|
614
|
-
|
|
666
|
+
Run it as its own long-lived process (`php bin/notifications`). To run a pass
|
|
667
|
+
yourself, use the one-shot `$engine->reconcile()`.
|
|
615
668
|
→ [Engine notificationsWorker](https://openreceive.org/guides/api-reference.md#engine-notificationsworker)
|
|
616
669
|
|
|
617
670
|
### Swap secrets
|
|
618
671
|
|
|
619
|
-
Setting `LSC_URI_PRIMARY` (and `LSC_URI_BACKUP`) auto-builds the matching
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
672
|
+
Setting `LSC_URI_PRIMARY` (and `LSC_URI_BACKUP`) auto-builds the matching swap
|
|
673
|
+
providers. Nothing in your code changes. One `openreceive_payments` row holds
|
|
674
|
+
at most one provider order, in its server-only `swap_data`. The repository
|
|
675
|
+
never selects it into public arrays. Do not log it or return it from your own
|
|
676
|
+
API.
|
|
677
|
+
|
|
678
|
+
**Setting either connection string commits you to refunds.** A deposit that
|
|
679
|
+
arrives short or late is claimed on a second visit. That needs a per-order URL
|
|
680
|
+
your app serves, and the attempt's `payment_hash` kept.
|
|
681
|
+
[Swap refunds](https://openreceive.org/guides/swap-refunds.md) covers all of it. Read it before you set
|
|
682
|
+
`LSC_URI_PRIMARY`.
|