diff --git a/ROADMAP.md b/ROADMAP.md index 006bafc..25c3153 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,8 +7,6 @@ Longer-term backlog. Near-term state lives in `AGENTS.md` → Current state. - Per-profile SMTP override (schema fields exist from the keysat-smtp-emails plan; needs the form + send path). - Rail-preference editing UI — only matters when two providers on one profile both serve the same rail; settable today via `PUT /v1/admin/merchant-profiles/:id/rail-preferences/:rail`. - **Auto-charge silently lapses a subscription on a 200-with-failure response (money-path bug; elevated above the other parked payments items).** `try_auto_charge_zaprite` returns `Ok(true)` on *any* HTTP 2xx (`subscriptions.rs:1403-1410`), reading the order `status` only for a log line. If Zaprite returns 200 carrying a `FAILED`/`DECLINED`/`EXPIRED` order status, the daemon fires `auto_charge_initiated` and then waits for an `order.paid` webhook that never arrives — the subscription silently lapses, no error surfaced, the customer churns. Safe fix (no production data needed): treat any non-`PAID` terminal order status as not-success and fall through to the manual-pay path — a conservative fail-safe, ~10 lines + a mock-provider test. (Found during the 2026-06-17 adjudication; it replaces the old "harden Zaprite failure-body shapes" item, which was already satisfied for non-2xx responses — those route correctly to WARN + `auto_charge_failed` audit + webhook + manual-pay fallback.) -- Keysat-side dedup cache for Zaprite contacts (same buyer purchasing recurring twice can create duplicate Zaprite contacts). **Adjudicated 2026-06-17 → lean DROP.** Harm is cosmetic (duplicate rows in the operator's Zaprite contact list) and unverified — Zaprite's own dedup-on-email behavior is undocumented; the fix (new `zaprite_contacts` table + threading a DB handle into `ZapriteProvider`) is HIGH blast radius on the money path and could introduce a stale-cache misrouting bug. **Cheap check before dropping for good: buy twice with the same email on a Zaprite sandbox — one contact → drop outright; two → defer until real recurring revenue makes it worth it.** - ## Agent compatibility & scoped API keys - **Agent-delegable payment-provider connect** (approved, not urgent — see