Skip to content

Add Anthropic Claude API (TypeScript) and Stripe Subscriptions (Next.js) rules - #346

Open
moukie76 wants to merge 3 commits into
PatrickJS:mainfrom
moukie76:add-anthropic-claude-stripe-rules
Open

Add Anthropic Claude API (TypeScript) and Stripe Subscriptions (Next.js) rules#346
moukie76 wants to merge 3 commits into
PatrickJS:mainfrom
moukie76:add-anthropic-claude-stripe-rules

Conversation

@moukie76

@moukie76 moukie76 commented Jul 29, 2026

Copy link
Copy Markdown

New Rules

Anthropic Claude API (TypeScript)

File: rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc

Covers the complete Anthropic SDK integration pattern:

  • Singleton client setup
  • Basic messages and response parsing
  • Streaming via Next.js Edge Routes (SSE)
  • Tool use with input_schema
  • Rate-limit error handling (429/529)
  • Token management and model selection guidance

Stripe Subscriptions (Next.js)

File: rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc

Covers the full Stripe subscription lifecycle:

  • Client setup with correct apiVersion
  • Checkout session creation with metadata for user association
  • Webhook route with raw body + signature verification (the most common mistake)
  • Idempotent event handling pattern
  • Customer portal session creation
  • Environment variable naming conventions

Placement

Both rules added to the Database and API section in alphabetical order:

  • Anthropic Claude API → before GraphQL (A < G)
  • Stripe Subscriptions → before TypeScript Axios (S < T)

Quality

  • Real-world patterns, not toy examples
  • Focuses on common pitfalls (raw body for webhooks, singleton client, rate limits)
  • TypeScript throughout with proper types

Summary by CodeRabbit

  • Documentation
    • Added guidance for integrating the Anthropic Claude API with TypeScript, including streaming, tool use, error handling, and token management.
    • Added end-to-end guidance for Stripe Subscriptions in Next.js, including checkout, webhooks, customer portals, and secure configuration.
    • Updated the README to link to the new Claude API and Stripe rules while preserving existing database and API resources.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Integration rule documents

