Gateway simulator included (your own script signs and sends). Do not look at solutions.md before submitting.
Exercise 1 — The defense-less webhook (broken on purpose)
- Implement
POST /api/payments/webhook/trusting the body ({"event_id", "payment_id", "status", "timestamp"}) and mark the payment SUCCEEDED. Test it with curl. How long did it take to hack yourself? (anyone with your URL just charged). - Add HMAC-SHA256 signing:
X-Signature = hex(hmac(secret, timestamp + "." + raw_body)). Verify withhmac.compare_digest(why not==?). - Attacks to try: valid body without signature → 401; a signature of ANOTHER body → 401; the same body+signature with a timestamp from 1 hour ago → 400 (replay). Add the 5-minute window.
Exercise 2 — Fast ACK + deferred processing
- Simulate the queue:
process_gateway_event(payload)that takes 3s (sleep) + creates the record. First synchronous: measure the webhook's response time with curl (how much? what would the gateway do with a 5s timeout?). - Wrap it in
transaction.on_commit+ a simulated queue (background thread ordelay()if you already set up Celery). Answer 200 in <100ms and process just the same. - Demonstrate idempotency: the same event_id 3 times → 1 record created, 2×200 responses.
Exercise 3 — Out of order
- Send
payment.succeededand THENpayment.pendingfor the same payment. Does your handler regress the payment to PENDING? (if yes: a state-machine bug — 00b'sconfirm()/transitions must reject the illegal transition). - What status do you answer each one? (the illegal one: 400/409/200-ignored? Justify against the provider's retry policy).
Exercise 4 — Your outgoing webhook
- Create
notify_organizer(event_id, payload): sign it just like you verify incoming, POST to a test URL (requestbin or your own test endpoint), retries with backoff 1m/5m/30m/2h/6h (aoutgoing_webhook_delivery(attempts, next_retry, status)table). - Simulate the URL being down (503): verify it retries per the schedule and that after 5 attempts it is marked FAILED for manual review.
- What do you do with a URL answering 410 Gone permanently? (hint: don't retry forever — deactivate the subscription and notify).
Exercise 6 — The incoming webhook's fortress
- Implement §6's checklist on your fake-gateway endpoint: all 7 pieces, each with its test (verification, window, dedup, ACK+queue, rate limit, secret filtering in logs, invalid-signature metric).
- The replay attack: capture a valid webhook and forward it as-is after 10 minutes: does the window reject it? And after 2 minutes with a different
event_id: does dedup accept it or duplicate it? Document both limits. - The outbox for outgoing: migrate Lesson 17's outgoing webhooks to the outbox→poller mechanism (25): an atomicity test (failed transaction → zero enqueued webhooks) and a retry test (the other gateway down → backoff → convergence).
Submit
Paste code, curl outputs and the retry table. Module 3 closes; next Lesson 18 — Sessions vs tokens.