Files
where_woof/openspec/changes/renewal-unlock/proposal.md

29 lines
1.9 KiB
Markdown

## Why
The metering slice (credits) blocks alerts when exhausted, and paid-gating blocks on lapsed orders — but the owner is never *told why* or what to do. The 2014 system had a `late_payment` flow ("your payments are suspended"); this change restores that: an **unlock notification** to the owner when a scan is gated, plus **renewal tracking** (a renews date with lazy expiry) so lapsed status is computed, not manually maintained.
## What Changes
- **`orders.renews_at`** (idempotent): admin sets the next renewal date. Gating treats a `paid` order with `renews_at` in the past as lapsed (lazy expiry — no background job needed).
- **Unlock notification**: when a scan is blocked because the order is lapsed **or** credits are exhausted, the owner receives a notice: *"Your Where Woof tag was scanned — your plan is lapsed / out of SMS credits. Renew to see the finder's details."* with the tag link.
- **Notify channel abstraction** (`internal/notify`): **ntfy** (self-hosted on .13 — free) by default, **SMS** optional via env (`NOTIFY_CHANNEL=ntfy|sms|both`). Email is a documented future channel (SMTP infra absent). Unlock notices are exempt from SMS metering (they're system notices).
- **Admin**: OrderResource gains `renews_at`; a lapsed badge on orders past renewal.
## Capabilities
### New Capabilities
- `renewal-unlock`: renewal date + lazy expiry + owner unlock notification when gated.
### Modified Capabilities
- `sms-alerting`: ADDED — gated scans notify the owner with the reason + renewal call-to-action.
- `database`: ADDED — `orders.renews_at`.
## Impact
- **Go**: `internal/notify` (ntfy + sms channels), scan.go gating calls notify on lapsed/credit blocks, orders query for renews_at, template/message builder.
- **Schema**: idempotent ALTER; `make db-up`.
- **Admin**: OrderResource renews_at + lapsed indicator.
- Config: `NOTIFY_CHANNEL` env (default ntfy), `NTFY_TOPIC` env.