docs(license): PR 2 deploy + operator instructions

ADMIN.md gains a "Running a Gumroad webhook" section: how the URL
secret works, how to add a SKU to products.yaml, how to inspect
gumroad_events (recent activity + failures-only queries), how to
replay a failed delivery, and how to test without buyers via
Gumroad's "Send Test Ping" button.

The deployed-vs-queued matrix flips Gumroad + Postmark to
"code merged, deploy pending" so it's clear the bits exist on
main but the live box still runs PR 1.

SETUP-LICENSE-SERVER.md §3 commits the eventual compose.yml shape
with PR 2 environment + secrets lines included but commented out,
ready to uncomment at deploy time. The §3 chown step already covers
the new secret files because it uses `chmod 400 secrets/*` /
`chown 10001:10001 secrets/*`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 01:33:53 +00:00
parent 2bbaba954b
commit 86ad21db79
2 changed files with 60 additions and 8 deletions

View File

@@ -167,13 +167,55 @@ verifies against, so existing buyers continue to work.
| Mint API + Postgres + auth | **Live** |
| `datatools-admin` CLI (manual mints) | **Live** |
| `licenses.datatools.unalogix.com/health` public | **Live** |
| Gumroad webhook receiver | **PR 2** |
| Postmark transactional email | **PR 2** |
| Gumroad webhook receiver | **PR 2 — code merged, deploy pending** |
| Postmark transactional email | **PR 2 — code merged, deploy pending** |
| Buyer renewal / re-delivery portal | **PR 3** |
| Cloudflare in front (DDoS / WAF) | Deferred (DNS at supercp/cPanel) |
| Production signing keypair | Deferred (still using dev key) |
| Automated DB backups | **Pending** — see §"Backups" |
### Running a Gumroad webhook (PR 2)
Once PR 2 is deployed, sales fire `POST` to
`https://licenses.datatools.unalogix.com/webhooks/gumroad?secret=<gumroad_secret>`.
Auth is the URL secret (Gumroad's recommended pattern). The handler
audit-logs the raw payload, mints idempotently keyed on `sale_id`,
sends the buyer their blob via Postmark, and returns 200 (always —
non-2xx would trigger 3-day retry storms).
**Adding a new SKU:**
1. Create the product in Gumroad and copy its `product_id`.
2. Edit `/srv/datatools-license/app/server/config/products.yaml`,
add a row under `gumroad:` with that ID + the tier you sold.
3. `cd /srv/datatools-license && docker compose restart api` — the
config is read at startup and cached.
**Inspecting webhook activity:**
```bash
# Recent webhook deliveries (all storefronts share this table)
ssh michael@46.225.166.142 'cd /srv/datatools-license && docker compose exec -T postgres \
psql -U datatools_api -d datatools_licenses -c \
"SELECT received_at, order_id, processed, error FROM gumroad_events ORDER BY received_at DESC LIMIT 20;"'
# Failures only (replay candidates)
ssh michael@46.225.166.142 'cd /srv/datatools-license && docker compose exec -T postgres \
psql -U datatools_api -d datatools_licenses -c \
"SELECT id, received_at, order_id, error FROM gumroad_events WHERE processed=false ORDER BY received_at DESC;"'
```
**Replaying a failed webhook** (after fixing the products.yaml mapping
or whatever surfaced the error): the safest path is to ask the buyer
to re-trigger via Gumroad's "Send Test Ping" button in their order
record, *or* mint manually via `datatools-admin mint --source manual`
and add a note linking to the original `gumroad_events.id`.
**Testing without buyers:** Gumroad's seller dashboard has a "Send
Test Ping" button. It sets `test=true` in the payload; the adapter
tags the resulting license with `notes='gumroad test ping'` so it's
trivially filterable later.
---
## TL;DR — I just need a license for my dev machine