Guides
Gérer les webhooks
Copier la page
Un gestionnaire de webhook complet. Vérifiez la signature sur le corps brut, vérifiez avec le API, agissez une seule fois, et répondez rapidement.
Lia envoie charge.paid, charge.refunded et reward.paid à votre adresse de webhook, signée avec votre secret de webhook. Voir Webhooks pour chaque champ.
#Node.js avec Express
Lisez le corps brut : la signature est sur les octets exacts Lia envoyés, vérifiez-la avant toute analyse JSON.
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
import { lia } from "./lia.js";
const app = express();
function verified(rawBody, header, secret) {
const parts = Object.fromEntries((header ?? "").split(",").map((part) => part.split("=")));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > 300) return null;
const expected = Buffer.from(createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"));
const given = Buffer.from(parts.v1);
if (expected.length !== given.length || !timingSafeEqual(expected, given)) return null;
return JSON.parse(rawBody);
}
app.post("/lia/webhook", express.raw({ type: "application/json" }), async (req, res) => {
const event = verified(req.body.toString("utf8"), req.get("Lia-Signature"), process.env.LIA_WEBHOOK_SECRET);
if (!event) return res.status(400).end();
// Answer first; do the work after.
res.status(200).end();
if (event.type === "charge.paid") {
// A nudge: check with Lia, then grant once per charge.
const { charge } = await lia("GET", `/charges/${event.data.id}`);
if (charge.status !== "paid") return;
await db.grantOnce(charge.id, charge.playerId, () => grant(charge.playerId, charge.productId));
await lia("POST", `/charges/${charge.id}/deliver`);
}
if (event.type === "charge.refunded") {
await db.takeBack(event.data.id); // if you'd already given it
}
if (event.type === "reward.paid") {
await db.noteReward(event.data.key, event.data.playerId, event.data.amount);
}
});#Cloudflare Workers
async function verified(request, secret) {
const rawBody = await request.text();
const parts = Object.fromEntries((request.headers.get("Lia-Signature") ?? "").split(",").map((part) => part.split("=")));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > 300) return null;
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
const signature = new Uint8Array(parts.v1.match(/../g).map((byte) => parseInt(byte, 16)));
const ok = await crypto.subtle.verify("HMAC", key, signature, new TextEncoder().encode(`${t}.${rawBody}`));
return ok ? JSON.parse(rawBody) : null;
}
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (request.method === "POST" && url.pathname === "/lia/webhook") {
const event = await verified(request, env.LIA_WEBHOOK_SECRET);
if (!event) return new Response(null, { status: 400 });
ctx.waitUntil(handle(event, env)); // the work, after answering
return new Response(null, { status: 200 });
}
return new Response("Not found", { status: 404 });
},
};#Principes généraux
- Vérifiez la signature, puis vérifiez avec le API. Un webhook dit qu'il s'est passé quelque chose ;
GET /charges/:iddit ce'est vrai maintenant. - Agir une fois. Les webhooks peuvent arriver deux fois, et votre page peut avoir déjà été livrée. Clé ce que vous faites sur l'identifiant de charge ou la clé de récompense.
- Réponse
2xxrapidement. Lia tente un webhook deux fois, immédiatement, puis abandonne. Faites le travail lent après avoir répondu. - Don'ne se fie pas uniquement aux webhooks. Rattrapez-vous avec
Lia.charges()quand un joueur ouvre votre application, donc un webhook manqué ne perd jamais une vente. - Faites attention au mode. Les événements de test arrivent au même adresse : vérifiez
data.mode. - Ignorez les types que vous n''ne pas savoir, et toujours répondre
200.
#Tester votre gestionnaire
Faites un achat test en tant que testeur (voir Tests): il envoie un véritable, signé charge.paid avec mode: "test". Pour l'essayer sur votre ordinateur, placez votre serveur local derrière un tunnel https et demandez à l'équipe Lia de pointer votre adresse webhook vers là-bas.
Traduit automatiquement. L'anglais est la source de vérité. Lire en anglais