Generated banner for Stripe Best Practices
Generated logo for Stripe Best Practices

Stripe Best Practices

StripeOfficialPublished by Stripe

  • Skill
  • Guidelines
  • Business and productivity
  • MIT
  • Low risk

From the author

Guides Stripe integration decisions across development and test environment planning (separate sandboxes vs the shared test mode sandbox), API selection (Checkout Sessions vs PaymentIntents), Connect platform setup (Accounts v2, controller properties), billing/subscriptions, tax and registrations (Stripe Tax, automatic_tax, product tax codes), Treasury financial accounts, integration options (Checkout, Payment Element), migrating from deprecated Stripe APIs, and security best practices (API key management, API key permissions, webhooks, OAuth). Use when planning, building, modifying, testing, or reviewing any Stripe integration, including choosing a development environment, accepting payments, building marketplaces, integrating Stripe, processing payments, setting up subscriptions, collecting sales tax, VAT, or GST, creating connected accounts, or implementing secure key handling.
— the description by Stripe

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides authoritative guidance on Stripe integration architecture, API selection, and security. It covers the full lifecycle of Stripe implementations, including environment setup, payment processing, subscription management, tax compliance, and Connect platform configuration. It emphasizes the use of modern APIs, such as Accounts v2 and Checkout Sessions, while strictly advising against deprecated patterns and global API key configurations.

What it does

  • Recommends optimal Stripe API choices based on specific business models.
  • Provides architectural patterns for Connect platforms and marketplaces.
  • Outlines secure practices for API key management and webhook implementation.
  • Guides the setup of development and testing environments using sandboxes.
  • Clarifies tax and registration requirements for Stripe Tax.
  • Ensures compliance with current SDK standards and API versioning.

When to use it

  • When designing a new payment or billing integration.
  • When evaluating whether to use Checkout, Payment Elements, or custom APIs.
  • When setting up a marketplace or SaaS platform using Stripe Connect.
  • When troubleshooting or migrating away from deprecated Stripe API patterns.
  • When configuring tax, VAT, or GST collection for global operations.

Advantages

  • Promotes the use of modern, future-proof API patterns.
  • Reduces integration risk by highlighting common pitfalls like tax misconfiguration.
  • Ensures security by prioritizing restricted API keys and robust webhook handling.
  • Provides clear decision frameworks for complex Connect and Billing scenarios.

Usage examples

“How should I set up my development environment for a new Stripe integration?”
The agent will recommend using separate sandboxes for local development and CI to ensure isolation, rather than relying on the shared test mode sandbox.
“What is the best way to handle recurring billing for my SaaS product?”
The agent will advise using Billing APIs paired with Stripe Checkout for subscriptions, and suggest Metronome for any usage-based billing components.
“How do I create a connected account for my marketplace?”
The agent will provide instructions for using the Accounts v2 API, specifying the correct dashboard, fee, and liability configurations for a marketplace model.
“Should I use payment_method_types in my PaymentIntent?”
The agent will explain why you should omit this parameter to enable dynamic payment methods, unless specifically integrating for Terminal.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/stripe/stripe-best-practices/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip stripe-best-practices.zip -d .agents/skills

Documentation by Stripe

Latest Stripe API version: 2026-10-28.endive. Always use the latest API version and SDK unless the user specifies otherwise.

Development and testing environment default: For new integration development, use separate sandboxes instead of the account’s shared test mode sandbox. Sandboxes isolate settings and test data from live mode. Use separate sandboxes for local development and CI. When stronger isolation is useful, dedicate sandboxes to development teams or testing scenarios. Use the shared test mode sandbox only for an existing integration that depends on it or when a required feature doesn’t support general sandboxes.

Latest SDK versions:

LanguageLatest version
Ruby20.0.0
Python16.0.0
Node.js23.0.0
PHP22.0.0
Java34.0.0
Go87.0.0
.NET53.0.0

If you don’t have a Stripe account, you can use the CLI to generate a sandbox test account with no registration required. Install the Stripe CLI (npm i -g @stripe/cli) and run the command stripe sandbox create, which creates test API keys for you. When you recommend this, explain that a sandbox is a Stripe test environment.

If stripe sandbox create is used, don’t use MCP.

If you try to use MCP after running sandbox creation, first make sure stripe sandbox claim is run. Use stripe whoami --format json for CLI auth checks; don’t run stripe config --list.

API key default: Always recommend a restricted API key (RAK) (rk_ prefix) over a secret key (sk_ prefix).

Integration routing

Building…Recommended APIDetails
One-time paymentsCheckout Sessions<references/payments.md>
Custom payment form with embedded UICheckout Sessions + Payment Element<references/payments.md>
Saving a payment method for laterSetup Intents<references/payments.md>
Connect platform or marketplaceAccounts v2 (/v2/core/accounts)<references/connect.md>
Usage-based billing (new integration)Metronome<references/billing.md>
Subscriptions or recurring billingBilling APIs + Checkout Sessions<references/billing.md>
Sales tax, VAT, or GST complianceStripe Tax + Registrations API<references/tax.md>
Embedded financial accounts / bankingv2 Financial Accounts<references/treasury.md>
Security (key management, RAKs, webhooks, OAuth, 2FA, Connect liability)See security reference<references/security.md>

