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
Merged

Conversation

D-K-P
Copy link
Member

@D-K-P D-K-P commented Sep 23, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a guide for setting up a Stripe webhook handler, including environment variables and code examples.
    • Added a task for uploading videos to Supabase Storage with detailed instructions.
    • Expanded example collection to include Stripe webhook and Supabase storage upload examples.
  • Documentation

    • Enhanced comments and instructions in the video processing example for better user understanding.

Copy link
Contributor

coderabbitai bot commented Sep 23, 2024

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

Commits

Files that changed from the base of the PR and between e52a272 and 619f2d3.

Walkthrough

The pull request introduces updates across several documentation files, enhancing the examples for Trigger.dev. It clarifies instructions for the videoUrl field in ffmpeg-video-processing.mdx, adds a comprehensive guide for handling Stripe webhooks in stripe-webhook.mdx, and introduces a task for uploading videos to Supabase Storage in supabase-storage-upload.mdx. Additionally, new entries are added to the mint.json file to reflect these examples, improving the overall documentation for users.

Changes

Files Change Summary
docs/examples/ffmpeg-video-processing.mdx Updated comments for the videoUrl field to clarify that <video-url> should be replaced with the actual video URL.
docs/examples/stripe-webhook.mdx Introduced a guide for setting up a Stripe webhook handler, detailing environment variables, code examples for Remix and Next.js, and instructions for local testing using the Stripe CLI.
docs/examples/supabase-storage-upload.mdx Added a task for uploading videos to Supabase Storage, detailing the process of downloading a video, converting it, and uploading it to a specified bucket using the AWS SDK.
docs/mint.json Added new entries for "examples/stripe-webhook" and "examples/supabase-storage-upload" to expand the list of available examples.

Possibly related PRs

🐰 In the garden of code, we hop with delight,
New guides and tasks, shining ever so bright.
From Stripe to Supabase, we share and we learn,
With each little update, our knowledge does churn.
So let's dance through the docs, with joy in our hearts,
For every new feature, a fresh journey starts! 🌼✨


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?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 examples

The testing instructions provide a good starting point, but they could be more comprehensive. Consider the following improvements:

  1. Provide a complete example URL to demonstrate the expected format.
  2. Explain how to access the dashboard for testing.
  3. 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
}
  1. After running the task, check the logs in the dashboard for the uploaded object key and bucket name.
  2. 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 handling

The task code provides a good structure for handling Stripe events. However, the //do stuff comment in the checkout.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 instructions

The testing instructions are clear and comprehensive. However, there are two minor grammatical issues to address:

  1. Line 137: Consider adding a period after "locally" to separate the sentences.
  2. 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

Commits

Files that changed from the base of the PR and between c8eae9d and c998c60.

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 introduction

The 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.

Comment on lines 38 to 75
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,
};
},
});
Copy link
Contributor

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:

  1. Error handling: Consider wrapping the main logic in a try-catch block to handle potential errors gracefully.

  2. Bucket name: The bucket name is hardcoded, which might not be flexible for all users. Consider making it a parameter or an environment variable.

  3. 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.

  4. 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.

Comment on lines 19 to 36
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
},
});
Copy link
Contributor

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:

  1. There's a typo in the environment variable name on line 34. It should be SUPABASE_S3_SECRET_ACCESS_KEY instead of SUPABASE_S3__SECRET_ACCESS_KEY.

  2. 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.

  3. 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.

Suggested change
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");
}

Comment on lines 24 to 113
## 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>
Copy link
Contributor

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:

  1. Consistency in imports: In the Remix example, stripe is imported in lowercase, while in the Next.js example, it's Stripe with a capital 'S'. Consider using consistent casing across examples.

  2. 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.

  3. 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.

Copy link

changeset-bot bot commented Sep 23, 2024

⚠️ No Changeset found

Latest commit: 619f2d3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 improvements

The 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:

  1. 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:
  1. 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

Commits

Files that changed from the base of the PR and between c998c60 and 20cc1fd.

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 introduction

The 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 variables

The 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.

Comment on lines 24 to 113
## 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>
Copy link
Contributor

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:

  1. Consistency in imports: In the Remix example (line 37), stripe is imported in lowercase, while in the Next.js example (line 77), it's Stripe with a capital 'S'. Consider using consistent casing across examples.

  2. 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.

  3. 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.

Comment on lines 115 to 133
## 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
}
}
},
});
```
Copy link
Contributor

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.

Copy link

pkg-pr-new bot commented Sep 23, 2024

pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/build@1345
pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev@1345
pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/core@1345
pnpm add https://pkg.pr.new/triggerdotdev/trigger.dev/@trigger.dev/sdk@1345

commit: 619f2d3

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 values

The 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 and TRIGGER_API_SECRET.


32-80: Suggestions for improving the Next.js example

The Next.js example is well-structured, but consider the following improvements:

  1. Add a comment explaining the purpose of the type-only import on line 38.
  2. Consider adding more detailed error messages in the error responses.
  3. Add a comment explaining why you're using as string for STRIPE_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 example

The Remix example is well-structured, but consider the following improvements:

  1. Ensure consistency with the Next.js example by using the same import style for Stripe (currently using Stripe in Next.js and stripe in Remix).
  2. Add a comment explaining why you're using as string for STRIPE_WEBHOOK_SECRET on line 101.
  3. 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 instructions

The testing instructions are clear and helpful. Consider the following improvements:

  1. Add a comma after "To test everything is working" on line 142.
  2. Add a comma before "and" in the last sentence of point 3 on line 147.
  3. 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

Commits

Files that changed from the base of the PR and between 20cc1fd and ff87911.

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 metadata

The metadata section provides clear and concise information about the content of this document.


7-15: LGTM: Clear overview and key features

The overview and key features sections effectively summarize the purpose and main points of the document.

Comment on lines 124 to 138
## 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
},
});
```
Copy link
Contributor

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.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 suggested

The 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 suggested

The "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

Commits

Files that changed from the base of the PR and between ff87911 and e52a272.

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 overview

The 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 example

The 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.

Comment on lines 24 to 122
## 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>
Copy link
Contributor

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:

  1. Consistency in imports: In the Next.js example, Stripe is imported with a capital 'S', while in the Remix example, it's stripe in lowercase. Consider using consistent casing across examples.

  2. 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)

@coderabbitai coderabbitai bot mentioned this pull request Oct 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant