Start learning free

LESSON 109 OF 173 · BACKEND: PAYMENTS AND OTHER SERVICES

Webhooks: The Provider Calls You

Practice

Some payments finish later, not during the request: the customer confirms in their bank's app a few minutes after checkout, or pays by bank transfer. Your shop can't wait for that. Instead, the provider calls your shop when it happens: it sends a POST to an address you gave it. That address is a webhook. For once, your shop is the server and the provider is the client.

Events: the provider sends an event as JSON: what happened in "type", and the payment it happened to in "data", an object inside the object. express.raw puts the body in req.body exactly as it was sent, and JSON.parse turns that JSON text into an object. The same idea for a mail service that tells you an email bounced:

const bounced = [];

app.post("/webhooks/mail", express.raw({ type: "application/json" }), (req, res) => {
  const event = JSON.parse(req.body);
  if (event.type === "email.bounced") {
    bounced.push(event.data.to);
  }
  res.json({ received: true });
});

Answer {"received": true} to every event, even the ones you don't act on, like payment.failed: a provider resends an event until it gets a 2xx answer. The path has no id in it, so the order's id comes from the event, in "data":

POST /webhooks/payments  {"type": "payment.failed", "data": {"id": "pay_7", "amount": 21, "order_id": 10}}
                         →  200  {"received": true}
GET /orders/10           →  200  {…, "payment_id": null, "status": "pending", "total": 21}
POST /webhooks/payments  {"type": "payment.succeeded", "data": {"id": "pay_8", "amount": 21, "order_id": 10}}
                         →  200  {"received": true}
GET /orders/10           →  200  {…, "payment_id": "pay_8", "status": "paid", "total": 21}

Your task: add POST /webhooks/payments, with express.raw({ type: "application/json" }) between the path and the function. Read the event with JSON.parse, and when its type is "payment.succeeded", mark the order paid with the payment's id. Answer { received: true } for every event.


Assignment
Follow the Coding Tutorial to build the webhook POST /webhooks/payments.


Hint
The payment is event.data. Use the same UPDATE as in the pay route, run with payment.id and payment.order_id, in the order of the ? marks.


STEP 1 OF 6
    Ctrl + Enter to check