Layer / File(s) Summary
Anthropic Claude API guidance
rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc
Adds TypeScript/Next.js guidance for Claude client setup, requests, streaming, prompts, tool use, error handling, and token management.
Stripe subscription guidance
rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc
Adds Next.js guidance for subscription checkout, signed webhooks, customer portals, environment variables, and security constraints.
README rule catalog
README.md
Adds links for the Anthropic Claude API and Stripe Subscriptions rules to the Database and API list.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: patrickjs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the new rules, but it does not follow the required template sections or include the checklist and maintainer notes. Rewrite the description using the repository template: add Summary, Contribution Type, Value, Added/Changed Files, Quality Checklist, and Notes for Maintainers.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding two new rules for Anthropic Claude API and Stripe Subscriptions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc`:
- Around line 42-51: Harden the POST route before constructing the Anthropic
client or calling anthropic.messages.stream: authenticate the caller, validate
the parsed messages against the expected chat-message schema, enforce request
and conversation size limits, and apply per-user/application rate limiting.
Reject unauthorized, malformed, oversized, or rate-limited requests with
appropriate responses while preserving valid streaming behavior.
- Around line 92-103: Update the Error Handling example around the
Anthropic.APIStatusError checks to either explicitly document reliance on the
supported SDK’s built-in retry policy or show bounded exponential-backoff
retries for 429 and 529 responses, honoring retry headers when available before
rethrowing. Ensure the documented behavior does not imply retries are performed
when the example only rethrows.
- Around line 72-90: Expand the Tool Use example around
anthropic.messages.create to demonstrate the complete tool-execution loop:
inspect returned content for each tool_use block, execute the corresponding
tool, and send matching user messages containing tool_result blocks with the
correct IDs. Continue requesting responses until Claude returns the final text
response, while supporting multiple tool calls in a single response.

In `@rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc`:
- Around line 69-89: Update the webhook switch example to cover
invoice.payment_succeeded and customer.subscription.updated in addition to the
existing checkout.session.completed and customer.subscription.deleted cases,
invoking the appropriate payment-success and subscription-update handlers while
preserving the existing response flow. If handlers cannot be provided,
explicitly mark the example as partial and adjust the lifecycle coverage rule
accordingly.
- Around line 68-80: The webhook switch must persistently deduplicate Stripe
deliveries before invoking side effects. Add a unique processed-event record
keyed by event.id, check and create it atomically in the transaction that
performs handleCheckoutComplete or handleSubscriptionCancelled, and ensure the
event record is written before applying order or subscription changes so retries
exit without repeating effects.
- Around line 50-65: Update the POST webhook handler to read the Stripe
signature via req.headers.get("stripe-signature") instead of calling headers(),
remove the unused headers import, and add the required imports for the exported
stripe client and Stripe type so the constructEvent call compiles.
- Around line 25-43: Update createCheckoutSession at
rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc:25-43 and the
checkout/portal helper guidance at
rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc:93-97 to derive
userId and customerId from authenticated server state rather than caller
parameters, validate priceId against trusted prices, and allow only restricted
successUrl, cancelUrl, and returnUrl values before creating Stripe sessions.
- Around line 68-82: Update the webhook handler around the event-type switch to
durably enqueue the verified Stripe event, return new Response("ok", { status:
200 }) immediately, and move handleCheckoutComplete and
handleSubscriptionCancelled processing to asynchronous background handling after
enqueueing. Preserve idempotency and ensure processing occurs only for
successfully verified events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 60741858-53bf-4cb4-8996-31dc6a1b7749

📥 Commits

Reviewing files that changed from the base of the PR and between b044f95 and 143c526.

📒 Files selected for processing (3)
  • README.md
  • rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc
  • rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc

Comment on lines +42 to +51
export async function POST(req: Request) {
const { messages } = await req.json()
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

const stream = anthropic.messages.stream({
model: "claude-opus-4-6",
max_tokens: 2048,
system: "You are a helpful assistant.",
messages,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Validate and limit client-controlled chat requests.

This route forwards arbitrary messages from req.json() to a paid provider without authentication, schema validation, size limits, or per-user rate limiting. A public caller can exhaust quota or submit oversized conversations. Authenticate the caller, validate the message shape, cap request/history size, and enforce application-level rate limits before calling Anthropic. (docs.anthropic.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc` around
lines 42 - 51, Harden the POST route before constructing the Anthropic client or
calling anthropic.messages.stream: authenticate the caller, validate the parsed
messages against the expected chat-message schema, enforce request and
conversation size limits, and apply per-user/application rate limiting. Reject
unauthorized, malformed, oversized, or rate-limited requests with appropriate
responses while preserving valid streaming behavior.

