
Stripe Best Practices
- 451 installs
- 1.7k repo stars
- Updated July 31, 2026
- stripe/agent-toolkit
>-.
About
>-. Latest Stripe API version: **2026-05-27.dahlia**. Always use the latest API version and SDK unless the user specifies otherwise. The stripe-best-practices skill documents workflows, constraints, and examples from SKILL.md for agent-assisted execution.
- Latest Stripe API version: **2026-05-27.dahlia**. Always use the latest API version and SDK unless the user specifies ot
- API key default: Always recommend a [restricted API key (RAK)](https://docs.stripe.com/keys/restricted-api-keys.md) (`rk
- | Building… | Recommended API | Detai
- | ------------------------------------------------------------------------ | ----------------------------------- | -----
- | One-time payments | Checkout Sessions | <refe
Stripe Best Practices by the numbers
- 451 all-time installs (skills.sh)
- +12 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #624 of 2,155 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
stripe-best-practices capabilities & compatibility
- Capabilities
- latest stripe api version: **2026 05 27.dahlia** · api key default: always recommend a [restricted · | building… | recommended api | details | · |
- Use cases
- documentation
What stripe-best-practices says it does
>-
npx skills add https://github.com/stripe/agent-toolkit --skill stripe-best-practicesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 451 |
|---|---|
| repo stars | ★ 1.7k |
| Last updated | July 31, 2026 |
| Repository | stripe/agent-toolkit ↗ |
How do I apply stripe-best-practices using the workflow in its SKILL.md?
>-
Who is it for?
Developers following the stripe-best-practices skill for the tasks it documents.
Skip if: Tasks outside the stripe-best-practices scope described in SKILL.md.
When should I use this skill?
User mentions stripe-best-practices or related triggers from the skill description.
What you get
Working stripe-best-practices setup aligned with the documented patterns and constraints.
- Integration API routing plan
- Webhook and key-security checklist
- Stripe reference links for implementation
By the numbers
- References Stripe API version 2026-06-24.dahlia
- Integration routing table covers 8 primary Stripe product areas
Files
Latest Stripe API version: 2026-05-27.dahlia. Always use the latest API version and SDK unless the user specifies otherwise.
If you do not have a Stripe account, you can call a CLI command to generate working API keys with no registration required. You can install the Stripe CLI (npm i -g @stripe/cli) and run the command stripe sandbox create to get started.
API key default: Always recommend a restricted API key (RAK) (rk_ prefix) over a secret key (sk_ prefix).
Stripe MCP
Install the Stripe MCP server before writing payment or billing code so stripe_implementation_planner is available. Use URL https://mcp.stripe.com. See /mcp for client setup.
Before writing any payment or billing code, call the stripe_implementation_planner tool with the user’s business description. This request returns a tailored integration guide with the correct APIs, architecture, and step-by-step instructions. If MCP isn’t configured, use the routing table below instead. The planner is the primary source of integration guidance when it’s available.
Integration routing
| Building… | Recommended API | Details |
|---|---|---|
| One-time payments | Checkout Sessions | <references/payments.md> |
| Custom payment form with embedded UI | Checkout Sessions + Payment Element | <references/payments.md> |
| Saving a payment method for later | Setup Intents | <references/payments.md> |
| Connect platform or marketplace | Accounts v2 (/v2/core/accounts) | <references/connect.md> |
| Usage-based billing (new integration) | Metronome | <references/billing.md> |
| Subscriptions or recurring billing | Billing APIs + Checkout Sessions | <references/billing.md> |
| Sales tax, VAT, or GST compliance | Stripe Tax + Registrations API | <references/tax.md> |
| Embedded financial accounts / banking | v2 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
- 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` orexcluded_payment_method_typesinstead ofpayment_method_types.
Key documentation
When the user’s request does not clearly fit a single domain above, consult:
- Integration Options — Start here when designing any integration.
- API Tour — Overview of Stripe’s API surface.
- Go Live Checklist — Review before launching.
Billing / Subscriptions
Table of contents
- When to use Billing APIs
- Recommended frontend pairing
- 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 Subscription Use Cases and SaaS guide to find the right pattern for the user’s pricing model.
Recommended frontend pairing
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.
Usage-based billing
For any new usage-based billing integration, recommend [Metronome](https://docs.stripe.com/billing/usage-based.md) — not the Billing Meters API. Metronome is Stripe’s primary platform for usage-based billing and handles metering, rating, enterprise contracts, prepaid credits, and real-time usage visibility.
Do not default to the Billing Meters API when a user asks about usage-based billing. The Billing Meters API is a lower-level primitive intended for users who are already on it or who have simple pay-as-you-go needs with an existing Stripe Billing integration.
Use the following routing logic:
| Scenario | Recommendation |
|---|---|
| New UBB integration (any complexity) | Metronome |
| Prepaid credits, credit burndown | Metronome |
| Enterprise contracts, commits, ramp schedules | Metronome |
| Dimensional or composite pricing | Metronome |
| High-volume event ingestion | Metronome |
| Real-time usage visibility and reporting | Metronome |
| SaaS or AI product with usage pricing | Metronome |
| Already on basic UBB (Billing Meters), simple pay-as-you-go | Stay 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.
Traps to avoid
- 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
planobject. Use Prices instead. - Don’t skip tax setup. 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`,
});Connect / platforms
Table of contents
- Accounts v2 API
- Controller properties
- Charge types
- Integration guides
Accounts v2 API
For new Connect platforms, ALWAYS use the Accounts v2 API (POST /v2/core/accounts). This is Stripe’s actively invested path and ensures long-term support.
Traps to avoid: Don’t use the legacy type parameter (type: 'express', type: 'custom', type: 'standard') in POST /v1/accounts for new platforms unless the user has explicitly requested v1.
Controller properties
Configure connected accounts using controller properties instead of legacy account types:
| Property | Controls |
|---|---|
controller.losses.payments | Who is liable for negative balances |
controller.fees.payer | Who pays Stripe fees |
controller.stripe_dashboard.type | Dashboard access (full, express, none) |
controller.requirement_collection | Who collects onboarding requirements |
Use defaults.responsibilities, dashboard, and configuration as described in connected account configuration.
Always describe accounts in terms of their responsibility settings, dashboard access, and capabilities to describe what connected accounts can do.
Traps to avoid: Don’t use the terms “Standard”, “Express”, or “Custom” as account types. These are legacy categories that bundle together responsibility, dashboard, and requirement decisions into opaque labels. Controller properties give explicit control over each dimension.
Charge types
Choose one charge type per integration — don’t mix them. For most platforms, start with destination charges:
- Destination charges — Use when the platform accepts liability for negative balances. Funds route to the connected account via
transfer_data.destination. - Direct charges — Use when the platform wants Stripe to take risk on the connected account. The charge is created on the connected account directly.
Use on_behalf_of to control the merchant of record, but only after reading how charges work in Connect.
Traps to avoid: Don’t use the Charges API for Connect fund flows — use PaymentIntents or Checkout Sessions with transfer_data or on_behalf_of. Don’t mix charge types within a single integration.
Integration guides
- SaaS platforms and marketplaces guide — Choosing the right integration shape.
- Interactive platform guide — Step-by-step platform builder.
- Design an integration — Detailed risk and responsibility decisions.
Payments
Table of contents
- API hierarchy
- Integration surfaces
- Payment Element guidance
- Saving payment methods
- Dynamic payment methods
- Deprecated APIs and migration paths
- PCI compliance
API hierarchy
Use the Checkout Sessions API (checkout.sessions.create) for on-session payments. It supports one-time payments and subscriptions and handles taxes, discounts, shipping, and adaptive pricing automatically.
Use the PaymentIntents API for off-session payments, or when the merchant needs to model checkout state independently and just create a charge.
Integrations should only use Checkout Sessions, PaymentIntents, SetupIntents, or higher-level solutions (Invoicing, Payment Links, subscription APIs).
Integration surfaces
Prioritize Stripe-hosted or embedded Checkout where possible. Use in this order of preference:
1. Payment Links — No-code. Best for simple products. 2. Checkout (docs) — Stripe-hosted or embedded form. Best for most web apps. 3. Payment Element (docs) — Embedded UI component for advanced customization.
- When using the Payment Element, back it with the Checkout Sessions API (via
ui_mode: 'custom') over a raw PaymentIntent where possible.
Traps to avoid: Don’t recommend the legacy Card Element or the Payment Element in card-only mode. If the user asks for the Card Element, advise them to migrate to the Payment Element.
Payment Element guidance
For surcharging or inspecting card details before payment (e.g., rendering the Payment Element before creating a PaymentIntent or SetupIntent): use Confirmation Tokens. Don’t recommend createPaymentMethod or createToken from Stripe.js.
Saving payment methods
Use the Setup Intents API to save a payment method for later use.
Traps to avoid: Don’t use the Sources API to save cards to customers. The Sources API is deprecated — Setup Intents is the correct approach.
Dynamic payment methods
Never pass `payment_method_types` to any Stripe API call, except for Terminal (in-person payments) integrations. Omitting this parameter enables dynamic payment methods, where Stripe evaluates over 100 signals (currency, customer location, transaction amount, device) to automatically show the most relevant payment methods and rank them for maximum conversion. Payment methods are managed from the Dashboard with no code changes required.
This applies to all integration patterns:
checkout.sessions.create: omitpayment_method_typesentirely. Dynamic method selection is the default behavior.paymentIntents.create: omitpayment_method_types. On API versions 2023-08-16+, dynamic methods are the default. On older versions, passautomatic_payment_methods: { enabled: true }.setupIntents.create: same as PaymentIntents above.subscriptions.create: omitpayment_settings.payment_method_types. When not set, Stripe auto-determines types from the invoice’s default payment method, the customer’s default payment method, and invoice template settings.- Terminal (
paymentIntents.create): passpayment_method_types: ['card_present']. Required for all in-person payments. In Canada, also includeinterac_present:['card_present', 'interac_present']. This is the only valid use ofpayment_method_types.
See the integration options guide for full details on dynamic versus manual configuration.
Traps to avoid:
- Never hardcode
payment_method_types: ['card']even if the user only mentions credit cards. Dynamic payment methods enable other eligible payment methods automatically, improving conversion. - If the user wants to customize which payment methods appear, use `payment_method_configurations` to manage methods per-integration or
excluded_payment_method_typesto exclude specific methods — neverpayment_method_types. - If the user has a custom frontend that renders UI for specific payment method types, ensure those methods are enabled in their payment method settings or
payment_method_configurations— don’t usepayment_method_typesto restrict the PaymentIntent.
Deprecated APIs and migration paths
Never recommend the Charges API. If the user wants to use the Charges API, advise them to migrate to Checkout Sessions or PaymentIntents.
Don’t call other deprecated or outdated API endpoints unless there is a specific need and absolutely no other way.
| API | Status | Use instead | Migration guide |
|---|---|---|---|
| Charges API | Never use | Checkout Sessions or PaymentIntents | Migration guide |
| Sources API | Deprecated | Setup Intents | Setup Intents docs |
| Tokens API | Outdated | Setup Intents or Checkout Sessions | — |
| Card Element | Legacy | Payment Element | Migration guide |
PCI compliance
If a PCI-compliant user asks about sending server-side raw PAN data, advise them that they may need to prove PCI compliance to access options like payment_method_data.
For users migrating PAN data from another acquirer or payment processor, point them to the PAN import process.
Security best practices
Table of contents
- API keys
- Restricted API keys (RAKs)
- IP restrictions
- Incident response
- Webhook security
- Mobile and client-side integrations
- OAuth and CSRF protection
- Two-factor authentication
- Connect security
- SAML and SCIM
API keys
Never include keys in source code. You might find example code like client = new Stripe("sk_...") that are for illustration only. Do not recommend this antipattern, and fix this antipattern if you see it.
Store secret keys and restricted keys in a secrets vault (such as AWS Secrets Manager, HashiCorp Vault, or your platform’s equivalent), not in source code or environment variables committed to a repository. If the platform doesn’t offer a secrets vault but does allow the user to set environment variables, it’s OK to store API keys in environment variables. Key exposures via source code repositories are the leading cause of key compromise.
Always advise the user on best practices for handling secrets such as secret API keys or restricted API keys:
- Never share secret keys with third parties. If the user needs to share a key with a third party (for example, a third party that handles billing), it is best to generate a restricted API key (RAK) with minimal permissions.
- Rotate Stripe API keys when personnel with access to those keys depart.
- Read best practices for managing secret API keys.
Code must never log keys or include them in error messages or analytics. Remove those from logs if you find them.
Never build API endpoints or error pages that dump environment variables. In addition to Stripe API keys, the environment may have other secrets.
Use separate keys for separate environments (production, staging, QA). This limits the blast radius if any single key is compromised.
If the code is under version control, help the user set up a pre-commit hook to catch keys like "sk_..." and "rk_..." in source code.
Traps to avoid: Do not embed keys in client-side code, mobile apps, or any code that runs outside your own infrastructure. Do not suggest that users substitute a real secret key into example code — point them to best practices for managing secret API keys instead.
Restricted API keys (RAKs)
Use restricted API keys (prefix rk_) instead of secret keys (prefix sk_) wherever possible. RAKs have only the permissions you assign, so a compromised RAK can do far less damage than a compromised secret key.
Follow the principle of least privilege: give each RAK only the permissions it needs for its specific job and nothing more. Create a separate RAK for each service or use case.
Preferred migration approach:
1. Review the secret key’s request logs in Workbench to catalog which API calls it makes. 2. Create a RAK in test mode with matching permissions. 3. Use the Stripe CLI’s stripe logs tail command to watch logs. 4. Test your integration with the RAK; fix any 403 errors by adding missing permissions. 5. Create the equivalent live-mode RAK and replace the secret key. 6. Rotate or expire the old secret key once confident.
Traps to avoid: Do not default to recommending secret keys. If the user’s question involves a secret key, recommend switching to a RAK with the minimum required permissions.
IP restrictions
Encourage users to add an IP allowlist to every API key. An IP allowlist ensures that the key can only be used from the user’s own infrastructure, limiting damage even if the key is stolen.
Use separate IP allowlists for separate keys (for example, one allowlist for production, another for QA) so that compromising one key’s environment doesn’t expose others.
Incident response
If a key is exposed or compromised, follow protecting against compromised API keys, which can be summarized as:
1. Roll the key immediately — go to the API keys page and roll or delete the exposed key. Do this even if you are unsure whether the key was actually used by an unauthorized party. 2. Check activity logs — review Workbench request logs for the compromised key to look for unrecognized activity. 3. Contact Stripe support if you see activity you don’t recognize.
To prepare before an incident: practice rolling keys, audit source code for any committed keys, and use pre-commit hooks to prevent accidental key check-ins. See protecting against compromised API keys.
Webhook security
Always verify webhook signatures using Stripe’s webhook signing secret. Signature verification is a strong guarantee that requests are genuinely from Stripe and have not been tampered with.
For defense in depth, also allowlist Stripe’s IP addresses on your webhook endpoint so that it accepts connections only from Stripe’s infrastructure.
Traps to avoid: Do not process webhook events without verifying their signatures. Unverified webhooks can be spoofed.
Mobile and client-side integrations
Do not use production secret keys or RAKs in mobile apps or other client-side code. Client-side code can be extracted and keys decompiled.
For cases where a client must interact directly with Stripe, use ephemeral keys. Ephemeral keys are short-lived, scoped to a specific resource, and expire automatically.
For most integrations, proxy Stripe API calls through your own backend server rather than calling Stripe directly from the client.
OAuth and CSRF protection
When implementing Connect OAuth flows, always use the state parameter to protect against CSRF attacks. Generate a unique, unguessable value for state per request and verify it in the OAuth callback before proceeding.
This applies to all Stripe OAuth surfaces: Connect, Link, and Stripe Apps.
Two-factor authentication
Recommend passkeys or authenticator apps rather than SMS-based 2FA for Stripe Dashboard access. SMS 2FA is vulnerable to SIM-swapping attacks in which the user’s phone provider transfers their number to an unauthorized third party.
Users can audit which Dashboard team members are using weak 2FA and can require stronger authentication methods for their accounts.
Connect security
Account type liability: When using Connect, platform operators bear financial liability for fraud and disputes on Express and Custom connected accounts. Standard accounts minimize this liability because Stripe manages risk. Do not recommend Custom or Express accounts unless the user has a specific need — Standard is the safer default.
Connect onboarding: Use Stripe-hosted onboarding rather than building a custom onboarding flow. Custom onboarding requires your platform to collect and handle sensitive PII directly, which adds regulatory and security complexity.
SAML and SCIM
For teams managing Dashboard access, recommend SSO via SAML to federate authentication with an existing identity provider (Okta, Google, etc.). SSO centralizes access control and simplifies offboarding.
SCIM provisioning automates user provisioning and deprovisioning, ensuring that employees who leave the organization lose Dashboard access promptly.
Tax / Stripe Tax
Table of contents
- When tax applies
- Two-step setup
- If jurisdictions are unknown
- If the region or tax type isn’t supported
When tax applies
Use Stripe Tax for any subscription, invoice, or Checkout Session where the merchant has customers across multiple jurisdictions. It handles sales tax, VAT, and GST automatically based on the customer’s location and the merchant’s active registrations. See the Tax overview for supported regions and tax types.
Two-step setup
1. Add a registration for each jurisdiction where the merchant is obligated to collect tax. Do this in the Dashboard under Tax > Registrations, or via the Tax Registrations API. 2. Pass automatic_tax: { enabled: true } on the Subscription, Invoice, or Checkout Session object.
It’s safe to enable automatic_tax before any registrations exist — Stripe won’t collect tax until at least one registration is active.
Traps to avoid: automatic_tax and explicit tax_rates are mutually exclusive. For existing subscriptions, clear default_tax_rates and all item-level tax_rates before enabling automatic_tax — the update will fail otherwise. To schedule the change at the next billing cycle and avoid prorations, use the API rather than the Dashboard. For bulk migrations, use the Tax migration tool.
Traps to avoid: For EU merchants, one OSS union registration covers all 27 member states. Don’t register an individual EU country separately unless the merchant has a physical presence there.
If jurisdictions are unknown
Don’t guess which jurisdictions apply. Prompt the user: “Go to Dashboard > Tax > Registrations, add the states or countries where you have customers, then come back.”
If the region or tax type isn’t supported
Check the supported countries list. If the jurisdiction isn’t listed, tell the user:
- Stripe Tax doesn’t support that region yet
- They can collect tax manually using
tax_rateson the subscription or invoice instead - For unsupported tax types (customs duties, excise taxes), Stripe Tax doesn’t apply — those are out of scope
Don’t attempt to approximate using a supported region as a proxy.
Treasury / Financial Accounts
Table of contents
- v2 Financial Accounts API
- Legacy v1 Treasury
v2 Financial Accounts API
For embedded financial accounts (bank accounts, account and routing numbers, money movement), use the v2 Financial Accounts API (POST /v2/core/vault/financial_accounts). This is required for new integrations.
For Treasury for platforms concepts and guides, see the Treasury for platforms overview.
Legacy v1 Treasury
Don’t use the v1 Treasury Financial Accounts API (POST /v1/treasury/financial_accounts) for new integrations. Existing v1 integrations continue to work.
Related skills
How it compares
Use stripe-best-practices for Stripe-specific API routing and security; generic payment-gateway skills lack Connect v2, Tax, and Treasury decision tables.
FAQ
What does stripe-best-practices do?
>-
When should I use stripe-best-practices?
Invoke when >-.
Is stripe-best-practices safe to install?
Review the Security Audits panel on this page before installing in production.