Guides / Payments
Stripe disputes have a response deadline — typically 7–21 days depending on the card network. Every hour you're unaware is an hour you can't spend gathering evidence. An immediate alert gives you maximum time.
In your Stripe Dashboard → Webhooks, add a new endpoint and enable:
charge.dispute.createdcharge.dispute.updatedcharge.dispute.closed// 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")!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return new Response("Invalid signature", { status: 400 });
}
if (event.type === "charge.dispute.created") {
const dispute = event.data.object as Stripe.Dispute;
const amount = (dispute.amount / 100).toFixed(2);
const currency = dispute.currency.toUpperCase();
await logit.now("disputes", {
event: "Dispute opened",
description: `${currency} ${amount} — ${dispute.reason} — respond by ${new Date(dispute.evidence_details.due_by! * 1000).toLocaleDateString()}`,
icon: "⚖️",
notify: true, // always ping immediately for disputes
tags: {
reason: dispute.reason,
status: dispute.status,
currency: dispute.currency,
},
metadata: {
disputeId: dispute.id,
chargeId: dispute.charge,
amount: dispute.amount,
reason: dispute.reason,
evidenceDueBy: dispute.evidence_details.due_by,
stripeStatus: dispute.status,
},
});
}
if (event.type === "charge.dispute.closed") {
const dispute = event.data.object as Stripe.Dispute;
await logit.now("disputes", {
event: `Dispute ${dispute.status}`,
description: `${dispute.currency.toUpperCase()} ${(dispute.amount / 100).toFixed(2)} — outcome: ${dispute.status}`,
icon: dispute.status === "won" ? "✅" : "❌",
notify: true,
tags: { reason: dispute.reason, outcome: dispute.status },
metadata: { disputeId: dispute.id, chargeId: dispute.charge },
});
}
return new Response("ok");
}
Multiple disputes in a short window may indicate fraud:
| Field | Value |
|---|---|
| Name | Dispute spike |
| Channel | disputes |
| Event name | Dispute opened |
| Threshold | 3 |
| Window | 24 hours |
| Cooldown | 24 hours |
| Action | Push notification + Slack webhook |
When the alert fires:
notify: true on every dispute — they're too important to let threshold rules delay.evidenceDueBy timestamp in metadata so it's immediately visible in the LogIt event detail.Try LogIt free
7-day trial. No credit card required.