dist/plugins/api-commerce-stripe/skills/api-commerce-stripe/SKILL.md
Stripe payment processing — Checkout Sessions, Payment Intents, subscriptions, webhooks, Connect, customer management, error handling
npx skillsauth add agents-inc/skills api-commerce-stripeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Use the
stripenpm package for all server-side Stripe operations. Always verify webhook signatures withconstructEvent()using the raw request body, never the parsed body. Use idempotency keys on all mutating requests. Keep the secret key server-side only. Handle errors withinstanceof Stripe.errors.StripeError. Amounts are always in the smallest currency unit (e.g., cents for USD).
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST NEVER expose STRIPE_SECRET_KEY in client-side code — it stays on the server only)
(You MUST verify webhook signatures with stripe.webhooks.constructEvent() using the RAW request body — never parsed JSON)
(You MUST use idempotency keys on all mutating (POST) requests to prevent duplicate charges)
(You MUST handle all Stripe errors with instanceof Stripe.errors.StripeError — never swallow payment errors)
(You MUST express monetary amounts in the smallest currency unit — cents for USD, not dollars)
</critical_requirements>
Auto-detection: Stripe, stripe, stripe.checkout.sessions, stripe.paymentIntents, stripe.customers, stripe.subscriptions, stripe.webhooks, constructEvent, PaymentIntent, CheckoutSession, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, stripe.prices, stripe.products, stripe.refunds, stripe.transfers, stripe.accounts, Stripe.errors, idempotencyKey, payment_intent.succeeded, checkout.session.completed
When to use:
Key patterns covered:
When NOT to use:
Detailed Resources:
Core Setup & Payments:
Webhooks & Events:
Subscriptions & Billing:
Connect & Platforms:
Stripe is a payment infrastructure platform. The stripe npm package is the server-side SDK for interacting with the Stripe API. All payment processing happens server-side for security.
Core principles:
1000 means $10.00, not $1000.instanceof Stripe.errors.StripeError and handle by type for appropriate user responses.apiVersion in the constructor to lock behavior.When to use Stripe:
When NOT to use:
Create a singleton Stripe client. Secret key from env, API version pinned. See examples/core.md for full setup.
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2026-02-25.clover",
});
Never hardcode the secret key or omit apiVersion (behavior changes silently on Stripe API upgrades).
Use mode: "payment" for one-time, mode: "subscription" for recurring. Stripe hosts the payment page. Always include {CHECKOUT_SESSION_ID} in the success URL (Stripe replaces this template automatically). See examples/core.md for full examples.
const session = await stripe.checkout.sessions.create({
mode: "payment", // or "subscription" or "setup"
line_items: [{ price: priceId, quantity }],
success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/cancel`,
});
Use Payment Intents when you need full control over the payment UI (e.g., Stripe Elements). Always use automatic_payment_methods (not the legacy payment_method_types array) and include an idempotency key. See examples/core.md for full examples.
const paymentIntent = await stripe.paymentIntents.create(
{
amount: amountInCents,
currency,
automatic_payment_methods: { enabled: true },
},
{ idempotencyKey: `pi_${orderId}` },
);
return { clientSecret: paymentIntent.client_secret };
Name parameters amountInCents to avoid dollar/cent confusion. Return client_secret to the frontend.
Create customers with idempotency keys (based on email to prevent duplicates). Attach payment methods in two steps: attach, then set as default via invoice_settings.default_payment_method. See examples/core.md for full examples.
Products and prices are separate resources in Stripe's data model. Add recurring: { interval } only for subscription prices. Name the amount parameter amountInCents. See examples/core.md for full examples.
Omit amount for a full refund. Use payment_intent (preferred over charge). Always include an idempotency key unique to the refund amount. See examples/core.md for full examples.
Catch errors with instanceof Stripe.errors.StripeCardError (and other error subclasses). StripeCardError returns user-safe messages with decline_code. StripeInvalidRequestError is a developer bug. StripeConnectionError and StripeRateLimitError are retry-able. See examples/core.md for the complete error handling pattern.
if (error instanceof Stripe.errors.StripeCardError) {
return { success: false, message: error.message, code: error.code };
}
</patterns>
<red_flags>
High Priority Issues:
STRIPE_SECRET_KEY must never appear in browser bundles. Use STRIPE_PUBLISHABLE_KEY (starts with pk_) for client-side Stripe.js only.constructEvent() verification, attackers can send fake events to fulfill orders, grant access, or modify records.req.body (parsed JSON) instead of the raw body string/buffer causes signature verification to fail silently. With Express, use express.raw({ type: "application/json" }) on the webhook route.{ idempotencyKey } on create/update operations.amount: 10 creates a $0.10 charge, not $10.00. Always multiply by 100 or name variables amountInCents.Medium Priority Issues:
apiVersion in the constructor, Stripe uses your account's default version. API changes can silently break your integration.payment_method_types instead of automatic_payment_methods — The legacy array approach requires manual updates as new payment methods become available. automatic_payment_methods: { enabled: true } is the modern approach.catch blocks hide payment failures. Always log the error's requestId for debugging with Stripe support.requires_action status — Payment Intents may require 3D Secure authentication. Check paymentIntent.status after confirmation.Common Mistakes:
200 immediately, then process asynchronously.sk_test_ and pk_test_ only work with test data. Verify your environment configuration.expand for nested objects — Stripe returns IDs by default for related objects. Use expand: ["latest_invoice.payment_intent"] to get full objects.Gotchas & Edge Cases:
invoice.paid may arrive before invoice.created. Design handlers to be order-independent.{CHECKOUT_SESSION_ID} is a literal template — Stripe replaces this placeholder in the success_url. Do not URL-encode it.proration_behavior: "none" to disable.amount: 500 in JPY means 500 yen, not 5 yen. Check Stripe.ZERO_DECIMAL_CURRENCIES.transfers capability — Connected accounts must have card_payments and transfers capabilities enabled before receiving transfers.</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST NEVER expose STRIPE_SECRET_KEY in client-side code — it stays on the server only)
(You MUST verify webhook signatures with stripe.webhooks.constructEvent() using the RAW request body — never parsed JSON)
(You MUST use idempotency keys on all mutating (POST) requests to prevent duplicate charges)
(You MUST handle all Stripe errors with instanceof Stripe.errors.StripeError — never swallow payment errors)
(You MUST express monetary amounts in the smallest currency unit — cents for USD, not dollars)
Failure to follow these rules will create security vulnerabilities, duplicate charges, and silent payment failures.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety