Document P0 webhook fix + test seam; ship :54; track EVALUATION.md

This commit is contained in:
Keysat
2026-06-12 22:42:29 -05:00
parent be7dfa5d8c
commit ffdb59aa8f
4 changed files with 131 additions and 39 deletions
+18 -3
View File
@@ -51,9 +51,24 @@ card+lightning+onchain), NOT stored per row.
3. else earliest-`connected_at` (deterministic) + a warning.
`payment::build_provider(&row, ...)` constructs a **real** `BtcpayProvider` /
`ZapriteProvider` from the DB row — there is **no mock seam** in this path, so
integration tests can't drive it with `MockPaymentProvider` without one. The legacy
`state.payment` arc is a compat shim and is NOT consulted by the new resolver.
`ZapriteProvider` from the DB row. Tests swap in a mock via the always-compiled
`AppState::provider_override` seam (`None` in production), honored by
`AppState::provider_from_row` at every resolution site — see [testing](testing.md).
The legacy `state.payment` arc is a compat shim and is NOT consulted by the new
resolver.
## Webhook settle confirmation (anti-forgery)
Zaprite webhooks carry no signature, so the settle handler does **not** trust the
webhook body's claim. `api/webhook.rs::handle_inner` re-fetches
`provider.get_invoice_status` and requires `Settled` before persisting status or
taking ANY settle-derived action (license issuance, tier-change, subscription
renewal — the guard sits ahead of all of them). On a provider-API error it acks
`200` without issuing — the reconcile loop re-confirms and issues on its next tick
(fail-closed on issuance, and a 2xx avoids a provider retry-storm). **Not yet
done**: a literal paid-amount/currency check (the trait exposes only a status
enum); trusting the provider's own `Settled` determination is the current
boundary — see the auditor's open P1.
## Provider connect