Webhook Event Processor
Build what Stripe and PayPal integrations run in production: receive a batch of JSON events, validate them, compute per-user balances, and answer with a proper API response.
How it works in the real world
When a customer pays through Stripe, Stripe POSTs a webhook — a JSON event — to the shop's server. The server must:
- Parse — decode the JSON payload into Python objects.
- Validate — real traffic contains junk: unknown event types, impossible amounts. Filter, never crash.
- Process — apply business logic: payments add to a balance, refunds subtract.
- Respond — return a JSON summary with an honest status code so the sender knows what happened.
This parse → validate → process → respond loop is the beating heart of every backend integration you'll ever build.
Parse the payload
Parse the JSON in raw with json.loads. Print how many events arrived and the type of the first one: 5 then payment.
events = json.loads(raw) gives a list of dicts — len() and events[0]["type"].
import json
raw = '[{"type": "payment", "user": "ana", "amount": 120}, {"type": "refund", "user": "ana", "amount": 30}, {"type": "login", "user": "bo", "amount": 0}, {"type": "payment", "user": "bo", "amount": 80}, {"type": "payment", "user": "ana", "amount": -5}]'
events = json.loads(raw)
print(len(events))
print(events[0]["type"])
Run your code to check this step…
Validate the events
An event is valid when its type is payment or refund AND its amount is > 0. Build the valid list; print how many are valid and how many were rejected: 3 then 2.
valid = [e for e in events if e["type"] in ("payment", "refund") and e["amount"] > 0]
import json
raw = '[{"type": "payment", "user": "ana", "amount": 120}, {"type": "refund", "user": "ana", "amount": 30}, {"type": "login", "user": "bo", "amount": 0}, {"type": "payment", "user": "bo", "amount": 80}, {"type": "payment", "user": "ana", "amount": -5}]'
events = json.loads(raw)
valid = [e for e in events if e["type"] in ("payment", "refund") and e["amount"] > 0]
print(len(valid))
print(len(events) - len(valid))
Run your code to check this step…
Process: per-user balances
Payments add to a user's balance; refunds subtract. Compute balances from the valid events and print each user as user: balance (first-seen order): ana: 90 then bo: 80.
delta = amount if type == "payment" else -amount; balances[user] = balances.get(user, 0) + delta.
valid = [
{"type": "payment", "user": "ana", "amount": 120},
{"type": "refund", "user": "ana", "amount": 30},
{"type": "payment", "user": "bo", "amount": 80},
]
balances = {}
for e in valid:
delta = e["amount"] if e["type"] == "payment" else -e["amount"]
balances[e["user"]] = balances.get(e["user"], 0) + delta
for user, balance in balances.items():
print(f"{user}: {balance}")
Run your code to check this step…
Respond like an API
Build the response dict — {"status": 200, "processed": 3, "skipped": 2, "balances": {...}} — and print it with json.dumps. Expected exactly:{"status": 200, "processed": 3, "skipped": 2, "balances": {"ana": 90, "bo": 80}}
Dicts keep insertion order — create keys in the order shown, then json.dumps(response).
import json
balances = {"ana": 90, "bo": 80}
processed = 3
skipped = 2
response = {"status": 200, "processed": processed, "skipped": skipped, "balances": balances}
print(json.dumps(response))
Run your code to check this step…
Project shipped!
You just built a complete, working webhook event processor — the same architecture running in production software. Pick your next build →