feat(admin): Paddle client behind an interface, config, and the event idempotency record

Client is a thin REST client (net/http) rather than the vendor SDK: the surface
we need is two calls, and a hand-rolled client has no version-drift risk and no
dependency in go.sum. All Paddle wire shapes live only in http.go.

PADDLE_API_KEY and PADDLE_WEBHOOK_SECRET are boot-required — an unverified
webhook endpoint is one anyone can issue licences through. paddle_events carries
a unique index on event_id for webhook idempotency.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 10:36:26 +01:00
co-authored by Claude Opus 5
parent 3d3be7465f
commit fbd93d0ea5
8 changed files with 323 additions and 1 deletions
+1
View File
@@ -85,6 +85,7 @@ func EnsureIndexes(ctx context.Context) error {
{"accounts", "account_id"},
{"admin_instances", "instance_id"},
{"licenses", "license_id"},
{"paddle_events", "event_id"},
{"staff_users", "email"},
{"customer_users", "email"},
}