Comment on lines +72 to +90
## Tool Use
```ts
const response = await anthropic.messages.create({
model: "claude-opus-4-6",
max_tokens: 1024,
tools: [{
name: "get_weather",
description: "Get current weather for a location",
input_schema: {
type: "object",
properties: {
location: { type: "string", description: "City name" },
},
required: ["location"],
},
}],
messages: [{ role: "user", content: "What's the weather in Paris?" }],
})
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching prompt file:"
fd -a "anthropic-claude-api-typescript-cursorrules-prompt-file\.mdc$" . || true

file="$(fd "anthropic-claude-api-typescript-cursorrules-prompt-file\.mdc$" . | head -n1 || true)"
if [ -n "${file:-}" ]; then
  echo "Path: $file"
  wc -l "$file"
  echo "--- lines 1-140 ---"
  sed -n '1,140p' "$file" | nl -ba -v1
fi

echo "--- tool-related searches in repository ---"
rg -n "tool(use)?|messages\.create|anthropic\.messages|messages:\s*\[\s*\{[^}]*tool_result" . -g '!node_modules' -g '!dist' -g '!build' | head -n 200

Repository: PatrickJS/awesome-cursorrules

Length of output: 487


🌐 Web query:

Anthropic messages create tool_use response tool_result next user example

💡 Result:

The Anthropic Messages API utilizes an iterative, stateful loop to handle tool use [1][2][3]. Because the API is stateless, your application must maintain the conversation history and append new turns as the process progresses [4][5][6]. The standard workflow for client-side tool execution consists of the following steps: 1. Initial Request: Send a message to the API with your tool definitions (the tools parameter) and the user's prompt [2][3]. 2. Identify Tool Use: If Claude decides to use a tool, the API returns a response with a stop_reason of "tool_use" [1][2][3]. The response content includes one or more tool_use blocks, each containing a unique id, the tool name, and the required input arguments [7][3]. 3. Execute Tool: Your application extracts the tool_use blocks, executes the requested operations locally, and collects the results [7][4]. 4. Return Results: Send a new request to the API. This request must include the conversation history up to this point, specifically appending: - The assistant's original message (containing the tool_use blocks) [4][8]. - A new user message containing one or more tool_result blocks [9][7][10]. Each tool_result block must include the tool_use_id matching the original tool_use request [4][5]. 5. Repeat: Continue this loop until the model provides a final answer with a stop_reason of "end_turn" [1][2][7]. When providing multiple tool results in a single turn, all tool_result blocks must be included in the content array of the same user message [4][10]. If errors occur during tool execution, you should return a tool_result block with the is_error: true flag to allow the model to recover [4].

Citations:


🌐 Web query:

Anthropic API tool use Python Node TypeScript tool_result message after tool_use response

💡 Result:

To use Anthropic's tool calling feature, you follow a request-response loop where your application handles the tool execution [1][2]. Interaction Sequence 1. Send a user message with the tools definition [1][2]. 2. Claude responds with a stop_reason of tool_use and one or more tool_use content blocks containing the tool name, input parameters, and a unique id [1][2]. 3. Your application executes the tool logic locally [1]. 4. Send a new request that includes the full conversation history: the original user message, the assistant's message (containing the tool_use blocks), and a new user message containing the tool_result blocks [1][2]. Formatting Requirements for Tool Results When sending tool_result messages back to the API, you must adhere to these strict rules [3][4]: - Tool results must be sent in a message with the role of user [3][5]. - In the content array of that message, all tool_result blocks must come FIRST [3][4]. - Any additional text must follow the tool_result blocks [3]. - You cannot insert other messages between the assistant's tool-use response and your user-message result [3]. - Each tool_result must include the tool_use_id corresponding to the original tool_use block [3][5]. - For multiple tool calls, include all results in a single user message rather than sending separate messages for each [4][6]. - If a tool fails, return the tool_result with is_error: true and a description of the error so Claude can recover [3][6]. SDK Helpers Both the Python and TypeScript SDKs provide toolRunner helpers that automate this loop, handling the message construction and execution automatically [7][8][9]. - Python: client.beta.messages.tool_runner [9] - TypeScript: client.beta.messages.toolRunner [7][8] Using these helpers is recommended unless you require complete manual control over the loop [6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="./rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc"

printf '--- line count ---\n'
wc -l "$file"

printf '--- content with line numbers ---\n'
awk '{ printf "%6d  %s\n", NR, $0 }' "$file"

printf '--- tool-related searches in repository ---\n'
rg -n "tool(use)?|messages\.create|anthropic\.messages|messages:\s*\[\s*\{[^}]*tool_result|toolRunner" . -g '!node_modules' -g '!dist' -g '!build' | head -n 250

Repository: PatrickJS/awesome-cursorrules

Length of output: 258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="./rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc"

printf '%s\n' '--- line count ---'
wc -l "$file"

printf '%s\n' '--- content with line numbers ---'
awk '{ printf "%6d  %s\n", NR, $0 }' "$file"

printf '%s\n' '--- tool-related searches in repository ---'
rg -n "tool(use)?|messages\.create|anthropic\.messages|messages:\s*\[\s*\{[^}]*tool_result|toolRunner" . -g '!node_modules' -g '!dist' -g '!build' | head -n 250 || true

Repository: PatrickJS/awesome-cursorrules

Length of output: 32453


Complete the client-tool execution loop.

The shown code only sends the initial tool request; when Claude returns one or more tool_use blocks, the guide should show executing each tool and sending back user messages with matching tool_result blocks until the model returns a final text response.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc` around
lines 72 - 90, Expand the Tool Use example around anthropic.messages.create to
demonstrate the complete tool-execution loop: inspect returned content for each
tool_use block, execute the corresponding tool, and send matching user messages
containing tool_result blocks with the correct IDs. Continue requesting
responses until Claude returns the final text response, while supporting
multiple tool calls in a single response.

