Skill 05 · Stripe Best Practices
Subchapter 5.2
references/connect.mdMarkdown11 KBView on GitHub
POST /v2/core/accounts). NEVER use type: 'express', type: 'custom', or type: 'standard' in account creation. NEVER use stripe.accounts.create({ type: ... }). These are deprecated v1 patterns.dashboard: "none" unless the user explicitly asks for white-label with full custom UI. Default to express for marketplaces and full for SaaS. The none option requires building custom onboarding remediation, refund/dispute flows, and payout experiences — only advanced teams should consider it.notification_banner) for connected account dashboards. It keeps accounts healthy as requirements evolve.application_fee_amount with separate charges and transfers. Use transfer-math fee retention instead. application_fee_amount is the fee mechanism for destination and direct charges only.Before processing live payments or transfers, ALWAYS verify capability status using the v2 configuration path. Do NOT use deprecated v1 fields.
For SaaS / Merchant accounts (direct charges):
configuration.merchant.capabilities.card_payments.status === 'active'charges_enabled (deprecated v1 field)For Marketplace / Recipient accounts (destination or separate charges):
configuration.recipient.capabilities.stripe_balance.stripe_transfers.status === 'active'payouts_enabled or charges_enabled (deprecated v1 fields)Track capability state transitions with account webhooks and re-check capability status before payment or transfer operations.
Configure connected accounts using three independent dimensions:
| Dimension | Field | What it controls |
|---|---|---|
| Dashboard access | dashboard | Stripe-hosted dashboard for connected accounts |
| Fee collection | defaults.responsibilities.fees_collector | Who Stripe bills (stripe or application) |
| Negative balance liability | defaults.responsibilities.losses_collector | Who absorbs unresolved negative balances |
dashboard: "express" — cobranded, lightweight, low maintenancedashboard: "full" — full Stripe Dashboard for independent businessesdashboard: "none" — platform must build ALL UX including onboarding remediation, disputes, payoutsIf dashboard is express, provide access through login links (opens in a new tab). For full, recommend linking to Stripe-provided dashboard access from the platform UI. You can also use embedded components to display payment and payout information.
SaaS (direct charges):
dashboard: "full"fees_collector: "stripe" — connected account pays Stripe fees directlylosses_collector: "stripe" — Stripe owns negative balance liabilityMarketplace (destination charges):
dashboard: "express"fees_collector: "application" — platform owns pricinglosses_collector: "application" — platform owns negative balance liability (required for transfer reversals during disputes)| Business model | Dashboard | Fees | Losses | Charge pattern | Notes |
|---|---|---|---|---|---|
| Marketplace | express | application | application | Destination | Platform owns checkout |
| On-demand services | express | application | application | Destination | Fast seller onboarding |
| SaaS platform with payments | full | stripe | stripe | Direct | Sellers run own businesses/stores, own customer relationship |
| AI/API platform (SaaS) | full | stripe | stripe | Direct | Providers own payment relationship |
| E-commerce enabler (Shopify-like) | full | stripe | stripe | Direct | Sellers create own online stores, accept own payments |
| Crowdfunding | express | application | application | Separate charges and transfers | Hold-and-release / delayed payouts |
| Subscription platform | express | application | application | Destination | Platform manages recurring checkout |
| Multi-seller cart | express | application | application | Separate charges and transfers | Multiple sellers per transaction |
| White-label commerce | none | application | application | Destination or direct | Advanced: platform controls all UX |
Create with configuration.recipient requesting stripe_transfers on stripe_balance. Do NOT request configuration.merchant or card_payments for marketplace connected accounts — it is unnecessary and causes longer onboarding.
Create with configuration.merchant requesting card_payments (and other needed LPMs). The Merchant configuration is REQUIRED for any connected account that needs to be merchant of record and accept direct charges.
First determine: who owns the customer relationship?
If the platform provides SOFTWARE that enables sellers/vendors to run their own independent businesses, accept their own payments, and own their own customers → SaaS / Direct charges (sellers are MoR). Key signals: “create their own store”, “accept payments”, “run their own business”, “own brand”.
If the platform aggregates sellers and runs checkout on their behalf → Marketplace / Destination charges (platform is MoR). Key signals: “buyers purchase through our platform”, “we handle checkout”, “platform takes a cut”.
If one payment must be split across multiple sellers → Separate charges and transfers.
Direct charges (SaaS): Charge created on connected account. Connected account is merchant of record. Use application_fee_amount for platform revenue. Requires configuration.merchant + dashboard: "full" + losses_collector: "stripe".
Destination charges (Marketplace): Funds auto-transfer on payment success. Platform is MoR. Use application_fee_amount to collect platform fees. NOT for hold-and-release.
Separate charges and transfers: Platform controls transfer timing. Collect fees by transferring less than the charge amount. Do NOT use application_fee_amount.
For destination charges and direct charges: use application_fee_amount on the PaymentIntent. This is the standard fee mechanism.
application_fee_amount to preserve marginFor separate charges and transfers: collect fees by transferring less than the charge amount. NEVER use application_fee_amount.
Always use webhooks for reliable payment confirmation. Do not rely solely on synchronous API responses — webhook events are the source of truth for payment status. Verify incoming webhook signatures (opens in a new tab) and consider allowlisting Stripe’s public IP addresses (opens in a new tab).
For subscription platforms or any platform collecting recurring payments from or on behalf of connected accounts:
customer_account on SetupIntent and Subscription API calls to associate the subscription with the connected accountAlways recommend these baseline components:
account_onboarding — onboard connected accountsnotification_banner — REQUIRED: keeps accounts healthy as requirements evolveaccount_management — account settings and infoAdditional components based on needs:
paymentspayments or standalone payment_detailspayments or standalone disputes_listpayoutsbalance_report, payout_reconciliation_reportDefault to embedded onboarding (account_onboarding component or account links). Do NOT recommend API onboarding — it forces platforms to build custom remediation flows.
BLOCKED combinations (never recommend):
losses_collector: "stripe" with destination charges or separate charges and transfersapplication_fee_amount with separate charges and transferslosses_collector: "stripe" (API rejection)CAUTION:
dashboard: "full" with destination or separate charges has limited functionality; prefer dashboard: "express" for those charge patternstype: 'standard', type: 'express', type: 'custom') — use v2 dimensions insteadcharges_enabled or payouts_enabled — use v2 capability status pathsdashboard: "none" without explicit white-label requirementon_behalf_of for standard marketplace flows