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 (Django)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 46 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 48 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/django/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/django/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 Django project — the app you are already working in. You
23
23
  do not need a copy of the OpenReceive source: the Python package is on PyPI
@@ -127,6 +127,8 @@ you store it. Ask one question per message.
127
127
  `settings.py` before anything reads the environment. When the app is started
128
128
  with Docker Compose, give the service `env_file: .env`. Restart the server
129
129
  after writing the file.
130
+ Under Compose, recreate the container with `docker compose up -d`:
131
+ `docker compose restart` keeps its old environment.
130
132
 
131
133
  Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
132
134
  saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
@@ -255,7 +257,12 @@ itself, and they hold for every integration.
255
257
  such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
256
258
  `<Checkout>`; the `resume-payment-hash` attribute, fed from the
257
259
  `openreceive-state` event (`event.detail.state.payment_hash`), on
258
- `<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
260
+ `<openreceive-checkout>`.
261
+ That visit can come hours later, and `authorize` must still know the payer:
262
+ a session that expired in between answers 403 and strands the deposit. On a
263
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
264
+ per-order token in its URL that your page trades for that cookie.
265
+ https://openreceive.org/guides/swap-refunds.md
259
266
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
260
267
  the price from `amount_for` and both drop-ins render it above the
261
268
  amount. Without it the checkout is a QR and "$1.00" with no sign of what the
@@ -303,6 +310,12 @@ products, and you do not join them.
303
310
  expired invoice does not cancel the order; a later checkout may mint another
304
311
  attempt. The engine refuses a new checkout under a reference that already
305
312
  settled (409).
313
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
314
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
315
+ checkout only while the order is unpaid. When it settles on the page, update
316
+ the page's status line from the `openreceive-settled` event on
317
+ `<openreceive-checkout>`: `on_paid` has already marked the order paid. Keep
318
+ that checkout mounted, because it is the payer's receipt.
306
319
  - **Do not relate to `OpenReceivePayment`.** No `ForeignKey`, no `OneToOne`, no
307
320
  `ManyToMany` either direction, and no admin `inline` on it. `reference` is not
308
321
  unique (many attempts per order). Fulfillment is a guarded transition on YOUR
@@ -314,6 +327,27 @@ products, and you do not join them.
314
327
  classes would sit in front of the engine's own JSON-only, same-site and
315
328
  authorize gates and change the wire contract. Your DRF views stay yours.
316
329
 
330
+ ### If the database is Supabase
331
+
332
+ Supabase is Postgres, so the tables above go there like any others. Three
333
+ things differ; https://openreceive.org/guides/supabase.md has each in full.
334
+
335
+ - Connect through its transaction pooler (port `6543`, user
336
+ `postgres.<project-ref>`). Run migrations through the session pooler
337
+ (port `5432` on the same host). Django already turns prepared statements off for PostgreSQL, which the
338
+ transaction pooler needs.
339
+ - Verify Supabase's certificate authority: commit the project's
340
+ `prod-ca-2021.crt` (it is not a secret) and add
341
+ `?sslmode=verify-full&sslrootcert=prod-ca-2021.crt` to the URL.
342
+ - After the migration, close both tables to Supabase's Data API, or anyone
343
+ with the project's anon key can read `swap_data`:
344
+
345
+ ```sql
346
+ alter table openreceive_payments enable row level security;
347
+ alter table openreceive_meta enable row level security;
348
+ revoke all on table openreceive_payments, openreceive_meta from anon, authenticated;
349
+ ```
350
+
317
351
  ## If you build your own checkout UI
318
352
 
319
353
  The engine serves JSON only, so the view is yours — but the drop-ins
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (FastAPI)
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/fastapi/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/fastapi/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 FastAPI application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the engine is on PyPI
@@ -126,6 +126,8 @@ you store it. Ask one question per message.
126
126
  with `uvicorn --env-file .env` (update the app's run script or Procfile, not
127
127
  just your own command). When it is started with Docker Compose, give the
128
128
  service `env_file: .env`. Restart the server after writing the file.
129
+ Under Compose, recreate the container with `docker compose up -d`:
130
+ `docker compose restart` keeps its old environment.
129
131
  Doctor needs the file too: run it as
130
132
  `dotenv -f .env run -- openreceive doctor …`. The `dotenv` command comes
131
133
  with python-dotenv, which `uvicorn --env-file` already needs; if it is
@@ -248,7 +250,12 @@ itself, and they hold for every integration.
248
250
  such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
249
251
  `<Checkout>`; the `resume-payment-hash` attribute, fed from the
250
252
  `openreceive-state` event (`event.detail.state.payment_hash`), on
251
- `<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
253
+ `<openreceive-checkout>`.
254
+ That visit can come hours later, and `authorize` must still know the payer:
255
+ a session that expired in between answers 403 and strands the deposit. On a
256
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
257
+ per-order token in its URL that your page trades for that cookie.
258
+ https://openreceive.org/guides/swap-refunds.md
252
259
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
253
260
  the price from `amount_for` and both drop-ins render it above the amount.
254
261
  Without it the checkout is a QR and "$1.00" with no sign of what the dollar
@@ -308,6 +315,13 @@ orders, users, or products, and you do not join them.
308
315
  expired invoice does not cancel the order; a later checkout may mint another
309
316
  attempt. The library refuses a new checkout under a reference that already
310
317
  settled (409).
318
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
319
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
320
+ checkout only while the order is unpaid. When it settles on the page, update
321
+ the page's status line from `onSettled` on `<Checkout>`, or the
322
+ `openreceive-settled` event on `<openreceive-checkout>`: `on_paid` has already
323
+ marked the order paid. Keep that checkout mounted, because it is the payer's
324
+ receipt.
311
325
  - **Pass a sync SQLAlchemy `Engine` for this app's database.** Do not add an
312
326
  ORM relationship from Order to `openreceive_payments`, and do not implement
313
327
  `PaymentRepository` unless no SQLAlchemy engine can reach this database.
@@ -318,6 +332,27 @@ orders, users, or products, and you do not join them.
318
332
  second session from your ORM. Database writes only in the hook; emails,
319
333
  jobs, and pushes in `after_paid` or after commit.
320
334
 
335
+ ### If the database is Supabase
336
+
337
+ Supabase is Postgres, so the tables above go there like any others. Three
338
+ things differ; https://openreceive.org/guides/supabase.md has each in full.
339
+
340
+ - Connect through its transaction pooler (port `6543`, user
341
+ `postgres.<project-ref>`). Run migrations through the session pooler
342
+ (port `5432` on the same host). The transaction pooler keeps no prepared statements: give the SQLAlchemy
343
+ engine `connect_args={"prepare_threshold": None}`.
344
+ - Verify Supabase's certificate authority: commit the project's
345
+ `prod-ca-2021.crt` (it is not a secret) and add
346
+ `?sslmode=verify-full&sslrootcert=prod-ca-2021.crt` to the URL.
347
+ - After the migration, close both tables to Supabase's Data API, or anyone
348
+ with the project's anon key can read `swap_data`:
349
+
350
+ ```sql
351
+ alter table openreceive_payments enable row level security;
352
+ alter table openreceive_meta enable row level security;
353
+ revoke all on table openreceive_payments, openreceive_meta from anon, authenticated;
354
+ ```
355
+
321
356
  ## If you build your own checkout UI
322
357
 
323
358
  The drop-ins (`<Checkout>`, `<openreceive-checkout>`) already obey all of this.
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (Fastify)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 39 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 41 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/fastify/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/fastify/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 Fastify application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the packages are on npm, and
@@ -123,6 +123,8 @@ you store it. Ask one question per message.
123
123
  `import "dotenv/config";` at the top of the server entry, as the quickstart
124
124
  does. When the app is started with Docker Compose, give the service
125
125
  `env_file: .env`. Restart the server after writing the file.
126
+ Under Compose, recreate the container with `docker compose up -d`:
127
+ `docker compose restart` keeps its old environment.
126
128
 
127
129
  Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
128
130
  saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
@@ -235,7 +237,12 @@ itself, and they hold for every integration.
235
237
  such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
236
238
  `<Checkout>`; the `resume-payment-hash` attribute, fed from the
