Skip to content

Supabase Storage and Stripe webhook examples #1345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 23, 2024
6 changes: 3 additions & 3 deletions docs/examples/ffmpeg-video-processing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ To test this task, use this payload structure:

```json
{
"videoUrl": "<video-url>"
"videoUrl": "<video-url>" // Replace <a-video-url> with the URL of the video you want to upload
}
```

Expand Down Expand Up @@ -252,7 +252,7 @@ To test this task, use this payload structure:

```json
{
"videoUrl": "<video-url>"
"videoUrl": "<video-url>" // Replace <a-video-url> with the URL of the video you want to upload
}
```

Expand Down Expand Up @@ -357,7 +357,7 @@ To test this task in the dashboard, you can use the following payload:

```json
{
"videoUrl": "<video-url>"
"videoUrl": "<video-url>" // Replace <a-video-url> with the URL of the video you want to upload
}
```

Expand Down
2 changes: 2 additions & 0 deletions docs/examples/intro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
151 changes: 151 additions & 0 deletions docs/examples/stripe-webhook.mdx
Original file line number Diff line number Diff line change
@@ -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:

<CodeGroup>

```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<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,
}
);
}
}
}
```

```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<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 });
}
}
}
```

</CodeGroup>

## 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).
74 changes: 74 additions & 0 deletions docs/examples/supabase-storage-upload.mdx
Original file line number Diff line number Diff line change
@@ -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": "<a-video-url>" // Replace <a-video-url> with the URL of the video you want to upload
}
```
2 changes: 2 additions & 0 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down