From c139b3338ac71dcb082abca59cc7887d72cd79ce Mon Sep 17 00:00:00 2001 From: JoniMartin27 <130197986+JoniMartin27@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:07:11 +0200 Subject: [PATCH 1/2] docs(trace): document automatic email delivery of the Pro license Reflects the new license-worker email + webhook delivery: Resend setup, Polar order.paid webhook, and the no-double-send rule between redirect and webhook. Updates the human checklist accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) --- PAYMENTS.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/PAYMENTS.md b/PAYMENTS.md index e2ecbe2..3421d7a 100644 --- a/PAYMENTS.md +++ b/PAYMENTS.md @@ -22,21 +22,39 @@ licencia cuando esté lista. Polar = merchant of record (IVA global + PCI). ## Backend de entrega (repo `trace`, ya construido) -Tras pagar, Polar redirige al **worker de licencias**, que verifica la compra con -la API de Polar y mintea/entrega la clave **Ed25519 offline**: -`https://trace-license.jonathanmartinpaez.workers.dev` +Tras pagar, Polar entrega la clave **Ed25519 offline** por dos vías, ambas +verificadas server-side antes de mintear: +- **Redirect** `/?checkout_id=…` → verifica con la API de Polar → muestra la clave + en pantalla (y la envía por email si no hay webhook). +- **Webhook** `POST /webhook` (evento `order.paid`) → verifica la firma del webhook + (HMAC, anti-replay) → mintea → **envía la clave por email**. Se dispara aunque el + comprador cierre la pestaña: es el canal fiable. + +Worker: `https://trace-license.jonathanmartinpaez.workers.dev` +Email vía **Resend** (un `fetch`, sin SDK), opcional pero recomendado. ## Pendiente HUMANO para que la entrega funcione al 100% -(En el panel de Polar / Cloudflare — ver detalle en la memoria del repo `trace`.) +(En el panel de Polar / Cloudflare / Resend — ver detalle en +`trace/services/license-worker/README.md`.) 1. **Confirmar** que el producto de Polar detrás de ese checkout cuesta **$39** y está activo (importe cobrado = importe anunciado en la landing). 2. **Success URL** del checkout en Polar → `https://trace-license.jonathanmartinpaez.workers.dev/?checkout_id={CHECKOUT_ID}` -3. Secrets del worker: `wrangler secret put TRACE_SIGNING_KEY` y +3. Secrets base del worker: `wrangler secret put TRACE_SIGNING_KEY` y `wrangler secret put POLAR_ACCESS_TOKEN` (token de Polar scope `checkouts:read`). -4. Al cablear de verdad, fijar el `PRODUCT_ID` real en el worker (falla cerrado). +4. Fijar el `PRODUCT_ID` real en el worker (`wrangler.toml`, falla cerrado). +5. **Email automático al comprador (Resend):** + - Verificar el dominio `fervon.dev` en Resend (registros DKIM/SPF en Cloudflare). + - `wrangler secret put RESEND_API_KEY` + `EMAIL_FROM = "Trace "` + en `wrangler.toml [vars]`. +6. **Entrega fiable aunque cierren la pestaña (webhook):** + - En Polar → Settings → Webhooks: endpoint `…workers.dev/webhook`, evento + `order.paid`, formato Raw (Standard Webhooks). + - `wrangler secret put POLAR_WEBHOOK_SECRET` (el secret que muestra Polar). + - Con el webhook configurado, **el webhook es quien envía el email** y el redirect + deja de duplicar (solo muestra la clave en pantalla). ## Verificar From f96e6fe2af32e26c94f0ee38804ed9cc9c42c987 Mon Sep 17 00:00:00 2001 From: JoniMartin27 <130197986+JoniMartin27@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:18:07 +0200 Subject: [PATCH 2/2] docs(trace): document the hardened fail-closed, idempotent delivery Single email channel (webhook), one canonical license per buyer via KV, PRODUCT_ID + KV now required. Updated the human checklist accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) --- PAYMENTS.md | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/PAYMENTS.md b/PAYMENTS.md index 3421d7a..338799e 100644 --- a/PAYMENTS.md +++ b/PAYMENTS.md @@ -22,39 +22,42 @@ licencia cuando esté lista. Polar = merchant of record (IVA global + PCI). ## Backend de entrega (repo `trace`, ya construido) -Tras pagar, Polar entrega la clave **Ed25519 offline** por dos vías, ambas -verificadas server-side antes de mintear: -- **Redirect** `/?checkout_id=…` → verifica con la API de Polar → muestra la clave - en pantalla (y la envía por email si no hay webhook). -- **Webhook** `POST /webhook` (evento `order.paid`) → verifica la firma del webhook - (HMAC, anti-replay) → mintea → **envía la clave por email**. Se dispara aunque el - comprador cierre la pestaña: es el canal fiable. +Diseño **blindado y fail-closed** (los pagos son la parte más comprometida del +sistema, así que sin bifurcaciones: si falta cualquier requisito, el worker se niega +a entregar). Tras pagar, Polar entrega la clave **Ed25519 offline** así: +- **Redirect** `/?checkout_id=…` → verifica con la API de Polar → **muestra** la clave + canónica en pantalla. **Nunca envía email.** +- **Webhook** `POST /webhook` (evento `order.paid`) → verifica la firma (HMAC, + anti-replay) → mintea/recupera la clave canónica → **la envía por email exactamente + una vez**. Único canal de email; se dispara aunque cierren la pestaña. + +**Idempotencia (KV obligatorio):** una sola licencia por comprador (clave +`productId:email`). Reabrir el redirect o que Polar reintente el webhook devuelve +**siempre la misma clave**, y el email se envía **una única vez**. Worker: `https://trace-license.jonathanmartinpaez.workers.dev` -Email vía **Resend** (un `fetch`, sin SDK), opcional pero recomendado. +Email vía **Resend** (un `fetch`, sin SDK). ## Pendiente HUMANO para que la entrega funcione al 100% (En el panel de Polar / Cloudflare / Resend — ver detalle en -`trace/services/license-worker/README.md`.) +`trace/services/license-worker/README.md`. **Todo es obligatorio: el worker falla +cerrado si falta algo.**) -1. **Confirmar** que el producto de Polar detrás de ese checkout cuesta **$39** y - está activo (importe cobrado = importe anunciado en la landing). -2. **Success URL** del checkout en Polar → +1. **Confirmar** que el producto de Polar cuesta **$39** y está activo. +2. **KV de idempotencia:** `wrangler kv namespace create DELIVERIES` → pegar el id en + `wrangler.toml`. +3. **Return URL** del checkout en Polar → `https://trace-license.jonathanmartinpaez.workers.dev/?checkout_id={CHECKOUT_ID}` -3. Secrets base del worker: `wrangler secret put TRACE_SIGNING_KEY` y - `wrangler secret put POLAR_ACCESS_TOKEN` (token de Polar scope `checkouts:read`). -4. Fijar el `PRODUCT_ID` real en el worker (`wrangler.toml`, falla cerrado). -5. **Email automático al comprador (Resend):** - - Verificar el dominio `fervon.dev` en Resend (registros DKIM/SPF en Cloudflare). - - `wrangler secret put RESEND_API_KEY` + `EMAIL_FROM = "Trace "` - en `wrangler.toml [vars]`. -6. **Entrega fiable aunque cierren la pestaña (webhook):** - - En Polar → Settings → Webhooks: endpoint `…workers.dev/webhook`, evento - `order.paid`, formato Raw (Standard Webhooks). - - `wrangler secret put POLAR_WEBHOOK_SECRET` (el secret que muestra Polar). - - Con el webhook configurado, **el webhook es quien envía el email** y el redirect - deja de duplicar (solo muestra la clave en pantalla). +4. Secrets base: `wrangler secret put TRACE_SIGNING_KEY` y + `wrangler secret put POLAR_ACCESS_TOKEN` (scope `checkouts:read`). +5. **`PRODUCT_ID` real** en `wrangler.toml` (ya obligatorio, falla cerrado). +6. **Email automático (Resend):** + - Verificar el dominio `fervon.dev` en Resend (DKIM/SPF en Cloudflare). + - `wrangler secret put RESEND_API_KEY` + `EMAIL_FROM` en `wrangler.toml`. +7. **Webhook (canal de email):** Polar → Settings → Webhooks: endpoint + `…workers.dev/webhook`, evento `order.paid`, formato Raw (Standard Webhooks) + + `wrangler secret put POLAR_WEBHOOK_SECRET`. ## Verificar