openreceive 0.4.25 → 0.4.27

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.
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (PHP)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 41 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 43 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/php/full.md
8
8
  # Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
9
9
  ```
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/php/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.25.
20
+ These directions describe OpenReceive 0.4.27.
21
21
 
22
22
  Add OpenReceive to a PHP application — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the engine is on Packagist
@@ -124,6 +124,8 @@ you store it. Ask one question per message.
124
124
  front controller before `Service::fromEnvironment()`. When the app is started
125
125
  with Docker Compose, give the service `env_file: .env`. Restart PHP-FPM or
126
126
  the server after writing the file.
127
+ Under Compose, recreate the container with `docker compose up -d`:
128
+ `docker compose restart` keeps its old environment.
127
129
 
128
130
  Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
129
131
  saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
@@ -246,6 +248,10 @@ itself, and they hold for every integration.
246
248
  `payment_hash` and reopen the attempt with `POST /swaps/status`, which has no
247
249
  such window. On the element: the `resume-payment-hash` attribute, fed from
248
250
  the `openreceive-state` event (`event.detail.state.payment_hash`).
251
+ That visit can come hours later, and `authorize` must still know the payer:
252
+ a session that expired in between answers 403 and strands the deposit. On a
253
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
254
+ per-order token in its URL that your page trades for that cookie.
249
255
  https://openreceive.org/guides/swap-refunds.md
250
256
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
251
257
  the price from `amountFor` and the drop-in renders it above the amount.
@@ -263,10 +269,11 @@ itself, and they hold for every integration.
263
269
  array keys, and the browser packages' TypeScript APIs are camelCase.
264
270
  - Money is integers or decimal strings — never binary floats. `amountFor`
265
271
  returns `'value' => '12.00'`, a string; never `12.00`.
266
- - PHP starts every request from nothing. The engine is built for that — the
267
- settlement gate is a row in `openreceive_meta`, not process memory — so do
268
- not add a cache, a static, or an APCu entry to "remember" the wallet or a
269
- reconcile timer between requests.
272
+ - PHP starts every request from nothing. The engine is built for that: the
273
+ settlement gate is a row in `openreceive_meta`, and under a web server
274
+ `Service::fromEnvironment()` keeps the wallet's info and the market data in
275
+ a private temp directory. So do not add a cache, a static, or an APCu entry
276
+ to "remember" the wallet or a reconcile timer between requests.
270
277
 
271
278
  ## Your tables, not ours
272
279
 
@@ -298,6 +305,12 @@ not replace your orders, users, or products, and you do not join them.
298
305
  expired invoice does not cancel the order; a later checkout may mint another
299
306
  attempt. The engine refuses a new checkout under a reference that already
300
307
  settled (409).
308
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
309
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
310
+ checkout only while the order is unpaid. When it settles on the page, update
311
+ the page's status line from the `openreceive-settled` event on
312
+ `<openreceive-checkout>`: `onPaid` has already marked the order paid. Keep
313
+ that checkout mounted, because it is the payer's receipt.
301
314
  - **Pass this app's PDO.** `new SqlPaymentRepository(new PdoConnection($pdo))`
302
315
  over the connection the app already opens; do not implement
303
316
  `PaymentRepository` unless no PDO can reach this database. `reference` is not
@@ -587,8 +600,20 @@ if (str_starts_with($path, '/openreceive')) {
587
600
 
588
601
  `Service::fromEnvironment()` builds the wallet client from `NWC_URI` and runs
589
602
  the receive-only preflight. A missing, invalid or spend-capable code throws
590
- before any route is served. PHP starts every request from nothing, so that
591
- check runs on every request that reaches the engine.
603
+ before any route is served.
604
+
605
+ PHP starts every request from nothing. Without a cache, every checkout route
606
+ would ask the wallet for its info event again and fetch the BTC price and the
607
+ swap provider's currencies and rates again, which costs seconds per click.
608
+ Under a web server, `fromEnvironment()` keeps them in a directory under the
609
+ system temp dir that only the PHP user can open
610
+ (`OpenReceive\Support\FileSharedCache`). The wallet's info is kept for 10
611
+ minutes. A new code gets a new directory, so it is always checked live.
612
+ Command-line runs, such as `bin/doctor` and the notifications worker, always
613
+ ask the wallet. No secret goes in the cache: codes, payments and swap data
614
+ stay out of it. If your app already runs a cache, pass it as `sharedCache:`
615
+ ([Service](https://openreceive.org/guides/api-reference.md#openreceiveserverservice)); `sharedCache: false`
616
+ turns caching off.
592
617
 
593
618
  The settlement gate the engine relies on lives in `openreceive_meta`, not in
594
619
  memory. That is why a fleet of PHP-FPM workers shares one wallet-scan budget,
@@ -712,8 +737,11 @@ foreach (\OpenReceive\Server\Doctor::report(
712
737
  - where the handler is mounted;
713
738
  - the receive-only wallet preflight.
714
739
 
715
- `$engine->doctor()` is the same report for an engine you already built. Put it
716
- behind a `bin/doctor` script. The demo's is twelve lines.
740
+ `$engine->doctor()` is the same report for an engine you already built, but it
741
+ runs the wallet preflight only when you pass the same factory:
742
+ `$engine->doctor(walletCheck: static fn () => \OpenReceive\Server\Service::fromEnvironment())`.
743
+ Without it the preflight line reads `skipped`. Put the report behind a
744
+ `bin/doctor` script. The demo's is twelve lines.
717
745
  → [Doctor](https://openreceive.org/guides/api-reference.md#openreceiveserverdoctor)
718
746
 
719
747
  Then open the checkout in a browser. Confirm the payment-method icons and
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (Rails)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 42 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 43 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/rails/full.md
8
8
  # Skip the download only if you already have all of it: pasted, read from disk or fetched raw.
9
9
  ```
@@ -17,7 +17,7 @@ curl -fsSL https://openreceive.org/agent-directions/rails/full.md
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start the quickstart only once the NWC code is saved, and the LSC code is saved or the user said "Bitcoin only".
19
19
 
20
- These directions describe OpenReceive 0.4.25.
20
+ These directions describe OpenReceive 0.4.27.
21
21
 
22
22
  Add OpenReceive to a Rails application — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the gem is on RubyGems, the
@@ -122,6 +122,8 @@ you store it. Ask one question per message.
122
122
  add `dotenv-rails` to the Gemfile. When it is started with Docker Compose,
123
123
  give the service `env_file: .env`. Restart the server after writing the
124
124
  file; the values are read at boot.
125
+ Under Compose, recreate the container with `docker compose up -d`:
126
+ `docker compose restart` keeps its old environment.
125
127
 
126
128
  Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
127
129
  saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
@@ -237,7 +239,12 @@ itself, and they hold for every integration.
237
239
  such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
238
240
  `<Checkout>`; the `resume-payment-hash` attribute, fed from the
239
241
  `openreceive-state` event (`event.detail.state.payment_hash`), on
240
- `<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
242
+ `<openreceive-checkout>`.
243
+ That visit can come hours later, and `authorize` must still know the payer:
244
+ a session that expired in between answers 403 and strands the deposit. On a
245
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
246
+ per-order token in its URL that your page trades for that cookie.
247
+ https://openreceive.org/guides/swap-refunds.md
241
248
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
242
249
  the price from `config.amount_for` and both drop-ins render it above the
243
250
  amount. Without it the checkout is a QR and "$1.00" with no sign of what the
@@ -282,6 +289,12 @@ not replace your orders, users, or products, and you do not join them.
282
289
  expired invoice does not cancel the order; a later checkout may mint another
283
290
  attempt. The engine refuses a new checkout under a reference that already
284
291
  settled (409).
292
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
293
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
294
+ checkout only while the order is unpaid. When it settles on the page, update
295
+ the page's status line from the `openreceive-settled` event on
296
+ `<openreceive-checkout>`: `on_paid` has already marked the order paid. Keep
297
+ that checkout mounted, because it is the payer's receipt.
285
298
  - **Do not associate `OpenReceivePayment`.** No `has_many`, no `belongs_to`, no
286
299
  foreign key either direction. `reference` is not unique (many attempts per
287
300
  order). Fulfillment is a guarded transition on YOUR order row inside
@@ -409,13 +422,16 @@ That is the whole install. `openreceive-rails` depends on `openreceive`,
409
422
  nothing else added. It is built from `NWC_URI`. If your app brings its own NWC
410
423
  client, set `config.nwc_client` instead.
411
424
 
412
- There is one native prerequisite. `nwc-ruby` uses `rbsecp256k1`, which builds
413
- libsecp256k1 from source. Minimal images (`ruby:3.3-slim`, fresh Docker builds)
414
- need the autotools for that, or `bundle install` fails with
415
- `autoreconf: not found`. Install them before bundling:
425
+ Two native builds need system packages. `nwc-ruby` uses `rbsecp256k1`, which
426
+ builds libsecp256k1 from source and needs the autotools; without them
427
+ `bundle install` fails with `autoreconf: not found`. Its connection libraries
428
+ also need a newer `openssl` gem than Ruby ships, and Bundler compiles that gem
429
+ against the OpenSSL headers; without them it fails installing `openssl` at
430
+ `checking for openssl/ssl.h... no`. Minimal images (`ruby:3.3-slim`, fresh
431
+ Docker builds) have neither. Install them before bundling:
416
432
 
417
433
  ```sh
418
- apt-get install -y autoconf automake libtool build-essential pkg-config
434
+ apt-get install -y autoconf automake libtool build-essential pkg-config libssl-dev
419
435
  ```
420
436
 
421
437
  Full Ruby images and typical developer machines already have these.
@@ -603,9 +619,11 @@ settlement transaction, only for the first settled attempt for a reference.
603
619
 
604
620
  The engine's controllers inherit from `config.parent_controller`. The generated
605
621
  initializer sets it to `"ApplicationController"`. That is how the engine picks
606
- up your application's `protect_from_forgery`. Keep `csrf_meta_tags` in the
607
- layout that renders the checkout. The checkout client sends `X-CSRF-Token` from
608
- it automatically.
622
+ up your application's `protect_from_forgery`. The layout that renders the
623
+ checkout needs `<%= csrf_meta_tags %>` in its `<head>`: add it if it is not
624
+ there. The checkout client sends `X-CSRF-Token` from it automatically; without
625
+ it, every checkout answers 403 "Can't verify CSRF token authenticity". Do not
626
+ skip forgery protection for the engine to get past that 403.
609
627
 
610
628
  The same inheritance also brings every global `before_action` that your
611
629
  `ApplicationController` declares. A filter that redirects signed-out users to a