237
239
  `openreceive-state` event (`event.detail.state.payment_hash`), on
238
- `<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
240
+ `<openreceive-checkout>`.
241
+ That visit can come hours later, and `authorize` must still know the payer:
242
+ a session that expired in between answers 403 and strands the deposit. On a
243
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
244
+ per-order token in its URL that your page trades for that cookie.
245
+ https://openreceive.org/guides/swap-refunds.md
239
246
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
240
247
  the price from `amountFor` and both drop-ins render it above the amount.
241
248
  Without it the checkout is a QR and "$1.00" with no sign of what the dollar
@@ -290,6 +297,13 @@ products, and you do not join them.
290
297
  expired invoice does not cancel the order; a later checkout may mint another
291
298
  attempt. The library refuses a new checkout under a reference that already
292
299
  settled (409).
300
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
301
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
302
+ checkout only while the order is unpaid. When it settles on the page, update
303
+ the page's status line from `onSettled` on `<Checkout>`, or the
304
+ `openreceive-settled` event on `<openreceive-checkout>`: `onPaid` has already
305
+ marked the order paid. Keep that checkout mounted, because it is the payer's
306
+ receipt.
293
307
  - **Pass this app's `db` handle.** Do not add a Prisma/Drizzle relation from
294
308
  Order to `openreceive_payments`, and do not implement `PaymentRepository`
295
309
  unless no supported handle can reach this database. `reference` is not unique
@@ -300,6 +314,30 @@ products, and you do not join them.
300
314
  only in the hook; emails, jobs, and pushes after commit. Placeholder style is
301
315
  the dialect you declared: `?` on sqlite, `$1` on postgres.
302
316
 
317
+ ### If the database is Supabase
318
+
319
+ Supabase is Postgres, so the tables above go there like any others. Three
320
+ things differ; https://openreceive.org/guides/supabase.md has each in full.
321
+
322
+ - Connect through its transaction pooler (port `6543`, user
323
+ `postgres.<project-ref>`). Run migrations through the session pooler
324
+ (port `5432` on the same host).
325
+ - Verify Supabase's certificate authority: delete `sslmode` from the URL and
326
+ give `pg` `ssl: { ca }`, the text of the project's `prod-ca-2021.crt` (an
327
+ environment variable such as `SUPABASE_CA_CERT`; it is not a secret). Never
328
+ `rejectUnauthorized: false`.
329
+ - After the migration, close both tables to Supabase's Data API, or anyone
330
+ with the project's anon key can read `swap_data`:
331
+
332
+ ```sql
333
+ alter table openreceive_payments enable row level security;
334
+ alter table openreceive_meta enable row level security;
335
+ revoke all on table openreceive_payments, openreceive_meta from anon, authenticated;
336
+ ```
337
+
338
+ `npx openreceive scaffold payments --supabase` writes the HTTPS-API route,
339
+ which is for Cloudflare Workers only. A Node server uses the connection above.
340
+
303
341
  ## If you build your own checkout UI
304
342
 
305
343
  The drop-ins (`<Checkout>`, `<openreceive-checkout>`) already obey all of this.
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (Flask)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 43 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 45 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/flask/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/flask/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 Flask application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the engine is on PyPI
@@ -131,6 +131,8 @@ you store it. Ask one question per message.
131
131
  `dotenv -f .env run -- gunicorn …` (python-dotenv's CLI,
132
132
  `pip install "python-dotenv[cli]"`), in the app's run script or Procfile,
133
133
  not just your own command. Restart the server after writing the file.
134
+ Under Compose, recreate the container with `docker compose up -d`:
135
+ `docker compose restart` keeps its old environment.
134
136
  Doctor reads `.env` from its working directory by itself. Never `source`,
135
137
  `export` or `cat` the file: the codes contain `&`, and the shell prints the
136
138
  pieces.
@@ -252,7 +254,12 @@ itself, and they hold for every integration.
252
254
  such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
253
255
  `<Checkout>`; the `resume-payment-hash` attribute, fed from the
254
256
  `openreceive-state` event (`event.detail.state.payment_hash`), on
255
- `<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
257
+ `<openreceive-checkout>`.
258
+ That visit can come hours later, and `authorize` must still know the payer:
259
+ a session that expired in between answers 403 and strands the deposit. On a
260
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
261
+ per-order token in its URL that your page trades for that cookie.
262
+ https://openreceive.org/guides/swap-refunds.md
256
263
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
257
264
  the price from `amount_for` and both drop-ins render it above the amount.