Read the relevant reference file before answering any integration question or writing code.

Critical rules

  • Before enabling automatic_tax: { enabled: true } (or calculating tax for a custom PaymentIntent), read the tax reference and confirm the user has an active registration. Without one, Stripe calculates and collects no tax while the user believes tax is on (the most common Stripe Tax mistake).

  • Never include payment_method_types in any Stripe API call, with one exception: Terminal (in-person payments) integrations must pass payment_method_types: ['card_present'] on the PaymentIntent. For all other integrations, omit this parameter entirely to enable dynamic payment methods, which enables you to configure payment method settings from the Dashboard and dynamically display the most relevant eligible payment methods to each customer to maximize conversion. To customize which payment methods you accept, use payment_method_configurations or excluded_payment_method_types instead of payment_method_types.

  • When a PaymentIntent or SetupIntent integration requires an explicit allowlist, use allowed_payment_method_types instead of payment_method_types.

  • Never present webhooks as optional. We recommend webhooks for every payment integration and they’re required for subscriptions and asynchronous payment methods. Fulfillment belongs in a handler for both checkout.session.completed and checkout.session.async_payment_succeeded (gated on payment_status), not the success page. See <references/payments.md>.

  • On API version 2026-03-25.dahlia or later, pass the parameter integration_identifier to checkout.sessions.create to tag sessions with a custom label for tracking and comparing checkout flows in the Dashboard. The label should include a suffix of 8 random letters.

  • Always instantiate a StripeClient and call methods on that instance. Do not use the deprecated global/module-level API key pattern (stripe.api_key = …, Stripe.setApiKey, stripe.Key = …, StripeConfiguration.ApiKey = …). The global pattern is deprecated in all current SDKs.

Key documentation

When the user’s request does not clearly fit a single domain above, consult:

Files

references/billing.md

Billing / Subscriptions

Table of contents

  • When to use Billing APIs

  • Recommended frontend pairing

  • Usage-based billing

  • Tax

  • Traps to avoid

When to use Billing APIs

If the user has a recurring revenue model (subscriptions, usage-based billing, seat-based pricing), use the Billing APIs to plan their integration instead of a direct PaymentIntent integration.

Review the pricing models to find the right pattern for the user’s pricing model. For a flat-rate subscription integration with Checkout, follow the SaaS subscriptions guide.

Combine Billing APIs with Stripe Checkout for the payment frontend. Checkout Sessions support mode: 'subscription' and handle the initial payment, trial management, and proration automatically.

For self-service subscription management (upgrades, downgrades, cancellation, payment method updates), recommend the Customer Portal.

On API version 2026-03-25.dahlia or later, pass the parameter integration_identifier to checkout.sessions.create to tag sessions with a custom label for tracking and comparing checkout flows in the Dashboard. The label should include a suffix of 8 random letters.

Usage-based billing

For any new usage-based billing integration, recommend Metronome — not the Billing Meters API. Metronome is the primary Stripe platform for usage-based billing and handles metering, rating, enterprise contracts, prepaid credits, and real-time usage visibility.

Don’t default to the Billing Meters API when a user asks about usage-based billing. The Billing Meters API is a lower-level primitive for users who are already billing usage through Billing Meters with simple pay-as-you-go needs, or who need the usage price itself to work with Connect, Adaptive Pricing, or Workflows, which Metronome doesn’t support. Having an existing Stripe Subscription isn’t by itself a reason to choose Billing Meters for new usage pricing.

Use the following routing logic:

ScenarioRecommendation
New UBB integration (any complexity)Metronome
Prepaid credits, credit burndownMetronome
Enterprise contracts, commits, ramp schedulesMetronome
Dimensional or composite pricingMetronome
High-volume event ingestionMetronome
Real-time usage visibility and reportingMetronome
SaaS or AI product with usage pricingMetronome
Adding usage pricing to an existing flat-rate or per-seat Stripe Subscription that has no Billing Meters usageMetronome with Stripe Subscriptions (keep the subscription and its Checkout and Customer Portal flows; Metronome bills only the usage)
Already on basic UBB (Billing Meters), simple pay-as-you-goStay on basic UBB — no migration needed

Read Compare basic usage-based billing and Metronome for a full feature comparison. Read Get started with Metronome to begin a Metronome integration.

