Skip to content

Commit 60c3178

Browse files
authored
Supabase Storage and Stripe webhook examples (#1345)
* Added supabase storage basic example and updated ffmpeg instructions * Added stripe webhook example * Copy tweaks * Typos * Updated stripe example to check for a specific event before triggering the task * Simplified the storage example * Added examples to intro page * Added further instructions * Code improvements * Final tweaks
1 parent 3772b5a commit 60c3178

File tree

5 files changed

+232
-3
lines changed

5 files changed

+232
-3
lines changed

docs/examples/ffmpeg-video-processing.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ To test this task, use this payload structure:
138138

139139
```json
140140
{
141-
"videoUrl": "<video-url>"
141+
"videoUrl": "<video-url>" // Replace <a-video-url> with the URL of the video you want to upload
142142
}
143143
```
144144

@@ -252,7 +252,7 @@ To test this task, use this payload structure:
252252

253253
```json
254254
{
255-
"videoUrl": "<video-url>"
255+
"videoUrl": "<video-url>" // Replace <a-video-url> with the URL of the video you want to upload
256256
}
257257
```
258258

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

358358
```json
359359
{
360-
"videoUrl": "<video-url>"
360+
"videoUrl": "<video-url>" // Replace <a-video-url> with the URL of the video you want to upload
361361
}
362362
```
363363

docs/examples/intro.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ description: "Learn how to use Trigger.dev with these practical task examples."
1313
| [React to PDF](/examples/react-pdf) | Use `react-pdf` to generate a PDF and save it to Cloudflare R2. |
1414
| [Resend email sequence](/examples/resend-email-sequence) | Send a sequence of emails over several days using Resend with Trigger.dev. |
1515
| [Sharp image processing](/examples/sharp-image-processing) | Use Sharp to process an image and save it to Cloudflare R2. |
16+
| [Stripe webhook](/examples/stripe-webhook) | Trigger a task from Stripe webhook events. |
17+
| [Supabase Storage upload](/examples/supabase-storage-upload) | Download a video from a URL and upload it to Supabase Storage using S3. |
1618
| [Vercel AI SDK](/examples/vercel-ai-sdk) | Use Vercel AI SDK to generate text using OpenAI. |

docs/examples/stripe-webhook.mdx

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
---
2+
title: "Trigger a task from Stripe webhook events"
3+
sidebarTitle: "Stripe webhook"
4+
description: "This example demonstrates how to handle Stripe webhook events using Trigger.dev."
5+
---
6+
7+
## Overview
8+
9+
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.
10+
11+
## Key features
12+
13+
- Shows how to create a Stripe webhook handler
14+
- Triggers a task from your backend when a `checkout.session.completed` event is received
15+
16+
## Environment variables
17+
18+
You'll need to configure the following environment variables for this example to work:
19+
20+
- `STRIPE_WEBHOOK_SECRET` The secret key used to verify the Stripe webhook signature.
21+
- `TRIGGER_API_URL` Your Trigger.dev API url: `https://api.trigger.dev`
22+
- `TRIGGER_SECRET_KEY` Your Trigger.dev secret key
23+
24+
## Setting up the Stripe webhook handler
25+
26+
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.
27+
28+
Here are examples of how you can set up a handler using different frameworks:
29+
30+
<CodeGroup>
31+
32+
```ts Next.js
33+
// app/api/stripe-webhook/route.ts
34+
import { NextResponse } from "next/server";
35+
import { tasks } from "@trigger.dev/sdk/v3";
36+
import Stripe from "stripe";
37+
import type { stripeCheckoutCompleted } from "@/trigger/stripe-checkout-completed";
38+
// 👆 **type-only** import
39+
40+
export async function POST(request: Request) {
41+
const signature = request.headers.get("stripe-signature");
42+
const payload = await request.text();
43+
44+
if (!signature || !payload) {
45+
return NextResponse.json(
46+
{ error: "Invalid Stripe payload/signature" },
47+
{
48+
status: 400,
49+
}
50+
);
51+
}
52+
53+
const event = Stripe.webhooks.constructEvent(
54+
payload,
55+
signature,
56+
process.env.STRIPE_WEBHOOK_SECRET as string
57+
);
58+
59+
// Perform the check based on the event type
60+
switch (event.type) {
61+
case "checkout.session.completed": {
62+
// Trigger the task only if the event type is "checkout.session.completed"
63+
const { id } = await tasks.trigger<typeof stripeCheckoutCompleted>(
64+
"stripe-checkout-completed",
65+
event.data.object
66+
);
67+
return NextResponse.json({ runId: id });
68+
}
69+
default: {
70+
// Return a response indicating that the event is not handled
71+
return NextResponse.json(
72+
{ message: "Event not handled" },
73+
{
74+
status: 200,
75+
}
76+
);
77+
}
78+
}
79+
}
80+
```
81+
82+
```ts Remix
83+
// app/webhooks.stripe.ts
84+
import { type ActionFunctionArgs, json } from "@remix-run/node";
85+
import type { stripeCheckoutCompleted } from "src/trigger/stripe-webhook";
86+
// 👆 **type-only** import
87+
import { tasks } from "@trigger.dev/sdk/v3";
88+
import Stripe from "stripe";
89+
90+
export async function action({ request }: ActionFunctionArgs) {
91+
// Validate the Stripe webhook payload
92+
const signature = request.headers.get("stripe-signature");
93+
const payload = await request.text();
94+
95+
if (!signature || !payload) {
96+
return json({ error: "Invalid Stripe payload/signature" }, { status: 400 });
97+
}
98+
99+
const event = Stripe.webhooks.constructEvent(
100+
payload,
101+
signature,
102+
process.env.STRIPE_WEBHOOK_SECRET as string
103+
);
104+
105+
// Perform the check based on the event type
106+
switch (event.type) {
107+
case "checkout.session.completed": {
108+
// Trigger the task only if the event type is "checkout.session.completed"
109+
const { id } = await tasks.trigger<typeof stripeCheckoutCompleted>(
110+
"stripe-checkout-completed",
111+
event.data.object
112+
);
113+
return json({ runId: id });
114+
}
115+
default: {
116+
// Return a response indicating that the event is not handled
117+
return json({ message: "Event not handled" }, { status: 200 });
118+
}
119+
}
120+
}
121+
```
122+
123+
</CodeGroup>
124+
125+
## Task code
126+
127+
This task is triggered when a `checkout.session.completed` event is received from Stripe.
128+
129+
```ts trigger/stripe-checkout-completed.ts
130+
import { task } from "@trigger.dev/sdk/v3";
131+
import type stripe from "stripe";
132+
133+
export const stripeCheckoutCompleted = task({
134+
id: "stripe-checkout-completed",
135+
run: async (payload: stripe.Checkout.Session) => {
136+
// Add your custom logic for handling the checkout.session.completed event here
137+
},
138+
});
139+
```
140+
141+
## Testing your task locally
142+
143+
To test everything is working you can use the Stripe CLI to send test events to your endpoint:
144+
145+
1. Install the [Stripe CLI](https://stripe.com/docs/stripe-cli#install), and login
146+
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.
147+
3. When triggering the event, use the `checkout.session.completed` event type. With the Stripe CLI: `stripe trigger checkout.session.completed`
148+
4. If your endpoint is set up correctly, you should see the Stripe events logged in your console with a status of `200`.
149+
5. Then, check the [Trigger.dev](https://cloud.trigger.dev) dashboard and you should see the successful run of the `stripe-webhook` task.
150+
151+
For more information on setting up and testing Stripe webhooks, refer to the [Stripe Webhook Documentation](https://stripe.com/docs/webhooks).
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: "Upload a video to Supabase Storage using S3"
3+
sidebarTitle: "Supabase Storage upload"
4+
description: "This example demonstrates how to download a video from a URL and upload it to Supabase Storage using Trigger.dev."
5+
---
6+
7+
## Overview
8+
9+
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.
10+
11+
## Key features
12+
13+
- Fetches a video from a provided URL
14+
- Uploads the video file to Supabase Storage
15+
16+
## Task code
17+
18+
```ts trigger/supabase-storage-upload.ts
19+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
20+
import { logger, task } from "@trigger.dev/sdk/v3";
21+
import fetch from "node-fetch";
22+
23+
// Initialize S3 client for Supabase Storage
24+
const s3Client = new S3Client({
25+
region: process.env.SUPABASE_REGION, // Your Supabase project's region e.g. "us-east-1"
26+
endpoint: `https://${process.env.SUPABASE_PROJECT_ID}.supabase.co/storage/v1/s3`,
27+
credentials: {
28+
// These credentials can be found in your supabase storage settings, under 'S3 access keys'
29+
accessKeyId: process.env.SUPABASE_ACCESS_KEY_ID ?? "",
30+
secretAccessKey: process.env.SUPABASE_SECRET_ACCESS_KEY ?? "",
31+
},
32+
});
33+
34+
export const supabaseStorageUpload = task({
35+
id: "supabase-storage-upload",
36+
run: async (payload: { videoUrl: string }) => {
37+
const { videoUrl } = payload;
38+
39+
// Fetch the video as an ArrayBuffer
40+
const response = await fetch(videoUrl);
41+
const videoArrayBuffer = await response.arrayBuffer();
42+
const videoBuffer = Buffer.from(videoArrayBuffer);
43+
44+
const bucket = "my_bucket"; // Replace "my_bucket" with your bucket name
45+
const objectKey = `video_${Date.now()}.mp4`;
46+
47+
// Upload the video directly to Supabase Storage
48+
await s3Client.send(
49+
new PutObjectCommand({
50+
Bucket: bucket,
51+
Key: objectKey,
52+
Body: videoBuffer,
53+
})
54+
);
55+
logger.log(`Video uploaded to Supabase Storage bucket`, { objectKey });
56+
57+
// Return the video object key
58+
return {
59+
objectKey,
60+
bucket: bucket,
61+
};
62+
},
63+
});
64+
```
65+
66+
## Testing your task
67+
68+
To test this task in the dashboard, you can use the following payload:
69+
70+
```json
71+
{
72+
"videoUrl": "<a-video-url>" // Replace <a-video-url> with the URL of the video you want to upload
73+
}
74+
```

docs/mint.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@
325325
"examples/open-ai-with-retrying",
326326
"examples/pdf-to-image",
327327
"examples/sharp-image-processing",
328+
"examples/stripe-webhook",
329+
"examples/supabase-storage-upload",
328330
"examples/react-pdf",
329331
"examples/resend-email-sequence",
330332
"examples/vercel-ai-sdk"

0 commit comments

Comments
 (0)