From 29f2b79419f1640035aec74fe367affe1cb3259a Mon Sep 17 00:00:00 2001 From: John O'Sullivan <85578318+johno-stripe@users.noreply.github.com> Date: Mon, 17 Aug 2026 17:15:09 -0400 Subject: [PATCH] Add Agent Plugins 1.0 provider package (#487) * bootstrapping agent-plugins provider * nesting into a folder in case we start hosting more than one plugin in the future * Align Agent Plugin paths with nested layout Document the multi-plugin provider convention and keep sync automation pointed at the relocated portable package. Co-authored-by: Cursor Committed-By-Agent: cursor * changing installation instructions --------- Co-authored-by: Cursor --- .github/workflows/guard-skills.yml | 1 + .github/workflows/sync-skills.yml | 3 +- README.md | 7 + providers/README.md | 5 +- providers/agent-plugins/plugin/mcp.json | 9 + providers/agent-plugins/plugin/plugin.json | 20 + .../plugin/skills/connect-recommend/SKILL.md | 357 +++++++++++++++++ .../references/account-types.md | 226 +++++++++++ .../references/charge-patterns.md | 315 +++++++++++++++ .../references/company-researcher.md | 106 +++++ .../references/compatibility-matrix.md | 199 ++++++++++ .../references/decision-matrix.md | 372 ++++++++++++++++++ .../references/discovery-questions.md | 346 ++++++++++++++++ .../references/recommendation-template.md | 221 +++++++++++ .../references/terminology-rules.md | 91 +++++ .../plugin/skills/stripe-apps/SKILL.md | 207 ++++++++++ .../stripe-apps/references/authentication.md | 101 +++++ .../skills/stripe-apps/references/backend.md | 113 ++++++ .../stripe-apps/references/canonical-docs.md | 48 +++ .../stripe-apps/references/discovery.md | 164 ++++++++ .../stripe-apps/references/extension-types.md | 124 ++++++ .../stripe-apps/references/onboarding-ux.md | 67 ++++ .../stripe-apps/references/publishing.md | 145 +++++++ .../stripe-apps/references/ui-extensions.md | 120 ++++++ .../skills/stripe-apps/references/webhooks.md | 92 +++++ .../skills/stripe-apps/references/workflow.md | 191 +++++++++ .../skills/stripe-best-practices/SKILL.md | 71 ++++ .../references/billing.md | 67 ++++ .../references/connect.md | 173 ++++++++ .../references/payments.md | 81 ++++ .../references/security.md | 119 ++++++ .../stripe-best-practices/references/tax.md | 142 +++++++ .../references/treasury.md | 16 + .../plugin/skills/stripe-directory/SKILL.md | 76 ++++ .../plugin/skills/stripe-docs/SKILL.md | 42 ++ .../plugin/skills/stripe-projects/SKILL.md | 169 ++++++++ .../plugin/skills/upgrade-stripe/SKILL.md | 185 +++++++++ scripts/sync.js | 2 + 38 files changed, 4791 insertions(+), 2 deletions(-) create mode 100644 providers/agent-plugins/plugin/mcp.json create mode 100644 providers/agent-plugins/plugin/plugin.json create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/SKILL.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/account-types.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/charge-patterns.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/company-researcher.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/compatibility-matrix.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/decision-matrix.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/discovery-questions.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/recommendation-template.md create mode 100644 providers/agent-plugins/plugin/skills/connect-recommend/references/terminology-rules.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/SKILL.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/authentication.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/backend.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/canonical-docs.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/discovery.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/extension-types.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/onboarding-ux.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/publishing.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/ui-extensions.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/webhooks.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-apps/references/workflow.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/SKILL.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/references/billing.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/references/connect.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/references/payments.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/references/security.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/references/tax.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-best-practices/references/treasury.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-directory/SKILL.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-docs/SKILL.md create mode 100644 providers/agent-plugins/plugin/skills/stripe-projects/SKILL.md create mode 100644 providers/agent-plugins/plugin/skills/upgrade-stripe/SKILL.md diff --git a/.github/workflows/guard-skills.yml b/.github/workflows/guard-skills.yml index a9e9f41..a3fdfd7 100644 --- a/.github/workflows/guard-skills.yml +++ b/.github/workflows/guard-skills.yml @@ -9,6 +9,7 @@ on: - 'providers/codex/plugin/skills/**' - 'providers/cursor/plugin/skills/**' - 'providers/grok/plugin/skills/**' + - 'providers/agent-plugins/plugin/skills/**' jobs: block: diff --git a/.github/workflows/sync-skills.yml b/.github/workflows/sync-skills.yml index 0d7171d..6a60c61 100644 --- a/.github/workflows/sync-skills.yml +++ b/.github/workflows/sync-skills.yml @@ -53,7 +53,8 @@ jobs: providers/claude/plugin/.claude-plugin/plugin.json \ providers/codex/plugin/.codex-plugin/plugin.json \ providers/cursor/plugin/.cursor-plugin/plugin.json \ - providers/grok/plugin/.grok-plugin/plugin.json + providers/grok/plugin/.grok-plugin/plugin.json \ + providers/agent-plugins/plugin/plugin.json # Skip if nothing changed if git diff --staged --quiet; then diff --git a/README.md b/README.md index 44d6b7c..552aabd 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,13 @@ Run this command in your project: grok plugin install stripe --trust ``` +### Agent Plugins + +Installation methods currently vary by client for the new [Agent Plugins](https://agent-plugins.org/) standard, but you can point your client to our package via: +- Git URL: `https://github.com/stripe/ai` +- Subdirectory: [`providers/agent-plugins/plugin/`](/providers/agent-plugins/plugin). + + ## Manual installation > Manually installed skills don’t auto-update. Run `npx skills update -y` to get the latest versions. diff --git a/providers/README.md b/providers/README.md index a4e8f73..6942582 100644 --- a/providers/README.md +++ b/providers/README.md @@ -2,6 +2,10 @@ This directory contains plugins for different AI code editors. +## Layout + +Each provider can contain multiple plugins. Keep each plugin in its own subdirectory under `providers//` so its manifest, skills, and other assets remain isolated as more plugins are added. + ## Skills **Do not edit skill files in provider directories manually.** @@ -10,4 +14,3 @@ Skills in `providers/*/plugin/skills/` are automatically synced from [docs.strip To manually trigger a sync, go to the [workflow page](https://github.com/stripe/agent-toolkit/actions/workflows/sync-skills.yml) and click "Run workflow". - diff --git a/providers/agent-plugins/plugin/mcp.json b/providers/agent-plugins/plugin/mcp.json new file mode 100644 index 0000000..c202f0d --- /dev/null +++ b/providers/agent-plugins/plugin/mcp.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", + "mcpServers": { + "stripe": { + "type": "streamable-http", + "url": "https://mcp.stripe.com" + } + } +} diff --git a/providers/agent-plugins/plugin/plugin.json b/providers/agent-plugins/plugin/plugin.json new file mode 100644 index 0000000..69795ef --- /dev/null +++ b/providers/agent-plugins/plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "stripe", + "version": "0.1.0", + "description": "Stripe agent plugin with skills for Stripe integrations including best practices, API and SDK upgrade guidance, and a remote MCP server configuration.", + "author": { + "name": "Stripe", + "url": "https://stripe.com" + }, + "homepage": "https://docs.stripe.com", + "repository": "https://github.com/stripe/ai", + "license": "MIT", + "keywords": [ + "stripe", + "payments", + "webhooks", + "api", + "security" + ] +} diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/SKILL.md b/providers/agent-plugins/plugin/skills/connect-recommend/SKILL.md new file mode 100644 index 0000000..66a4ae7 --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/SKILL.md @@ -0,0 +1,357 @@ +--- +name: connect-recommend +description: >- + Use this skill when the user asks about Stripe Connect configuration, charge + patterns, Dashboard access, or how to get started with Connect, is building a + marketplace, platform, multi-vendor store, gig platform, or subscription + platform, needs to pay out sellers, vendors, or providers, mentions split + payments, revenue sharing, multi-party payments, or similar payment + distribution concepts, provides a company URL or business description for a + recommendation, builds SaaS that routes money between parties (for example, + POS, booking, invoicing — not operational SaaS without payment routing), asks + about onboarding or KYC for merchants, sellers, and vendors, mentions + connected account Dashboard or responsibility configurations, or asks about + payment flows, white-label payments, or embedded payments. + +--- + +## Connect recommend + +Recommend the right Stripe Connect integration configuration. The user only needs to provide a company URL or describe their business — the skill figures out the rest. + +### Interaction model + +**User must confirm interactions**. Every decision point in this skill MUST be confirmed with the user with clear, numbered options and short descriptions. One question at a time — never overwhelm the user. + +**Auto-act on low-cost actions**. Never ask permission for: + +- Generating the markdown recommendation plan — just generate it +- Scanning the codebase — just scan it +- Reading reference files — just read them + +**Never end with passive text**. Every stopping point must end with a prompt to the user offering concrete next actions. + +### Terminology rules (user-facing output) + +**Before generating any user-facing output, read **. Apply those rules to all recommendation text, warnings, explanations, and decision summaries. + +Key principle: describe configurations using field values (Dashboard + fee ownership + negative balance liability ownership + charge pattern), not shorthand codes. + +### Output Brevity + +Keep responses concise. The user is making decisions, not reading documentation. + +- Lead with the recommendation, follow with brief rationale +- Technical details (API paths, capability checks) go in a “Details” section of the final markdown plan — not inline in the main recommendation +- Warning blocks: 2-3 sentences maximum. State the issue and the fix. No mechanism deep-dives unless the user asks. +- Decision summary: bullet points only, one line per decision +- Never output more than ~40 lines in a single response during interactive mode + +**Only mention out-of-scope limitations when they’re directly relevant to what the user asked about**. Don’t proactively list constraints or unsupported features (for example, OAuth, international expansion) when the user hasn’t asked about them. “Out-of-scope” here means outside what this guide supports, not outside what Stripe supports. Research these topics in the Stripe public documentation (docs.stripe.com) rather than saying they’re out-of-scope. + +### Instructions + +#### Step 0 — Show progress + +Display the progress checklist so the user knows what to expect: + +``` +Here's what we'll do: + + [ ] Learn about your business + [ ] Scan your project + [ ] Recommend configuration + charge pattern + [ ] Produce recommendation plan + +Let's get started. +``` + +#### Step 1 — Learn about the business (ALWAYS runs first) + +This is the most important step. Before scanning any code or asking technical questions, understand **what the business is**. + +**1a. Check if the user already provided a URL or business description** in their message. Look for: + +- A URL (for example, `https://...`, `www.`, `.com`, `.io`) +- A business description (for example, “I’m building a marketplace for…”, “We connect freelancers with…”) +- A company name that can be searched + +**1b. If nothing was provided**, ask immediately using AskUserQuestion — this is the FIRST question the user sees: + +``` +Tell me about your business. Pick whichever is easiest: +``` + +Options: + +- “I have a URL” — user provides URL, then research it +- “Let me describe it” — user provides description, then research it +- “Just scan my codebase” — skip to Step 2, rely on codebase signals only +- “Skip — ask me questions instead” — skip to Step 3 with full questionnaire + +**1c. Research the business** — read and follow the company-researcher instructions: + +Read and perform those research steps, using the company URL (if provided) and business description (if provided) as inputs. + +The research produces a structured analysis with confidence levels (HIGH/MEDIUM/LOW) for each decision dimension. + +**1d. Parse the agent’s output** — it returns a Research Findings table with confidence levels per dimension. Read the decision matrix at and map the findings to a recommended configuration. Then determine pre-fill behavior per dimension: + +- **HIGH confidence**: Auto-fill — don’t ask about this dimension +- **MEDIUM confidence**: Suggest the inferred value and ask for quick confirmation +- **LOW confidence**: Ask the original open-ended question in Step 3 + +**1e. Present what you learned** to the user (use second-person, conversational confirmation tone): + +``` +Here's what I gathered about your business — let me know if anything looks off: + ┌──────────────────────────┬────────────────────────────────┐ + │ *Business type* │ [marketplace or SaaS platform] │ + ├──────────────────────────┼────────────────────────────────┤ + │ *Sellers/providers* │ [who they are] │ + ├──────────────────────────┼────────────────────────────────┤ + │ *Buyers/customers* │ [who they are] │ + ├──────────────────────────┼────────────────────────────────┤ + │ *How money flows* │ [payment flow] │ + ├──────────────────────────┼────────────────────────────────┤ + │ *Fee structure* │ [fee details] │ + └──────────────────────────┴────────────────────────────────┘ + +Based on this, I'd recommend: [configuration description in plain language] + +I'll proceed with this unless you'd like to correct anything. +``` + +For MEDIUM confidence items, append: “I’m also assuming [X] — sound right?” + +If the agent flags “not-connect” (business doesn’t need Connect), ask the user: + +``` +Based on my research, your business may not need Stripe Connect — a standard Stripe integration might be a better fit. +``` + +Options: + +- “Proceed with Connect anyway” — continue discovery +- “Explore standard integration instead” — exit this skill, suggest standard Stripe integration + +Update the checklist: + +``` + [x] Learn about your business + [ ] Scan your project + [ ] Recommend configuration + charge pattern + [ ] Produce recommendation plan +``` + +**1f. Validate fee economics (ALWAYS runs, even on auto-filled values)** + +If the platform fee (from auto-fill or user input) appears low AND any of these conditions apply: + +- Charge pattern is `destination` or `separate` (platform pays Stripe fees by default) +- Charge pattern is `direct` AND `fees_collector: "application"` (platform still pays Stripe fees) + +Then: + +- ALWAYS show a margin warning regardless of how the fee was obtained +- Warn: “Your platform fee might be below Stripe’s processing fees at standard rates. Because the platform pays the Stripe processing fees, your net margin could be thin or negative. Check [stripe.com/pricing](https://stripe.com/pricing) for your region’s rates.” +- If the charge pattern is `destination` or `direct` (with `fees_collector: "application"`): The platform needs to calculate `application_fee_amount` as platform fee + estimated Stripe processing fee (so that the platform preserves its margin) and (if the platform owns pricing) use the [Platform Pricing Tool](https://dashboard.stripe.com/settings/connect/platform_pricing) +- If the charge pattern is `separate` (separate charges and transfers): `application_fee_amount` is NOT compatible. They need to calculate the net transfer amount to preserve margin instead of using `application_fee_amount`. +- Recommend monitoring the [margin report](https://docs.stripe.com/connect/margin-reports.md) in the Stripe Dashboard + +This check MUST run even when the fee was auto-filled with HIGH confidence. The user needs to understand the fee economics before proceeding. + +#### Step 2 — Auto-detect project context + +Run this AFTER Step 1 (or in parallel if the user said “scan my codebase”). Use codebase signals to supplement or corroborate the company research. **Don’t ask before scanning — just scan.** + +1. **Existing Connect config**: Check for `connect-recommend-plan.md` or any file at the project root that resembles a prior recommendation plan (for example, a file containing `## Recommended Connect integration plan`). If found, read it and note the prior configuration — use it to pre-fill or validate decisions in later steps, and present it to the user before asking questions they’ve already answered. +2. **Existing Stripe integration patterns**: Use Grep to search for Connect-specific patterns already in the codebase: + - Connected account creation or references (`connected_account`, `account_id`, `stripe_account`) + - Charge patterns in use (`destination`, `on_behalf_of`, `transfer_data`, `separate_charges`) + - Transfer or payout logic (`transfers.create`, `payouts.create`) + - Webhook handlers for Connect events (`account.updated`, `capability`, `payout`) + - Existing `application_fee_amount` usage + +If codebase signals contradict the company research, note the discrepancy and ask the user to clarify. + +Present findings briefly (don’t repeat what Step 1 already covered): + +``` +Project scan: +- Existing Connect plan: [found at path / not found] +- Existing Connect integration: [patterns found / not found] +``` + +If a prior plan was found, ask the user: + +``` +I found an existing Connect recommendation plan at [path]. +``` + +Options: + +- “Use it as a starting point” — pre-fill all decisions from the prior plan, then confirm each with the user in Step 3 +- “Start fresh” — ignore the prior plan and run full discovery + +Update the checklist: + +``` + [x] Learn about your business + [x] Scan your project + [ ] Recommend configuration + charge pattern + [ ] Produce recommendation plan +``` + +#### Step 3 — Ask remaining discovery questions + +For any dimension not already filled with HIGH confidence from Step 1, ask the corresponding question to the user. Skip dimensions that were auto-filled or explicitly confirmed. + +**Read ** for complete question scripts, option mappings, and edge-case logic for Step 3, Step 3b (hybrid flows), Step 3c (sales-led/scope detection), and the fee-structure checkpoint. + +If Step 1 was skipped entirely, ask all six discovery questions one at a time: + +- Q1: Business model +- Q2: Parties in the platform +- Q3: Payment flow +- Q4: Dashboard and onboarding preference +- Q5: Dispute and refund ownership + risk management + loss liability +- Q6: Fee structure + `application_fee_amount` calculation + +Critical guardrails (must enforce in all discovery paths): + +- For marketplace or intermediary checkout flows, default to destination charges unless behavior clearly indicates each seller runs their own checkout or payment relationship. +- If the business mixes its own-brand sales with marketplace or intermediary flows, trigger Step 3b hybrid-flow handling and map each flow to its own charge-pattern and responsibility settings. +- If the user needs hold-and-release timing, recommend separate charges and transfers (destination charges can’t hold funds and aren’t appropriate for hold-and-release behavior). +- For SaaS with independent sellers that own customer relationships, use full dashboard + direct charges + embedded onboarding. +- If the user asks “what account type should I use?”, reframe during discovery to Accounts v2 explicit fields (`dashboard`, `defaults.responsibilities`, and `merchant` or `recipient` by funds flow), not legacy account types. Read for the full v2 configuration reference. +- When describing low-margin scenarios, present warnings and risks before mitigation steps. +- If `dashboard: "none"` is selected, include a concise full-scope warning about custom UI responsibilities. +- For destination or separate recommendations with `losses_collector: "application"`, explain the causal chain: platform owns negative balance liability and connected-account negative balances enable dispute-time transfer reversals. +- Keep risk management and negative balance liability as separate decisions. +- Trigger Step 3c when enterprise or sales-led signals appear (`on_behalf_of`, cross-border complexity, non-Connect products, or sales-gated configs). + +Fee structure checkpoint before Step 4: + +1. Confirm fee type and fee amount +2. Confirm how `application_fee_amount` is calculated +3. Confirm whether a margin warning is required +4. Include stripe.com/pricing link in output context + +#### Step 4 — Generate recommendation + +Read the decision matrix at and apply it to the user’s answers. For charge pattern details, read . + +**Step 4a — Compatibility validation (MANDATORY before presenting recommendation)** + +Read and cross-check the proposed `(dashboard, fees_collector, losses_collector)` + `chargePattern` combination against the compatibility matrix. + +1. **BLOCKED combination?** Do NOT present it. Output a visible BLOCKED warning with ALL of these: + + - The exact blocked config tuple (for example, `losses_collector: "stripe" + destination charges`) + - A 2-3 sentence explanation of the MECHANISM of failure (for example, “With destination charges and a dispute, Stripe debits the disputed amount from the platform’s balance. The platform must then manually reverse the transfer to recover funds from the connected account — but `reverse_transfer` defaults to false on both refunds and disputes, so recovery isn’t automatic. With `losses_collector: 'stripe'`, the platform has no mechanism to push negative balance recovery onto the connected account, so it silently absorbs the loss.”) + - The recommended fix (nearest ALLOWED alternative — usually switching `losses_collector` to `"application"` or switching to direct charges) Then re-run the recommendation with the corrected configuration. + +2. **CAUTION combination?** Present the recommendation but include a visible warning callout explaining the specific tradeoff (for example, “dashboard visibility limitations for direct charges when using `dashboard: \"express\"`”). + +3. **Additional compatibility checks (include concise warnings when triggered):** + + - If the user mentioned **OAuth** for connecting accounts, include a 1-2 sentence warning that accounts can disconnect and recommend embedded onboarding for stronger platform control. + - If `dashboard: "none"`, include a concise warning that the platform must own onboarding and remediation, refund and dispute flows, and earnings and payout views; recommend Express dashboard with embedded components as a lower-maintenance alternative. + - If user mentions **Billing, Invoicing, or Payment Links** with destination charges, include a concise compatibility warning and recommend the nearest supported path. + - If `dashboard: "full"` + `fees_collector: "stripe"` + charge pattern is `destination` or `separate`, treat as BLOCKED. Do NOT present this configuration. Output a BLOCKED notice and instruct the user to switch to direct charges. + - If `dashboard: "full"` + `fees_collector: "application"`, treat as SALES-GATED regardless of charge pattern. Do NOT recommend for self-serve paths. Redirect to [Stripe sales](https://stripe.com/contact/sales). + - If `dashboard: "express"` + `fees_collector: "stripe"`, treat as BLOCKED and recommend either switching to full dashboard (Stripe-owned pricing) or platform-owned pricing. + +4. **Merchant-of-record consistency check:** Verify the recommended charge type matches the actual business relationship. Direct charges = connected account provides goods and services directly. Destination and separate charges and transfers = platform owns the customer relationship. Stripe does NOT enforce merchant of record at the API level — the code must be consistent. + +5. **Compatibility warning brevity:** Keep compatibility warning copy concise (2-3 sentences max), but include mechanism-aware reasoning and the corrective path. + +**Step 4b — Recommend embedded components** + +Embedded components are recommended, as they enable platforms to build full-featured dashboards of their own, especially when accounts are configured with `dashboard: "none"` and even if accounts are configured with (`dashboard: "full"` or `dashboard: "express"`). Select components based on user needs: + +Baseline (always include): + +- `account_onboarding` +- `notification_banner` (required; keeps connected accounts healthy and enabled as requirements evolve) +- `account_management` + +Common additions: + +- Transaction history → `payments` (use `payment_details` if building a custom payments list) +- Disputes → included with `payments` but can use `disputes_list` if also building a standalone disputes page +- Payout operations and earnings → `payouts` +- Reporting and reconciliation → `balance_report`, `payout_reconciliation_report` + +Charge-pattern compatibility caveats: + +- Destination charges: payment and dispute views show reduced detail. +- Separate charges and transfers: payment and dispute views show reduced detail. +- Direct: payment and dispute views operate with full fidelity. + +Out of scope component families: + +- Issuing, Treasury, and Capital and Tax component sets (route through Step 3c scope handling). + +Be prepared to output a list of embedded components in the next step. + +Update the checklist: + +``` + [x] Learn about your business + [x] Scan your project + [x] Recommend configuration + charge pattern + [ ] Produce recommendation plan +``` + +#### Step 5 — Generate recommendation plan + +**Read ** and follow its “Output requirements” checklist and “Canonical recommendation template” structure. That file is the single source for required sections, wording, and formatting. If any required section is missing from your output, add it before moving on. + +Then ask the user: + +``` +Does this recommendation look right? +``` + +Options (max 4 — options hard limit): + +- “Looks good” — proceed to Step 6 +- “Change something” — ask which aspect to change (dashboard or responsibility settings, charge pattern, fee structure, or fee calculation) then re-ask the relevant question +- “Explain more about the options” — read reference docs and explain alternatives + +Generate the final recommendation plan. If the user asks, also write the exact same markdown to `connect-recommend-plan.md` at the project root. + +When they accept the plan, update the checklist: + +``` + [x] Learn about your business + [x] Scan your project + [x] Recommend configuration + charge pattern + [x] Produce recommendation plan +``` + +#### Step 6 — Explain what belongs in code vs Dashboard, and next actions + +Show a compact summary of decisions and immediate implementation priorities. + +Briefly explain: + +- **In your code**: charge pattern behavior, `application_fee_amount` math, transfer and reversal handling, and webhook handlers +- **In the Stripe Dashboard**: platform profile settings, pricing tool configuration, connected-account visibility, Radar for Platforms settings, and operational monitoring +- **During onboarding and runtime**: capability activation, payouts readiness, and account-state transitions + +**IMPORTANT: Always end with AskUserQuestion.** Never end with passive text. + +Use AskUserQuestion: + +``` +What would you like to do next? +``` + +Options: + +- “Refine a decision” — adjust dashboard, responsibilities, charge pattern, or fee model +- “Expand implementation steps” — provide a deeper technical rollout checklist +- “Generate `connect-recommend-plan.md` and build” — write the plan to a markdown file and handoff to a coding agent diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/account-types.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/account-types.md new file mode 100644 index 0000000..51621bf --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/account-types.md @@ -0,0 +1,226 @@ +## Stripe Connect Account Configuration (Accounts v2) + +> **IMPORTANT: Use Accounts v2 API** +> +> Do NOT use the legacy `type` parameter (`standard`, `express`, `custom`) when creating connected accounts. These are v1 terms and are no longer the recommended path. Instead, use the Accounts v2 API (`stripe.v2.core.accounts`) and configure each account along three independent dimensions: **dashboard access**, **fee collection**, and **loss liability**. This gives platforms precise control without being locked into a rigid archetype. + +### Account Configuration Dimensions + +Accounts v2 replaces the three fixed account types with three independent configuration dimensions. Each dimension is set separately, so platforms can mix and match to fit their exact business model. + +#### 1. Dashboard access (`dashboard`) + +Controls what connected accounts see when they log in. + +| Value | Dashboard access | Use when | +| --- | --- | --- | +| `express` | Lightweight dashboard showing earnings, payouts, and basic tax information. Stripe-branded with platform name. | Marketplace sellers, gig workers, or any connected account that needs visibility but not full Stripe control. | +| `full` | Full, independent Stripe Dashboard. Connected accounts can manage their own settings, view all transactions, and install apps. | SaaS platforms where connected accounts are established businesses that want to operate independently. | +| `none` | No Stripe dashboard. The platform owns the connected-account UI — use **[Embedded Components](https://docs.stripe.com/connect/supported-embedded-components.md)** (`@stripe/connect-js`) for pre-built widgets (account management, payouts, tax forms, and more) or build fully custom. | White-label platforms where connected accounts must never see Stripe branding. Use embedded components for pre-built functionality with white-label feel. **Fully custom (no embedded components)** adds significant complexity — the platform must build and maintain all connected account UX including onboarding remediation, refund and dispute flows, and ongoing requirement collection. | + +#### 2. Fee collection (`defaults.responsibilities.fees_collector`) + +Determines who is responsible for collecting Stripe processing fees from connected accounts. + +| Value | Behavior | Use when | +| --- | --- | --- | +| `stripe` | Stripe bills connected accounts directly for processing fees. The platform doesn’t need to handle fee logistics. | Most platforms. Simpler to operate. Connected accounts see Stripe fees on their own statements. | +| `application` | The platform is responsible for collecting fees from connected accounts and remitting them to Stripe. The platform receives a single invoice from Stripe. | Enterprise or white-label platforms that want full control over billing relationships, or that bundle Stripe fees into their own pricing. | + +> **Fee collection behavior depends on charge type.** The `fees_collector` setting interacts with the charge pattern: +> +> - **Direct charges:** `fees_collector` determines who pays Stripe processing fees. With `fees_collector: "stripe"`, the connected account pays fees directly. The `fee_payer` parameter can further control this — see [direct charges fee payer behavior](https://docs.stripe.com/connect/direct-charges-fee-payer-behavior.md). +- **Destination charges and separate charges and transfers:** The platform always pays Stripe processing fees regardless of the `fees_collector` setting, because the charge lives on the platform account. The `fees_collector` setting in these cases governs the platform-level billing relationship with Stripe (single invoice vs per-account), not per-transaction fee deduction. + +#### 3. Loss liability (`defaults.responsibilities.losses_collector`) + +Determines who bears financial responsibility for negative balances, disputes, and refunds on connected account activity. + +| Value | Behavior | Use when | +| --- | --- | --- | +| `stripe` | Stripe bears financial responsibility for negative balances on connected accounts that remain unresolved (for example, from disputes or fraud). | Most platforms. Reduces financial risk from unrecoverable negative balances. | +| `application` | The platform bears losses from unresolved negative balances, and is responsible for managing disputes. | Platforms with sophisticated risk management, high-risk verticals, or those that want to internalize loss economics for better unit economics. | + +### Common Configurations + +| Business Shape | Dashboard | Fees Collector | Losses Collector | Notes | +| --- | --- | --- | --- | --- | +| **Marketplace** | `express` | `application` | `application` | Platform owns fees and losses. Sellers get a lightweight dashboard. Required for Express dashboard + destination charges. Common for two-sided marketplace models. | +| **SaaS enabling payments** | `full` | `stripe` | `stripe` | Connected accounts are independent businesses with their own full Stripe Dashboard. Platform collects revenue through application fees. **Use direct charges only** — other charge types with `losses_collector: 'stripe'` cause the platform to silently carry negative balance liabilities. | +| **White-label / enterprise** | `none` | `application` | `application` | Platform owns the entire connected-account UI. No Stripe branding. Platform manages all billing and risk. Full control with higher operational responsibility. Compatible with all charge types. | +| **Managed marketplace** | `express` | `application` | `application` | Platform wants seller-facing dashboard and also owns risk. Express dashboard requires platform to own both fees and losses. Compatible with all charge types — destination and separate charges require webhook-driven recovery flows for refunds and disputes (CAUTION: connected accounts have limited dispute and refund visibility from their dashboard). | + +#### Configuration Compatibility Warnings + +> **CRITICAL: `losses_collector: 'stripe'` restricts you to direct charges only — but only when `dashboard: "full"`.** +> +> For `dashboard: "none"`, the only allowed path is `fees_collector: 'application'` + `losses_collector: 'application'`. All other responsibility combinations with `none` are BLOCKED, including direct charges with Stripe-owned responsibilities. +> +> When Stripe owns loss liability but the platform uses destination charges, separate charges and transfers, or `on_behalf_of` variants, the liability model doesn’t align with how these charge flows are debited and recovered. See `compatibility-matrix.md` for the full compatibility matrix. + +Key rules: + +- **Express dashboard** requires `fees_collector: 'application'` AND `losses_collector: 'application'` +- **`losses_collector: 'stripe'` + destination charges or separate charges and transfers** = BLOCKED. Platform silently inherits negative balance liability, fees are misattributed, and connected accounts can’t manage refunds or disputes from their dashboard. +- **`losses_collector: 'application'`** is compatible with all charge types when `fees_collector` is also `'application'`, with one exception: `full` dashboard + `application/application` is SALES-GATED (redirect to [Stripe sales](https://stripe.com/contact/sales)). With `fees_collector: 'stripe'` (full or none dashboard), all charge types are BLOCKED. +- **`dashboard: "full"` + `fees_collector: "application"`** = SALES-GATED. Do NOT recommend for self-serve paths. Redirect to [Stripe sales](https://stripe.com/contact/sales). + +### v2 API Example + +Create a connected account using Accounts v2: + +**Marketplace connected account (destination charges or separate charges and transfers):** + +```javascript +const account = await stripe.v2.core.accounts.create({ + contact_email: 'seller@example.com', + display_name: 'Seller Name', + dashboard: 'express', + identity: { country: 'us', entity_type: 'individual' }, + configuration: { + recipient: { + capabilities: { + stripe_balance: { stripe_transfers: { requested: true } }, + }, + }, + }, + defaults: { + currency: 'usd', + responsibilities: { + fees_collector: 'application', + losses_collector: 'application', + }, + }, +}); +``` + +**SaaS connected account (direct charges):** + +```javascript +const account = await stripe.v2.core.accounts.create({ + contact_email: 'merchant@example.com', + display_name: 'Merchant Name', + dashboard: 'full', + identity: { country: 'us', entity_type: 'individual' }, + configuration: { + merchant: { + capabilities: { + card_payments: { requested: true }, + }, + }, + }, + defaults: { + currency: 'usd', + responsibilities: { + fees_collector: 'stripe', + losses_collector: 'stripe', + }, + }, +}); +``` + +Key points about this API: + +- **`dashboard`** is set at the top level, not inside configuration. +- **`identity.country`** and **`identity.entity_type`** replace the old `country` and `business_type` fields. +- For marketplace connected accounts: use `configuration.recipient` with `stripe_balance.stripe_transfers` — do NOT request `configuration.merchant` or `card_payments` (unnecessary and causes longer onboarding). +- For SaaS connected accounts: use `configuration.merchant` with `card_payments` — the connected account is merchant of record (that is, direct charges where the connected account’s name appears on customer bank statements). +- **`defaults.responsibilities`** is where you set fee and loss liability. These are the v2 replacements for what was previously implied by account type. +- **`defaults.currency`** sets the default settlement currency. + +#### Merchant Configuration (Required for Merchant of Record) + +> **For SaaS or direct charges only.** Marketplace connected accounts should use `configuration.recipient` instead — see example above. + +In Accounts v2, the `configuration.merchant` block is what makes a connected account capable of accepting payments as the merchant of record. This is required when using **direct charges** (where the charge is created on the connected account and their business name appears on customer bank statements). + +Without the Merchant configuration, the connected account can’t process payments directly — it can only receive transfers from the platform. + +```javascript +configuration: { + merchant: { + capabilities: { + card_payments: { requested: true }, + }, + }, +}, +``` + +**When to include Merchant configuration:** + +- **Direct charges** — REQUIRED. The connected account is the merchant of record. +- **Destination charges** — NOT needed. Use `configuration.recipient` with `stripe_transfers` instead. Requesting `configuration.merchant` or `card_payments` for marketplace accounts is unnecessary and causes longer onboarding. +- **Separate charges & transfers** — NOT needed. Use `configuration.recipient` with `stripe_transfers` instead. + +### Decision Guide + +**Choose `dashboard: 'express'` when…** + +- You are building a marketplace or on-demand platform +- Connected accounts need to see their earnings and payout history +- You want Stripe to host the seller dashboard so you can focus on your product +- You want fast onboarding with Stripe-hosted flows + +**Choose `dashboard: 'full'` when…** + +- Connected accounts are established businesses that expect a full payments dashboard +- You are a SaaS platform where merchants operate independently +- Connected accounts may want to install Stripe apps or manage their own settings +- Sellers already have or expect to have their own Stripe relationship + +**Choose `dashboard: 'none'` when…** + +- You need a fully white-labeled UI with no Stripe branding +- Connected accounts should never interact with a Stripe-hosted dashboard +- The platform wants to take on more responsibility: must support ongoing requirement collection, and refund and dispute flows (can use embedded components) +- **Fully custom (no embedded components)** adds significant complexity — the platform must build and maintain all connected account UX including onboarding remediation, refund and dispute flows, and ongoing requirement collection + +**Choose `losses_collector: 'stripe'` when…** + +- You want Stripe to bear financial responsibility for unresolved negative balances on connected accounts +- You are starting out and want to minimize financial risk +- You don’t have a dedicated risk or fraud operations team +- You plan to use direct charges + +**Choose `losses_collector: 'application'` when…** + +- You have a mature risk management operation +- You want to internalize loss economics (for example, you believe your fraud rate is low enough to profit from self-insuring) +- You operate in a vertical where you have better risk signal than Stripe +- You need full control over dispute response workflows +- You plan to use destination charges or separate charges and transfers, and on_behalf_of isn’t used + +**Choose `fees_collector: 'stripe'` when…** + +- You want the simplest operational model +- You are fine with Stripe billing connected accounts directly +- You don’t want to manage fee invoicing or reconciliation + +**Choose `fees_collector: 'application'` when…** + +- You want to control the entire billing relationship with connected accounts +- You bundle Stripe processing fees into your own platform pricing +- You need consolidated invoicing from Stripe to your platform + +### Legacy Migration Note + +The terms **Standard**, **Express**, and **Custom** refer to the v1 Accounts API and its `type` parameter. They are no longer the recommended way to create connected accounts. Here is how they roughly map to v2 dimensions: + +| Legacy v1 Type | Approximate v2 Equivalent | +| --- | --- | +| Standard | `dashboard: 'full'`, `fees_collector: 'stripe'`, `losses_collector: 'stripe'` | +| Express | `dashboard: 'express'`, `fees_collector: 'application'`, `losses_collector: 'application'` | +| Custom | `dashboard: 'none'`, `fees_collector: 'application'`, `losses_collector: 'application'` | + +The mapping is approximate — v2 allows combinations that were impossible in v1, and legacy types have behavioral nuances that don’t carry over to their v2 “equivalents.” For example, the fee payer behavior in the approximate v2 config equivalent is different from what the legacy type provided. + +Stripe docs also expose legacy fee-payer variants for direct charges: + +| Legacy fee-payer value (docs) | Meaning | +| --- | --- | +| `application_express` | Historical billing behavior for legacy Express accounts | +| `application_custom` | Historical billing behavior for legacy Custom accounts | + +These are external Stripe-doc terms tied to legacy account behavior. For new integrations, use Accounts v2 responsibilities (`fees_collector`, `losses_collector`) instead. + +Don’t treat this table as “these are the same thing.” It is a rough conceptual guide. Legacy accounts retain their original behaviors; v1 and v2 coexist. All new integrations should use v2. diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/charge-patterns.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/charge-patterns.md new file mode 100644 index 0000000..19e90d6 --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/charge-patterns.md @@ -0,0 +1,315 @@ +## Stripe Connect Charge Patterns + +### Overview + +Connect offers three ways to create charges involving connected accounts. The charge pattern determines who is the merchant of record, how funds flow, and how fees and refunds work. + +### Comparison Table + +| Feature | Direct Charges | Destination Charges | Separate Charges & Transfers | +| --- | --- | --- | --- | +| **Merchant of record** | Connected account | Platform | Platform | +| **Payment created on** | Connected account | Platform account | Platform account | +| **Statement descriptor** | Connected account’s | Platform’s (can set connected account’s) | Platform’s | +| **Platform fee** | `application_fee_amount` | `application_fee_amount` or calculate using `transfer_data.amount` | Manual calculation | +| **Refund source** | Connected account’s balance | Platform’s balance | Platform’s balance | +| **Multi-seller split** | No (one seller per charge) | No (one destination per charge) | Yes (multiple transfers) | +| **Account requirements** | Most v2 configs — see BLOCKED combinations in the controller compatibility note below; the only charge type safe with `losses_collector: 'stripe'` | Requires `losses_collector: 'application'` | Requires `losses_collector: 'application'` | +| **Complexity** | Low | Low | High | +| **Best for** | SaaS, seller-owned transactions | Marketplaces, on-demand | Multi-seller carts, complex splits | + +### Direct Charges + +> **Controller Property Compatibility:** Works with most controller configurations, but NOT all. BLOCKED combinations for direct charges include: `fees_collector: 'stripe' + losses_collector: 'application'` (full or none dashboard), and Express dashboard configs other than `application/application`. This is the **only** charge type safe with `losses_collector: 'stripe'`. If the platform wants Stripe to own losses, direct charges are the only option. + +#### How it works + +The charge is created directly on the connected account. The connected account is the merchant of record — their name appears on the customer’s bank statement. The platform collects an application fee. + +#### Code pattern + +```javascript +// Backend: Create PaymentIntent on connected account +const paymentIntent = await stripe.paymentIntents.create({ + amount: 10000, // $100.00 + currency: 'usd', + application_fee_amount: 1500, // $15.00 platform fee + metadata: { + orderId: 'order_123', + }, +}, { + stripeAccount: 'acct_connected_account_id', // Key: stripeAccount header +}); + +// Return client_secret to frontend +res.json({ clientSecret: paymentIntent.client_secret }); +``` + +#### Frontend (with Stripe.js) + +```javascript +// Must initialize Stripe with connected account +const stripe = await loadStripe('pk_test_...', { + stripeAccount: 'acct_connected_account_id', +}); + +// Then confirm payment as usual +const result = await stripe.confirmPayment({ + elements, + confirmParams: { + return_url: 'https://yoursite.com/success', + }, +}); +``` + +#### Fund flow + +``` +Customer pays $100 + → $100 lands in connected account's balance + → $15 application fee transferred to platform + → Connected account keeps $85 +``` + +#### Refunds + +```javascript +// Refund comes from connected account's balance +const refund = await stripe.refunds.create({ + charge: 'ch_xxx', + // Optionally refund the application fee too: + refund_application_fee: true, +}, { + stripeAccount: 'acct_connected_account_id', +}); +``` + +#### When to use + +- Direct-charge integrations where sellers own the customer relationship (legacy v1 Standard-style pattern) +- SaaS platforms (Shopify model) +- When the connected account’s name should appear on bank statements +- When sellers handle their own disputes + +> **Legacy mapping note (external docs terms):** Stripe docs still reference legacy v1 naming (`standard`, `express`, `custom`) and legacy fee-payer behaviors (`application_express`, `application_custom`) for older accounts. For migration mapping to Accounts v2 dimensions, see the “Legacy migration note” section in the account-types reference. + +### Destination Charges + +> **Controller Property Compatibility:** REQUIRES `losses_collector: 'application'`. Using destination charges with `losses_collector: 'stripe'` creates a liability-model mismatch for this charge flow. See `compatibility-matrix.md` for details. + +#### How it works + +The charge is created on the platform’s account. The platform is the merchant of record. Funds are automatically transferred to the connected account using `transfer_data`. This is a common pattern for marketplaces. + +#### Code pattern + +```javascript +// Backend: Create PaymentIntent on platform account +const paymentIntent = await stripe.paymentIntents.create({ + amount: 10000, // $100.00 + currency: 'usd', + application_fee_amount: 1500, // $15.00 collected; platform net = $15.00 − Stripe processing fees + transfer_data: { + destination: 'acct_connected_account_id', // Funds go here + }, + metadata: { + bookingId: 'booking_123', + riderId: 'user_456', + operatorId: 'user_789', + }, +}); + +// Return client_secret to frontend +res.json({ clientSecret: paymentIntent.client_secret }); +``` + +#### Alternative: Specify transfer amount instead of fee + +```javascript +const paymentIntent = await stripe.paymentIntents.create({ + amount: 10000, // $100.00 + currency: 'usd', + transfer_data: { + destination: 'acct_connected_account_id', + amount: 8500, // $85.00 goes to connected account (platform keeps $15) + }, +}); +``` + +#### Frontend (standard Stripe.js) + +```javascript +// Initialize Stripe with platform's publishable key (no stripeAccount needed) +const stripe = await loadStripe('pk_test_platform_key'); + +const result = await stripe.confirmPayment({ + elements, + confirmParams: { + return_url: 'https://yoursite.com/success', + }, +}); +``` + +#### Fund flow + +``` +Customer pays $100 + → $100 lands in platform's balance + → $85 automatically transferred to connected account + → Platform nets $15 (application_fee_amount) − Stripe processing fees +``` + +#### Refunds + +```javascript +// Refund comes from platform's balance +const refund = await stripe.refunds.create({ + payment_intent: 'pi_xxx', + // Optionally: + reverse_transfer: true, // Claw back from connected account + refund_application_fee: true, // Refund the platform fee too +}); +``` + +#### When to use + +- **Marketplaces** where the platform owns the customer relationship +- On-demand platforms (Uber, DoorDash model) +- When you want the platform name on bank statements +- Express dashboard accounts (common pairing) +- When the platform handles disputes +- **NOT for hold-and-release or delivery-gated payouts** — funds transfer automatically to the connected account upon payment success. Use separate charges and transfers for delivery-gated payouts or any scenario requiring the platform to hold funds before releasing. + +#### Destination Charges with `on_behalf_of` + +> **Not covered by this guide.** `on_behalf_of` is an advanced variant that changes the merchant of record to the connected account while the charge lives on the platform. It has narrow use cases and significant complexity. +> +> If your integration requires `on_behalf_of`, consult the [Stripe Connect documentation](https://docs.stripe.com/connect/charges.md) or [contact Stripe sales](https://stripe.com/contact/sales). +> +> **Do NOT use `on_behalf_of` for marketplace use cases** — the platform should be the merchant of record. Use regular destination charges instead. + +### Separate Charges and Transfers + +> **Controller Property Compatibility:** REQUIRES `losses_collector: 'application'`. Same negative balance liability issue as destination charges — using separate charges and transfers with `losses_collector: 'stripe'` means the platform actually carries the losses despite the configuration. See `compatibility-matrix.md` for details. + +#### How it works + +The charge and transfer are separate API calls. This gives maximum flexibility — you can split a single payment across multiple connected accounts, delay transfers, or create complex fee structures. + +#### Code pattern + +```javascript +// Step 1: Create PaymentIntent (no transfer_data) +const paymentIntent = await stripe.paymentIntents.create({ + amount: 10000, // $100.00 + currency: 'usd', + metadata: { + orderId: 'order_123', + }, +}); + +// Step 2: After payment_intent.succeeded webhook fires — latest_charge is null +// at creation time and only populated on the confirmed PaymentIntent from the event +// IMPORTANT: Always verify the webhook signature before processing event data. +// See https://stripe.com/docs/webhooks/signatures for verification steps. +const confirmedIntent = event.data.object; // payment_intent.succeeded payload +const transfer = await stripe.transfers.create({ + amount: 8500, // $85.00 to connected account + currency: 'usd', + destination: 'acct_connected_account_id', + source_transaction: confirmedIntent.latest_charge, // charge ID from confirmed PaymentIntent + metadata: { + orderId: 'order_123', + }, +}); +``` + +#### Multi-seller split + +```javascript +// One payment, multiple sellers (for example, a multi-seller cart) +await stripe.paymentIntents.create({ + amount: 25000, // $250.00 total + currency: 'usd', +}); + +// After payment_intent.succeeded webhook fires — latest_charge is null at creation time. +// IMPORTANT: Always verify the webhook signature before processing event data. +// See https://stripe.com/docs/webhooks/signatures for verification steps. +const confirmedIntent = event.data.object; // payment_intent.succeeded payload +const chargeId = confirmedIntent.latest_charge; + +// Transfer to seller A +await stripe.transfers.create({ + amount: 8000, + currency: 'usd', + destination: 'acct_seller_a', + source_transaction: chargeId, +}); + +// Transfer to seller B +await stripe.transfers.create({ + amount: 12000, + currency: 'usd', + destination: 'acct_seller_b', + source_transaction: chargeId, +}); + +// Platform keeps $50 (25000 - 8000 - 12000 = 5000) +``` + +#### Fund flow + +``` +Customer pays $250 + → $250 lands in platform's balance + → Platform creates transfer: $80 to Seller A + → Platform creates transfer: $120 to Seller B + → Platform keeps $50 +``` + +#### Refunds + +```javascript +// Refund the charge +const refund = await stripe.refunds.create({ + charge: 'ch_xxx', +}); + +// Manually reverse transfers +await stripe.transfers.createReversal('tr_seller_a', { + amount: 8000, +}); +await stripe.transfers.createReversal('tr_seller_b', { + amount: 12000, +}); +``` + +#### When to use + +- Multi-seller carts (one payment, multiple recipients) +- Delayed payouts (hold funds, transfer later) +- Hold-and-release / delivery-gated payout (payment precedes delivery, platform releases funds on confirmation) +- Delivery-gated payouts (collect payment now, transfer to seller after fulfillment) +- Complex fee structures or splits +- When you need maximum control over fund flow timing +- Crowdfunding-style platforms + +### Decision Guide + +``` +Is there one seller per transaction? +├── Yes → Does the platform need to hold funds before releasing to the seller? +│ ├── Yes (hold-and-release or delivery confirmation) → SEPARATE CHARGES & TRANSFERS +│ └── No → Is the seller the merchant of record? +│ ├── Yes → DIRECT CHARGES +│ └── No → DESTINATION CHARGES ← Common marketplace default +└── No (multiple sellers) → SEPARATE CHARGES & TRANSFERS +``` + +**Quick rules:** + +- **Marketplace with one seller, immediate payout** → Destination charges +- **Marketplace with hold-and-release or delivery-gated payout** → Separate charges and transfers +- **SaaS where seller owns the relationship** → Direct charges +- **Multi-seller cart or complex splits** → Separate charges and transfers diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/company-researcher.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/company-researcher.md new file mode 100644 index 0000000..2dccdbe --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/company-researcher.md @@ -0,0 +1,106 @@ +## Company Researcher Agent + +Research a company using its website URL or a text description, then map findings to the Stripe Connect decision matrix. Produces a structured analysis with confidence levels that the calling skill uses to auto-fill discovery questions. + +### Inputs + +You will receive one or both of: + +- **Company URL** — a website to fetch and analyze +- **Company description** — freeform text about what the business does + +### Instructions + +#### Step 1 — Gather company information from the web + +**If a URL is provided:** + +1. `WebFetch` the homepage. Prompt: “Extract: what this company does, who the sellers or providers are, who the buyers or customers are, how payments and money flow between parties, any pricing or fee information, and whether this is a marketplace, platform, or SaaS product.” + +2. Attempt to fetch deeper pages for additional signals. Try these URL suffixes in parallel and use whatever succeeds: + + - `/about`, `/about-us`, `/how-it-works` — for business model clarity + - `/pricing`, `/plans` — for fee structure + +3. If the homepage fetch fails (403, 404, timeout, empty content), fall back to `WebSearch` using the domain name plus “business model how it works”. + +**If only a description is provided (no URL):** + +1. `WebSearch` for the company name (if identifiable) plus “business model” and “pricing”. +2. If the description is generic (for example, “I’m building a marketplace”), skip web search — classify directly from the description text. Maximum confidence for description-only inferences is MEDIUM. + +**If both `WebFetch` and `WebSearch` are unavailable or fail:** + +If no description text is available (URL-only input and web research failed), return the early-exit output from Step 4 with all dimensions set to LOW confidence and the note: “Web research unavailable and no description provided. Cannot perform research.” + +Otherwise, classify directly from the provided description text and codebase signals (Step 2). Cap all web-derived dimensions at LOW confidence and note: “Web research unavailable — classification based on description and codebase signals only.” + +**If neither URL nor description is provided:** + +Return the early-exit output (see Step 4 failure format) with all dimensions set to LOW confidence and the note: “No company URL or description provided. Cannot perform research.” + +#### Step 2 — Cross-reference with codebase signals (if a project exists) + +Check if there’s an existing project to scan: + +1. `Glob` for `package.json`, `requirements.txt`, `Gemfile`, `go.mod`, `pom.xml` at the project root. + +2. If a project exists, `Grep` for business model signals: + + - Seller and provider patterns: `seller`, `vendor`, `operator`, `provider`, `merchant`, `host`, `creator` + - Buyer patterns: `buyer`, `customer`, `rider`, `guest`, `client` + - Payment patterns: `commission`, `fee`, `split`, `payout`, `transfer`, `earnings` + - Multi-party patterns: `marketplace`, `platform`, `connect` + +3. Use codebase signals to corroborate or strengthen web research findings. For example, if the homepage says “marketplace” and the codebase has terms like `commission`, `payout`, `split`, `listing`, `booking`, `cart`, `order`, `storefront`, or `seller`/`vendor`/`provider` patterns, that’s stronger confirmation. + +#### Step 3 — Assess confidence per dimension + +For each of the 6 dimensions below, report what you found and how confident you are. Do NOT interpret the decision matrix or derive a recommended configuration — that happens downstream. + +| Dimension | What to determine | Confidence: HIGH | Confidence: MEDIUM | Confidence: LOW | +| --- | --- | --- | --- | --- | +| **Business model** | marketplace, on-demand services, professional services, SaaS with payments, crowdfunding, subscription platform, rental marketplace, event ticketing, e-commerce (white-label), B2B platform | Explicit on homepage or about page | Inferred from product description or competitor comparison | Guessing from vague signals | +| **Parties** | Who are the sellers or providers? Who are the buyers? | Roles explicitly named on the site | Inferred from business model type | No party information found | +| **Payment flow** | Platform collects → pays out? Buyers pay sellers directly? Platform processes on behalf? | Pricing page or docs describe the flow | Inferred from business model (for example, marketplaces usually collect) | No payment information found | +| **Onboarding control** | Embedded, Stripe-hosted redirect, or fully custom or API | Custom onboarding shown on site, or white-label signals | Default inference from business model | Contradictory signals | +| **Dispute responsibility** | Platform handles, sellers handle, or shared | Explicitly stated in terms/policies | Inferred from model (marketplace → platform usually) | No information | +| **Fee structure** | Percentage, flat, tiered, subscription+tx | Pricing page shows exact fee structure | Inferred from competitor patterns or partial information | No pricing information found | + +#### Step 4 — Produce structured output + +Write the Summary section as if speaking directly to the user, using second person. Say “Your barbers are…” not “The barbers are…”. Frame findings as a conversational confirmation seeking validation. + +Return your analysis in this exact format: + +``` +## Company Research: [Company Name or "Unknown"] + +### Summary +[2-3 sentences speaking directly to the user: what their company does, their key parties, and how money flows. Use "you/your" — for example, "Your platform connects customers with barbers who provide services. You collect payment from customers and pay out barbers after taking a platform fee."] + +### Research Findings + +| Dimension | Finding | Confidence | Evidence | +|----------------|------------------------------------------|------------------|--------------------------| +| Business Model | [type from the dimension table above] | [HIGH/MEDIUM/LOW] | [1-sentence explanation] | +| Parties | [sellers] (sellers) + [buyers] (buyers) | [HIGH/MEDIUM/LOW] | [1-sentence explanation] | +| Payment Flow | [observed flow description] | [HIGH/MEDIUM/LOW] | [1-sentence explanation] | +| Onboarding | [signals about onboarding preferences] | [HIGH/MEDIUM/LOW] | [1-sentence explanation] | +| Disputes | [who appears to handle] | [HIGH/MEDIUM/LOW] | [1-sentence explanation] | +| Fee Structure | [type]: [details] | [HIGH/MEDIUM/LOW] | [1-sentence explanation] | + +### Sources +- [list each URL fetched or search query used] +``` + +#### Step 5 — Handle edge cases + +| Scenario | What to do | +| --- | --- | +| **URL returns 403/404/timeout** | Fall back to `WebSearch` with the domain name. Note in Sources: “Direct URL unreachable, used web search.” | +| **URL is a SPA with minimal HTML** | `WebFetch` may return little content. Fall back to `WebSearch`. Check meta tags and page title. | +| **Pricing is behind a login** | Fee structure confidence drops to LOW. Note: “Pricing not publicly available.” | +| **Company does multiple things** | Note the ambiguity. Classify based on the primary product. Set confidence to MEDIUM with reasoning about which facet you chose. | +| **Not a marketplace or platform** | If the business is purely B2C with no multi-party payments, flag clearly: “This business appears to be a direct seller — standard Stripe integration may be more appropriate than Stripe Connect.” Set Business Model confidence to HIGH with value “not-connect”. | +| **Conflicting signals** | Note the conflict explicitly. Set confidence to MEDIUM. Provide your best inference with reasoning about why you chose one interpretation over the other. | diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/compatibility-matrix.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/compatibility-matrix.md new file mode 100644 index 0000000..676efaf --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/compatibility-matrix.md @@ -0,0 +1,199 @@ +## Connect integration compatibility reference + +This document encodes known Connect integration incompatibilities — combinations of account controller properties and charge types that cause serious issues for platforms. Use this as a validation checklist when recommending or reviewing any Connect configuration. + +### 1. Controller Property + Charge Type Compatibility Matrix + +Significant compatibility issues arise when account controller properties (dashboard, fees_collector, losses_collector) are paired with incompatible charge types. Each combination below is rated: + +- **BLOCKED** — Incompatible combination. Never recommend. Can cause liability-model mismatch, fee-model mismatch, or inability to manage key payment operations. +- **CAUTION** — Technically functional but has significant drawbacks. Present with explicit warnings. +- **ALLOWED** — Supported combination. Proceed normally. +- **OUT OF SCOPE** — Not supported by this guide. Redirect to Stripe docs or sales. +- **Reasoning depth vs output brevity** — This reference is intentionally detailed so the assistant can reason about liability and transfer mechanics. User-facing warnings should stay concise and action-oriented. +- **Output guardrail** — Keep recommendation warnings concise (typically one to two sentences). Use the mechanism details in this document to choose the right warning and alternative path, not to dump every detail verbatim. + +#### Core Rule + +> **For GA configurations with `losses_collector: "stripe"`, ONLY direct charges are safe.** +> +> For destination charges and separate charges and transfers, use `losses_collector: "application"` so responsibility aligns with dispute and transfer-reversal flows. In this guide, combinations that pair these charge patterns with `losses_collector: "stripe"` are marked BLOCKED. +> +> **Exception:** Express dashboard with `losses_collector: "stripe"` (regardless of fees_collector) is blocked for ALL charge types including direct — these configs are still in beta. Don’t recommend them. + +> **Note:** `on_behalf_of` configurations aren’t supported by this guide. `on_behalf_of` columns are retained in the matrix for compatibility detection only — if the assistant encounters `on_behalf_of` requirements, it should redirect to Stripe docs or sales. + +#### Full Matrix (v2 field names) + +| Dashboard | Fees Collector | Losses Collector | Direct | Destination | Destination `on_behalf_of` | Separate charges and transfers | Separate charges and transfers `on_behalf_of` | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `full` | `stripe` | `stripe` | ALLOWED | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `full` | `stripe` | `application` | BLOCKED | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `full` | `application` | `application` | SALES-GATED | SALES-GATED | OUT OF SCOPE | SALES-GATED | OUT OF SCOPE | +| `full` | `application` | `stripe` | SALES-GATED | SALES-GATED | OUT OF SCOPE | SALES-GATED | OUT OF SCOPE | +| `express` | `application` | `application` | ALLOWED | CAUTION | OUT OF SCOPE | CAUTION | OUT OF SCOPE | +| `express` | `stripe` | `stripe` | BLOCKED* | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `express` | `stripe` | `application` | BLOCKED | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `express` | `application` | `stripe` | BLOCKED* | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `none` | `stripe` | `stripe` | BLOCKED | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `none` | `stripe` | `application` | BLOCKED | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `none` | `application` | `stripe` | BLOCKED | BLOCKED | OUT OF SCOPE | BLOCKED | OUT OF SCOPE | +| `none` | `application` | `application` | ALLOWED | ALLOWED | OUT OF SCOPE | ALLOWED | OUT OF SCOPE | + +\*Express dashboard with `losses_collector: "stripe"` configs are still in beta. Even when GA, destination charges and separate charges and transfers still require platform-run dispute or refund recovery (including transfer reversals), which aligns with `losses_collector: "application"` instead. + +#### CAUTION Details + +**express + application + application + destination charges (without `on_behalf_of`) and separate charges and transfers:** + +- Connected accounts can’t manage refunds, disputes, or Radar rules from their Express dashboard for these charge types (see [Express dashboard payments docs](https://docs.stripe.com/connect/express-dashboard/payments.md)) +- Stripe debits disputes to the platform first for these charge patterns; recovery depends on reversing prior transfers back from connected accounts +- This pattern is only viable when the platform owns losses (`losses_collector: "application"`) and runs webhook-driven refund or dispute recovery workflows +- Platform must handle failure modes (for example, insufficient connected-account balance) and negative-balance remediation +- `on_behalf_of` is out of scope for this guide. Redirect to Stripe docs or sales instead of recommending it. + +#### Blessed Paths (Safe Defaults) + +| Business Model | Dashboard | Fees | Losses | Charge Type | Rating | Notes | +| --- | --- | --- | --- | --- | --- | --- | +| **Marketplace** | `express` | `application` | `application` | Destination | CAUTION | Recommended path — CAUTION applies: connected accounts have limited dispute or refund visibility from their Express dashboard; platform must run webhook-driven recovery workflows. Always include the Express dispute-visibility warning. | +| **SaaS** | `full` | `stripe` | `stripe` | Direct | ALLOWED | Stripe-managed fee and loss defaults; connected accounts are independent merchants | +| **Enterprise or White-label** | `none` | `application` | `application` | Destination or Direct | ALLOWED | Full platform control | + +### 2. Why Blocked Combos Fail + +When `losses_collector: "stripe"` is combined with non-direct charges (destination or separate charges and transfers), this guide marks the combination as BLOCKED for three documented reasons: + +1. **Liability settings should align with where disputes are debited.** For destination charges and separate charges and transfers, disputes are debited from the platform balance. Use `losses_collector: "application"` so the liability model matches this funds flow. + +2. **Payment fees for these charge types are assessed on the platform.** For destination charges or separate charges and transfers, Stripe collects payment fees from the platform account regardless of `fees_collector`. (Rates vary by region — see [stripe.com/pricing](https://stripe.com/pricing).) Note: Legacy types behave differently, see [Fee behavior](https://docs.stripe.com/connect/direct-charges-fee-payer-behavior.md). + +3. **Recovery from connected accounts requires explicit transfer-reversal handling.** For destination and separate disputes, Stripe debits the platform first; the platform then recovers funds by reversing transfers through the API or Dashboard. Refunds can auto-reverse transfers when `reverse_transfer: true`, but dispute recovery isn’t automatic and requires explicit logic. + +### 3. Merchant of record enforcement gap + +Whoever provides the good or service at the transaction level should be the merchant of record. The charge type dictates who the merchant of record is: + +- **Direct charges** → Connected account is merchant of record (their name on bank statements) +- **Destination charges and separate charges and transfers** → Platform is merchant of record +- **`on_behalf_of` variants** → Connected account is merchant of record (despite charge living on platform account) + +**CRITICAL:** Platforms declare their intended merchant-of-record setup during platform onboarding, but can then create charges with any pattern regardless. Stripe will NOT enforce this selection at the API level. The recommendation must ensure the charge type matches the user’s actual business relationship (who provides the goods and services). + +### 4. Additional compatibility risks + +#### 4a. OAuth or Connecting Existing Stripe Accounts + +**Risk level:** OUT OF SCOPE + +Connecting existing Stripe accounts through OAuth is a v1-only pattern primarily used in sales-assisted integrations. This guide doesn’t support OAuth-based onboarding. + +**Why OAuth is problematic:** + +- Connected accounts can disconnect at any time, severing the platform’s ability to process payments +- Platform loses visibility into the connected account’s state and requirements +- Less platform control over onboarding flow and requirement collection +- Not compatible with all embedded components + +**If the user mentions OAuth, “connect existing Stripe accounts,” or “link existing accounts”:** Direct them to the [Connect documentation](https://docs.stripe.com/connect.md) and recommend [contacting Stripe sales](https://stripe.com/contact/sales). This guidance only supports creating new connected accounts with embedded onboarding. + +#### 4b. Custom Onboarding Complexity + +**Risk level:** CAUTION + +Platforms that choose `dashboard: "none"` and build custom onboarding underestimate the ongoing burden: + +- **KYC lifecycle ownership** shifts fully to the platform: initial collection, ongoing requirement monitoring, and remediation when verification fails. +- **Country-specific legal entity requirements** change frequently. What works for US entities doesn’t work for EU, and new countries add new requirements. +- **Ongoing requirement collection** is required, not one-time. When regulatory and compliance requirements change (updated KYC rules, new regulatory requirements, and more), the platform must update collection flows and prompt existing accounts. +- **Invalid information** from connected accounts leads to accounts stuck in restricted states. Without Stripe’s built-in validation, platforms end up manually remediating stuck accounts. +- **Higher remediation and maintenance burden** compared to embedded or hosted onboarding, because API-based onboarding requires custom collection logic and ongoing updates as requirements evolve. + +**Recommendation:** Use embedded onboarding components or Stripe-hosted onboarding unless the platform has dedicated compliance engineering resources AND a specific branding requirement that embedded components can’t meet. This reduces compliance and maintenance burden (see [Onboard your connected account](https://docs.stripe.com/connect/marketplace/tasks/onboard.md)). + +#### 4c. Dashboard DIY (Missing Refund or Dispute Flows) + +**Risk level:** CAUTION + +Platforms that build their own connected-account dashboard (`dashboard: "none"`) commonly build earnings and payout views but **neglect refund and dispute management flows**. Without these: + +- Connected accounts can’t initiate refunds, leading to customer complaints escalating to chargebacks +- Connected accounts can’t respond to disputes, causing auto-losses +- Connected accounts can’t easily identify or remediate KYC requirement failures, causing prolonged restrictions + +**Recommendation:** If building a custom dashboard, day-one scope should include refund initiation, dispute response, and KYC requirement status and remediation with country-aware requirement handling. Strongly recommend using embedded components for these. If the platform can’t commit to this, use `dashboard: "express"` instead. + +#### 4d. Product compatibility by charge type + +**Risk level:** INFORMATIONAL (long-term gap) + +Not all Stripe products work with all Connect integration configurations. + +**Recommendation:** If the platform plans to use Billing, Invoicing, or Payment Links, recommend direct charges. + +For other charge types, when encountering Billing (Subscriptions, Invoicing), Tax, Payment Links, or Checkout Sessions, proceed with caution and look things up in the Stripe docs or recommend contacting sales. + +#### 4e. Geo Expansion Limitations + +**Risk level:** INFORMATIONAL (long-term gap) + +Certain integration paths have geographic restrictions: + +- **Cross-border payouts** have currency and timing limitations that vary by connected account country. +- **Instant payouts** are only available in select countries and may require specific account configurations. + +**Recommendation:** If the user mentions international expansion plans, their charge pattern and account configuration may need adjustment for new countries. Recommend checking Stripe’s country availability documentation. + +#### 4f. Taking on Pricing Without Expertise + +**Risk level:** CAUTION + +Platforms that choose `fees_collector: "application"` (platform owns pricing) should model Stripe processing fees explicitly, because unmodeled fees can reduce margins. + +**Recommendation:** This is already well-covered by the skill’s mandatory fee economics breakdowns. Reinforce during discovery: if the platform doesn’t have dedicated pricing expertise, recommend `fees_collector: "stripe"` and use `application_fee_amount` for platform revenue. + +#### 4g. Destination Charges + Disputes: Missing Transfer Reversals + +**Risk level:** CAUTION + +When a dispute occurs on a destination charge: + +1. The charge lives on the **platform’s** account (platform is merchant of record) +2. Stripe debits the **platform’s** balance for the disputed amount +3. However, the platform has already transferred funds to the connected account using `transfer_data` + +The platform’s balance is reduced but the connected account still has the funds. **A common implementation issue:** platforms fail to initiate a **transfer reversal** to recover the disputed amount from the connected account. + +**What should happen:** + +- Platform listens for `charge.dispute.created` webhook +- Platform creates a transfer reversal to pull funds back from the connected account +- If the connected account’s Stripe balance is insufficient, the reversal creates a negative balance on the connected account (requires `losses_collector: "application"`) + +**What commonly goes wrong:** + +- Platform doesn’t listen for dispute webhooks at all +- Platform processes disputes manually but forgets the transfer reversal step +- Platform assumes Stripe automatically reverses the transfer (it does NOT — `reverse_transfer` defaults to `false` on both refunds and disputes) +- Connected account balance is zero, and without `losses_collector: "application"`, there’s no mechanism to recover + +**Recommendation:** + +- Always verify incoming webhook signatures before processing — see [Verify webhook signatures](https://docs.stripe.com/webhooks.md#verify-events). Optionally restrict requests to [Stripe’s published IP addresses](https://docs.stripe.com/ips.md). +- Always implement a `charge.dispute.created` webhook handler that automatically reverses the associated transfer +- Use `reverse_transfer: true` on refunds to make transfer reversal automatic for voluntary refunds +- For disputes, build explicit transfer reversal logic — automatic reversal only happens for refunds, not disputes +- Ensure `losses_collector: "application"` is set so the connected account balance can go negative, enabling recovery +- Consider alerting on unrecovered dispute amounts where transfer reversal failed (for example, connected account already withdrew funds) + +### 5. Compatibility checks during discovery + +When generating a recommendation in the discovery flow, validate the final configuration against this checklist: + +1. **Compatibility matrix check:** Look up `(dashboard, fees_collector, losses_collector)` + `chargePattern` in the matrix above. If BLOCKED, don’t present. Explain why and recommend the nearest allowed alternative. +2. **Merchant-of-record consistency check:** Verify the recommended charge type matches who actually provides goods or services. Direct charges = connected account is merchant of record. Destination and separate charges and transfers = platform is merchant of record. +3. **OAuth check:** If the user mentions OAuth for connecting accounts, warn about tradeoffs and recommend Account Links. +4. **Custom onboarding check:** If `dashboard: "none"` and the user plans custom onboarding, warn about ongoing KYC collection and remediation burden and country-specific requirement drift. +5. **Dashboard scope check:** If `dashboard: "none"`, confirm the platform plans to build refund or dispute operations, not just earnings views. +6. **Fee expertise check:** If `fees_collector: "application"`, ensure the fee economics section includes explicit breakeven analysis. +7. **Warning brevity check:** Keep user-facing warnings concise (typically one to two sentences), using this document as reasoning context. diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/decision-matrix.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/decision-matrix.md new file mode 100644 index 0000000..fa927b5 --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/decision-matrix.md @@ -0,0 +1,372 @@ +## Connect Integration Decision Matrix + +### IMPORTANT: Use Accounts v2 API + +**ALWAYS use the Accounts v2 API (`/v2/core/accounts`) for new integrations.** Do NOT use the legacy v1 API with `type: 'express'`, `type: 'standard'`, or `type: 'custom'`. These are legacy categories that bundle together responsibility, dashboard, and requirement decisions into opaque labels. + +Instead, configure accounts using three independent dimensions: + +- **Dashboard access**: `express` (lightweight), `full` (independent businesses), `none` (white-label) +- **Fee collection**: `stripe` (Stripe bills connected accounts) or `application` (platform manages billing) +- **Loss liability**: `stripe` (Stripe bears unresolved negative balances) or `application` (platform bears negative balances) + +### Business Model → Recommendation Mapping + +| Business Model | Dashboard | Fees | Losses | Charge Pattern | Onboarding | Reasoning | +| --- | --- | --- | --- | --- | --- | --- | +| **Marketplace** | `express` | `application` | `application` | Destination | Embedded | Platform owns customer relationship; platform-owned pricing + loss liability required for Express dashboard today | +| **On-demand services** | `express` | `application` | `application` | Destination | Embedded | Fast onboarding for drivers and providers; platform-owned pricing + loss liability required for Express | +| **Professional services** | `express` | `application` | `application` | Destination | Embedded | Similar to marketplace; platform-owned pricing + loss liability required for Express | +| **SaaS with payments** | `full` | `stripe` | `stripe` | Direct | Embedded | Sellers want independence, own Stripe accounts, own branding | +| **Crowdfunding** | `express` | `application` | `application` | Separate | Embedded | Multi-party splits and delayed release. Use transfer math (not `application_fee_amount`) and platform-owned loss liability for transfer reversals | +| **Subscription platforms** | `express` | `application` | `application` | Destination | Embedded | Recurring billing, platform manages subscriptions; platform-owned pricing + loss liability required for Express | +| **E-commerce (white-label)** | `none` | `application` | `application` | Destination or Direct | Embedded | Full branding control. Use embedded components for white-label feel. Going fully custom (no embedded components) adds significant complexity — the platform must build and maintain all connected account UX including onboarding remediation, refund and dispute flows, and ongoing requirement collection. | +| **Rental marketplace** | `express` | `application` | `application` | Destination | Embedded | Platform owns booking flow; platform-owned pricing + loss liability required for Express | +| **Event ticketing** | `express` | `application` | `application` | Destination | Embedded | Platform manages event and ticket flow; platform-owned pricing + loss liability required for Express | +| **B2B platforms** | `none` | `application` | `application` | Separate | Embedded | For complex enterprise multi-party flows, prefer Separate charges and transfers with transfer math. Don’t default to Destination in these scenarios. Often requires sales engagement for billing complexity — [Stripe sales](https://stripe.com/contact/sales). | + +**Note:** `fees` and `losses` columns refer to `defaults.responsibilities.fees_collector` and `defaults.responsibilities.losses_collector` in the v2 API. Values are `"stripe"` or `"application"` (your platform). + +### Decision Tree Logic + +#### Account Configuration Selection (Accounts v2) + +Rather than choosing a legacy “account type”, configure three independent dimensions: + +**If the user asks “what account type should I use?” (or similar):** Reframe explicitly before giving settings: “In Accounts v2, avoid the legacy `type` parameter and configure behavior with explicit fields: `dashboard`, `defaults.responsibilities.fees_collector`, `defaults.responsibilities.losses_collector`, and the appropriate account configuration (`merchant` for direct charges or `recipient` for destination or separate flows).” Then provide the recommended field values. + +**Dashboard access:** + +``` +What dashboard should connected accounts see? +├── No dashboard needed (fully embedded or white-label) → dashboard: "none" +├── Independent businesses needing full Stripe access → dashboard: "full" +└── Lightweight dashboard for sellers or providers → dashboard: "express" ← DEFAULT +``` + +**Responsibilities:** + +``` +Who collects fees and bears losses? +├── Marketplace (destination or separate charges) → fees_collector: "application", losses_collector: "application" +│ Platform is merchant of record and should be responsible for paying Stripe fees +│ Platform-owned pricing + loss liability is REQUIRED for Express dashboard today +│ Platform-owned loss liability enables connected account negative balances for transfer reversals +├── SaaS (direct charges) → fees_collector: "stripe", losses_collector: "stripe" ← DEFAULT +└── White-label or enterprise → fees_collector: "application", losses_collector: "application" (use embedded components; fully custom adds significant complexity) +``` + +**Detailed rules:** + +- If sellers are independent businesses wanting their own Stripe access → `dashboard: "full"` +- If sellers need lightweight access (common for marketplaces) → `dashboard: "express"` (typical default) +- If fully white-labeled, sellers never see Stripe → `dashboard: "none"` +- Marketplace defaults: `dashboard: "express"` + `fees_collector: "application"` + `losses_collector: "application"` +- SaaS defaults: `dashboard: "full"` + `fees_collector: "stripe"` + `losses_collector: "stripe"` +- Hybrid Express defaults (same accounts used for direct + destination or separate): keep `dashboard: "express"` + `fees_collector: "application"` + `losses_collector: "application"` for both sides + +#### Charge Pattern Selection + +``` +How many sellers per transaction? +├── Multiple sellers → Separate charges & transfers +└── One seller + └── Who should the customer pay at checkout? + ├── Seller runs checkout (seller name on receipt or statement) → Direct charges + └── Platform runs checkout (platform name on receipt or statement) → Destination charges ← DEFAULT +``` + +**Detailed rules:** + +- If platform owns customer relationship → **Destination** (most marketplaces) +- If seller owns customer relationship → **Direct** (SaaS model) +- If customers discover services on your platform and complete checkout in your platform flow (you own checkout UX, order confirmation, and payment operations) → **Destination** +- Language saying payments should “belong to” or be “associated with” sellers, or that sellers should “run their own account,” is usually a payout expectation (who receives proceeds) or a dashboard-access preference, not a checkout-ownership signal. Destination charges satisfy payout expectations through automatic transfers and Express dashboard satisfies “own account” expectations. Only choose Direct when sellers independently own the checkout flow (their own payment page, their branding on statements, they handle refunds and disputes). +- Choose **Direct** when the behavior is SaaS enablement: each seller runs their own payment relationship, customers pay the seller directly, seller branding appears on receipts and statements, and seller-side operations handle payment support, refunds, and disputes. +- If multi-party splits needed → **Separate** (carts, one payment split across multiple parties) +- If “platform collects then pays out” → **Destination** +- If “buyers pay sellers directly” → **Direct** +- If one payment maps to one connected account and funds transfer immediately (payout timing to bank is controlled by payout schedule, not release logic) → **Destination** +- If the platform needs to hold funds and only transfer to the connected account after a trigger (delivery, job completion, approval, campaign end) → **Separate** (use transfer math; don’t use `application_fee_amount`) +- If one payment must be split across multiple connected accounts (for example, a multi-vendor cart) → **Separate** +- For B2B enterprise flows with multi-party allocation, approval gates, or staged release, prefer **Separate** and do NOT default to **Destination** +- If unsure and the flow is single-recipient, immediate-transfer marketplace behavior → **Destination** (safest default) + +#### Hybrid model guidance + +Some platforms run two sides of business with the same connected accounts. This is supported, but every transaction must be explicitly classified to the correct side: + +- **Connected account is merchant of record** → **Direct** charges +- **Platform is merchant of record** → **Destination** or **Separate** charges and transfers + +When both sides share Express connected accounts, keep controller settings aligned with the allowed path in this guide: `dashboard: "express"` + `fees_collector: "application"` + `losses_collector: "application"` for both direct and destination or separate contexts. + +Hybrid models add material complexity: + +- Two payment flows to build and maintain (direct + destination or separate) +- Webhook handling across both payment lifecycle and transfer and reversal lifecycle +- Expanded end-to-end testing matrix (refunds, disputes, transfer reversals, negative balance behavior) + +Launch the most business-critical side first, stabilize webhook and reconciliation behavior, then add the second side. + +#### Onboarding Method Selection + +``` +How much control over onboarding UX? +├── "Stripe handles everything" → Embedded components (recommended default) +├── "Some customization" → Embedded components with [appearance options API](/connect/embedded-appearance-options) +└── "Fully custom" → API-based — NOT RECOMMENDED for platforms integrating without dedicated Stripe guidance. + Requires building custom remediation flows. Direct to [Stripe sales](https://stripe.com/contact/sales). +``` + +**Detailed rules:** + +- `dashboard: "express"` → **Embedded components** (recommended, keeps users in-app) or Stripe-hosted redirect (fallback) +- `dashboard: "full"` → **Embedded components** or Stripe-hosted redirect +- `dashboard: "none"` + want embedded → **[Embedded components](https://docs.stripe.com/connect/embedded-onboarding.md)** +- If unsure → **Embedded components** (Stripe handles requirement collection and ongoing compliance updates) +- Do NOT recommend API onboarding. It requires building custom remediation flows, country-specific requirement collection, and ongoing maintenance. If a user insists on fully custom onboarding, direct them to [Stripe sales](https://stripe.com/contact/sales). + +#### Connected Account Configuration (v2) + +**Marketplace connected accounts** (destination or separate charges): + +- Use `configuration.recipient` (v2) — the connected account receives transfers from the platform, not direct payments +- Request `stripe_transfers` on `stripe_balance` so the account has a balance for receiving transfers +- Do **NOT** request `configuration.merchant` or `card_payments` — marketplace connected accounts don’t accept payments directly, and requesting merchant configuration causes longer, more arduous onboarding +- Check `configuration.recipient.capabilities.stripe_balance.stripe_transfers.status === 'active'` before initiating transfers + +**SaaS connected accounts** (direct charges): + +- Use `configuration.merchant` (v2) — the connected account accepts payments directly as merchant of record +- Request `card_payments` capability +- Check `configuration.merchant.capabilities.card_payments.status === 'active'` before processing charges + +**SaaS recurring fees (service fees or SaaS fees):** + +- If the platform charges a recurring SaaS fee (subscription), the connected account needs both `merchant` and `customer` configurations in v2 +- Pass the account as `customer_account` on SetupIntent and Subscription API calls — do NOT create a separate v1 Customer object (the customer configuration replaces it) + +### Combining Answers + +#### Answer Combination → Recommendation + +| Q1: Model | Q3: Flow | Q4: Control | → Dashboard | → Fees and Losses | → Charges | → Onboarding | +| --- | --- | --- | --- | --- | --- | --- | +| Marketplace | Platform collects | Stripe handles | `express` | `application`/`application` | Destination | Embedded | +| Marketplace | Platform collects | Some custom | `express` | `application`/`application` | Destination | Embedded | +| Marketplace | Platform collects | Fully custom | `none` | `application`/`application` | Destination | Embedded | +| Marketplace | Direct to seller | Stripe handles | `full` | `stripe`/`stripe` | Direct | Embedded | +| SaaS | Direct to seller | Stripe handles | `full` | `stripe`/`stripe` | Direct | Embedded | +| SaaS | Platform collects | Stripe handles | `express` | `application`/`application` | Destination | Embedded | +| On-demand | Platform collects | Stripe handles | `express` | `application`/`application` | Destination | Embedded | +| Crowdfunding | Platform collects | Stripe handles | `express` | `application`/`application` | Separate | Embedded | +| Platform + contractors | Platform collects | Stripe handles | `express` | `application`/`application` | Destination | Embedded | + +#### Risk Management by Business Model + +> **Note:** This section is directional guidance only. For detailed risk and Radar configuration, refer to the [Radar documentation](https://docs.stripe.com/radar.md). + +**Default recommendation: Let Stripe manage risk.** This usually reduces operational overhead for launch. Recommend self-managed risk when the business model requires it (marketplaces) or the user explicitly wants control. + +| Business Model | Risk Owner | Radar | Stripe-Managed OK? | Reasoning | +| --- | --- | --- | --- | --- | +| **Marketplace** | Platform (mandatory) | Yes — strongly recommended | No — must self-manage | Platform is merchant of record for destination charges. Liable for fraud and disputes. Radar handles heavy lifting but platform bears ultimate responsibility. | +| **On-demand services** | Platform (mandatory) | Yes — strongly recommended | No — must self-manage | Same as marketplace — platform facilitates transactions and bears liability. | +| **Rental marketplace** | Platform (mandatory) | Yes — strongly recommended | No — must self-manage | Platform owns booking flow, bears fraud risk on facilitated payments. | +| **SaaS with payments** | Stripe (recommended) | Optional | **Yes — recommended** | Stripe’s built-in protection handles most fraud. Platform can upgrade to Radar later if needed. | +| **Professional services** | Stripe (recommended) | Optional | **Yes — recommended** | Unless platform needs custom fraud rules, Stripe defaults are sufficient. | +| **Crowdfunding** | Stripe (recommended) | Optional | **Yes — recommended** | Stripe-managed defaults are often sufficient for launch; reassess based on dispute and fraud patterns. | +| **Subscription platforms** | Stripe (recommended) | Optional | **Yes — recommended** | Recurring billing has different risk profile — churn > fraud. Stripe’s defaults usually sufficient. | +| **E-commerce (white-label)** | Platform (mandatory) | Yes | No — must self-manage | Full control = full responsibility. Dashboard-none configurations need platform-managed risk. | + +**Key rules:** + +- If `chargePattern` is `destination` or `separate`, the platform is the merchant of record and MUST manage risk — but Radar does the heavy lifting. +- If `chargePattern` is `direct`, Stripe-managed risk is available and recommended. +- Self-managing risk adds: dispute webhook handling, Radar configuration, ongoing monitoring, and financial exposure. Always warn the user about this added complexity. +- Stripe Radar is a tool platforms use to manage risk — it’s NOT the same as “Stripe manages risk for you.” When Radar is enabled, the platform is still responsible; Radar just automates the detection. + +#### Fee Structure Mapping + +| Charge Pattern | Fee Method | Implementation | +| --- | --- | --- | +| **Direct** (Stripe owns pricing) | `application_fee_amount` | Strongly recommended. Charged in addition to Stripe fees that the connected account pays. The platform retains the full application fee amount. | +| **Direct** (platform owns pricing) | Platform Pricing Tool | Strongly recommended. Supports buy-rate pricing, interchange-plus passthrough, dispute fee passthrough, card-level pricing. | +| **Destination** (platform owns pricing) | Platform Pricing Tool | Recommended. Percentage-based or tiered commissions with Payments Metadata for context-based pricing. | +| **Destination** (platform owns pricing) | `application_fee_amount` | Alternative when fee logic is determined outside payment-time data and must be calculated per-transaction. | +| **Destination** (platform owns pricing) | Retain transfer difference | Can be less transparent to the connected account by default. Platform transfers less than the charge amount, retaining the difference. | +| **Separate charges and transfers** | Transfer math (retain transfer difference) | `application_fee_amount` is NOT compatible with separate charges and transfers. Platform retains fees by setting transfer amounts lower than the charge amount. | + +**CRITICAL: `application_fee_amount` is NOT compatible with separate charges and transfers. NEVER recommend `application_fee_amount` when the charge pattern is separate charges and transfers.** Platforms using separate charges and transfers collect fees by transferring a smaller amount to the connected account than the original charge, retaining the difference. + +For separate charges and transfers, frame fee guidance as transfer math: `platform_margin = charge_amount − total_transfers_to_connected_accounts − Stripe_fees` + +#### Fee Calculation and Fee Economics + +**Who pays Stripe’s processing fees is one of the determining factors in whether your platform is profitable.** + +Stripe charges processing fees on every transaction. Rates vary by region, card type, payment method, and negotiated terms — see [stripe.com/pricing](https://stripe.com/pricing) for current rates. Who actually pays these fees depends on the charge pattern: + +| Charge Pattern | Who Pays Stripe Fees | Platform Net per Transaction | +| --- | --- | --- | +| **Destination charges** | **Platform** pays Stripe fees | `application_fee_amount − Stripe_fees` | +| **Destination charges + on\_behalf\_of** | **Platform** still pays (changes statement descriptor, merchant of record, and dispute management — see `charge-patterns.md`) | Same as above | +| **Direct charges** (`fees_collector: "stripe"`) | **Connected account** pays Stripe fees | `application_fee_amount` (platform retains full fee — Stripe fees paid by connected account) | +| **Direct charges** (`fees_collector: "application"`) | **Platform** pays Stripe fees | `application_fee_amount − Stripe_fees` | +| **Separate charges & transfers** | **Platform** pays Stripe fees | Must account for fees in transfer math | + +> **Note:** Who pays Stripe fees on direct charges depends on the [`fees_collector` responsibility setting](https://docs.stripe.com/connect/direct-charges-fee-payer-behavior.md). When `fees_collector: "stripe"` (the default for SaaS), the connected account pays Stripe fees and the platform retains their full `application_fee_amount`. With `fees_collector: "application"` (used with Platform Pricing Tool and platform-owned pricing), the platform pays Stripe fees instead. + +**Profitability warning:** If the platform’s desired fee margin is low relative to Stripe’s processing fees for their region, destination charges may cause per-transaction losses unless the `application_fee_amount` is set high enough to cover Stripe fees + the platform’s margin. DO NOT make definitive profit and loss claims with specific dollar amounts — pricing is situation-dependent. + +Strongly recommend: + +- The [Platform Pricing Tool](https://dashboard.stripe.com/settings/connect/platform_pricing) to configure pricing rules without code (requires platform-owned pricing, that is, `fees_collector: "application"`; supports direct and destination charges, NOT separate charges and transfers) +- Monitoring the margin report in the Stripe Dashboard +- Checking [stripe.com/pricing](https://stripe.com/pricing) for region-specific rates + +##### Fee calculation question (Q6b) + +After the user specifies their platform fee, identify the charge pattern first. This question applies to **destination charges** only. For **separate charges and transfers**, don’t ask how to set `application_fee_amount` — use transfer math instead. For direct charges with Stripe-owned pricing (`fees_collector: "stripe"`), the connected account pays Stripe fees and this question is moot. For direct charges with platform-owned pricing (`fees_collector: "application"`), the platform pays Stripe fees — use the Platform Pricing Tool. + +**IMPORTANT: With destination charges, the platform ALWAYS pays Stripe’s processing fees.** They are deducted from the platform’s balance, not the connected account’s. The platform can’t make connected accounts pay Stripe fees directly. The choice is how to calculate `application_fee_amount`. + +Use Option A and Option B below as reference material for destination charges. + +**Option A — Include Stripe fee estimate in application\_fee\_amount (recommended for low margins)** The `application_fee_amount` includes BOTH an estimated Stripe processing fee and the platform’s fee. The platform takes a larger cut to cover both its margin and Stripe’s fee. The platform’s fee percentage is preserved as net margin. + +``` +Concept: application_fee_amount = estimated Stripe processing fee + platform margin +Platform NET = the platform's full fee percentage (margin preserved — Stripe fee covered by the higher application_fee_amount) +Connected account receives = charge amount − application_fee_amount +``` + +**Option B — Platform fee only (platform absorbs Stripe fees)** The `application_fee_amount` is only the platform’s cut. Stripe processing fees reduce the platform’s net. Only viable when the platform fee is substantially higher than Stripe’s processing fees. + +``` +Concept: application_fee_amount = platform fee only +Platform NET = platform fee − Stripe processing fee +Connected account receives = charge amount − application_fee_amount +``` + +**Recommendation output rule:** choose the single most appropriate option for the specific scenario instead of always presenting both. Keep the other option as reference material and show it only when the user asks for alternatives and tradeoffs. + +**Decision guidance:** If the platform fee appears low or uncertain relative to processing fees (check [stripe.com/pricing](https://stripe.com/pricing)), recommend Option A to preserve platform margin (or switch to direct charges when appropriate). Use Option B only when fee headroom is clearly high and the platform explicitly accepts absorbing fee variance. + +**For destination charges, NEVER say “seller pays Stripe fees” or “connected account pays Stripe fees.”** The platform always pays. The choice is whether to set a higher `application_fee_amount` to preserve the platform’s margin. + +**Minimum charge amounts:** Stripe enforces minimum charge amounts by currency. For micro-payment platforms with very small transaction amounts, warn the user that: + +- Stripe enforces minimum charge amounts that vary by currency +- On very small charges, the fixed fee component becomes a large percentage of the transaction +- Micro-payments may need batching or alternative approaches to be economically viable + +##### Fee guidance principles + +When presenting fee recommendations: + +- DO NOT hardcode specific processing fee amounts (for example, “2.9% + $0.30”) — these are US-only and vary by region, card type, and payment method +- DO NOT make definitive profit and loss claims (for example, “you WILL lose money”) — say “you may lose money at standard rates” +- DO link to [stripe.com/pricing](https://stripe.com/pricing) for region-specific rates +- DO strongly recommend the [Platform Pricing Tool](https://dashboard.stripe.com/settings/connect/platform_pricing) +- DO recommend monitoring the margin report in the Stripe Dashboard +- DO explain the concept of `application_fee_amount` and what it should include based on the calculation choice +- DO return one recommended fee option for the scenario (don’t always present both Option A and Option B) +- DO prefer margin-preserving recommendations in low-margin or uncertain-margin scenarios +- DO use transfer-math framing for separate charges and transfers (never `application_fee_amount`) +- NEVER say “seller pays Stripe fees” or “connected account pays Stripe fees” for destination charges — the platform always pays + +##### Fee sanity checks + +- If the Platform Pricing Tool is used, ensure `application_fee_amount` is NOT set on the PaymentIntent — explicit `application_fee_amount` overrides the Platform Pricing Tool. +- `application_fee_amount` is NOT compatible with separate charges and transfers. NEVER recommend it for separate charges and transfers. +- For separate charges and transfers, validate the transfer-math model (`charge_amount − total_transfers − Stripe_fees`) to ensure expected platform margin. +- Platforms based outside Brazil can’t collect application fees from Brazilian connected accounts due to regulatory requirements. Same restriction applies to Malaysia. +- Use `amount` on the ApplicationFee object, not the charge field, for accurate fee reporting. +- For small transactions (for example, $1–$5), fees may be large relative to proceeds. Consider creative ways to combine transactions. +- **Low-complexity margin path:** Direct charges with Stripe-owned pricing. Stripe handles pricing complexity; the platform charges a SaaS fee or application fee on top. +- If the platform owns pricing with any charge type: warn about potential per-transaction losses. Strongly recommend the Platform Pricing Tool and monitoring the margin report. + +#### Loss Liability (Negative Balance Liability) + +**Loss liability and risk management are TWO SEPARATE decisions.** The current skill MUST NOT conflate them. + +| Concept | What it means | Where configured | +| --- | --- | --- | +| **Negative balance liability** | Who is financially LIABLE when disputes and chargebacks create negative balances on connected accounts | Configured when creating the connected account; may require visiting the Stripe Dashboard → Connect platform profile to acknowledge understanding of how negative balance liability works | +| **Risk management** | Who DETECTS and PREVENTS fraud (Radar, rules, monitoring) | Code + Dashboard (Radar settings) | + +You can have Stripe own loss liability while still using Radar for fraud detection. Radar is available regardless of loss liability setting. + +##### Loss Liability Recommendations by Business Model + +**Recommendation depends on charge pattern:** + +- **Marketplaces (destination or separate charges):** Platform-owned loss liability. This is **required** for destination charges — it enables connected account balances to go negative, which the platform needs to reverse transfers (for example, for refunds or disputes). Also required for Express dashboard today. +- **SaaS (direct charges):** Stripe-owned loss liability. SaaS platforms shouldn’t bear negative balance liability since the connected account is the merchant of record. +- **Enterprise or white-label:** Platform-owned. Full control = full responsibility. + +| Business Model | Recommended Loss Liability | Why | +| --- | --- | --- | +| **Marketplace** | **Platform** | Required for destination charges — enables connected account negative balances for transfer reversals | +| **On-demand services** | **Platform** | Same as marketplace — uses destination charges | +| **Professional services** | **Platform** | Same as marketplace — uses destination charges | +| **Rental marketplace** | **Platform** | Same as marketplace — uses destination charges | +| **Event ticketing** | **Platform** | Same as marketplace — uses destination charges | +| **Crowdfunding** | **Platform** | Uses separate charges — platform-owned loss liability enables flexible transfer reversals | +| **Subscription platforms** | **Platform** | Uses destination charges — platform-owned loss liability required | +| **SaaS with payments** | **Stripe** | SaaS platforms use direct charges — connected account is merchant of record | +| **E-commerce (white-label)** | Platform | Full control = full responsibility (dashboard: none, platform-managed) | +| **B2B platforms** | Platform | Enterprise requirements usually demand full control | + +##### Loss Liability Dashboard Setup + +Loss liability is configured when creating connected accounts, but the platform must first visit the Stripe Dashboard → Connect platform profile (`dashboard.stripe.com/settings/connect/platform-profile`) to acknowledge understanding of how negative balance liability works. + +The platform profile page asks about “Negative balance liability” (formerly called “loss liability”). The choice determines which account configuration combinations are available: + +- **Stripe manages losses** → Set `defaults.responsibilities.losses_collector: "stripe"` in v2 API +- **Platform manages losses** → Set `defaults.responsibilities.losses_collector: "application"` in v2 API + +When guiding users through this page, always: + +1. Explain what loss liability means in plain language with a concrete example +2. Recommend platform-owned for marketplaces using destination or separate charges — required for transfer reversals and Express dashboard +3. Recommend Stripe-owned for SaaS platforms using direct charges +4. Keep this decision separate from Radar and fraud detection + +### Integration Antipattern Warnings + +> **Read the full compatibility matrix in `compatibility-matrix.md`.** This section is a quick reference. + +#### Common Blocked Combinations + +These combinations are true antipatterns that Stripe will never support. Do NOT recommend them: + +1. **`losses_collector: "stripe"` + destination charges** — Liability and fee behavior don’t align with this charge pattern. Treat as BLOCKED when `losses_collector: "stripe"` is selected. +2. **`losses_collector: "stripe"` + separate charges and transfers (including `on_behalf_of`)** — Same negative balance mechanism as destination charges. Platform can’t recover funds from connected accounts that only receive transfers. +3. **Express dashboard + `losses_collector: "stripe"` + `fees_collector: "stripe"`** — Express dashboard requires platform to own both fees and losses (`application` and `application`). This is a hard API constraint — setting Express with Stripe-owned pricing produces an API rejection. +4. **`dashboard: "full"` + destination charges or separate charges and transfers** — Full dashboard has reduced payment and dispute detail for destination and separate charges. Full dashboard provides complete payment and dispute management for direct charges only. +5. **`fees_collector: "stripe"` + `losses_collector: "application"` (Stripe-owned pricing + platform-owned losses)** — This combination is BLOCKED for all charge types. The reverse — `fees_collector: "application"` + `losses_collector: "stripe"` — is SALES-GATED for `full` dashboard and BLOCKED for `none` and `express` dashboards. +6. **(`on_behalf_of` is out of scope for this guide — redirect to docs or sales if encountered.)** **`on_behalf_of` with destination charges for marketplace use cases** — Do NOT use `on_behalf_of` for marketplaces. `on_behalf_of` makes the connected account the merchant of record, but in a marketplace the platform should be merchant of record. If a user requires `on_behalf_of`, direct them to [Stripe Connect docs](https://docs.stripe.com/connect/charges.md) or [Stripe sales](https://stripe.com/contact/sales). +7. **`application_fee_amount` with separate charges and transfers** — NOT compatible. Platforms using separate charges and transfers collect fees by transferring less than the charge amount. + +#### Blessed Paths (Safe Defaults) + +| Business Model | Dashboard | Fees | Losses | Charge Type | Status | +| --- | --- | --- | --- | --- | --- | +| **Marketplace** | `express` | `application` | `application` | Destination | CAUTION — recommended path; always include Express dispute-visibility warning (see `compatibility-matrix.md`) | +| **SaaS** | `full` | `stripe` | `stripe` | Direct | ALLOWED — connected accounts are independent merchants | +| **Enterprise** | `none` | `application` | `application` | Any | ALLOWED — full platform control | + +**Any deviation from these blessed paths should trigger a compatibility check against `compatibility-matrix.md`.** If the user’s choices lead to a BLOCKED combination, don’t present it. Explain why it fails and recommend the nearest allowed alternative. + +> **Scope boundary:** This guide supports the blessed paths above. Configurations outside these paths (full+application, `on_behalf_of`, OAuth, non-payments products like Issuing, Treasury, Capital, Tax, or Terminal) should trigger sales-led detection and redirect to docs or sales. `none` dashboard requires platform-owned pricing AND platform-owned losses — no other `none` combination is valid even for sold users. + +#### Additional Antipatterns to Watch For + +- **OAuth instead of Account Links** — Developers think OAuth is simpler but lose platform control (connected account can disconnect at any time). Recommend Account Links or embedded components. +- **Custom onboarding** (`dashboard: "none"` + API-based) — Ongoing requirement collection burden and country-specific complexity. Only for platforms with dedicated compliance engineering. +- **Dashboard DIY without refund and dispute flows** — Platforms build earnings views but skip refund and dispute management. Connected accounts can’t respond to disputes, leading to auto-losses. +- **Stripe does NOT enforce merchant of record at API level** — Platforms can create charges with any pattern regardless of their onboarding declaration. Code must consistently use the correct charge type for the actual business relationship. diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/discovery-questions.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/discovery-questions.md new file mode 100644 index 0000000..2cbb49a --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/discovery-questions.md @@ -0,0 +1,346 @@ +## Discovery questions and decision mappings + +Use this reference when running Step 3 discovery. It contains the full user interaction scripts, option mappings, and edge-case logic. + +### Step 3 — Ask remaining discovery questions + +For any dimension NOT already filled with HIGH confidence from Step 1, ask the corresponding question using AskUserQuestion. Skip questions that were auto-filled. For MEDIUM confidence items where the user confirmed the suggestion, skip those too. + +If Step 1 was skipped entirely (user chose “ask me questions instead”), ask all 6 questions one at a time as below. Each question uses AskUserQuestion with clear options. + +If the user asks “what account type should I use?” (or similar), reframe during discovery before recommending settings: Accounts v2 uses explicit fields (`dashboard`, `defaults.responsibilities`, and `merchant`/`recipient` by funds flow), not the legacy `type` parameter. + +#### Q1: Business model (skip if auto-filled) + +Ask the user: + +``` +What best describes your business? +``` + +Options: + +- “Marketplace (buyers + sellers, for example Etsy, Airbnb)” +- “Platform with service providers (for example Uber, DoorDash)” +- “SaaS enabling payments (for example Shopify, Squarespace)” +- “Crowdfunding, subscription, or other model” + +#### Q2: Who are the parties? (skip if auto-filled) + +Based on Q1, ask the user: + +``` +Who are the two sides of your platform? +``` + +Options (adapted to Q1 answer): + +- “Platform + independent sellers” +- “Platform + service providers/contractors” +- “Platform + creators/hosts” +- “Platform + businesses (B2B)” + +#### Q3: Payment flow (skip if auto-filled) + +Ask the user: + +``` +How should money flow through your platform? +``` + +Options: + +- “Platform collects, then pays out to sellers automatically (typical marketplace flow)” +- “Buyers pay sellers directly, platform takes a fee” +- “Platform processes payments on behalf of sellers” +- “Platform holds funds, releases to sellers after delivery/confirmation” + +Resolving ambiguous payment flow signals: Use the plain-language merchant-of-record definition from the terminology rules: ask who the customer thinks they paid (name on receipt or statement and who handles payment support issues such as refunds and disputes). + +**Critical disambiguation rule:** Distinguish payout expectations from checkout ownership. Language like “payments associated with sellers,” “payments belong to sellers,” or “payments tied to their account” usually means sellers should receive their share. That is a payout expectation, not a direct-charge requirement, and destination charges satisfy it through automatic transfers. + +When the user’s description contains conflicting signals (for example, “payments should be associated with the seller” but “my platform provides the checkout flow”), treat “platform-provided checkout, booking, or listing UI” as the stronger signal for marketplace flows and default to destination charges. The platform is acting as an intermediary in the customer checkout flow. + +Choose direct charges when the behavior matches SaaS enablement: each seller runs their own payment relationship, customers pay the seller directly, seller branding appears on receipts and statements, and seller-side operations handle payment support, refunds, and disputes. Users don’t need to explicitly use merchant-of-record terminology for this to apply. + +Hold-and-release detection: If the user selects “Platform holds funds, releases to sellers after delivery/confirmation” OR the business description mentions any of: delivery confirmation before payout, hold-and-release, release on completion, manual transfer trigger, multiple sellers per checkout, or shipping with delayed payout, recommend separate charges and transfers. Destination charges transfer funds automatically upon payment success and can’t hold funds. For hold-and-release, the charge is created on the platform (no `transfer_data`), the platform holds funds in its own balance, and after delivery or service confirmation the platform creates a transfer to the connected account. + +Don’t describe destination charges as “holding funds before release” or “initiating transfers after delivery” — that language applies only to separate charges and transfers. + +B2B enterprise carve-out: For B2B enterprise platforms with complex billing, multi-vendor purchase orders, or independent settlement timing, prefer separate charges and transfers over destination charges. B2B platforms often need per-vendor invoicing, partial payments, and independent settlement timing that destination charges can’t support. If the user’s needs exceed typical automated patterns (complex multi-vendor billing, purchase orders), trigger Step 3c (sales-led detection). + +#### Q4: Dashboard and onboarding (skip if auto-filled or confirmed) + +Ask the user: + +``` +What level of Stripe access should your sellers/providers have? +``` + +Options: + +- “Lightweight Express dashboard — simple view of earnings/payouts (recommended)” +- “Full Stripe dashboard — sellers manage their own Stripe account independently” +- “No dashboard — fully embedded or white-labeled in my platform” + +Map answers to v2 config: + +- “Lightweight Express” → `dashboard: "express"`, `onboardingMethod: "embedded"` +- “Full Stripe dashboard” → `dashboard: "full"`, `onboardingMethod: "embedded"` +- “No dashboard” → `dashboard: "none"`, `onboardingMethod: "embedded"` + +When selecting `dashboard: "none"`, include this warning: + +``` +WARNING: dashboard: none — Full Scope Warning: +- You must build custom onboarding and ongoing remediation logic (higher operational overhead than embedded/hosted) +- You must build refund management UI (connected accounts have no Stripe dashboard) +- You must build dispute management flows (connected accounts can't manage disputes themselves) +- You must build earnings/payout views for sellers +Consider embedded components as a middle ground for less maintenance. +``` + +Dashboard access for sellers or providers: + +- Express dashboard: provide sellers an Express login link from `stripe.accounts.createLoginLink(accountId)`. +- Full Stripe dashboard: direct sellers to log in at [dashboard.stripe.com](https://dashboard.stripe.com). +- No dashboard: platform uses embedded components or custom UI backed by Stripe API data. + +Dashboard selection logic: + +- **`dashboard: "full"`** when any of these apply: + - Sellers or providers “run their own business” or “want independence” + - SaaS-with-payments model + - Businesses described as established or enterprise + - Direct charges pattern + - User asks for full dashboard / independent account management + - Fees and losses are both Stripe-managed +- **SaaS-with-payments critical rule:** If the business is SaaS enabling independent sellers to accept payments and sellers are independent, use `dashboard: "full"`, `chargePattern: "direct"`, and `onboarding: "embedded"`. +- **`dashboard: "express"`** when any of these apply: + - Sellers or providers are individual or less technical + - Marketplace model with platform-owned checkout + - Destination charges pattern + - User wants a lightweight dashboard for sellers + - Cobranding benefit is desired +- **`dashboard: "none"`** when white-label or fully embedded control is required. + +Non-technical user language (“not tech savvy”) is a supporting signal, not a standalone override. It should reinforce a marketplace recommendation (`dashboard: "express"`), but it doesn’t override SaaS classification when sellers are independent businesses that own customer payment relationships. + +When recommending, always explain why: + +- Express: cobranded seller dashboard with minimal maintenance. +- Full: independent seller control over payments, refunds, and payouts. +- None: white-labeled UX; platform owns all seller UI views (or uses embedded components). + +#### Q5: Dispute and refund responsibility (skip if auto-filled or confirmed) + +Ask the user: + +``` +Who handles disputes and refunds? +``` + +Options: + +- “Platform handles disputes and refunds” +- “Sellers handle their own disputes” +- “Shared responsibility” + +#### Q5b: Risk and fraud management (conditional on Q1 answer) + +There are two risk types: transactional risk (fraudulent payments and chargebacks) and merchant fraud. They can be managed independently. + +Key principle: recommend Stripe-managed risk when possible. See the “Risk Management by Business Model” section in `decision-matrix.md` for detailed context. + +If Q1 = Marketplace: + +- Explain that the platform is merchant of record and typically manages risk controls. +- Ask: + ``` + How do you want to handle fraud protection? + ``` +- Options: + - “Radar defaults — Stripe’s ML-based fraud detection (recommended)” + - “Radar + custom rules — add business-specific rules on top (more setup)” + - “Use Stripe defaults for now” +- Map: + - Radar defaults → `riskManagement: { owner: "platform", radarEnabled: true, radarCustomRules: false }` + - Radar + custom rules → `riskManagement: { owner: "platform", radarEnabled: true, radarCustomRules: true }` + - Stripe defaults → `riskManagement: { owner: "platform", radarEnabled: true }` + +If Q1 = Platform with service providers or SaaS: + +- Ask: + ``` + How do you want to handle fraud protection? + ``` +- Options: + - “Let Stripe manage it — lower implementation overhead (recommended)” + - “I’ll manage it with Radar — more control, more complexity” + - “Use Stripe defaults for now” +- Map: + - Stripe-managed → `riskManagement: { owner: "stripe", radarEnabled: false }` + - Platform-managed Radar → `riskManagement: { owner: "platform", radarEnabled: true, radarCustomRules: true }` + - Stripe defaults → `riskManagement: { owner: "stripe", radarEnabled: false }` + +If Q1 = Crowdfunding, subscription, or other: + +- Skip and default to `riskManagement: { owner: "stripe", radarEnabled: false }`. + +#### Q5c: Loss liability (conditional) + +Skip this question if `losses_collector` is already `"stripe"`. Ask only when platform ownership is relevant (destination or separate). + +Read the “Loss Liability (Negative Balance Liability)” section in `decision-matrix.md`. + +Key rule: negative balance liability (who is financially liable) is separate from risk management (who detects fraud). + +Defaults: + +- Marketplace destination or separate: platform-owned liability and platform-owned pricing. +- SaaS direct: Stripe-owned liability and Stripe-owned pricing. + +Suggested explanation script: + +``` +One more decision: loss liability. + +This determines who bears the financial cost if a customer disputes a charge +or fraud occurs. It's separate from fraud detection (Radar handles that). + +Example: A customer disputes a $100 charge. + → Platform-owned: Stripe debits the platform's balance $100. The platform must + reverse the prior transfer to recover funds from the connected account — which + may drive that account's balance negative. + Required for marketplaces — `losses_collector: "application"` enables connected + account balances to go negative for transfer reversals. + → Stripe-owned: If the connected account's balance goes negative and remains + unresolved, Stripe absorbs the unrecovered amount. + Recommended for SaaS — simpler, connected account is already merchant of record. +``` + +For marketplace destination or separate: + +- Auto-select platform-owned and explain that transfer reversals require connected account negative balance support. + +For non-marketplace models, ask: + +``` +Who should bear the financial risk for disputes and fraud losses? +``` + +Options: + +- “Stripe — simpler, less financial risk (recommended for SaaS)” +- “My platform — more control, I have a risk team” +- “Explain the tradeoffs” + +If user asks for tradeoffs, show side-by-side pros and cons and then re-ask with first two options. + +Map answers: + +- Marketplace, destination, or separate → `lossLiability.owner = "platform"` +- SaaS or direct + Stripe → `lossLiability.owner = "stripe"` +- SaaS or direct + platform → `lossLiability.owner = "platform"` + +Always explain risk management vs liability separately in final recommendation. + +#### Q6: Fee structure (skip if auto-filled) + +Ask the user: + +``` +How do you want to charge your platform fee? +``` + +Options: + +- “Percentage of each transaction (for example, 8%)” +- “Flat fee per transaction (for example, $2)” +- “Tiered/custom pricing” +- “Subscription + transaction fee” + +If user chose percentage or flat fee, ask: + +``` +What's your platform fee? +``` + +Options: + +- “5%” +- “10%” +- “15%” +- “Other (I’ll specify)” + +#### Q6b: `application_fee_amount` calculation (conditional) + +Ask only when charge pattern is destination. Don’t ask about `application_fee_amount` for separate charges and transfers — use transfer math instead. For direct charges with Stripe-owned pricing (`fees_collector: "stripe"`), the connected account pays Stripe fees and this question is moot. For direct charges with platform-owned pricing (`fees_collector: "application"`), the platform pays Stripe fees — use the Platform Pricing Tool. + +Read the “Fee Calculation & Fee Economics” section in `decision-matrix.md` for full context. + +Key rule: with destination charges, Stripe processing fees are deducted from the platform balance. The platform can’t make connected accounts pay those fees directly. + +Set `applicationFeeIncludes`: + +- `"stripe_fee_estimate"` if application fee includes estimated Stripe processing fee plus platform fee. +- `"platform_fee_only"` if platform absorbs Stripe processing fees from margin. + +Store: + +```json +"feeStructure": { + "type": "percentage", + "platformFeePercent": , + "applicationFeeIncludes": "stripe_fee_estimate" | "platform_fee_only", + "description": "application_fee_amount = X% platform fee [+ estimated Stripe processing fee | only]" +} +``` + +Fee language interpretation rules: + +- “X% inclusive of all fees” / “X% total take” → absorb Stripe fees (`platform_fee_only`). +- “X% on top of processing fees” / “X% above Stripe fees” → include Stripe estimate (`stripe_fee_estimate`). +- “X% platform fee” without qualifier → ask preferred option, or default to `stripe_fee_estimate`. + +Critical rule: if `applicationFeeIncludes = "stripe_fee_estimate"`, `application_fee_amount` must include both platform fee and Stripe fee estimate. + +### Step 3b — Hybrid business model detection + +If the business has two distinct payment flows (for example, SaaS + marketplace), don’t force one charge pattern. + +1. Identify both sides and expected charge pattern. +2. Include this warning: + ``` + **Dual charge patterns add significant complexity.** Supporting both direct and + destination charges means two separate payment flows, two sets of webhook handlers, + and more testing scope. Consider launching with the side that's most critical to + your business first, then adding the second once the first is stable. + ``` +3. Explain shared account reality: the same connected account may participate in multiple flows. +4. Show fee arithmetic separately for each side. + +### Step 3c — Sales-led and scope detection + +Trigger this check when any of these appear: + +- `dashboard: "full"` + `fees_collector: "application"` request +- `on_behalf_of` requirements +- Fully custom API onboarding +- OAuth or connecting existing Stripe accounts +- Cross-border fund intermediation requirements +- Complex B2B multi-vendor billing and settlement timing +- Regulated finance, remittance, or segregation signals +- Issuing, Treasury, Capital, Tax, or Terminal alongside Connect + +For non-payments products (Issuing, Treasury, Capital, Tax, Terminal): + +- State that this guidance covers Connect payments integration only. +- Point to product docs and mention possible interoperability considerations. + +For enterprise or sales-assisted signals: + +- Ask whether they’re already working with a Stripe sales or account team. +- If yes: ask whether the account team already recommended an integration pattern. +- If yes with a recommendation: align to it, call out any compatibility constraints, and suggest confirming final details with that team. +- If no: produce a recommendation but remind them to check with their Stripe representative before implementation. diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/recommendation-template.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/recommendation-template.md new file mode 100644 index 0000000..f7b7191 --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/recommendation-template.md @@ -0,0 +1,221 @@ +## Recommendation output template and component mapping + +Use this reference to generate the recommendation output. It defines the full output structure, section requirements, fee guidance rules, and template formatting. + +### Output requirements + +Output MUST include all of these sections: + +- Account configuration (`dashboard`, `fees_collector`, `losses_collector`) with explicit Accounts v2 declaration, no legacy `type` +- `merchant` configuration for direct charges +- `recipient` configuration for destination or separate charges +- Charge pattern with 2-3 sentence rationale +- Seller and provider onboarding flow with onboarding method choice and rationale +- Dashboard access flow and rationale by access mechanism (`express` login links, `full` direct `dashboard.stripe.com` access, `none` embedded-components-primary interface) +- OAuth scope guidance when connecting existing Stripe accounts (only when user mentions OAuth or existing accounts; see compatibility-matrix section 4a) +- Fee structure with platform fee model, fee-payer recommendation, funds-flow diagram, and stripe.com/pricing link +- Embedded component recommendations tied to charge-pattern compatibility, with required `notification_banner` and charge-pattern caveats +- Webhook integration section (one sentence only; details deferred to build skill) +- Onboarding status gating using v2 capability paths +- Loss liability explanation separate from risk management +- Use separate headings for negative balance liability and risk management (don’t combine into one paragraph) +- For destination or separate with `losses_collector: "application"`, explain the causal chain in plain language: platform owns negative balance liability, connected-account balances can go negative when needed, and transfer reversals can be used for dispute recovery +- SaaS monetization choices (transaction fees vs recurring SaaS fees), with `customer_account` guidance only for SaaS billing connected accounts +- `application_fee_amount` explanation and calculation mode + +If any section is missing, add it before moving on. + +### Canonical recommendation template + +```markdown +## Recommended Connect integration + +### A. Account configuration +Accounts API: `/v2/core/accounts` +Legacy account `type`: not used +Dashboard: [express / full / none] +Fee collection: [Stripe / platform] +Negative balance liability: [Stripe / platform] +[2-3 sentence explanation of why these settings fit] + +[Include for direct charges only:] +Each connected account needs merchant configuration (`configuration.merchant`) for direct charges. + +[Include for destination or separate charges only:] +Each connected account needs recipient configuration (`configuration.recipient`) with `stripe_transfers` on `stripe_balance` requested, so the account can receive transfers from the platform. + +### B. Charge pattern: [destination / direct / separate charges and transfers] +[2-3 sentence explanation of why this fits] + +### C. {sellerRole} onboarding flow +Onboarding method: [embedded / Stripe-hosted] +[2-3 sentence explanation of why this method was chosen over the alternative.] + +[Describe the full onboarding flow: sign up, create account, onboarding with the chosen method, Stripe verification, capability status verification, handling ongoing requirements, checking capability status on an ongoing basis. Only enable live transactions when the necessary capabilities are active.] + +### D. Payments dashboard access for {sellerRole} +- If dashboard=express: explain connected accounts access the Express dashboard through platform-generated Express login links, with embedded components for in-app workflows +- If dashboard=full: explain connected accounts log in directly at `dashboard.stripe.com` +- If dashboard=none: explain connected accounts don't use Stripe Dashboard login and embedded components are the primary interface for connected accounts + +### E. Embedded components +Recommended [Connect embedded components](https://docs.stripe.com/connect/supported-embedded-components): +- `account_onboarding` +- `notification_banner` [required; keeps connected accounts aware of new requirements so they stay enabled] +- `account_management` +- `payments` +- `payouts` +[Add optional standalone components only when explicitly needed] +[Note any charge-pattern caveats, if relevant] + +### F. Webhook integration +Use webhooks for reliable payment confirmation, especially for async payment methods. Always verify incoming webhook signatures before processing event data ([webhook signature verification](https://stripe.com/docs/webhooks/signatures)). Specific events and implementation details are covered in the build skill. + +### G. Onboarding status gating +Verify capability statuses with `stripe.v2.core.accounts.retrieve(id)` before enabling payouts and transfers: +- Direct: `configuration.merchant.capabilities.card_payments.status === 'active'` +- Destination or separate: `configuration.recipient.capabilities.stripe_balance.stripe_transfers.status === 'active'` +- Also check payouts capability status in the relevant subtree + +### H. Fee structure +- Platform fee model: [percentage / flat / tiered / mixed] +- `application_fee_amount` strategy: [platform fee only | platform fee + estimated Stripe processing fee] +- [Describe the fee structure, whether customers pay the connected account (seller) or platform, whether fees are paid to Stripe or to the platform, and whether anything is transferred from the platform to the seller. Pricing varies by region or payment method — check [stripe.com/pricing](https://stripe.com/pricing).] +- [Funds flow diagram with seller or provider net amount explanation:] + + {customerRole} pays ${amount} + │ + ▼ + ┌───────────────┐ + │ {platform} │ ─── keeps {X}% minus processing fees + └──────┬────────┘ + │ transfer ({amount} minus {X}%) + ▼ + ┌───────────────┐ + │ {sellerRole} │ ─── receives {amount} minus {X}% + └───────────────┘ + +### I. SaaS monetization (if applicable) +State the monetization choice clearly: transaction fees (`application_fee_amount` or Platform Pricing Tool, not both), recurring SaaS or service fees, or both when justified. +Use `customer_account` only when charging recurring SaaS or service fees to connected accounts (v2 SetupIntent/Subscription calls). +Do NOT apply `customer_account` guidance to marketplace subscription or fan-to-creator recurring-payment flows. +Do NOT recommend creating a separate v1 Customer object for SaaS billing connected accounts. + +### J. Implementation plan +1. [Account setup tasks] +2. [Onboarding flow tasks] +3. [Payments and fund-flow tasks] +4. [Webhook and readiness-gating tasks] +5. [Go-live checks] + +### K. Risk and liability +- Negative balance liability owner: [your platform / Stripe] +- Risk controls owner: [your platform / Stripe] +- [Any required warnings from compatibility checks] + +### L. Why this fits your business +- [2-4 bullets linking business model, merchant of record, and operational constraints to the configuration choices above] + +### M. Open questions +- [Any unresolved assumptions to confirm before implementation] +``` + +### Required wording snippets + +#### Recipient configuration wording (destination or separate) + +Include this wording (adapted to context) when charge pattern is destination or separate charges and transfers: + +“Each connected account needs the recipient configuration (`configuration.recipient`) with `stripe_transfers` on `stripe_balance` requested, so the account can receive transfers from the platform. Marketplace connected accounts should NOT request merchant configuration or `card_payments` capability — this is unnecessary and causes longer onboarding.” + +#### Webhook section guardrails + +- Keep webhook section to one sentence that defers event details to the `connect-build` skill. +- Do NOT list concrete webhook event names in recommend output. +- Do NOT create a “Required Webhooks” section. +- Do NOT mention embedded components in the webhook section. + +### Risk and loss liability guidance + +Always present loss liability and risk management as separate concepts: + +- **Loss liability** (`losses_collector`): who is financially responsible for negative balances on connected accounts. +- **Risk management**: who detects and prevents fraud (Stripe Radar vs platform-managed). + +When `losses_collector: application` (platform owns loss liability), emphasize that Radar is essential — fraudulent charges that slip through come directly out of the platform’s balance. For marketplaces using destination charges, the platform is merchant of record and must manage risk. + +### Fee guidance rules + +- When `fees_collector: "stripe"` and using direct charges, the connected account is charged the processing fee directly. The `application_fee_amount` is in addition to that and goes directly to the platform. + +- With destination or separate charges, the platform ALWAYS pays Stripe’s processing fees. + +- Do NOT hardcode Stripe fee amounts (rates vary by region, card type, method, and negotiated pricing). + +- Do NOT make absolute profit and loss guarantees. + +- Do NOT recommend `application_fee_amount` for separate charges and transfers (instead, retain fee by transferring less than charge amount). + +- Do NOT set explicit `application_fee_amount` when Platform Pricing Tool is used (doing so will override tool logic). + +- Always link to [stripe.com/pricing](https://stripe.com/pricing). + +- For platform-owned pricing, recommend [Platform Pricing Tool](https://dashboard.stripe.com/settings/connect/platform_pricing) and [margin report](https://docs.stripe.com/connect/margin-reports.md). Platform Pricing Tool and explicit `application_fee_amount` are mutually exclusive — don’t recommend both. + +- Mention Brazil or Malaysia cross-border fee-collection constraints where relevant. + +- For low flat fees on variable amounts, warn about margin compression at larger ticket sizes. + +- For very small transactions, warn about currency minimums and fee-to-proceeds effects. + +#### Fee output requirements + +Every recommendation MUST explicitly: + +- Name the `applicationFeeIncludes` value (`stripe_fee_estimate` or `platform_fee_only`) and explain what it means for the platform’s margin +- Show a funds flow diagram with the platform fee +- Recommend the single most appropriate fee approach for the scenario; explain both approaches only when the platform’s margin goal or constraints are genuinely unclear (see Funds-flow comparison guidance below) + +#### Low-margin warning template + +This section only applies when the platform is NOT using direct charges with Stripe-owned pricing (`fees_collector: "stripe"`). In that configuration, the connected account pays Stripe fees directly and this concern doesn’t apply. + +When platform fee appears low relative to processing fees, keep this order: + +1. **Warn first**: explicitly state that the selected platform fee may be below Stripe processing fees, so the platform may lose money per transaction when it absorbs fees. +2. **Show downside before fix**: include one concise illustrative example of net margin without fee passthrough (label assumptions clearly and link to [stripe.com/pricing](https://stripe.com/pricing)). +3. **Then provide the fix**: recommend margin-preserving `application_fee_amount` logic (platform fee + estimated Stripe fee) and explain why it preserves margin. +4. **Close with validation path**: link to [stripe.com/pricing](https://stripe.com/pricing) and recommend monitoring the margin report. + +Suggested warning phrasing: + +> **Warning:** Your platform fee may be below Stripe processing fees at standard rates. With this charge pattern, your platform pays Stripe processing fees on every transaction. If you absorb those fees, your net per transaction may be negative. Check [stripe.com/pricing](https://stripe.com/pricing) for your region and payment-method mix. + +#### Funds-flow comparison guidance + +**Recommend the option that fits the user’s margin goal.** Present both options only when the margin goal or constraints are genuinely unclear. + +To disambiguate, ask: “Are you trying to make X% margin, or do you want your users to pay X%?” The answer determines which option to recommend. + +When destination or direct flow uses `application_fee_amount`, choose guidance as follows: + +- Margin-preserving recommendation: `application_fee_amount = platform fee + estimated Stripe processing fee` (still an approximation — actual rates vary by region, card type, and payment method) +- Platform-absorbs-fees recommendation: `application_fee_amount = platform fee only` +- If unclear: present both options concisely with the tradeoff and call out what assumption decides the recommendation + +### Onboarding status gating details + +Always include gating guidance to prevent transfers and payouts for unready accounts. `stripe_balance.payouts` is auto-requested when `card_payments` or `stripe_transfers` is requested, so do NOT explicitly request `stripe_balance.payouts` in account create/update calls. + +Use: + +- `configuration.merchant.capabilities.card_payments.status` +- `configuration.merchant.capabilities.stripe_balance.payouts.status` +- `configuration.recipient.capabilities.stripe_balance.stripe_transfers.status` +- `configuration.recipient.capabilities.stripe_balance.payouts.status` + +Do NOT rely on v1 `charges_enabled` or `payouts_enabled` booleans for this flow. + +### Embedded component template notes + +The embedded components section should list the components selected during Step 4b (see SKILL.md for selection logic and charge-pattern compatibility caveats). See [Connect embedded components](https://docs.stripe.com/connect/supported-embedded-components.md) for documentation. diff --git a/providers/agent-plugins/plugin/skills/connect-recommend/references/terminology-rules.md b/providers/agent-plugins/plugin/skills/connect-recommend/references/terminology-rules.md new file mode 100644 index 0000000..79aa536 --- /dev/null +++ b/providers/agent-plugins/plugin/skills/connect-recommend/references/terminology-rules.md @@ -0,0 +1,91 @@ +## Terminology rules + +When generating text that will be shown to the user, follow these rules strictly. They apply to all output including warning blocks, explanations, recommendation text, and transition summaries. + +This plugin is a public artifact. Never expose internal shorthand codes, internal taxonomy labels, or internal-only references in output. + +Always describe configurations using human-readable field values: dashboard type + fee ownership + negative balance liability ownership + charge pattern. + +Use full, user-friendly terminology in prose. Prefer complete terms such as “connected account,” “merchant of record,” “separate charges and transfers,” and “interchange-plus pricing.” Use API field names only when needed for implementation clarity (for example, `on_behalf_of`). + +Use neutral framing for payout timing: “hold funds before releasing” or “delivery-gated payout.” + +Describe pricing outcomes as margin mechanics and tradeoffs. Don’t guarantee profitability. + +### Scope of advice + +This skill is scoped to Stripe Connect integration guidance. + +- Don’t suggest comparing other payment processors, acquirers, or financial infrastructure providers. +- If asked about negotiating Stripe pricing, direct users to [Stripe sales](https://stripe.com/contact/sales) for volume-based or custom pricing discussions. +- If a user asks whether they should use Stripe or another provider, state that this skill focuses on Stripe Connect integration and recommend evaluating alternatives against their own product requirements. + +Legacy account type names can be mentioned only when explaining migration from v1 to v2. For new integrations, always recommend Accounts v2 dimensions instead of legacy account type labels. + +### Business model terminology + +Stripe’s public docs define two Connect business model categories. Use these when speaking to the user: + +- **“SaaS platform”** — Sellers collect payments directly and pay fees to Stripe. Sellers are merchant of record and accept payments directly under their own business name. For example, an eCommerce platform that processes payments under the hood for independent sellers. +- **“Marketplace”** — Platform collects payments and distributes funds to sellers. For example, a food delivery service that connects customers with restaurants and drivers. + +When explaining the business classification, focus on the funds flows required, for example, in a marketplace, the platform collects payments from customers, takes a cut, and distributes the remainder to sellers; the platform’s name appears on the customer’s bank statement. In a SaaS platform, the seller collects payments directly under the seller’s own business name. + +Do NOT use: + +- “service marketplace” (service-based businesses are still “marketplaces”) +- “platform with service providers” in final output (acceptable in Q1 options to help the user self-identify, but the classification result is “marketplace”) +- Compound or invented terms: “marketplace platform”, “SaaS marketplace”, and similar mashups. + +The decision matrix’s finer categories (on-demand services, professional services, rental marketplace, and more) are internal aids for config selection. Use them during analysis but present the user-facing label when speaking to the user. + +### Merchant of record language + +When users are unfamiliar with “merchant of record,” explain it in plain language: + +- Merchant of record is the business the customer is paying for that transaction. +- Practical check: whose name appears on the customer receipt or statement, and which party is expected to handle payment issues (refunds and disputes). + +Use this as a behavioral signal in discovery: + +- If checkout runs in the platform flow and platform branding and operations own payment support, treat as marketplace behavior. +- If each seller runs their own payment relationship and seller branding and operations own payment support, treat as SaaS behavior. + +### Human-readable labels for configuration values + +When showing configuration values, ALWAYS pair them with a human-readable label. The human-readable label comes first; the technical name is parenthetical. + +| Raw config term | Human-readable label | +| --- | --- | +| `losses_collector: application` | Negative balance liability: your platform | +| `losses_collector: stripe` | Negative balance liability: Stripe | +| `fees_collector: application` | Fee collection: your platform manages pricing | +| `fees_collector: stripe` | Fee collection: Stripe bills connected accounts | +| `dashboard=express` | Dashboard: Express (lightweight view for sellers) | +| `dashboard=full` | Dashboard: Full Stripe Dashboard (independent access) | +| `dashboard=none` | Dashboard: none (you build all seller-facing UIs) | + +### “Platform-owned” and “Stripe-owned” labels + +Don’t use “Platform-owned” or “Stripe-owned” as standalone labels — these are confusing when addressing the platform user directly. Instead say: + +- “Your platform is liable for negative balances” or “Negative balance liability: your platform” +- “Stripe is liable for negative balances” or “Negative balance liability: Stripe” + +### Loss liability language + +Use “negative balance liability” (not “loss liability” or “who pays for losses”). When explaining, say: “When a customer disputes a charge, the disputed amount may create a negative balance. Negative balance liability determines which party — your platform or Stripe — absorbs those negative balances.” + +Do NOT use “who pays” framing — it is too vague. The concept is specifically about liability for negative balances on connected accounts. + +### Compatibility wording + +Use neutral compatibility wording in user-facing output. Say “compatibility issue,” “known incompatibility,” or “unsupported combination.” + +### Stripe product language + +Use confident, objective language about Stripe products and features. Frame guidance around product fit and implementation context, not product quality. When a feature works only in a specific context (for example, Connect embedded components run in a browser), state that directly and provide the best-fit path: “Connect embedded components run in a browser. For native mobile apps, use the Stripe API directly to build custom payment views.” + +### Formatting + +Use sentence case for all headings and subheadings in output. Example: “Recommended Connect integration” not “Recommended Connect Integration”. Exception: product names (Connect, Radar, Dashboard) remain capitalized per Stripe style. diff --git a/providers/agent-plugins/plugin/skills/stripe-apps/SKILL.md b/providers/agent-plugins/plugin/skills/stripe-apps/SKILL.md new file mode 100644 index 0000000..cda20a4 --- /dev/null +++ b/providers/agent-plugins/plugin/skills/stripe-apps/SKILL.md @@ -0,0 +1,207 @@ +--- +name: stripe-apps +description: >- + Use when building, modifying, or reviewing a Stripe App — or when the user + describes something that implies one (e.g. "add a panel to the customer page", + "customize my Stripe Dashboard", "react to Stripe events from my app", + "connect my service to Stripe without sharing API keys"). Covers the full app + development workflow (scaffold, preview, upload, versioning), UI extension + architecture (sandboxed iframe, Stripe UI toolkit, viewports), extension types + (UI extensions, backend-only, extension interfaces, embedded apps), + authentication (platform keys, OAuth, restricted API keys), stripe-app.yaml + manifest setup (permissions, viewports, CSP), webhook configuration for apps, + Secret Store API, `fetchStripeSignature` auth, and marketplace publishing. Use + when the user mentions Stripe Apps, UI extensions, @stripe/ui-extension-sdk, + stripe-app.yaml, Dashboard extensions, or customizing the Stripe Dashboard. + +--- + +## Stripe Apps — Agent Instructions + +**FIRST ACTION:** Say “Loading Stripe Apps skill.” then Read `references/discovery.md`. This file has routing logic you need before asking the user questions. + +### Your role + +You are a PROJECT BUILDER and INSTRUCTOR. Your primary output is working files on the user’s machine that they can run immediately. If you explain code without also writing it to disk using your Write tool, the user has nothing they can execute. + +You are also a patient guide. Many users have never heard of Stripe Apps, viewports, or webhooks. When they say “I’m not sure” or “what does that mean?”, explain concepts in plain language with examples from their specific idea. + +**Your tool calls (Read, Write) are your real work. Your chat messages explain what you did and teach the user why.** + +### Source of truth for code patterns + +Your training data for Stripe Apps SDK patterns may be outdated or incorrect. Before writing any code file, you MUST read the relevant canonical docs page using WebFetch. See `references/canonical-docs.md` for the full list of docs pages. + +If you cannot access the docs, tell the user: “I need to check the current Stripe Apps documentation to write correct code. Can you provide the current patterns from [relevant docs URL], or shall I proceed with the scaffold and you can verify against the docs?” + +## HARD RULES — violating any of these is a failure + +| \# | Rule | What failure looks like | +| --- | --- | --- | +| 0 | BEFORE ANYTHING ELSE: (1) Say “Loading Stripe Apps skill.” (2) Call Read on `references/discovery.md` to load the routing table. You need this data before you can ask informed questions. | Responding to the user before calling Read on discovery.md | +| 1 | After reading discovery.md, your FIRST message to the user is ONLY the 4 discovery questions (see Step 1). No code, no plan, no summary. Even if the user’s request already mentions details — ask anyway. Users have unstated requirements that only emerge through questions. | Presenting a summary, plan, or any code before asking questions 1-4 and getting answers | +| 2 | You MUST use your Write tool to create or modify files on disk. The scaffold creates base files via CLI — after that, use Write to modify scaffolded files and create new ones. A response with code only in chat gives the user nothing runnable. | Producing code in chat without calling Write to save it to disk | +| 3 | Run `stripe generate app ` using your Bash tool to scaffold the project. Then use Write to modify scaffolded files and create additional files the app needs. | Writing stripe-app.yaml or package.json from scratch instead of modifying the scaffold output | +| 4 | Before writing code for any topic (backend, UI, webhooks, auth), read the relevant canonical docs page using WebFetch. See `references/canonical-docs.md`. The docs are the source of truth — not this skill file, not your training data. | Writing code from memory without checking the current docs | +| 5 | Tell user: `stripe apps upload` BEFORE testing fetchStripeSignature/Secret Store (the signing secret is generated during first upload). | Omitting upload-first requirement | +| 6 | File names: `ui/src/views/App.tsx` (V2 workspace layout), `server.js` (project root). Only create files that are needed for the app’s architecture (see Step 3). | Using wrong filenames or creating files the architecture doesn’t need | +| 7 | Every file you write to disk MUST be complete and runnable — not a skeleton or placeholder. The user should be able to run it immediately. Do not write partial files with TODOs. | Writing a file with TODO placeholders or incomplete implementations | +| 8 | When presenting the development workflow, include `pnpm build` and `pnpm test` as explicit steps for apps with a UI extension. Backend-only apps without TypeScript skip `pnpm build`. | Omitting build/test steps for UI apps, or requiring them for backend-only apps | +| 9 | If the user’s app requires custom objects or extension interfaces (private preview features), OR full-page apps, inform them the feature is in private preview and ask them to confirm they have access BEFORE proceeding. Do not silently proceed with a private preview feature. | Building with private preview features without confirming user has access | + +## BLOCKED — these produce broken apps + +| BLOCKED (never use) | Use instead | +| --- | --- | +| `stripe apps create` | `stripe generate app ` | +| Raw HTML in UI extensions (`
`, ``, `

`, `