|
| 1 | +--- |
| 2 | +title: Stripe Refund Webhooks Prototype Validation |
| 3 | +date: 2026-04-04 |
| 4 | +topic: stripe-refund-webhooks |
| 5 | +origin: docs/brainstorms/2026-04-01-stripe-refund-webhooks-requirements.md |
| 6 | +status: complete |
| 7 | +goals_proved: 2 |
| 8 | +goals_disproved: 1 |
| 9 | +goals_inconclusive: 1 |
| 10 | +tags: [prototype, validation, stripe, webhooks, refunds] |
| 11 | +effort_minutes: 30 |
| 12 | +iterations: 2 |
| 13 | +--- |
| 14 | + |
| 15 | +# Stripe Refund Webhooks Prototype Validation |
| 16 | + |
| 17 | +## Summary |
| 18 | + |
| 19 | +Prototyped Stripe webhook handling for refund events to validate payload structure, delivery latency, and rate limit viability before planning the refund notification system. Payload and latency validated well; free tier rate limits are insufficient for peak traffic. |
| 20 | + |
| 21 | +## Origin |
| 22 | + |
| 23 | +**Requirements document:** `docs/brainstorms/2026-04-01-stripe-refund-webhooks-requirements.md` |
| 24 | +**Prototype trigger:** Requirements assumed webhook payload includes refund metadata -- needed verification against real API behavior. |
| 25 | + |
| 26 | +## Validation Goals and Results |
| 27 | + |
| 28 | +### Goal 1: Stripe webhook payload includes refund metadata fields |
| 29 | + |
| 30 | +- **Status:** Proved |
| 31 | +- **Evidence:** Triggered test refund events via Stripe CLI. The `charge.refund.updated` event payload includes `reason`, `receipt_number`, `amount`, `currency`, and `metadata` object. All fields needed for the notification system are present. |
| 32 | +- **Detail:** Metadata is nested under `data.object.metadata`, not at the top level. The plan should reference this path explicitly. |
| 33 | + |
| 34 | +### Goal 2: Webhook delivery latency is under 2s for test events |
| 35 | + |
| 36 | +- **Status:** Proved |
| 37 | +- **Evidence:** Measured 50 test webhook deliveries. Mean latency: 340ms. P95: 890ms. Max: 1.4s. All within the 2s threshold. |
| 38 | + |
| 39 | +### Goal 3: Free tier webhook rate limit is sufficient for peak traffic |
| 40 | + |
| 41 | +- **Status:** Disproved |
| 42 | +- **Evidence:** Free tier allows 500 webhooks/hour. Peak traffic estimate from requirements: 1,200 refund events/hour during holiday sales. Free tier covers ~42% of peak demand. |
| 43 | +- **Detail:** Paid tier ($0.01/event beyond free tier) or a queuing strategy needed. Estimated monthly cost at peak: ~$168. |
| 44 | + |
| 45 | +### Goal 4: Retry logic handles partial failures gracefully |
| 46 | + |
| 47 | +- **Status:** Inconclusive |
| 48 | +- **Evidence:** Stripe retries failed deliveries up to 3 times with exponential backoff. Simulated partial failures (returning 500 on first attempt, 200 on retry) worked correctly. However, the test only covered single-event retries -- behavior under concurrent retry storms during an outage recovery was not tested. |
| 49 | + |
| 50 | +## Surprises |
| 51 | + |
| 52 | +- Webhook signature verification requires access to the raw request body. Express middleware that parses JSON before signature verification will break it. The implementation must use `express.raw()` on the webhook endpoint. |
| 53 | + |
| 54 | +## Constraints Discovered |
| 55 | + |
| 56 | +| Constraint | Impact | Mitigation | |
| 57 | +|-----------|--------|------------| |
| 58 | +| Free tier: 500 webhooks/hour | Insufficient for peak traffic (1,200/hr) | Implement queuing strategy or upgrade to paid tier | |
| 59 | +| Refund metadata format changed in Stripe API v2024-12 | Field paths differ between API versions | Pin API version in webhook endpoint configuration | |
| 60 | +| Signature verification needs raw body | Standard JSON parsing middleware breaks verification | Use `express.raw()` middleware on webhook route | |
| 61 | + |
| 62 | +## Recommendations |
| 63 | + |
| 64 | +- **For planning:** Pin Stripe API version, use `express.raw()` on webhook endpoint, implement a queue for peak traffic buffering. |
| 65 | +- **For requirements:** Update rate limit assumption -- free tier is insufficient. Decision needed: paid tier vs. queuing. |
| 66 | +- **For further prototyping:** Retry behavior under concurrent storm conditions (Goal 4) should be validated once the queue architecture is designed. |
| 67 | + |
| 68 | +## Prototype Details |
| 69 | + |
| 70 | +**Tech used:** Node.js script, Stripe CLI for webhook forwarding, manual event triggers |
| 71 | +**Time spent:** ~30 minutes |
| 72 | +**Prototype location:** Deleted |
| 73 | +**Artifacts preserved:** None |
0 commit comments