Comment on lines +92 to +103
## Error Handling
```ts
try {
const message = await anthropic.messages.create(...)
} catch (err) {
if (err instanceof Anthropic.APIStatusError) {
if (err.status === 429) // rate limited — exponential backoff
if (err.status === 529) // overloaded — retry with backoff
}
throw err
}
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo "Files matching anthropic rule:"
fd -i 'anthropic-claude-api-typescript-cursorrules-prompt-file\.mdc|anthropic.*cursorrules|anthropic.*mdc' . || true

file="rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc"
if [ -f "$file" ]; then
  echo
  echo "Target file line count:"
  wc -l "$file"
  echo
  echo "Relevant section:"
  nl -ba "$file" | sed -n '1,140p'
fi

echo
echo "Search for anthropic.retry / retry policy references in repo:"
rg -n "retry|APIStatusError|Anthropic\\.messages\\.create|529|429" . --glob '!node_modules' --glob '!dist' --glob '!build' || true

Repository: PatrickJS/awesome-cursorrules

Length of output: 445


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc"
echo "Target file line count:"
wc -l "$file"

echo
echo "Relevant section around lines 80-108:"
awk 'NR>=80 && NR<=108 { printf "%4d\t%s\n", NR, $0 }' "$file"

echo
echo "Search for retry/status-related references in repo:"
rg -n "retry|APIStatusError|Anthropic\\.messages\\.create|529|429" . --glob '!node_modules' --glob '!dist' --glob '!build' || true

Repository: PatrickJS/awesome-cursorrules

Length of output: 3693


Implement bounded retry behavior or document SDK retries.

Lines 97-100 only if on 429/529, then rethrow; no backoff is performed. Either defer to the Anthropic SDK retry policy for the supported SDK version or implement bounded exponential backoff that also respects retry headers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/anthropic-claude-api-typescript-cursorrules-prompt-file.mdc` around
lines 92 - 103, Update the Error Handling example around the
Anthropic.APIStatusError checks to either explicitly document reliance on the
supported SDK’s built-in retry policy or show bounded exponential-backoff
retries for 429 and 529 responses, honoring retry headers when available before
rethrowing. Ensure the documented behavior does not imply retries are performed
when the example only rethrows.