258
265
  Without it the checkout is a QR and "$1.00" with no sign of what the dollar
@@ -315,6 +322,12 @@ orders, users, or products, and you do not join them.
315
322
  expired invoice does not cancel the order; a later checkout may mint another
316
323
  attempt. The library refuses a new checkout under a reference that already
317
324
  settled (409).
325
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
326
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
327
+ checkout only while the order is unpaid. When it settles on the page, update
328
+ the page's status line from the `openreceive-settled` event on
329
+ `<openreceive-checkout>`: `on_paid` has already marked the order paid. Keep
330
+ that checkout mounted, because it is the payer's receipt.
318
331
  - **Pass a sync SQLAlchemy `Engine` for this app's database.** Do not add an
319
332
  ORM relationship from Order to `openreceive_payments`, and do not implement
320
333
  `PaymentRepository` unless no SQLAlchemy engine can reach this database.
@@ -325,6 +338,27 @@ orders, users, or products, and you do not join them.
325
338
  `db.session`. Database writes only in the hook; emails,
326
339
  jobs, and pushes in `after_paid` or after commit.
327
340
 
341
+ ### If the database is Supabase
342
+
343
+ Supabase is Postgres, so the tables above go there like any others. Three
344
+ things differ; https://openreceive.org/guides/supabase.md has each in full.
345
+
346
+ - Connect through its transaction pooler (port `6543`, user
347
+ `postgres.<project-ref>`). Run migrations through the session pooler
348
+ (port `5432` on the same host). The transaction pooler keeps no prepared statements: give the SQLAlchemy
349
+ engine `connect_args={"prepare_threshold": None}`.
350
+ - Verify Supabase's certificate authority: commit the project's
351
+ `prod-ca-2021.crt` (it is not a secret) and add
352
+ `?sslmode=verify-full&sslrootcert=prod-ca-2021.crt` to the URL.
353
+ - After the migration, close both tables to Supabase's Data API, or anyone
354
+ with the project's anon key can read `swap_data`:
355
+
356
+ ```sql
357
+ alter table openreceive_payments enable row level security;
358
+ alter table openreceive_meta enable row level security;
359
+ revoke all on table openreceive_payments, openreceive_meta from anon, authenticated;
360
+ ```
361
+
328
362
  ## If you build your own checkout UI
329
363
 
330
364
  The drop-ins (`<Checkout>`, `<openreceive-checkout>`) already obey all of this.
@@ -3,7 +3,7 @@ This is the full file; follow it from Step 0.
3
3
  # OpenReceive agent directions (Laravel)
4
4
 
