-
-
Notifications
You must be signed in to change notification settings - Fork 710
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
Conversation
Warning Rate limit exceeded@D-K-P has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 3 seconds before requesting another review. How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughThe pull request introduces updates across several documentation files, enhancing the examples for Trigger.dev. It clarifies instructions for the Changes
Possibly related PRs
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Outside diff range and nitpick comments (4)
docs/examples/supabase-storage-upload.mdx (1)
78-86
: Enhance testing instructions with more context and examplesThe testing instructions provide a good starting point, but they could be more comprehensive. Consider the following improvements:
- Provide a complete example URL to demonstrate the expected format.
- Explain how to access the dashboard for testing.
- Include information about expected outcomes and how to verify the upload was successful.
Here's a suggested enhancement:
## Testing your task To test this task in the Trigger.dev dashboard: 1. Navigate to your project in the Trigger.dev dashboard. 2. Find the "supabase-storage-upload" task and click on "Run task". 3. Use the following payload, replacing `<a-video-url>` with a valid video URL: ```json { "videoUrl": "https://example.com/path/to/video.mp4", "bucket": "my_videos_bucket" // Optional: Specify a bucket name if different from the default }
- After running the task, check the logs in the dashboard for the uploaded object key and bucket name.
- You can verify the upload by checking your Supabase Storage bucket for the newly added video file.
Note: Ensure that the video URL is publicly accessible, or that you have the necessary permissions to access it.
This enhanced version provides more context and guidance for users testing the task. </blockquote></details> <details> <summary>docs/examples/stripe-webhook.mdx (3)</summary><blockquote> `16-23`: **Consider adding information on obtaining environment variable values.** The environment variables section is clear, but it might be helpful to add brief instructions or links on where to obtain these values, especially for new users. Consider adding something like: ```markdown - `STRIPE_WEBHOOK_SECRET`: Obtain this from your Stripe Dashboard under Developers > Webhooks. - `TRIGGER_API_URL`: This is typically `https://api.trigger.dev` unless specified otherwise. - `TRIGGER_API_SECRET`: Find this in your Trigger.dev dashboard under API Keys.
115-133
: Consider adding more detailed example for event handlingThe task code provides a good structure for handling Stripe events. However, the
//do stuff
comment in thecheckout.session.completed
case might not be sufficiently informative for users.Consider replacing the
//do stuff
comment with a more detailed example of what users might typically do when handling this event. For example:case "checkout.session.completed": { const session = payload.data.object as stripe.Checkout.Session; // Log the successful checkout console.log(`Checkout completed for session ${session.id}`); // You might want to fulfill the order here // await fulfillOrder(session); break; }This provides users with a clearer idea of how they might handle the event in a real-world scenario.
135-144
: Minor grammatical improvements and suggestion for testing instructionsThe testing instructions are clear and comprehensive. However, there are two minor grammatical issues to address:
- Line 137: Consider adding a period after "locally" to separate the sentences.
- Line 142: Consider adding a comma before "and" to separate the independent clauses.
Here are the suggested changes:
-To test everything is working you can use the Stripe CLI to send test events to your endpoint: +To test everything is working, you can use the Stripe CLI to send test events to your endpoint: -Then, check the [Trigger.dev](https://cloud.trigger.dev) dashboard and you should see the successful run of the `stripe-webhook` task. +Then, check the [Trigger.dev](https://cloud.trigger.dev) dashboard, and you should see the successful run of the `stripe-webhook` task.Additionally, consider adding a note about potential troubleshooting steps if the test fails, to make the instructions more robust.
Tools
LanguageTool
[uncategorized] ~137-~137: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~142-~142: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (4)
- docs/examples/ffmpeg-video-processing.mdx (3 hunks)
- docs/examples/stripe-webhook.mdx (1 hunks)
- docs/examples/supabase-storage-upload.mdx (1 hunks)
- docs/mint.json (1 hunks)
Files skipped from review due to trivial changes (1)
- docs/examples/ffmpeg-video-processing.mdx
Additional context used
LanguageTool
docs/examples/stripe-webhook.mdx
[uncategorized] ~137-~137: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~142-~142: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Additional comments not posted (3)
docs/examples/supabase-storage-upload.mdx (1)
1-17
: LGTM: Clear and informative introductionThe markdown introduction effectively communicates the purpose and key features of the example. It provides a good overview for users.
docs/examples/stripe-webhook.mdx (1)
1-15
: LGTM: Clear and concise overview and key features.The overview and key features sections provide a clear and concise explanation of the document's purpose and main functionalities.
docs/mint.json (1)
282-283
: LGTM! New examples added successfully.The additions of "examples/stripe-webhook" and "examples/supabase-storage-upload" are well-placed and consistent with the existing structure. These new entries align perfectly with the PR objectives of adding Supabase Storage and Stripe webhook examples.
export const supabaseStorageUpload = task({ | ||
id: "supabase-storage-upload", | ||
run: async (payload: { videoUrl: string }) => { | ||
const { videoUrl } = payload; | ||
|
||
// Generate temporary file path | ||
const tempDirectory = os.tmpdir(); | ||
const outputPath = path.join(tempDirectory, `video_${Date.now()}.mp4`); | ||
|
||
// Fetch the video and save it to a temporary file | ||
const response = await fetch(videoUrl); | ||
const videoBuffer = await response.arrayBuffer(); | ||
await fs.writeFile(outputPath, Buffer.from(videoBuffer)); | ||
|
||
const videoFile = await fs.readFile(outputPath); // Read the video file | ||
const bucket = "my_bucket"; // Replace "my_bucket" with your bucket name | ||
|
||
// Upload the video to Supabase Storage | ||
const objectKey = path.basename(outputPath); | ||
await s3Client.send( | ||
new PutObjectCommand({ | ||
Bucket: bucket, | ||
Key: objectKey, | ||
Body: videoFile, | ||
}) | ||
); | ||
logger.log(`Video uploaded to Supabase Storage bucket`, { objectKey }); | ||
|
||
// Delete the temporary video file | ||
await fs.unlink(outputPath); | ||
|
||
// Return the video object key | ||
return { | ||
objectKey, | ||
bucket: bucket, | ||
}; | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve error handling and flexibility in the upload task
The task implementation is generally correct, but there are several areas for improvement:
-
Error handling: Consider wrapping the main logic in a try-catch block to handle potential errors gracefully.
-
Bucket name: The bucket name is hardcoded, which might not be flexible for all users. Consider making it a parameter or an environment variable.
-
File extension: The temporary file always uses .mp4 extension, which might not be correct for all video types. Consider extracting the extension from the original URL or using a more generic name.
-
Filename generation: Using
Date.now()
for generating unique filenames might lead to collisions in high-concurrency scenarios. Consider using a more robust method like UUID.
Here's a suggested refactoring that addresses these issues:
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { logger, task } from "@trigger.dev/sdk/v3";
import fs from "fs/promises";
import fetch from "node-fetch";
import os from "os";
import path from "path";
import { v4 as uuidv4 } from 'uuid';
// ... S3 client initialization ...
export const supabaseStorageUpload = task({
id: "supabase-storage-upload",
run: async (payload: { videoUrl: string, bucket?: string }) => {
const { videoUrl, bucket = process.env.SUPABASE_DEFAULT_BUCKET } = payload;
if (!bucket) {
throw new Error("Bucket name is required. Provide it in the payload or set SUPABASE_DEFAULT_BUCKET environment variable.");
}
try {
// Generate temporary file path with UUID
const tempDirectory = os.tmpdir();
const fileExtension = path.extname(new URL(videoUrl).pathname) || '.video';
const outputPath = path.join(tempDirectory, `${uuidv4()}${fileExtension}`);
// Fetch the video and save it to a temporary file
const response = await fetch(videoUrl);
if (!response.ok) {
throw new Error(`Failed to fetch video: ${response.statusText}`);
}
const videoBuffer = await response.arrayBuffer();
await fs.writeFile(outputPath, Buffer.from(videoBuffer));
const videoFile = await fs.readFile(outputPath);
// Upload the video to Supabase Storage
const objectKey = path.basename(outputPath);
await s3Client.send(
new PutObjectCommand({
Bucket: bucket,
Key: objectKey,
Body: videoFile,
})
);
logger.log(`Video uploaded to Supabase Storage bucket`, { objectKey, bucket });
// Delete the temporary video file
await fs.unlink(outputPath);
// Return the video object key and bucket
return { objectKey, bucket };
} catch (error) {
logger.error("Error in supabaseStorageUpload task", { error });
throw error;
}
},
});
This refactored version includes better error handling, more flexible bucket naming, improved file extension handling, and uses UUID for unique filename generation. Make sure to install and import the uuid
package if you use this suggestion.
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; | ||
import { logger, task } from "@trigger.dev/sdk/v3"; | ||
import fs from "fs/promises"; | ||
import fetch from "node-fetch"; | ||
import os from "os"; | ||
import path from "path"; | ||
|
||
// Initialize S3 client for Supabase Storage | ||
// Update your .env file, and environment variables in the dashboard as instructed below | ||
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`, // Your Supabase project ID | ||
credentials: { | ||
// These credentials can be found in your supabase storage settings, under 'S3 access keys' | ||
accessKeyId: process.env.SUPABASE_S3_ACCESS_KEY_ID ?? "", // Access key ID | ||
secretAccessKey: process.env.SUPABASE_S3__SECRET_ACCESS_KEY ?? "", // Access key | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo and improve error handling in S3 client initialization
Good use of environment variables for security. However, there are a few improvements to be made:
-
There's a typo in the environment variable name on line 34. It should be
SUPABASE_S3_SECRET_ACCESS_KEY
instead ofSUPABASE_S3__SECRET_ACCESS_KEY
. -
Consider throwing an error if the environment variables are not set, instead of using empty strings as fallbacks. This will help catch configuration issues early.
-
The comment on line 29 hardcodes the region to "us-east-1", which might be misleading. Consider removing the specific region from the comment.
Here's a suggested improvement:
const s3Client = new S3Client({
- region: process.env.SUPABASE_REGION, // Your Supabase project's region e.g. "us-east-1"
+ region: process.env.SUPABASE_REGION, // Your Supabase project's region
endpoint: `https://${process.env.SUPABASE_PROJECT_ID}.supabase.co/storage/v1/s3`, // Your Supabase project ID
credentials: {
// These credentials can be found in your supabase storage settings, under 'S3 access keys'
- accessKeyId: process.env.SUPABASE_S3_ACCESS_KEY_ID ?? "", // Access key ID
- secretAccessKey: process.env.SUPABASE_S3__SECRET_ACCESS_KEY ?? "", // Access key
+ accessKeyId: process.env.SUPABASE_S3_ACCESS_KEY_ID, // Access key ID
+ secretAccessKey: process.env.SUPABASE_S3_SECRET_ACCESS_KEY, // Secret access key
},
});
+if (!process.env.SUPABASE_REGION || !process.env.SUPABASE_PROJECT_ID || !process.env.SUPABASE_S3_ACCESS_KEY_ID || !process.env.SUPABASE_S3_SECRET_ACCESS_KEY) {
+ throw new Error("Missing required Supabase environment variables");
+}
This change will ensure that all necessary environment variables are present and will throw an error if any are missing.
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; | |
import { logger, task } from "@trigger.dev/sdk/v3"; | |
import fs from "fs/promises"; | |
import fetch from "node-fetch"; | |
import os from "os"; | |
import path from "path"; | |
// Initialize S3 client for Supabase Storage | |
// Update your .env file, and environment variables in the dashboard as instructed below | |
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`, // Your Supabase project ID | |
credentials: { | |
// These credentials can be found in your supabase storage settings, under 'S3 access keys' | |
accessKeyId: process.env.SUPABASE_S3_ACCESS_KEY_ID ?? "", // Access key ID | |
secretAccessKey: process.env.SUPABASE_S3__SECRET_ACCESS_KEY ?? "", // Access key | |
}, | |
}); | |
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; | |
import { logger, task } from "@trigger.dev/sdk/v3"; | |
import fs from "fs/promises"; | |
import fetch from "node-fetch"; | |
import os from "os"; | |
import path from "path"; | |
// Initialize S3 client for Supabase Storage | |
// Update your .env file, and environment variables in the dashboard as instructed below | |
const s3Client = new S3Client({ | |
region: process.env.SUPABASE_REGION, // Your Supabase project's region | |
endpoint: `https://${process.env.SUPABASE_PROJECT_ID}.supabase.co/storage/v1/s3`, // Your Supabase project ID | |
credentials: { | |
// These credentials can be found in your supabase storage settings, under 'S3 access keys' | |
accessKeyId: process.env.SUPABASE_S3_ACCESS_KEY_ID, // Access key ID | |
secretAccessKey: process.env.SUPABASE_S3_SECRET_ACCESS_KEY, // Secret access key | |
}, | |
}); | |
if (!process.env.SUPABASE_REGION || !process.env.SUPABASE_PROJECT_ID || !process.env.SUPABASE_S3_ACCESS_KEY_ID || !process.env.SUPABASE_S3_SECRET_ACCESS_KEY) { | |
throw new Error("Missing required Supabase environment variables"); | |
} |
docs/examples/stripe-webhook.mdx
Outdated
## Setting up a Stripe webhook handler | ||
|
||
To set up your endpoint, you'll need to create a 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> | ||
|
||
```ts Remix | ||
// app/webhooks/stripe.ts | ||
import { type ActionFunctionArgs, json } from "@remix-run/node"; | ||
import type { stripeWebhook } from "src/trigger/stripe-webhook"; | ||
import { tasks } from "@trigger.dev/sdk/v3"; | ||
import stripe from "stripe"; | ||
|
||
export async function action({ request, params }: ActionFunctionArgs) { | ||
if (request.method !== "POST") { | ||
return json({ error: "Method not allowed" }, { status: 405 }); | ||
} | ||
|
||
const signature = request.headers.get("stripe-signature"); | ||
const payload = await request.text(); | ||
|
||
if (!signature || !payload) { | ||
return json({ error: "Invalid Stripe payload/signature" }, { status: 400 }); | ||
} | ||
|
||
try { | ||
// Construct the Stripe event using the payload, signature, and webhook secret | ||
const event = stripe.webhooks.constructEvent( | ||
payload, | ||
signature, | ||
process.env.STRIPE_WEBHOOK_SECRET as string | ||
); | ||
|
||
console.log("Received Stripe event:", event); | ||
|
||
// Trigger the 'stripe-webhook' task with the constructed event | ||
const { id } = await tasks.trigger<typeof stripeWebhook>("stripe-webhook", event); | ||
|
||
// Return a JSON response with the run ID of the triggered task | ||
return json({ runId: id }); | ||
} catch (e) { | ||
console.error("Error processing Stripe webhook:", e); | ||
return json({ error: e instanceof Error ? e.message : JSON.stringify(e) }, { status: 400 }); | ||
} | ||
} | ||
``` | ||
|
||
```ts Nextjs | ||
// app/api/stripe-webhook/route.ts | ||
import { NextResponse } from "next/server"; | ||
import { tasks } from "@trigger.dev/sdk/v3"; | ||
import Stripe from "stripe"; | ||
import { stripeWebhook } from "../../../trigger/stripe-webhook"; | ||
|
||
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, | ||
} | ||
); | ||
} | ||
|
||
try { | ||
const event = Stripe.webhooks.constructEvent( | ||
payload, | ||
signature, | ||
process.env.STRIPE_WEBHOOK_SECRET as string | ||
); | ||
|
||
const { id } = await tasks.trigger<typeof stripeWebhook>("stripe-webhook", event); | ||
|
||
return NextResponse.json({ runId: id }); | ||
} catch (e) { | ||
console.error("Error processing Stripe webhook:", e); | ||
return NextResponse.json( | ||
{ error: e instanceof Error ? e.message : JSON.stringify(e) }, | ||
{ status: 400 } | ||
); | ||
} | ||
} | ||
``` | ||
|
||
</CodeGroup> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestions for improving code examples
The code examples are well-structured, but consider the following improvements:
-
Consistency in imports: In the Remix example,
stripe
is imported in lowercase, while in the Next.js example, it'sStripe
with a capital 'S'. Consider using consistent casing across examples. -
More specific error handling: The current error handling is good, but it could be more specific. Consider adding specific catch blocks for common errors, such as
stripe.errors.StripeSignatureVerificationError
. -
Import path in Next.js example: The relative import path
"../../../trigger/stripe-webhook"
might be confusing. Consider using an absolute import path or explaining the project structure.
Here's an example of how you might improve the error handling in the Remix example:
} catch (e) {
console.error("Error processing Stripe webhook:", e);
- return json({ error: e instanceof Error ? e.message : JSON.stringify(e) }, { status: 400 });
+ if (e instanceof stripe.errors.StripeSignatureVerificationError) {
+ return json({ error: "Invalid signature" }, { status: 400 });
+ } else if (e instanceof Error) {
+ return json({ error: e.message }, { status: 400 });
+ } else {
+ return json({ error: "An unknown error occurred" }, { status: 500 });
+ }
}
Apply similar changes to the Next.js example as well.
Committable suggestion was skipped due to low confidence.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Outside diff range and nitpick comments (1)
docs/examples/stripe-webhook.mdx (1)
135-144
: Approve testing instructions with minor punctuation improvementsThe testing instructions are clear and helpful, providing a step-by-step guide for users to test their Stripe webhook implementation locally. To improve readability, consider the following minor punctuation adjustments:
- Add a period at the end of line 137:
-To test everything is working you can use the Stripe CLI to send test events to your endpoint: +To test everything is working, you can use the Stripe CLI to send test events to your endpoint:
- Add a comma before "and" on line 142:
-4. Then, check the [Trigger.dev](https://cloud.trigger.dev) dashboard and you should see the successful run of the `stripe-webhook` task. +4. Then, check the [Trigger.dev](https://cloud.trigger.dev) dashboard, and you should see the successful run of the `stripe-webhook` task.These minor adjustments will enhance the overall readability of the testing instructions.
Tools
LanguageTool
[uncategorized] ~137-~137: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~142-~142: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- docs/examples/stripe-webhook.mdx (1 hunks)
Additional context used
LanguageTool
docs/examples/stripe-webhook.mdx
[uncategorized] ~137-~137: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~142-~142: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Additional comments not posted (2)
docs/examples/stripe-webhook.mdx (2)
1-15
: LGTM: Clear and informative introductionThe metadata, overview, and key features sections provide a concise and clear introduction to the Stripe webhook example. The information is well-structured and helps users understand the purpose and main features of the example.
16-23
: LGTM: Well-defined environment variablesThe environment variables section clearly lists and explains the required variables for this example. This information is crucial for users to set up the webhook handler correctly.
docs/examples/stripe-webhook.mdx
Outdated
## Setting up a Stripe webhook handler | ||
|
||
First you'll need to create a 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> | ||
|
||
```ts Remix | ||
// app/webhooks/stripe.ts | ||
import { type ActionFunctionArgs, json } from "@remix-run/node"; | ||
import type { stripeWebhook } from "src/trigger/stripe-webhook"; | ||
import { tasks } from "@trigger.dev/sdk/v3"; | ||
import stripe from "stripe"; | ||
|
||
export async function action({ request, params }: ActionFunctionArgs) { | ||
if (request.method !== "POST") { | ||
return json({ error: "Method not allowed" }, { status: 405 }); | ||
} | ||
|
||
const signature = request.headers.get("stripe-signature"); | ||
const payload = await request.text(); | ||
|
||
if (!signature || !payload) { | ||
return json({ error: "Invalid Stripe payload/signature" }, { status: 400 }); | ||
} | ||
|
||
try { | ||
// Construct the Stripe event using the payload, signature, and webhook secret | ||
const event = stripe.webhooks.constructEvent( | ||
payload, | ||
signature, | ||
process.env.STRIPE_WEBHOOK_SECRET as string | ||
); | ||
|
||
console.log("Received Stripe event:", event); | ||
|
||
// Trigger the 'stripe-webhook' task with the constructed event | ||
const { id } = await tasks.trigger<typeof stripeWebhook>("stripe-webhook", event); | ||
|
||
// Return a JSON response with the run ID of the triggered task | ||
return json({ runId: id }); | ||
} catch (e) { | ||
console.error("Error processing Stripe webhook:", e); | ||
return json({ error: e instanceof Error ? e.message : JSON.stringify(e) }, { status: 400 }); | ||
} | ||
} | ||
``` | ||
|
||
```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 { stripeWebhook } from "../../../trigger/stripe-webhook"; | ||
|
||
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, | ||
} | ||
); | ||
} | ||
|
||
try { | ||
const event = Stripe.webhooks.constructEvent( | ||
payload, | ||
signature, | ||
process.env.STRIPE_WEBHOOK_SECRET as string | ||
); | ||
|
||
const { id } = await tasks.trigger<typeof stripeWebhook>("stripe-webhook", event); | ||
|
||
return NextResponse.json({ runId: id }); | ||
} catch (e) { | ||
console.error("Error processing Stripe webhook:", e); | ||
return NextResponse.json( | ||
{ error: e instanceof Error ? e.message : JSON.stringify(e) }, | ||
{ status: 400 } | ||
); | ||
} | ||
} | ||
``` | ||
|
||
</CodeGroup> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve code examples based on previous suggestions
The code examples for Remix and Next.js are well-structured, but there are some improvements that can be made based on previous suggestions:
-
Consistency in imports: In the Remix example (line 37),
stripe
is imported in lowercase, while in the Next.js example (line 77), it'sStripe
with a capital 'S'. Consider using consistent casing across examples. -
More specific error handling: The current error handling is good, but it could be more specific. Consider adding specific catch blocks for common errors, such as
stripe.errors.StripeSignatureVerificationError
. -
Import path in Next.js example: The relative import path
"../../../trigger/stripe-webhook"
on line 78 might be confusing. Consider using an absolute import path or explaining the project structure.
Here's an example of how you might improve the error handling in both examples:
} catch (e) {
console.error("Error processing Stripe webhook:", e);
- return json({ error: e instanceof Error ? e.message : JSON.stringify(e) }, { status: 400 });
+ if (e instanceof stripe.errors.StripeSignatureVerificationError) {
+ return json({ error: "Invalid signature" }, { status: 400 });
+ } else if (e instanceof Error) {
+ return json({ error: e.message }, { status: 400 });
+ } else {
+ return json({ error: "An unknown error occurred" }, { status: 500 });
+ }
}
Apply similar changes to both the Remix and Next.js examples.
Committable suggestion was skipped due to low confidence.
docs/examples/stripe-webhook.mdx
Outdated
## Task code | ||
|
||
This task listens for Stripe events and performs actions based on the `checkout.session.completed` event type: | ||
|
||
```ts trigger/stripe-webhook.ts | ||
import { task } from "@trigger.dev/sdk/v3"; | ||
import type stripe from "stripe"; | ||
|
||
export const stripeWebhook = task({ | ||
id: "stripe-webhook", | ||
run: async (payload: stripe.Event, { ctx }) => { | ||
switch (payload.type) { | ||
case "checkout.session.completed": { | ||
//do stuff | ||
} | ||
} | ||
}, | ||
}); | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhance task code with more detailed examples
The task code provides a good basic structure for handling Stripe events. However, it could be improved by adding more detailed comments or examples of what actions could be performed for different event types. Consider expanding the checkout.session.completed
case with a more concrete example:
export const stripeWebhook = task({
id: "stripe-webhook",
run: async (payload: stripe.Event, { ctx }) => {
switch (payload.type) {
case "checkout.session.completed": {
- //do stuff
+ // Example: Update order status in database
+ const session = payload.data.object as stripe.Checkout.Session;
+ await updateOrderStatus(session.id, "paid");
+
+ // Send confirmation email to customer
+ await sendConfirmationEmail(session.customer_email);
}
+ // Add more cases for other event types
+ // case "payment_intent.succeeded":
+ // case "customer.subscription.created":
}
},
});
This enhancement would provide users with a clearer understanding of how to implement specific actions based on different Stripe event types.
Committable suggestion was skipped due to low confidence.
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Outside diff range and nitpick comments (5)
docs/examples/intro.mdx (1)
16-17
: Great additions to the example tasks!The new entries for Stripe webhook and Supabase Storage upload are well-formatted and provide clear, concise descriptions. These examples will be valuable for developers working with these technologies.
Consider adding a brief mention of Trigger.dev's role in the Stripe webhook example description, similar to how it's mentioned in the Resend email sequence example. This could help emphasize the integration with Trigger.dev. For example:
-| [Stripe webhook](/examples/stripe-webhook) | Trigger a task from Stripe webhook events. | +| [Stripe webhook](/examples/stripe-webhook) | Trigger a task from Stripe webhook events using Trigger.dev. |docs/examples/stripe-webhook.mdx (4)
16-23
: Consider adding information on obtaining environment variable valuesThe environment variables are well-explained. To further assist users, consider adding brief instructions or links on how to obtain these values, especially for the
STRIPE_WEBHOOK_SECRET
andTRIGGER_API_SECRET
.
32-80
: Suggestions for improving the Next.js exampleThe Next.js example is well-structured, but consider the following improvements:
- Add a comment explaining the purpose of the
type-only
import on line 38.- Consider adding more detailed error messages in the error responses.
- Add a comment explaining why you're using
as string
forSTRIPE_WEBHOOK_SECRET
on line 56.Here's an example of how you might improve the error handling:
if (!signature || !payload) { return NextResponse.json( - { error: "Invalid Stripe payload/signature" }, + { error: "Missing Stripe signature or payload" }, { status: 400, } ); }
82-120
: Suggestions for improving the Remix exampleThe Remix example is well-structured, but consider the following improvements:
- Ensure consistency with the Next.js example by using the same import style for Stripe (currently using
Stripe
in Next.js andstripe
in Remix).- Add a comment explaining why you're using
as string
forSTRIPE_WEBHOOK_SECRET
on line 101.- Consider adding more detailed error messages in the error responses.
Here's an example of how you might improve the error handling:
if (!signature || !payload) { - return json({ error: "Invalid Stripe payload/signature" }, { status: 400 }); + return json({ error: "Missing Stripe signature or payload" }, { status: 400 }); }
140-149
: Minor grammatical improvements and suggestion for testing instructionsThe testing instructions are clear and helpful. Consider the following improvements:
- Add a comma after "To test everything is working" on line 142.
- Add a comma before "and" in the last sentence of point 3 on line 147.
- Consider adding a note about the importance of using a secure webhook secret in production environments.
Here's an example of how you might add the production note:
For more information on setting up and testing Stripe webhooks, refer to the [Stripe Webhook Documentation](https://stripe.com/docs/webhooks). + +Note: When moving to a production environment, ensure you use a secure, permanent webhook secret rather than the temporary one provided for testing.Tools
LanguageTool
[uncategorized] ~142-~142: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~147-~147: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- docs/examples/intro.mdx (1 hunks)
- docs/examples/stripe-webhook.mdx (1 hunks)
- docs/examples/supabase-storage-upload.mdx (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- docs/examples/supabase-storage-upload.mdx
Additional context used
LanguageTool
docs/examples/stripe-webhook.mdx
[uncategorized] ~142-~142: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~147-~147: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Additional comments not posted (2)
docs/examples/stripe-webhook.mdx (2)
1-5
: LGTM: Well-structured metadataThe metadata section provides clear and concise information about the content of this document.
7-15
: LGTM: Clear overview and key featuresThe overview and key features sections effectively summarize the purpose and main points of the document.
docs/examples/stripe-webhook.mdx
Outdated
## Task code | ||
|
||
This task is triggered when a `checkout.session.completed` event is received from Stripe. | ||
|
||
```ts trigger/stripe-webhook.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.Event, {}) => { | ||
// Add your custom logic for handling the checkout.session.completed event here | ||
}, | ||
}); | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider enhancing the task code example
The task code provides a good basic structure. To make it more informative, consider adding a more detailed example of what could be done in the run
function. This could help users understand how to handle the Stripe event data.
Here's an example of how you might enhance the task code:
export const stripeCheckoutCompleted = task({
id: "stripe-checkout-completed",
run: async (payload: stripe.Event, {}) => {
- // Add your custom logic for handling the checkout.session.completed event here
+ const session = payload.data.object as stripe.Checkout.Session;
+ console.log(`Processing completed checkout for session ${session.id}`);
+
+ // Example: Update order status in your database
+ // await updateOrderStatus(session.id, "paid");
+
+ // Example: Send a confirmation email to the customer
+ // await sendConfirmationEmail(session.customer_email);
},
});
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
## Task code | |
This task is triggered when a `checkout.session.completed` event is received from Stripe. | |
```ts trigger/stripe-webhook.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.Event, {}) => { | |
// Add your custom logic for handling the checkout.session.completed event here | |
}, | |
}); | |
``` | |
## Task code | |
This task is triggered when a `checkout.session.completed` event is received from Stripe. | |
```ts trigger/stripe-webhook.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.Event, {}) => { | |
const session = payload.data.object as stripe.Checkout.Session; | |
console.log(`Processing completed checkout for session ${session.id}`); | |
// Example: Update order status in your database | |
// await updateOrderStatus(session.id, "paid"); | |
// Example: Send a confirmation email to the customer | |
// await sendConfirmationEmail(session.customer_email); | |
}, | |
}); | |
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Outside diff range and nitpick comments (2)
docs/examples/stripe-webhook.mdx (2)
16-23
: Minor grammatical improvement suggestedThe environment variables section is clear and informative. Consider making a small grammatical improvement:
-You'll need to configure the following environment variables for this example to work: +You'll need to configure the following environment variables for this example to work:This change improves the sentence structure slightly.
140-148
: Minor grammatical improvement suggestedThe "Testing your task locally" section provides clear and comprehensive instructions. Consider making a small grammatical improvement:
-To test everything is working you can use the Stripe CLI to send test events to your endpoint: +To test that everything is working, you can use the Stripe CLI to send test events to your endpoint:This change improves the sentence structure and readability.
Tools
LanguageTool
[uncategorized] ~142-~142: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~148-~148: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- docs/examples/stripe-webhook.mdx (1 hunks)
Additional context used
LanguageTool
docs/examples/stripe-webhook.mdx
[uncategorized] ~25-~25: Possible missing comma found.
Context: ... Setting up the Stripe webhook handler First you'll need to create a [Stripe webhook...(AI_HYDRA_LEO_MISSING_COMMA)
[uncategorized] ~142-~142: A comma might be missing here.
Context: ...our task locally To test everything is working you can use the Stripe CLI to send test...(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)
[uncategorized] ~148-~148: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ev](https://cloud.trigger.dev) dashboard and you should see the successful run of th...(COMMA_COMPOUND_SENTENCE)
Additional comments not posted (2)
docs/examples/stripe-webhook.mdx (2)
1-15
: LGTM: Well-structured metadata and overviewThe metadata and overview sections provide clear and concise information about the example's purpose and key features. The content is well-organized and informative.
124-138
: Enhance the task code exampleThe task code provides a good basic structure, but it could be improved by adding a more detailed example of what actions could be performed when handling the Stripe event.
Consider enhancing the task code with a more concrete example:
export const stripeCheckoutCompleted = task({ id: "stripe-checkout-completed", run: async (payload: stripe.Event, {}) => { - // Add your custom logic for handling the checkout.session.completed event here + const session = payload.data.object as stripe.Checkout.Session; + console.log(`Processing completed checkout for session ${session.id}`); + + // Example: Update order status in your database + // await updateOrderStatus(session.id, "paid"); + + // Example: Send a confirmation email to the customer + // await sendConfirmationEmail(session.customer_email); }, });This enhancement provides users with a clearer understanding of how to implement specific actions based on the Stripe event data.
Likely invalid or redundant comment.
docs/examples/stripe-webhook.mdx
Outdated
## 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 | ||
); | ||
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"; | ||
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 | ||
); | ||
return json({ runId: id }); | ||
} | ||
default: { | ||
// Return a response indicating that the event is not handled | ||
return json({ message: "Event not handled" }, { status: 200 }); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
</CodeGroup> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve consistency and error handling in code examples
The code examples for Next.js and Remix are well-structured, but there are some improvements that can be made:
-
Consistency in imports: In the Next.js example,
Stripe
is imported with a capital 'S', while in the Remix example, it'sstripe
in lowercase. Consider using consistent casing across examples. -
More specific error handling: The current error handling is good, but it could be more specific. Consider adding specific catch blocks for common errors, such as
Stripe.errors.StripeSignatureVerificationError
.
Here's an example of how you might improve the error handling in both examples:
const event = Stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_WEBHOOK_SECRET as string
);
+} catch (e) {
+ console.error("Error processing Stripe webhook:", e);
+ if (e instanceof Stripe.errors.StripeSignatureVerificationError) {
+ return json({ error: "Invalid signature" }, { status: 400 });
+ } else if (e instanceof Error) {
+ return json({ error: e.message }, { status: 400 });
+ } else {
+ return json({ error: "An unknown error occurred" }, { status: 500 });
+ }
+}
Apply similar changes to both the Next.js and Remix examples.
Committable suggestion was skipped due to low confidence.
Tools
LanguageTool
[uncategorized] ~25-~25: Possible missing comma found.
Context: ... Setting up the Stripe webhook handler First you'll need to create a [Stripe webhook...(AI_HYDRA_LEO_MISSING_COMMA)
Summary by CodeRabbit
New Features
Documentation