infoalloggi-monorepo/apps/infoalloggi/src/pages/api/stripe-webhook.ts

130 lines
3.3 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from "next";
import Stripe from "stripe";
import { env } from "~/env.mjs";
import type { PaymentsId } from "~/schemas/public/Payments";
import { appRouter } from "~/server/api/root";
import { createTRPCContext, t } from "~/server/api/trpc";
const stripe = new Stripe(env.STRIPE_SECRET_KEY);
const endpointSecret = env.STRIPE_WEBHOOK_SECRET;
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method === "POST") {
const ctx = await createTRPCContext({ req: req, res: res });
const createCaller = t.createCallerFactory(appRouter);
const caller = createCaller(ctx);
const sig = req.headers["stripe-signature"];
let event: Stripe.Event | undefined;
if (!sig) {
res.status(400).send("Webhook Error: Missing Stripe signature");
return;
}
try {
const body = await buffer(req);
event = stripe.webhooks.constructEvent(body, sig, endpointSecret);
} catch (err) {
console.error("Error verifying webhook signature:", err);
return res.status(400).send(`Webhook Error: ${(err as Error).message}`);
}
// Handle the event
switch (event.type) {
case "payment_intent.created": {
const paymentIntent = event.data.object;
const { id, metadata } = paymentIntent;
if (!metadata) {
return res.status(400).send("No metadata in payment intent");
}
if (!metadata.paymentId || !(typeof metadata.paymentId === "string")) {
return res.status(400).send("No orderId or userId in metadata");
}
await caller.stripe.whIntentCreated({
pIntentId: id,
paymentId: metadata.paymentId as PaymentsId,
});
break;
}
case "payment_intent.succeeded": {
const paymentIntent = event.data.object;
const { id, metadata } = paymentIntent;
if (!metadata) {
return res.status(400).send("No metadata in payment intent");
}
if (!metadata.paymentId || !(typeof metadata.paymentId === "string")) {
return res.status(400).send("No orderId or userId in metadata");
}
await caller.stripe.whIntentSucceeded({
pIntentId: id,
paymentId: metadata.paymentId as PaymentsId,
pm_id:
typeof paymentIntent.payment_method === "string"
? paymentIntent.payment_method
: "",
});
break;
}
case "payment_intent.canceled": {
const paymentIntent = event.data.object;
await caller.stripe.whIntentFailed({
pIntentId: paymentIntent.id,
});
break;
}
case "payment_intent.payment_failed": {
const paymentIntent = event.data.object;
await caller.stripe.whIntentFailed({
pIntentId: paymentIntent.id,
});
break;
}
case "payment_intent.processing": {
const paymentIntent = event.data.object;
console.log("PaymentIntent processing", paymentIntent);
break;
}
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.status(200).json({ received: true });
} else {
res.setHeader("Allow", "POST");
res.status(405).end("Method Not Allowed");
}
}
export const config = {
api: {
bodyParser: false,
},
};
const buffer = (req: NextApiRequest) => {
return new Promise<Buffer>((resolve, reject) => {
const chunks: Buffer[] = [];
req.on("data", (chunk: Buffer) => {
chunks.push(chunk);
});
req.on("end", () => {
resolve(Buffer.concat(chunks));
});
req.on("error", reject);
});
};