Tengo un punto final en mi aplicación SvelteKit que maneja las solicitudes de webhook de Stripe. Cada solicitud se firma para que se pueda verificar que proviene de Stripe.
El código que tengo para verificar que el evento es de Stripe se ve así:
import Stripe from "stripe"; const WEBHOOK_SECRET = process.env["STRIPE_WH_SECRET"]; const stripe = new Stripe(process.env["STRIPE_SECRET"], { apiVersion: "2020-08-27", }); export async function post({ headers, body }) { let event: Stripe.Event; try { event = stripe.webhooks.constructEvent( body, headers["stripe-signature"], WEBHOOK_SECRET ); } catch (err) { return { status: 400, body: err, }; } // Do stuff with the event }Pero cuando recibe un evento de Stripe, aparece este error:
No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signingDespués de algunas investigaciones, descubrí que esta función analiza el cuerpo en JSON incluso antes de que se llamen los ganchos de SvelteKit , lo que significa que no hay forma de obtener directamente el cuerpo sin procesar, así que decidí que mi mejor opción era tratar de reconstruir el cuerpo original:
event = stripe.webhooks.constructEvent( JSON.stringify(body), headers["stripe-signature"], WH_SECRET );No estoy totalmente seguro de por qué esto no funciona, ya que después de investigar el código relevante en la biblioteca de Stripe , parece manejar bien las cadenas. Mi mejor conjetura es que en algún momento la codificación se estropea.
Cualquier ayuda con esto sería muy apreciada, ya que realmente me gustaría evitar cambiarme de SvelteKit, ya que prácticamente ya he terminado mi proyecto con él (no fue una gran idea, en retrospectiva).
La carga útil aceptada por stripe.webhooks.constructEvent(payload, signature, secret) debe ser de tipo string | Buffer , pero el rawBody recibido en la solicitud de SvelteKit es del tipo Uint8Array .
Pasar Uint8Array rawBody o JSON.stringify'd rawBody como la carga da como resultado el siguiente error de Stripe. Consulte: https://github.com/stripe/stripe-node#webhook-signing
message: 'No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing'
Necesitamos convertir rawBody en una string o Buffer sin alterar los contenidos. Para lograr esto podemos usar: Buffer.from(rawBody) .
Entonces su punto final se vería algo como:
... const stripeWebhookSecret = process.env['STRIPE_WEBHOOK_SECRET']; export const post: RequestHandler = async (request) => { const rawBody = Buffer.from(request.rawBody); const signature = request.headers['stripe-signature']; try { event = stripe.webhooks.constructEvent( rawBody, signature, stripeWebhookSecret ); ...La solicitud ahora viene con un arrayBuffer que se puede convertir en un cuerpo sin formato como este:
function toBuffer(ab: any) { const buf = Buffer.alloc(ab.byteLength); const view = new Uint8Array(ab); for (let i = 0; i < buf.length; ++i) { buf[i] = view[i]; } return buf; } export async function post(event: RequestEvent<Record<string, string>>) { ... const preRawBody = await event.request.arrayBuffer(); const rawBody = toBuffer(preRawBody); try { stripeEvent = stripe.webhooks.constructEvent( rawBody, stripeSignature, process.env.STRIPE_WEBHOOK_SECRET ); } ... }