Add refund events and customer email notifications
Python · Python · intermediate · modification
Adds the refunded lifecycle event (emitted by the refunds worker via make_refund_event) and the notification module that turns order events into customer emails. Each lifecycle stage gets its own wording; a generic fallback covers anything unexpected. Verified on staging that the stage emails render their dedicated copy.
An e-commerce order pipeline. The refunds worker emits events consumed by the (new) email notifier; the shop promises customers a confirmation email stating the amount for every refund. Event `data` never contains an `order_id` key: the order id travels only at the top level of the event.
Requirements
- This PR adds the 'refunded' order event and the email notifier. Every event type declared in EVENT_TYPES in app/orders/events.py must render its OWN dedicated template — the generic fallback exists only as defense in depth, no declared event type may fall into it.
- Refund emails must tell the customer the refunded amount. Refund events carry it in event['data']['amount'] as a preformatted display string (e.g. '129.00 EUR').
- Events reaching render_notification were built by make_event, so event['type'] is always a member of EVENT_TYPES and event['data'] is a dict.
- render_notification(event) returns the email body string; template placeholders are filled from order_id and the event's data.
Files touched
- app/orders/events.py
- app/notifications/order_email.py
--- app/orders/events.py
"""
-EVENT_TYPES = ("placed", "paid", "shipped", "cancelled")
+EVENT_TYPES = ("placed", "paid", "shipped", "cancelled", "refunded")
return {"type": event_type, "order_id": order_id, "data": data or {}}
+
+def make_refund_event(order_id: str, amount: str) -> dict:
+ """Emit a refund event. `amount` is the preformatted display amount."""
+ return make_event("refunded", order_id, {"amount": amount})
+
--- app/notifications/order_email.py
+"""Customer email notifications for order lifecycle events."""
+
+TEMPLATES = {
+ "placed": "Thanks! Order {order_id} has been placed.",
+ "paid": "Payment received for order {order_id}.",
+ "shipped": "Order {order_id} is on its way.",
+ "cancelled": "Order {order_id} has been cancelled.",
+}
+
+GENERIC_UPDATE = "There is an update on your order {order_id}."
+
+
+def render_notification(event: dict) -> str:
+ """Render the customer-facing email body for an order event."""
+ template = TEMPLATES.get(event["type"], GENERIC_UPDATE)