docs/guides/examples/stripe-webhook.mdx
This example shows how to set up a webhook handler in your existing app for incoming Stripe events. The handler triggers a task when a checkout.session.completed event is received. This is easily customisable to handle other Stripe events.
checkout.session.completed event is receivedYou'll need to configure the following environment variables for this example to work:
STRIPE_WEBHOOK_SECRET The secret key used to verify the Stripe webhook signature.TRIGGER_API_URL Your Trigger.dev API url: https://api.trigger.devTRIGGER_SECRET_KEY Your Trigger.dev secret keyFirst you'll need to create a Stripe webhook handler route that listens for POST requests and verifies the Stripe signature.
Here are examples of how you can set up a handler using different frameworks:
<CodeGroup>// app/api/stripe-webhook/route.ts
import { NextResponse } from "next/server";
import { tasks } from "@trigger.dev/sdk";
import Stripe from "stripe";
import type { stripeCheckoutCompleted } from "@/trigger/stripe-checkout-completed";
// 👆 **type-only** import
export async function POST(request: Request) {
const signature = request.headers.get("stripe-signature");
const payload = await request.text();
if (!signature || !payload) {
return NextResponse.json(
{ error: "Invalid Stripe payload/signature" },
{
status: 400,
}
);
}
const event = Stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_WEBHOOK_SECRET as string
);
// Perform the check based on the event type
switch (event.type) {
case "checkout.session.completed": {
// Trigger the task only if the event type is "checkout.session.completed"
const { id } = await tasks.trigger<typeof stripeCheckoutCompleted>(
"stripe-checkout-completed",
event.data.object
);
return NextResponse.json({ runId: id });
}
default: {
// Return a response indicating that the event is not handled
return NextResponse.json(
{ message: "Event not handled" },
{
status: 200,
}
);
}
}
}
// app/webhooks.stripe.ts
import { type ActionFunctionArgs, json } from "@remix-run/node";
import type { stripeCheckoutCompleted } from "src/trigger/stripe-webhook";
// 👆 **type-only** import
import { tasks } from "@trigger.dev/sdk";
import Stripe from "stripe";
export async function action({ request }: ActionFunctionArgs) {
// Validate the Stripe webhook payload
const signature = request.headers.get("stripe-signature");
const payload = await request.text();
if (!signature || !payload) {
return json({ error: "Invalid Stripe payload/signature" }, { status: 400 });
}
const event = Stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_WEBHOOK_SECRET as string
);
// Perform the check based on the event type
switch (event.type) {
case "checkout.session.completed": {
// Trigger the task only if the event type is "checkout.session.completed"
const { id } = await tasks.trigger<typeof stripeCheckoutCompleted>(
"stripe-checkout-completed",
event.data.object
);
return json({ runId: id });
}
default: {
// Return a response indicating that the event is not handled
return json({ message: "Event not handled" }, { status: 200 });
}
}
}
This task is triggered when a checkout.session.completed event is received from Stripe.
import { task } from "@trigger.dev/sdk";
import type stripe from "stripe";
export const stripeCheckoutCompleted = task({
id: "stripe-checkout-completed",
run: async (payload: stripe.Checkout.Session) => {
// Add your custom logic for handling the checkout.session.completed event here
},
});
To test everything is working you can use the Stripe CLI to send test events to your endpoint:
STRIPE_WEBHOOK_SECRET that you can use for testing.checkout.session.completed event type. With the Stripe CLI: stripe trigger checkout.session.completed200.stripe-webhook task.For more information on setting up and testing Stripe webhooks, refer to the Stripe Webhook Documentation.