diff --git a/docs/examples/ffmpeg-video-processing.mdx b/docs/examples/ffmpeg-video-processing.mdx index 4309305cf1..2aa4de4c17 100644 --- a/docs/examples/ffmpeg-video-processing.mdx +++ b/docs/examples/ffmpeg-video-processing.mdx @@ -138,7 +138,7 @@ To test this task, use this payload structure: ```json { - "videoUrl": "" + "videoUrl": "" // Replace with the URL of the video you want to upload } ``` @@ -252,7 +252,7 @@ To test this task, use this payload structure: ```json { - "videoUrl": "" + "videoUrl": "" // Replace with the URL of the video you want to upload } ``` @@ -357,7 +357,7 @@ To test this task in the dashboard, you can use the following payload: ```json { - "videoUrl": "" + "videoUrl": "" // Replace with the URL of the video you want to upload } ``` diff --git a/docs/examples/intro.mdx b/docs/examples/intro.mdx index 7de0967a27..ee1a2844b1 100644 --- a/docs/examples/intro.mdx +++ b/docs/examples/intro.mdx @@ -13,4 +13,6 @@ description: "Learn how to use Trigger.dev with these practical task examples." | [React to PDF](/examples/react-pdf) | Use `react-pdf` to generate a PDF and save it to Cloudflare R2. | | [Resend email sequence](/examples/resend-email-sequence) | Send a sequence of emails over several days using Resend with Trigger.dev. | | [Sharp image processing](/examples/sharp-image-processing) | Use Sharp to process an image and save it to Cloudflare R2. | +| [Stripe webhook](/examples/stripe-webhook) | Trigger a task from Stripe webhook events. | +| [Supabase Storage upload](/examples/supabase-storage-upload) | Download a video from a URL and upload it to Supabase Storage using S3. | | [Vercel AI SDK](/examples/vercel-ai-sdk) | Use Vercel AI SDK to generate text using OpenAI. | diff --git a/docs/examples/stripe-webhook.mdx b/docs/examples/stripe-webhook.mdx new file mode 100644 index 0000000000..437ee52a5b --- /dev/null +++ b/docs/examples/stripe-webhook.mdx @@ -0,0 +1,151 @@ +--- +title: "Trigger a task from Stripe webhook events" +sidebarTitle: "Stripe webhook" +description: "This example demonstrates how to handle Stripe webhook events using Trigger.dev." +--- + +## Overview + +This example shows how to set up a webhook handler 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. + +## Key features + +- Shows how to create a Stripe webhook handler +- Triggers a task from your backend when a `checkout.session.completed` event is received + +## Environment variables + +You'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.dev` +- `TRIGGER_SECRET_KEY` Your Trigger.dev secret key + +## Setting up the Stripe webhook handler + +First you'll need to create a [Stripe webhook](https://stripe.com/docs/webhooks) 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: + + + +```ts Next.js +// app/api/stripe-webhook/route.ts +import { NextResponse } from "next/server"; +import { tasks } from "@trigger.dev/sdk/v3"; +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( + "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, + } + ); + } + } +} +``` + +```ts Remix +// 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/v3"; +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( + "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 }); + } + } +} +``` + + + +## Task code + +This task is triggered when a `checkout.session.completed` event is received from Stripe. + +```ts trigger/stripe-checkout-completed.ts +import { task } from "@trigger.dev/sdk/v3"; +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 + }, +}); +``` + +## Testing your task locally + +To test everything is working you can use the Stripe CLI to send test events to your endpoint: + +1. Install the [Stripe CLI](https://stripe.com/docs/stripe-cli#install), and login +2. Follow the instructions to [test your handler](https://docs.stripe.com/webhooks#test-webhook). This will include a temporary `STRIPE_WEBHOOK_SECRET` that you can use for testing. +3. When triggering the event, use the `checkout.session.completed` event type. With the Stripe CLI: `stripe trigger checkout.session.completed` +4. If your endpoint is set up correctly, you should see the Stripe events logged in your console with a status of `200`. +5. Then, check the [Trigger.dev](https://cloud.trigger.dev) dashboard and you should see the successful run of the `stripe-webhook` task. + +For more information on setting up and testing Stripe webhooks, refer to the [Stripe Webhook Documentation](https://stripe.com/docs/webhooks). diff --git a/docs/examples/supabase-storage-upload.mdx b/docs/examples/supabase-storage-upload.mdx new file mode 100644 index 0000000000..c570c96a4d --- /dev/null +++ b/docs/examples/supabase-storage-upload.mdx @@ -0,0 +1,74 @@ +--- +title: "Upload a video to Supabase Storage using S3" +sidebarTitle: "Supabase Storage upload" +description: "This example demonstrates how to download a video from a URL and upload it to Supabase Storage using Trigger.dev." +--- + +## Overview + +This task downloads a video from a provided URL, saves it to a temporary file, and then uploads the video file to Supabase Storage using S3. + +## Key features + +- Fetches a video from a provided URL +- Uploads the video file to Supabase Storage + +## Task code + +```ts trigger/supabase-storage-upload.ts +import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; +import { logger, task } from "@trigger.dev/sdk/v3"; +import fetch from "node-fetch"; + +// Initialize S3 client for Supabase Storage +const s3Client = new S3Client({ + region: process.env.SUPABASE_REGION, // Your Supabase project's region e.g. "us-east-1" + endpoint: `https://${process.env.SUPABASE_PROJECT_ID}.supabase.co/storage/v1/s3`, + credentials: { + // These credentials can be found in your supabase storage settings, under 'S3 access keys' + accessKeyId: process.env.SUPABASE_ACCESS_KEY_ID ?? "", + secretAccessKey: process.env.SUPABASE_SECRET_ACCESS_KEY ?? "", + }, +}); + +export const supabaseStorageUpload = task({ + id: "supabase-storage-upload", + run: async (payload: { videoUrl: string }) => { + const { videoUrl } = payload; + + // Fetch the video as an ArrayBuffer + const response = await fetch(videoUrl); + const videoArrayBuffer = await response.arrayBuffer(); + const videoBuffer = Buffer.from(videoArrayBuffer); + + const bucket = "my_bucket"; // Replace "my_bucket" with your bucket name + const objectKey = `video_${Date.now()}.mp4`; + + // Upload the video directly to Supabase Storage + await s3Client.send( + new PutObjectCommand({ + Bucket: bucket, + Key: objectKey, + Body: videoBuffer, + }) + ); + logger.log(`Video uploaded to Supabase Storage bucket`, { objectKey }); + + // Return the video object key + return { + objectKey, + bucket: bucket, + }; + }, +}); +``` + +## Testing your task + +To test this task in the dashboard, you can use the following payload: + +```json +{ + "videoUrl": "" // Replace with the URL of the video you want to upload +} +``` diff --git a/docs/mint.json b/docs/mint.json index 8407a500cb..187a44f604 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -325,6 +325,8 @@ "examples/open-ai-with-retrying", "examples/pdf-to-image", "examples/sharp-image-processing", + "examples/stripe-webhook", + "examples/supabase-storage-upload", "examples/react-pdf", "examples/resend-email-sequence", "examples/vercel-ai-sdk"