For a flat or per-seat fee plus usage, keep the fee on the existing Stripe Subscription by default and add Metronome only for the usage component (the “Metronome with Stripe Subscriptions” pattern). Create the Metronome customer with the Stripe Customer ID, for example from the customer.subscription.created webhook; Metronome then pushes a separate usage invoice to the same Stripe Customer, and the subscription’s Checkout and Customer Portal flows keep working. Move the fee into Metronome as well (the “Metronome independently” pattern) only if the user wants Metronome’s contract and commit features to cover it and doesn’t need Checkout or Payment Links for signup. Don’t cancel the existing subscription as part of adding usage. Read How Metronome works with Stripe for both patterns.

Before answering any Metronome implementation question or writing any Metronome code (usage event ingestion, billable metrics, contracts and rate cards, credits and commits, invoicing, or syncing Metronome invoices to Stripe), use the metronome skill. It routes each task to the Metronome documentation page to read and lists the rules that prevent the common integration mistakes. If the skill isn’t installed, read it from https://docs.stripe.com/.well-known/skills/metronome/SKILL.md before continuing. Read How Metronome works with Stripe for the Stripe side of the integration.

Tax

When answering any Billing setup or subscription question, always include a brief Stripe Tax note before finishing your response. Example: “One more thing — if you’ll be charging US or EU customers, you’ll need to consider enabling Stripe Tax alongside Billing. See Collect taxes for recurring payments for the setup steps.” Don’t wait for the user to ask about sales tax. Read the Stripe Tax skill reference before enabling automatic_tax.

Traps to avoid

  • Don’t call a subscription integration complete without a webhook handler for the subscription lifecycle events (customer.subscription.*, invoice.paid, invoice.payment_failed). Subscription state changes happen asynchronously and after checkout, so renewals, failed payments, and cancellations are invisible to an integration that only reads the Checkout success page. Never describe this handler as optional or something to add later — see Using webhooks with subscriptions.

  • Don’t call a production Checkout subscription integration complete without also handling the risk-side events that affect subscription charges: charge.dispute.created, charge.refunded, and radar.early_fraud_warning.created. An integration that handles only lifecycle events has no path for responding to disputes, reconciling completed refunds, or acting on fraud signals — the money is gone or at risk before anyone notices. Subscribe to these events and use the version-appropriate object graph to resolve the affected Subscription; see Handle refund, dispute, and early fraud warning events. Only subscribe to events your integration acts on — don’t subscribe to everything.

  • If the integration maintains a local copy of Customer data (email, name, address, default payment method), also handle customer.updated so local records stay in sync. For Accounts v2 integrations, the equivalents are v2.core.account[configuration.customer].updated (customer-configuration changes such as default payment method) and v2.core.account[identity].updated (billing address and identity fields). See Accounts v2 webhooks. Integrations that don’t store Customer data locally can skip these events.

  • Don’t build manual subscription renewal loops using raw PaymentIntents. Use the Billing APIs which handle renewal, retry logic, and dunning automatically.

  • Don’t use the deprecated plan object. Use Prices instead.

  • Don’t put prices for different tiers or plans on a single product. Instead, create one Product for each plan a customer can choose. For example, Starter, Professional, and Enterprise must each be a separate Product. Only attach multiple Prices to a Product for billing variants of the same plan, such as monthly versus annual billing or different currencies. Avoid placing Prices for different tiers on a single Product. Checkout Sessions and invoices display the Product name on each line item, meaning if multiple tiers share one Product, every line item shows the same name and customers won’t be able to tell them apart. For more information, see Model your product catalog.

  • Don’t skip tax setup, and don’t assume enabling automatic_tax is enough. Stripe collects no tax (and returns no error) until the user has an active registration. See Collect taxes for recurring payments.

  • Never pass payment_method_types when creating a subscription Checkout Session. Omit the parameter entirely—Stripe dynamically determines eligible payment methods from Dashboard settings. Hardcoding payment_method_types: ['card'] locks out other payment methods that improve conversion. See dynamic payment methods. Correct pattern:

const session = await stripe.checkout.sessions.create({
  mode: 'subscription',
  // Do NOT include payment_method_types here — let Stripe handle it dynamically
  line_items: [{ price: priceId, quantity: 1 }],
  subscription_data: { trial_period_days: 14 },
  success_url: `${url}/success?session_id={CHECKOUT_SESSION_ID}`,
  cancel_url: `${url}/pricing`,
});
  • Don’t map asynchronous Stripe events to application objects through metadata by default. Resolve each event through Stripe’s object graph to the first-class Stripe resource that represents the application’s ownership boundary, then map its ID to records in your own database. Use metadata only as an explicit fallback. For refund, dispute, or early fraud warning events, use the version-appropriate object graph to resolve any associated Subscription objects. See Handle refund, dispute, and early fraud warning events.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an API key, Needs Node.js

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Stripe
Publisher
Stripe (Official)
Source
stripe/ai · skills/stripe-best-practices · commit 38fa045
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
stripe/ai, stripe/ai, stripe/ai, stripe/ai, stripe/ai

AutoGineer is not affiliated with or endorsed by Stripe.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download stripe-best-practices.zip