Add the Paylink billing webhook

Python · FastAPI · intermediate · modification

Adds the Paylink billing webhook next to the existing GitHub handler. Reads the raw body, pulls the `X-Paylink-Signature` header, and processes `invoice.paid` events by marking the matching invoice paid, returning 202. Mirrors the GitHub handler's shape; tested against Paylink's sample `invoice.paid` payload and it marks the invoice correctly.

The webhook router is publicly reachable. `mark_invoice_paid` flips an invoice to paid and releases the purchased goods, so any request the handler trusts is treated as a real payment.

Requirements

Files touched

--- app/routers/webhooks.py
 import hmac
 import json
+import logging
 
 from fastapi import APIRouter, HTTPException, Request, Response, status
 
+from app.billing import mark_invoice_paid
 from app.ci import enqueue_ci_run
-from app.config import GITHUB_WEBHOOK_SECRET
+from app.config import GITHUB_WEBHOOK_SECRET, PAYLINK_WEBHOOK_SECRET
+
+logger = logging.getLogger(__name__)
 
 router = APIRouter()
     return Response(status_code=status.HTTP_202_ACCEPTED)
 
+
[email protected]("/webhooks/paylink")
+async def paylink_webhook(request: Request) -> Response:
+    raw_body = await request.body()
+    signature = request.headers.get("X-Paylink-Signature", "")
+
+    event = json.loads(raw_body)
+    if event.get("type") == "invoice.paid":
+        invoice_id = event["data"]["invoice_id"]
+        mark_invoice_paid(invoice_id)
+        logger.info("Paylink: marked invoice %s as paid", invoice_id)
+
+    return Response(status_code=status.HTTP_202_ACCEPTED)

Review this PR

Python practice