Comment on lines +25 to +43
export async function createCheckoutSession({
priceId,
userId,
successUrl,
cancelUrl,
}: {
priceId: string
userId: string
successUrl: string
cancelUrl: string
}) {
return stripe.checkout.sessions.create({
mode: "subscription",
payment_method_types: ["card"],
line_items: [{ price: priceId, quantity: 1 }],
success_url: successUrl,
cancel_url: cancelUrl,
metadata: { userId },
subscription_data: { metadata: { userId } },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching cursorrules prompt:"
fd -a 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc$' . || true

file="$(fd 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  echo "--- $file line count ---"
  wc -l "$file"
  echo "--- lines 1-130 ---"
  cat -n "$file" | sed -n '1,130p'
  echo "--- Stripe create calls around checkout/portal ---"
  rg -n "createCheckoutSession|createPortalSession|checkout\.sessions\.create|billingPortal\.sessions\.create|successUrl|cancelUrl|returnUrl|userId|priceId|customerId" "$file"
fi

echo "--- search for Stripe session helpers/usages in repo (no execute) ---"
rg -n "checkout\.sessions\.create|billingPortal\.sessions\.create|stripe\.checkout|stripe\.billingPortal|createCheckoutSession|createPortalSession|successUrl|cancelUrl|returnUrl" . --glob '!node_modules' --glob '!dist' --glob '!build' || true

Repository: PatrickJS/awesome-cursorrules

Length of output: 6039


🌐 Web query:

Stripe Checkout Session create success_url cancel_url metadata allowlist return_url billing portal sessions

💡 Result:

Stripe Checkout Sessions and Billing Portal sessions are distinct, purpose-built tools for managing the customer payment lifecycle [1][2]. Stripe Checkout Sessions Checkout is used to collect initial payments, set up subscriptions, or collect payment details [1][2]. - success_url: The URL to which Stripe redirects customers after a successful payment or subscription creation [3][4]. - cancel_url: The URL to which customers are redirected if they choose to cancel the checkout process [3][4]. This is not supported when using embedded or custom UI modes [3][5]. - return_url: Used primarily in embedded or custom UI modes to specify where customers return upon completing or cancelling the checkout flow [6]. - metadata: A set of key-value pairs used to attach custom data (e.g., internal order IDs) to the session [6]. This metadata is visible in the Stripe Dashboard and available via webhooks/API, but it should not contain sensitive information [6]. Stripe Billing Portal Sessions The Billing Portal allows existing customers to manage their own subscriptions, payment methods, and billing details [7][1]. - return_url: The URL to which customers are redirected when they click the return link within the portal [8][9]. This is required unless a default return URL is configured in the Stripe Dashboard [10][11]. - Creation: Always authenticate the customer in your application before creating a portal session [10][1][12]. Create the session on demand using the customer's ID and redirect them to the returned session URL, which is short-lived for security [10][1][12]. - Configuration: You can specify a configuration ID to control which features are available in the portal (e.g., plan switching, cancellation) [8][10]. There is no "allowlist" parameter for Checkout or Portal sessions in the standard API definitions provided [3][8]. Access control for these sessions is managed by your server-side authentication (e.g., verifying the user is logged in before creating a session) and by using unique, short-lived session URLs [1][12]. In a typical SaaS integration, you use Checkout once for signup to generate a customer ID, and subsequently use the Billing Portal for all ongoing self-service management [1][12].

Citations:


Derive Stripe identity and navigation values from trusted server state.

The checkout and portal helpers currently pass caller-controlled parameters into Stripe session creation. Derive userId/customerId from authenticated server state, validate accepted priceId values against trusted prices, and restrict successUrl/cancelUrl/returnUrl URLs before creating either session.

📍 Affects 1 file
  • rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc#L25-L43 (this comment)
  • rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc#L93-L97
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc` around lines
25 - 43, Update createCheckoutSession at
rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc:25-43 and the
checkout/portal helper guidance at
rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc:93-97 to derive
userId and customerId from authenticated server state rather than caller
parameters, validate priceId against trusted prices, and allow only restricted
successUrl, cancelUrl, and returnUrl values before creating Stripe sessions.

Comment on lines +50 to +65
// app/api/stripe/webhook/route.ts
import { headers } from "next/headers"

export async function POST(req: Request) {
const body = await req.text() // raw body — never parse as JSON first
const signature = headers().get("stripe-signature")!

let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
)
} catch {
return new Response("Webhook signature verification failed", { status: 400 })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

npm exec tsc --noEmit

Repository: PatrickJS/awesome-cursorrules

Length of output: 5816


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repository files matching cursor prompt:"
fd -a 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc$' . || true

for f in $(fd 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc$' .); do
  echo "===== $f ====="
  wc -l "$f"
  sed -n '1,120p' "$f" | cat -n
done

echo "===== Next.js and Stripe dependency pins ====="
for f in package.json **/package.json; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    node -e "const p=require('./$f'); if(p.dependencies && (p.dependencies.next || p.dependencies.stripe)) { console.log(JSON.stringify({next:p.dependencies.next,stripe:p.dependencies.stripe}, null, 2)) } if(p.devDependencies && (p.devDependencies.next || p.devDependencies.stripe)) { console.log(JSON.stringify({devNext:p.devDependencies.next,devStripe:p.devDependencies.stripe}, null, 2)) }" "$f" 2>/dev/null || true
  fi
done

Repository: PatrickJS/awesome-cursorrules

Length of output: 4221


🌐 Web query:

Next.js App Router headers() function sync async current version

💡 Result:

In the current version of Next.js, the headers function from next/headers is an asynchronous function that returns a Promise [1][2]. Key details regarding this change: 1. Asynchronous Requirement: You must now use await or React's use function to access the headers object [1][3]. Accessing it synchronously is deprecated and will be removed in future versions [1][4]. 2. Migration: Next.js provides a codemod (next-async-request-api) to help automate the transition of your codebase to the asynchronous pattern [3][4]. 3. Usage Pattern: - For async Server Components: const headersList = await headers; [1][5] - For Client Components or synchronous contexts: const headersList = use(headers); [1][6] 4. Background: This change was introduced in Next.js 15 to improve how Next.js handles dynamic rendering and request-time APIs, allowing the framework to potentially prerender parts of a page before the full request context is available [6][4]. While some temporary backward compatibility existed in earlier versions of Next.js 15 to allow synchronous access to properties on the returned Promise, this has been phased out [7][4]. It is recommended to update all instances of headers to use the asynchronous pattern to avoid runtime errors and deprecation warnings [8][4].

Citations:


Fix the webhook header access and missing imports.

The snippet calls headers().get(...) directly, but the Next.js App API exposes headers() asynchronously, so this route should read req.headers.get("stripe-signature") instead. It also needs imports for the exported stripe client and the Stripe type before the route can compile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc` around lines
50 - 65, Update the POST webhook handler to read the Stripe signature via
req.headers.get("stripe-signature") instead of calling headers(), remove the
unused headers import, and add the required imports for the exported stripe
client and Stripe type so the constructEvent call compiles.

Comment on lines +68 to +80
// Handle events idempotently
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session
await handleCheckoutComplete(session)
break
}
case "customer.subscription.deleted": {
const sub = event.data.object as Stripe.Subscription
await handleSubscriptionCancelled(sub)
break
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc$' . || true

echo "== relevant file excerpt =="
file="$(fd 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc$' . | head -n 1)"
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '1,140p' "$file" | nl -ba | sed -n '55,90p'
fi

echo "== search webhook event id/idempotency occurrences in codebase =="
rg -n --no-heading 'event\.id|idempotenc|idempotency|webhook.*signature|handleCheckoutComplete|handleSubscriptionCancelled|stripe-webhook' . || true

echo "== git status/stat =="
git status --short || true
git diff --stat || true

Repository: PatrickJS/awesome-cursorrules

Length of output: 403


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt =="
sed -n '1,140p' /home/jailuser/git/rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc | awk '{printf "%6d  %s\n", NR, $0}'

echo "== search webhook event id/idempotency occurrences in codebase =="
rg -n --no-heading 'event\.id|idempotenc|idempotency|webhook.*signature|handleCheckoutComplete|handleSubscriptionCancelled|stripe-webhook' /home/jailuser/git || true

echo "== git status/stat =="
git status --short || true
git diff --stat || true

Repository: PatrickJS/awesome-cursorrules

Length of output: 6667


🌐 Web query:

Stripe webhook signature verification duplicate webhook event handle idempotently event.id

💡 Result:

To handle duplicate Stripe webhook events safely, you must combine signature verification with an idempotency strategy that uses the event's unique ID [1][2][3]. 1. Verify the Signature First Always verify the Stripe-Signature header before performing any other logic [4][5][6]. This must be done using the raw request body, as parsing the body (e.g., via JSON middleware) can alter it and cause verification failures [7][4][8]. Use Stripe's official libraries for this [7][5]. 2. Implement Idempotency Using event.id Because Stripe may deliver the same event multiple times (e.g., due to network retries), you must design your handler to be idempotent—meaning processing the same event multiple times has the same effect as processing it once [4][3][9]. The recommended approach is to track processed events using their unique event.id [1][3][8]: - Create a database table (e.g., processed_webhooks) with a unique constraint on the event_id column [1][2][10]. - Before executing your business logic, attempt to insert the event.id into this table [1][2][11]. - If the insertion fails due to a unique constraint violation, you know the event has already been processed [1][2][10]. In this case, acknowledge the request with a 200 OK status immediately so Stripe stops retrying, and exit the handler without performing further work [1][9][10]. - If the insertion succeeds, proceed with your business logic [2][11]. 3. Atomicity For maximum reliability, your business logic and the recording of the event.id should occur within the same database transaction [4][8]. This prevents scenarios where your code successfully processes the event but fails to record it, leading to redundant processing on the next retry [8]. 4. Return 2xx Quickly Your endpoint should return a 200 OK (or other 2xx status) as quickly as possible to prevent timeouts [5][6]. If your processing is complex, consider offloading the actual work to a background job, but ensure that your idempotency check is still enforced—ideally by using the event.id as a deduplication key for your background job queue [11][8]. Top results: [7][1][4][3][8]

Citations:


Implement real webhook idempotency before applying side effects.

The handler claims idempotent processing, but it only signature-verifies event; it never looks up/records event.id or an equivalent processed-event key. If Stripe retries the same delivery, handleCheckoutComplete or handleSubscriptionCancelled can run again. Add a persisted idempotency check with a uniqueness constraint, and include side-effect recording in the same transaction before applying order/subscription changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc` around lines
68 - 80, The webhook switch must persistently deduplicate Stripe deliveries
before invoking side effects. Add a unique processed-event record keyed by
event.id, check and create it atomically in the transaction that performs
handleCheckoutComplete or handleSubscriptionCancelled, and ensure the event
record is written before applying order or subscription changes so retries exit
without repeating effects.

Comment on lines +68 to +82
// Handle events idempotently
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session
await handleCheckoutComplete(session)
break
}
case "customer.subscription.deleted": {
const sub = event.data.object as Stripe.Subscription
await handleSubscriptionCancelled(sub)
break
}
}

return new Response("ok")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Locate file"
fd -a 'stripe-nextjs-subscriptions-cursorrules-prompt-file\.mdc' . || true

echo "## Show relevant file"
if [ -f rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc ]; then
  nl -ba rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc | sed -n '1,140p'
fi

echo "## Search for webhook handlers in repository"
rg -n "checkout\.session\.completed|customer\.subscription\.deleted|handleCheckoutComplete|handleSubscriptionCancelled|webhook|route\.ts|Stripe\.Signature|Stripe\.Webhooks" . --glob '!node_modules' --glob '!dist' --glob '!build' | head -200

Repository: PatrickJS/awesome-cursorrules

Length of output: 325


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Show relevant file"
cat -n rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc | sed -n '1,140p'

echo "## Search for webhook handlers in repository"
rg -n "checkout\.session\.completed|customer\.subscription\.deleted|handleCheckoutComplete|handleSubscriptionCancelled|webhook|route\.ts|Stripe\.Signature|Stripe\.Webhooks" . --glob '!node_modules' --glob '!dist' --glob '!build' | head -200

Repository: PatrickJS/awesome-cursorrules

Length of output: 8552


🌐 Web query:

Stripe webhooks Node.js return 200 immediately process async documentation

💡 Result:

To process Stripe webhooks in Node.js while returning a 200 status code immediately, you should perform signature verification, acknowledge the receipt of the event by sending a 200 response, and then offload the business logic to an asynchronous background task [1][2][3]. Stripe requires a 2xx response within 20 seconds; failing to do so may cause Stripe to retry the event, which can lead to duplicate processing if your system is not idempotent [4][1][3]. Recommended Implementation Steps: 1. Parse the Raw Body: In your webhook route, use middleware to access the raw request body (e.g., express.raw({ type: 'application/json' })). Do not use a JSON parser before verifying the signature, as this will corrupt the body and cause verification to fail [5][1][6]. 2. Verify the Signature: Use the stripe.webhooks.constructEvent method, passing the raw body, the Stripe-Signature header, and your webhook endpoint secret [5][6]. 3. Send 200 Response: Once verified, immediately send a 200 OK response (e.g., response.json({ received: true })) to inform Stripe that you have successfully received the payload [1][7][3]. 4. Process Asynchronously: After sending the response, dispatch the event to a background job queue (such as BullMQ, Inngest, or a similar system) to perform the actual business logic [1][2][3]. 5. Ensure Idempotency: Because Stripe may retry events if your initial response fails or takes too long, ensure your processing logic is idempotent (e.g., by checking if an event ID has already been processed in your database) [1][3]. By following this pattern, you decouple the HTTP acknowledgement from the processing logic, preventing timeouts and ensuring system scalability during high-traffic periods [2][8].

Citations:


Return 2xx before processing Stripe webhook events.

The example awaits handleCheckoutComplete/handleSubscriptionCancelled before returning "ok", while Stripe expects a 2xx response within the delivery timeout and the webhook rules require fast responses plus offloaded/background processing. Enqueue the verified event durably, return new Response("ok", { status: 200 }), then process it asynchronously.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc` around lines
68 - 82, Update the webhook handler around the event-type switch to durably
enqueue the verified Stripe event, return new Response("ok", { status: 200 })
immediately, and move handleCheckoutComplete and handleSubscriptionCancelled
processing to asynchronous background handling after enqueueing. Preserve
idempotency and ensure processing occurs only for successfully verified events.

Comment on lines +69 to +89
switch (event.type) {
case "checkout.session.completed": {
const session = event.data.object as Stripe.Checkout.Session
await handleCheckoutComplete(session)
break
}
case "customer.subscription.deleted": {
const sub = event.data.object as Stripe.Subscription
await handleSubscriptionCancelled(sub)
break
}
}

return new Response("ok")
}
```

## Webhook Rules
- **Idempotent**: webhook can fire multiple times — check if already processed
- **Fast**: return 200 immediately, process async if needed
- **Handle all lifecycle events**: `checkout.session.completed`, `invoice.payment_succeeded`, `customer.subscription.updated`, `customer.subscription.deleted`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align the switch with the promised lifecycle coverage.

The rule lists invoice.payment_succeeded and customer.subscription.updated, but the example silently acknowledges only two event types. Either add the missing handlers or explicitly label this as a partial example; otherwise subscription state and entitlements can remain stale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/stripe-nextjs-subscriptions-cursorrules-prompt-file.mdc` around lines
69 - 89, Update the webhook switch example to cover invoice.payment_succeeded
and customer.subscription.updated in addition to the existing
checkout.session.completed and customer.subscription.deleted cases, invoking the
appropriate payment-success and subscription-update handlers while preserving
the existing response flow. If handlers cannot be provided, explicitly mark the
example as partial and adjust the lifecycle coverage rule accordingly.

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