Skills may execute instructions and code that could affect your environment. Marketplace scans reduce risk but do not guarantee safety. Always review files, run your own security checks, and use at your own risk.
stripe-integration-expert
Security Scan Summary
Status: Safe
Source: Syntic Skills registry
Automated security scan completed with no high-risk patterns detected. Manual review is still required.
About This Skill
Use when building or debugging Stripe integrations: subscriptions with trials/proration, one-time payments, usage-based billing, checkout, webhooks, portal, or invoicing.
Downloadable SKILL.md
Download SKILL.md and place it in your Syntic skills folder. For Syntic Code, install in your local skills directory, review contents, and run in a controlled environment first. Acknowledge the risk notice above to enable the download.
---
name: stripe-integration-expert
description: Use when building or debugging Stripe integrations: subscriptions with trials/proration, one-time payments, usage-based billing, checkout, webhooks, portal, or invoicing.
category: Engineering
version: 1.0.0
tools: []
---
# Stripe Integration Expert
Design and reason about production-grade Stripe integrations: subscriptions with trials and proration, one-time payments, usage-based billing, checkout sessions, idempotent webhook handlers, customer portal, and invoicing. Patterns apply across Next.js, Express, and Django.
## Core Capabilities
Subscription lifecycle management (create, upgrade, downgrade, cancel, pause); trial handling and conversion tracking; proration calculation and credit application; usage-based billing with metered pricing; idempotent webhook handlers with signature verification; customer portal integration; invoice generation and PDF access; local testing via the Stripe CLI.
## Subscription Lifecycle State Machine
A subscription moves: `FREE_TRIAL` -> (paid) -> `ACTIVE` -> (cancel) -> `CANCEL_PENDING` -> (period end) -> `CANCELED`, with a `reactivate` path back. From `ACTIVE`, a `downgrade` moves to `DOWNGRADING`, which resolves to `ACTIVE` on a lower plan at period end. From `FREE_TRIAL`, trial end without payment moves to `PAST_DUE`; three failed payment attempts move to `CANCELED`; a successful payment moves back to `ACTIVE`.
DB subscription status values: `trialing | active | past_due | canceled | cancel_pending | paused | unpaid`.
## Client Setup
Initialize the Stripe SDK with the secret key, a pinned `apiVersion`, and `appInfo` metadata. Store price IDs per plan (monthly/yearly) in environment variables, keyed by plan tier, alongside a feature list per tier for display purposes.
## Checkout Session
To start a subscription checkout: authenticate the user; get or create a Stripe customer (create one with `email` and `metadata.userId` if none exists yet, persist `stripeCustomerId`); create a checkout session in `subscription` mode with the chosen `priceId`, `allow_promotion_codes: true`, a `trial_period_days` of 14 only if the user hasn't had a trial before, and `success_url`/`cancel_url` pointing back into the app with `userId` in metadata for webhook correlation.
## Subscription Upgrade/Downgrade
**Upgrade (immediate):** update the subscription item to the new price with `proration_behavior: "always_invoice"` and `billing_cycle_anchor: "unchanged"` — this applies immediately and invoices the prorated difference.
**Downgrade (at period end):** update the subscription item to the new price with `proration_behavior: "none"` — this takes effect at the next billing cycle with no proration.
**Preview proration before confirming:** retrieve the subscription, compute a proration date (now), and call the upcoming-invoice preview with the new price and proration date to get `amount_due` and the line items — show this to the user before they confirm an upgrade.
## Idempotent Webhook Handling
The core pattern: verify the Stripe signature against the raw request body and webhook secret before doing anything else — reject with 400 on failure. Check a processed-events table for the event ID; if already processed, return success immediately without reprocessing (Stripe retries on 500, so duplicate delivery is expected, not exceptional). Only after a successful handler run, mark the event ID as processed; if the handler throws, return 500 so Stripe retries and do NOT mark it processed.
Events to handle, and what each should do:
- `checkout.session.completed` — for subscription-mode sessions, pull `userId` from metadata, re-fetch the subscription from the Stripe API (never trust webhook payload data alone), and persist `stripeCustomerId`, `stripeSubscriptionId`, `stripePriceId`, `stripeCurrentPeriodEnd`, `subscriptionStatus`, and `hasHadTrial: true`.
- `customer.subscription.created` / `customer.subscription.updated` — look the user up by `stripeSubscriptionId` (fall back to `stripeCustomerId` if not found), then sync `stripePriceId`, `stripeCurrentPeriodEnd`, `subscriptionStatus`, and `cancelAtPeriodEnd`.
- `customer.subscription.deleted` — clear `stripeSubscriptionId`/`stripePriceId`/`stripeCurrentPeriodEnd` and set status to `canceled`.
- `invoice.payment_failed` — set status to `past_due`; check `attempt_count` and send a dunning email — a milder "retry" message before 3 attempts, a "final" warning at 3+.
- `invoice.payment_succeeded` — set status to `active` and refresh `stripeCurrentPeriodEnd` from `invoice.period_end`.
## Usage-Based Billing
Report usage against a metered subscription item with `createUsageRecord({ quantity, timestamp, action: "increment" })`. To meter API calls, look up the user's subscription, find the item whose price has `recurring.usage_type === "metered"`, and report one unit per call in middleware.
## Customer Portal
Create a billing-portal session for an authenticated user's `stripeCustomerId` with a `return_url` back into account settings, and redirect them to the returned `url`. Reject with 400 if the user has no billing account yet.
## Testing with Stripe CLI
`stripe login`, then `stripe listen --forward-to localhost:3000/api/webhooks/stripe` to forward webhooks to local dev. Trigger specific events for testing: `stripe trigger checkout.session.completed`, `stripe trigger customer.subscription.updated`, `stripe trigger invoice.payment_failed` — optionally override fields, e.g. `--override subscription:customer=cus_xxx`. `stripe events list --limit 10` shows recent events.
Test cards: success `4242 4242 4242 4242`; requires authentication `4000 0025 0000 3155`; decline `4000 0000 0000 9995`; insufficient funds `4000 0000 0000 9995`.
## Feature Gating
Treat a subscription as active if status is `active` or `trialing`. For `past_due`, extend a grace period — still treat it as active as long as `stripeCurrentPeriodEnd` hasn't passed yet. Everything else is inactive. Gate protected routes/actions on this check and redirect to billing with a `reason=subscription_required` query param on failure.
## Common Pitfalls
- **Webhook delivery order isn't guaranteed** — always re-fetch state from the Stripe API rather than trusting the order or content of webhook events alone for DB updates.
- **Double-processing webhooks** — Stripe retries on any non-2xx response, including 500s, so an idempotency table keyed on event ID is mandatory, not optional.
- **Trial conversion tracking** — persist `hasHadTrial: true` in the DB the first time a user completes checkout, to prevent repeat trial abuse on the same account.
- **Proration surprises** — always preview proration before an upgrade takes effect, since `always_invoice` charges immediately and users are frequently surprised by an unexpected mid-cycle charge.
- **Signature verification on the raw body** — frameworks that parse JSON automatically will break signature verification; the webhook route must read the raw, unparsed request body.
- **Storing amounts as floats** — Stripe amounts are integers in the smallest currency unit (cents); converting to float anywhere in the pipeline introduces rounding bugs.
Bundle Download
Includes SKILL.md and bundled support files where provided. Risk acknowledgement is required.
Install Targets
Syntic App
- 1. Create a dedicated folder for this skill in your local skills library.
- 2. Place SKILL.md into that folder.
- 3. Restart Syntic and invoke this skill on matching tasks.
Syntic Code (CLI)
- 1. Save SKILL.md in your local Syntic Code skills directory.
- 2. Keep related files in the same skill folder.
- 3. Run in a safe environment and validate outputs.
Source
https://github.com/alirezarezvani/claude-skills/blob/main/engineering-team/skills/stripe-integration-expert/SKILL.md
Open Source LinkRelated Skills
a11y-audit
Use when auditing WCAG 2.2 Level A/AA accessibility, fixing violations in React, Next.js, Vue, Angular...
Engineeringadversarial-reviewer
Use when reviewing recent code changes or a PR before merge and you want a genuinely critical review, not...
Engineeringagent-designer
Use when architecting multi-agent systems, selecting orchestration patterns, or evaluating agent performance.
Engineeringagent-harness
Use when building bounded agentic loops with verified task execution and state machines.