5
5
  ```sh
6
- # READ THIS FIRST: this file is 45 KB and a summary drops required steps. Download it whole:
6
+ # READ THIS FIRST: this file is 46 KB and a summary drops required steps. Download it whole:
7
7
  curl -fsSL https://openreceive.org/agent-directions/laravel/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/laravel/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 Laravel application — the app you are already working in.
23
23
  You do not need a copy of the OpenReceive source: the package is on Packagist
@@ -130,6 +130,8 @@ you store it. Ask one question per message.
130
130
  `.env` the server reads (an image that copies `.env.example` over it
131
131
  drops them), start `php artisan serve` with `--no-reload`, or set
132
132
  `clear_env = no` for php-fpm. Restart the server after writing the file.
133
+ Under Compose, recreate the container with `docker compose up -d`:
134
+ `docker compose restart` keeps its old environment.
133
135
 
134
136
  Do not invent placeholder URIs. Start the quickstart only once `NWC_URI` is
135
137
  saved and `LSC_URI_PRIMARY` is saved or explicitly declined. The first boot
@@ -248,7 +250,12 @@ itself, and they hold for every integration.
248
250
  such window. On the drop-ins: `resumePaymentHash`, fed from `onState`, on
249
251
  `<Checkout>`; the `resume-payment-hash` attribute, fed from the
250
252
  `openreceive-state` event (`event.detail.state.payment_hash`), on
251
- `<openreceive-checkout>`. https://openreceive.org/guides/swap-refunds.md
253
+ `<openreceive-checkout>`.
254
+ That visit can come hours later, and `authorize` must still know the payer:
255
+ a session that expired in between answers 403 and strands the deposit. On a
256
+ guest shop, tie the order to a long-lived signed cookie, or put a secret
257
+ per-order token in its URL that your page trades for that cookie.
258
+ https://openreceive.org/guides/swap-refunds.md
252
259
  - Show the payer WHAT THEY ARE BUYING. Return an optional `description` beside
253
260
  the price from `amountFor` and both drop-ins render it above the
254
261
  amount. Without it the checkout is a QR and "$1.00" with no sign of what the
@@ -293,6 +300,12 @@ not replace your orders, users, or products, and you do not join them.
293
300
  expired invoice does not cancel the order; a later checkout may mint another
294
301
  attempt. The engine refuses a new checkout under a reference that already
295
302
  settled (409).
303
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
304
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
305
+ checkout only while the order is unpaid. When it settles on the page, update
306
+ the page's status line from the `openreceive-settled` event on
307
+ `<openreceive-checkout>`: `onPaid` has already marked the order paid. Keep
308
+ that checkout mounted, because it is the payer's receipt.
296
309
  - **Do not model `openreceive_payments`.** No Eloquent model, no `hasMany`, no
297
310
  `belongsTo`, no foreign key either direction. `reference` is not unique (many
298
311
  attempts per order). Fulfillment is a guarded transition on YOUR order row
@@ -17,7 +17,7 @@ If your fetch tool gave you a summary, fetch that URL again for the full text.
17
17
  - Do not suggest rotating or revoking a code because it was pasted here.
18
18
  - Start Step 1 only once NWC_URI is saved, and LSC_URI_PRIMARY 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 this Lovable app: a TanStack Start app on Cloudflare
23
23
  Workers, with Lovable Cloud or a connected Supabase project as its database.
@@ -26,6 +26,10 @@ every step is a file edit, a package, a Supabase migration or a secret. Never
26
26
  ask the user to run a command. The TanStack Start recipe is appended to this
27
27
  file in full; its "On Supabase (Lovable)" section is the code to follow.
28
28
 
29
+ A TanStack Start app on a Node server you run, with its own Postgres, is not
30
+ a Lovable app: follow https://openreceive.org/agent-directions/tanstack-start.md
31
+ for it instead.
32
+
29
33
  Do not clone the OpenReceive repository, and do not copy a demo's models over
30
34
  tables that already exist. Find this app's orders and products, whatever they
31
35
  are named, and map the hooks onto those. Keep this app's UI, sign-in and
@@ -117,7 +121,7 @@ saved or explicitly declined.
117
121
 
118
122
  ## Step 1 — the packages
119
123
 
120
- Add `@openreceive/http` and `@openreceive/react` at 0.4.25 or newer.
124
+ Add `@openreceive/http` and `@openreceive/react` at 0.4.27 or newer.
121
125
  Nothing else: no `pg`, no Postgres driver, no OpenReceive scaffold. Do not
122
126
  change the Vite or Wrangler configuration.
123
127
 
@@ -157,6 +161,12 @@ every settlement.
157
161
  - **The order is unpaid or paid.** Do not copy attempt statuses (`pending`,
158
162
  `expired`, `failed`, `attention`) onto it, and do not add a relation to
159
163
  `openreceive_payments`.
164
+ - **A paid order's page says it is paid and shows no checkout.** Nothing is left
165
+ to pay, and a checkout cannot start a new payment for a paid order. Mount the
166
+ checkout only while the order is unpaid. When it settles on the page, update
167
+ the page's status line from `onSettled` on `<Checkout>`: `openreceive_on_paid`
168
+ has already marked the order paid. Keep that checkout mounted, because it is
169
+ the payer's receipt.
160
170
 
161
171
  ## Step 4 — `openreceive_on_paid`
162
172