plans: names docs credit confirmed late money

This commit is contained in:
shum committed 2026-10-22 09:56:14 +00:00
1 parent a047ec1801
commit c723da40cc
3 files changed
+11 -11

No files matched your search

+4 -4
View File
@@ -263,7 +263,7 @@ This section follows a name from the order to the chain. The service commits, wa
- with `bad_request`, a duration outside the hour's slack.
**Closed invoices.** An invoice that expires or is canceled leaves its registration `unpaid`.
- A late payment of that invoice does not queue the registration. What arrived is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request. This is because a new order of the same wallet for the name may have been paid meanwhile.
- A late payment of that invoice does not queue the registration. Its confirmed money is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request. This is because a new order of the same wallet for the name may have been paid meanwhile.
- Get a new invoice, on an expired invoice, makes a new order for the same name and wallet.
- A canceled invoice offers Order the name again instead. It leads to the name's search result, where the buyer orders anew and chooses how to pay. Nothing is ordered until then.
@@ -271,7 +271,7 @@ This section follows a name from the order to the chain. The service commits, wa
- A registration whose invoice received any payment, partial or late, is not deleted. It stays for support.
- The cleanup first reads the invoice from the provider itself (`pReadInvoice`, then settlement as for any read). The poller and the provider list pass read an invoice only within about 72 hours of its creation (`settleWindow`). After that, only a webhook brings a payment.
- Today's BTCPay handler acts only on `InvoiceProcessing`, `InvoiceSettled`, `InvoiceExpired` and `InvoiceInvalid`. It gains `InvoiceReceivedPayment` and `InvoicePaymentSettled` as read hints for a payment into a closed invoice.
- The cleanup's read is the backstop. A payment whose webhook was lost is still recorded and alerted. If the provider does not answer, the registration is kept until the next daily run.
- The cleanup's read is the backstop. A payment whose webhook was lost is still recorded, and its confirmed money credited to the order's balance and logged. If the provider does not answer, the registration is kept until the next daily run.
2. **No hold.** The service cannot hold a name it has not seen, so it holds none. Two orders may name the same hash. Whichever registers first wins. The other ends `taken`, with its payment kept as a balance.
3. **The worker.** There is one worker per registration, resumed after a restart. It uses the branch's chain client (`sendCommit`, `sendRegister`):
- JSON-RPC to the RPC node, `[chain] rpc_url`: a reth node, or a hosted provider such as Alchemy. The same node serves the service's chain reads (#7530's decision D5 named the reth node on the resolver host);
@@ -525,10 +525,10 @@ Migration `20261010_names` runs in SQLite and Postgres. The badge tables (`sx_ba
- A row found applied on chain with no receipt for any of its hashes is `mined`, with no hash known to have mined.
- The service answers identical repeats from the table after a restart. The per-name cap counts its rows of the last hour.
- **`sx_badge_service_name_transactions`:** the registration or edit, `kind` (commit, register, edit), `raw`, `tx_hash`, `registrar_nonce`, `sent_at`, `mined_block`, `status`. The sender resumes from the rows with no receipt.
- **`sx_badge_service_name_invoices`:** `invoice_id`, `registration_id`, `provider_ref`.
- **`sx_badge_service_name_invoices`:** `invoice_id`, `registration_id`, `provider_ref`, `late_credited` (the cents of a late payment already credited to the order's balance). Payments gain `confirmed_amount` (confirmed money only, only rising), which the late credit reads.
- Today's invoice queries are badge-only. So each of these gains this table: `GET /api/invoice/:id`, cancel, settlement, the poller's unpaid and overdue scans and its expiry update, and the lookups by `provider_ref`.
- Settling a name invoice queues its registration, instead of requiring a code hash. This happens in the transaction that marks the invoice paid; the invoice's and the registration's changes are published to the long poll only after that commit. It applies only to an invoice still open.
- An invoice settled after it expired or was canceled leaves its registration `unpaid` and credits what arrived to that order's balance. Its order page and history row say "Paid after the invoice closed; kept as your balance", with Use and Refund.
- An invoice settled after it expired or was canceled leaves its registration `unpaid` and credits its confirmed money to that order's balance. Its order page and history row say "Paid after the invoice closed; kept as your balance", with Use and Refund.
- The lookups by `provider_ref` cover both this table and `sx_badge_service_badge_code_invoices`.
- As in `Web/Server.hs` today, a row whose provider differs from the webhook's is ignored. This guards against a collision between providers.
- A ref must also be unique across the two tables. The lookup checks both and refuses a ref found in both.
@@ -342,11 +342,11 @@ An order carries the name's hash and length, the duration, the owner address, th
- Get a new invoice makes a new order for the same name and wallet with the current duration, so with a new commitment, once the old invoice is closed. A cancel that lost to settlement leaves a live order: the new order meets it as `order_exists`, and the page opens it through `find`.
- **Closed invoices and cleanup:**
- An invoice that expires or is canceled leaves its registration `unpaid`.
- A late payment of it does not queue it, since a new order for the same name and wallet may have been paid meanwhile. What arrived is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request.
- A late payment of it does not queue it, since a new order for the same name and wallet may have been paid meanwhile. Its confirmed money is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request.
- An `unpaid` registration is deleted, with its `name_invoices` and `invoices` rows, in one transaction 30 days after its invoice closed. One whose invoice received any payment, partial or late, stays, with that payment as its balance.
- The cleanup first reads the invoice from the provider itself (`pReadInvoice`, then settlement as for any read). The poller and the provider list pass read an invoice only within about 72 hours of its creation (`settleWindow`). After that only a webhook brings a payment.
- Today's BTCPay handler acts only on `InvoiceProcessing`, `InvoiceSettled`, `InvoiceExpired` and `InvoiceInvalid`. It gains `InvoiceReceivedPayment` and `InvoicePaymentSettled`, as read hints for a payment into a closed invoice.
- The cleanup's read is the backstop. A payment whose webhook was lost is still recorded and alerted. If the provider does not answer, the registration is kept until the next daily run.
- The cleanup's read is the backstop. A payment whose webhook was lost is still recorded, and its confirmed money credited to the order's balance and logged; money that never confirms alerts once the read window has passed. If the provider does not answer, the registration is kept until the next daily run.
- **`POST /api/name/register {labelHash, length, duration, owner, commitment, sealed, code, timestamp, signature}`** registers a name paid by a prepaid code. There is no invoice. The page sends it from Check your order, for a code kept on Check your code.
- It takes the code check's rate limit, since it too reveals a code's state.
- After the formats, the code's parse and the owner's signature, it applies the existing-order rule: an exact resend answers its `registrationId` in any phase, the code spent or not, so a lost response is safe even once its own registration makes the name look taken; anything else meeting a live order is `order_exists`.
@@ -581,7 +581,7 @@ An order carries the name's hash and length, the duration, the owner address, th
- A row found applied on chain with no receipt for any of its hashes is `mined`, with no hash known to have mined.
- The service answers identical repeats from the table after a restart, and the per-name cap counts its rows.
- **`name_transactions`:** `registration_id` or the edit's id, `kind` (commit, register, edit), `raw`, `tx_hash`, `registrar_nonce`, `sent_at`, `mined_block`, `status`. The sender resumes from the rows with no receipt.
- **`name_invoices`:** `invoice_id`, `registration_id`, `provider_ref`. The amount is the invoice's own.
- **`name_invoices`:** `invoice_id`, `registration_id`, `provider_ref`, `late_credited` (the cents of a late payment already credited to the order's balance). The amount is the invoice's own. Payments gain `confirmed_amount` (confirmed money only, only rising), which the late credit reads.
- **The badge tables are untouched**, so there is no table rebuild.
- **Invoice queries:** today's are badge-only, so each gains the name side.
- The queries: the shared `invoiceRowSelect` (inner-joined to `badge_code_invoices` and `badge_codes`); `qGetInvoice`, behind `GET /api/invoice/:id`, cancel and settlement (`settlementInvoice`); the poller's `qUnpaidRefs`, `qOverdueInvoices` and `qExpireOverdue` (whose update requires a `badge_code_invoices` row); and the lookups by `provider_ref`.
@@ -591,7 +591,7 @@ An order carries the name's hash and length, the duration, the owner address, th
- in the same database transaction that marks the invoice paid;
- with both publishes after it commits, so a long poll never reads `paid` with a stale `unpaid`.
- Today a settled signal marks even an expired invoice paid (`Orders.hs` `decide`, the poller's "an expired invoice can still be marked paid"), with no alert.
- For a name invoice already expired or canceled, settlement marks it paid, leaves the registration `unpaid`, and credits what arrived to that order's balance, logged at info (an alert only when the provider reports no cents figure), since a new order of the same wallet for the name may have been paid meanwhile. The order page and history then show "Paid after the invoice closed; kept as your balance", with Use and Refund (not drawn).
- For a name invoice already expired or canceled, settlement marks it paid, leaves the registration `unpaid`, and credits its confirmed money to that order's balance, logged at info (money that never confirms alerts once the read window has passed), since a new order of the same wallet for the name may have been paid meanwhile. The order page and history then show "Paid after the invoice closed; kept as your balance", with Use and Refund (not drawn).
- `InvoiceRow`'s `irBadgeType` and `irMonths` become the product: a badge's type and months, or a name's registration id.
- **Provider-ref lookups:** the webhook and poller queries that find an invoice by `provider_ref` cover both `badge_code_invoices` and `name_invoices`.
- As in `Web/Server.hs` today, a row whose provider differs from the webhook's is ignored, as a guard against a collision between providers.
@@ -930,7 +930,7 @@ How each part is checked before release: Haskell tests, web tests, the Docker bu
- closed invoices:
- a late payment of a closed name invoice leaving its registration `unpaid` and credited to its balance, each figure once;
- the 30-day cleanup deleting an `unpaid` registration, but sparing one whose invoice received any payment;
- the cleanup recording and alerting a payment the provider reports only at cleanup, and keeping the registration when the provider does not answer;
- the cleanup recording a payment the provider reports only at cleanup and crediting its confirmed money, and keeping the registration when the provider does not answer;
- `name_record_edits`:
- a wrong signer, a high-s signature or a passed deadline refused before any row or gas;
- a pending edit resumed after a restart: sent, its saved raw transaction rebroadcast, or, its registrar nonce used with no receipt, checked on chain by its nonce and the record;
@@ -241,7 +241,7 @@ sequenceDiagram
- the invoice becomes paid;
- the registration goes `unpaid` → `queued`, only while it is still live. A payment for an order that was closed (cancelled, expired, or closed by a newer order of the same wallet and name) is a late payment.
- Both changes are published to the long poll after the commit.
- **Late payment:** an invoice paid after it expired or was canceled leaves the registration `unpaid`. What arrived is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request; each reported figure credits only its increase, and the service logs it (gap 26).
- **Late payment:** an invoice paid after it expired or was canceled leaves the registration `unpaid`. Its confirmed money is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request; each confirmed rise credits only its increase, and the service logs it (gap 26).
- **The long poll** carries `wait` and `seenVersion`, so it also wakes on every change of the registration (its `version`).
**Get a new invoice.** The old invoice may still be open when the card form does not load, or the payment details are unavailable. The page then cancels it before ordering again:
@@ -921,7 +921,7 @@ Each gap says what the problem is, why it matters, and what is decided, or that
- The name side checks the invoice's prior state.
- A late payment of a closed invoice does not queue its registration. That registration stays `unpaid`.
- The 30-day cleanup never deletes it, because the cleanup spares any invoice that received a payment.
- The late payment is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request (it was kept for support to refund until then).
- The late payment's confirmed money is credited to that order's balance (owner, 2026-10-22), which Your names shows and which buys a name or is refunded by support on request (it was kept for support to refund until then).
- So one wallet never has two paid registrations for one name. The other guards for this, by commitment or by owner and label hash:
- an exact resend gets its own order; any other order while a live order holds the name is refused with `order_exists`, which names nothing;
- orders run one at a time per label hash, and unique constraints allow one live registration per owner and label hash;