Guides / Payments
Stripe Payment Links fire the same checkout.session.completed event as regular checkouts. The payment_link field tells you which link was used.
// app/api/webhooks/stripe/route.ts
import Stripe from "stripe";
import { logit } from "@/lib/logit";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get("stripe-signature")!;
const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
// Only handle Payment Link sessions
if (session.payment_link) {
const amount = (session.amount_total ?? 0) / 100;
const currency = (session.currency ?? "usd").toUpperCase();
// Fetch the payment link to get its label
const link = await stripe.paymentLinks.retrieve(session.payment_link as string);
await logit.now("payment-links", {
event: "Payment link conversion",
description: `${session.customer_details?.email ?? "Unknown"} โ $${amount.toFixed(2)} ${currency}`,
icon: "๐",
notify: true,
tags: {
linkId: session.payment_link as string,
currency: session.currency ?? "usd",
},
metadata: {
sessionId: session.id,
paymentLinkId: session.payment_link,
amount,
currency,
email: session.customer_details?.email,
linkUrl: link.url,
},
});
}
}
return new Response("ok");
}
If you have different payment links for different products or pricing pages, use a channel per link:
const channelMap: Record<string, string> = {
"plink_starter": "payment-links-starter",
"plink_pro": "payment-links-pro",
"plink_enterprise": "payment-links-enterprise",
};
const channel = channelMap[session.payment_link as string] ?? "payment-links";
await logit.now(channel, {
event: "Payment link conversion",
// ...
});
In your Stripe Dashboard โ Webhooks, enable:
checkout.session.completedpayment_link.created (optional โ to track when links are created)Try LogIt free
7-day trial. No credit card required.