# AI Editor RSP — Stripe webhook signed smoke without real payment

Task: `t_0d6831d7`
Production site: https://aieditorrsp.net
Webhook endpoint: `POST https://aieditorrsp.net/api/webhooks/stripe`
D1 database: `aieditorrsp-db`
Date: 2026-06-03 UTC

## Verdict

PASS.

No real payment was made. The production webhook route is reachable, Stripe signature verification accepts a valid signed payload, Stripe live event delivery works through the active endpoint, and duplicate event handling is idempotent.

## What changed during smoke

Initial Stripe live resend reached the Worker but did not create a `webhook_events` row, indicating the Worker `STRIPE_WEBHOOK_SECRET` did not match the existing Stripe endpoint signing secret.

Repair performed:

- Created replacement live Stripe webhook endpoint:
  - New active endpoint: `we_1TeF0cBKSbQI2GbGSFCPqxJA`
  - URL: `https://aieditorrsp.net/api/webhooks/stripe`
  - Events: `checkout.session.completed`, `customer.subscription.updated`, `customer.subscription.deleted`, `invoice.payment_succeeded`
- Updated Cloudflare Worker secret: `STRIPE_WEBHOOK_SECRET`
- Disabled old mismatched endpoint:
  - Old endpoint: `we_1TcOD6BKSbQI2GbGy9zFEqbx`
  - Final status: `disabled`
- Cleaned one trial-only smoke subscription left by an earlier failed cancel attempt:
  - `sub_1TeEu1BKSbQI2GbGofqOELNc` -> `canceled`

No source code was changed for the webhook handler.

## Evidence

### 1. Invalid signature rejects as expected

Probe:

```bash
curl -i -X POST https://aieditorrsp.net/api/webhooks/stripe \
  -H 'Content-Type: application/json' \
  -H 'stripe-signature: t=1,v1=deadbeef' \
  --data '{"id":"evt_probe","type":"customer.subscription.deleted","data":{"object":{"id":"sub_probe"}}}'
```

Result:

```text
HTTP/2 400
{"ok":false,"code":"STRIPE_SIGNATURE_INVALID"}
```

This confirms the production endpoint is reachable and does not process unsigned/invalid payloads.

### 2. Locally constructed signed payload against production route

Method:

- Used the active route secret configured in Cloudflare Worker after endpoint replacement.
- Built a synthetic `customer.subscription.deleted` event.
- Signed raw body as Stripe format: `t=<unix>,v1=<HMAC_SHA256(secret, "<t>.<raw_body>")>`.
- Posted twice to production.

Synthetic event:

```text
event_id: evt_smoke_t_0d6831d7_1780493970
event_type: customer.subscription.deleted
```

First POST:

```text
HTTP/2 200
{"ok":true}
```

Second POST with same event id/body/signature:

```text
HTTP/2 200
{"ok":true,"duplicate":true}
```

D1 evidence:

```text
stripe_event_id: evt_smoke_t_0d6831d7_1780493970
event_type: customer.subscription.deleted
provider: stripe
processed_at: 2026-06-03 13:39:31
payload_len: 364
```

### 3. Live Stripe event delivery through active endpoint

Method:

- Created a live Stripe customer and trial-only subscription using the AI Editor RSP monthly price.
- Canceled the trial subscription immediately.
- No payment method was attached and no real payment was made.
- Stripe emitted `customer.subscription.deleted`.
- Resent the event to the new active endpoint with Stripe CLI.

Stripe event:

```text
event_id: evt_1TeEuLBKSbQI2GbGiPN0mBPl
event_type: customer.subscription.deleted
customer_id: cus_UdVwlFGFUXgDW3
subscription_id: sub_1TeEuJBKSbQI2GbGLgUp4FKu
endpoint_id: we_1TeF0cBKSbQI2GbGSFCPqxJA
no_real_payment: true
```

D1 evidence:

```text
stripe_event_id: evt_1TeEuLBKSbQI2GbGiPN0mBPl
event_type: customer.subscription.deleted
provider: stripe
processed_at: 2026-06-03 13:39:45
payload_len: 5634
```

### 4. Idempotency evidence

The live Stripe event was resent again to the active endpoint.

D1 count before duplicate resend:

```text
1
```

D1 count after duplicate resend:

```text
1
```

This confirms repeated delivery of the same Stripe event id does not create duplicate `webhook_events` rows or re-run destructive data changes.

## Final Stripe endpoint state

```text
active endpoint:
  id: we_1TeF0cBKSbQI2GbGSFCPqxJA
  url: https://aieditorrsp.net/api/webhooks/stripe
  status: enabled
  livemode: true
  events:
    - checkout.session.completed
    - customer.subscription.updated
    - customer.subscription.deleted
    - invoice.payment_succeeded

old endpoint:
  id: we_1TcOD6BKSbQI2GbGy9zFEqbx
  url: https://aieditorrsp.net/api/webhooks/stripe
  status: disabled
```

## Residual risk

- This smoke does not prove a real `checkout.session.completed` entitlement from a paid Checkout flow, because Owner explicitly said not to make a real payment.
- Post-launch P1: observe the first real paid Checkout event and confirm `users.plan`, `credit_accounts`, and `orders` update from the real `checkout.session.completed` payload.
- The current no-payment smoke verifies the shared webhook entrypoint, signature verification, Stripe live delivery, D1 event logging, and duplicate-event guard.

## Next inputs

None required to continue launch flow under the stated acceptance rule.

If Product/QA later requires full paid entitlement proof, the next input is a real paid Checkout session or a controlled live test payment approval.
