stripe-connect
Build marketplace and platform payment flows with Stripe Connect. Use when: building two-sided marketplaces, splitting payments between buyers and sellers, onboarding sellers/providers to accept payments, handling platform fees and payouts, or managing connected accounts for a platform.
What this skill does
# Stripe Connect
## Overview
Stripe Connect enables platforms to route payments between buyers, sellers, and your platform. It handles regulatory compliance (KYC), payouts, and tax reporting for connected accounts.
**Account types:**
| Type | Onboarding | Branding | Best for |
|------|------------|----------|----------|
| **Express** | Stripe-hosted | Stripe UI | Marketplaces (recommended) |
| **Standard** | OAuth redirect | Seller's branding | Platforms where sellers have existing Stripe accounts |
| **Custom** | Fully custom | Your UI | Large platforms needing full control |
**Charge types:**
| Type | Who pays Stripe fees | Use when |
|------|---------------------|----------|
| Direct | Connected account | Seller wants full control |
| Destination | Platform | Platform manages UX |
| Separate charges + transfers | Platform | Complex routing |
## Setup
```bash
npm install stripe
```
```ts
// lib/stripe.ts
import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-06-20",
});
// Platform account key (your Stripe account)
// Connected accounts are identified by their account ID
```
---
## Express Accounts (Recommended)
### 1. Create a connected account
```ts
// POST /api/sellers/onboard
import { stripe } from "@/lib/stripe";
export async function createExpressAccount(email: string) {
const account = await stripe.accounts.create({
type: "express",
email,
capabilities: {
card_payments: { requested: true },
transfers: { requested: true },
},
business_type: "individual",
});
return account.id; // Store this as seller.stripeAccountId in your DB
}
```
### 2. Generate onboarding link
```ts
export async function createOnboardingLink(accountId: string, userId: string) {
const accountLink = await stripe.accountLinks.create({
account: accountId,
refresh_url: `${process.env.BASE_URL}/sellers/onboard/refresh?userId=${userId}`,
return_url: `${process.env.BASE_URL}/sellers/onboard/complete?userId=${userId}`,
type: "account_onboarding",
});
return accountLink.url; // Redirect seller here
}
// Full flow:
app.post("/api/sellers/onboard", async (req, res) => {
const { email, userId } = req.body;
const accountId = await createExpressAccount(email);
// Save accountId to your DB
await db.sellers.update(userId, { stripeAccountId: accountId });
const url = await createOnboardingLink(accountId, userId);
res.json({ url });
});
```
### 3. Check onboarding status
```ts
export async function isSellerOnboarded(accountId: string): Promise<boolean> {
const account = await stripe.accounts.retrieve(accountId);
return account.details_submitted && !account.requirements?.currently_due?.length;
}
app.get("/sellers/onboard/complete", async (req, res) => {
const { userId } = req.query;
const seller = await db.sellers.findById(userId);
const onboarded = await isSellerOnboarded(seller.stripeAccountId);
if (onboarded) {
await db.sellers.update(userId, { status: "active" });
res.redirect("/dashboard?onboarded=true");
} else {
// Seller didn't finish — show completion prompt
res.redirect("/sellers/onboard/pending");
}
});
```
---
## Charging Buyers
### Destination Charges (Platform collects, sends to seller)
```ts
// POST /api/payments/charge
export async function chargeWithDestination({
amount, // in cents
currency = "usd",
paymentMethodId,
customerId,
sellerAccountId,
platformFeePercent = 15,
}: {
amount: number;
currency?: string;
paymentMethodId: string;
customerId: string;
sellerAccountId: string;
platformFeePercent?: number;
}) {
const platformFee = Math.round(amount * (platformFeePercent / 100));
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
customer: customerId,
payment_method: paymentMethodId,
confirm: true,
transfer_data: {
destination: sellerAccountId, // Route net to seller
},
application_fee_amount: platformFee, // Platform keeps this
automatic_payment_methods: { enabled: true, allow_redirects: "never" },
});
return paymentIntent;
}
```
### Direct Charges (Seller's Stripe account)
```ts
// Charge appears on seller's Stripe dashboard; platform gets fee
export async function directCharge({
amount,
paymentMethodId,
sellerAccountId,
platformFeePercent = 10,
}: {
amount: number;
paymentMethodId: string;
sellerAccountId: string;
platformFeePercent?: number;
}) {
const platformFee = Math.round(amount * (platformFeePercent / 100));
const paymentIntent = await stripe.paymentIntents.create(
{
amount,
currency: "usd",
payment_method: paymentMethodId,
confirm: true,
application_fee_amount: platformFee,
},
{
stripeAccount: sellerAccountId, // Create on behalf of seller
}
);
return paymentIntent;
}
```
### Separate Charges + Transfers (most flexible)
```ts
// 1. Charge buyer on platform account
const paymentIntent = await stripe.paymentIntents.create({
amount: 10000, // $100
currency: "usd",
payment_method: paymentMethodId,
confirm: true,
});
// 2. Later: transfer to seller (e.g., after service delivered)
export async function payoutToSeller(
paymentIntentId: string,
sellerAccountId: string,
amount: number // amount to send seller (after platform fee)
) {
const transfer = await stripe.transfers.create({
amount,
currency: "usd",
destination: sellerAccountId,
source_transaction: paymentIntentId, // Links transfer to original charge
});
return transfer;
}
```
---
## Payouts
### Automatic payouts (default)
By default, Stripe automatically pays out to sellers based on their payout schedule. No extra code needed.
```ts
// Check payout schedule for a connected account
const account = await stripe.accounts.retrieve(sellerAccountId);
console.log(account.settings?.payouts?.schedule);
// { interval: "daily" | "weekly" | "monthly", ... }
```
### Manual / triggered payouts
```ts
// Trigger an instant payout (seller must have instant payouts enabled)
export async function triggerPayout(sellerAccountId: string, amount: number) {
const payout = await stripe.payouts.create(
{
amount,
currency: "usd",
method: "instant", // or "standard"
},
{
stripeAccount: sellerAccountId,
}
);
return payout;
}
```
### Check seller balance
```ts
export async function getSellerBalance(sellerAccountId: string) {
const balance = await stripe.balance.retrieve({
stripeAccount: sellerAccountId,
});
return {
available: balance.available[0]?.amount ?? 0,
pending: balance.pending[0]?.amount ?? 0,
};
}
```
---
## Webhooks for Connect
Connect webhooks can fire for your platform account or for events on connected accounts.
```ts
// POST /webhooks/stripe
import { stripe } from "@/lib/stripe";
app.post("/webhooks/stripe", express.raw({ type: "application/json" }), async (req, res) => {
const sig = req.headers["stripe-signature"] as string;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${(err as Error).message}`);
}
// For Connect events, check event.account
const connectedAccountId = (event as any).account as string | undefined;
switch (event.type) {
// Connected account completed onboarding
case "account.updated": {
const account = event.data.object as Stripe.Account;
if (account.details_submitted) {
await db.sellers.update(
{ stripeAccountId: account.id },
{ status: "active" }
);
}
break;
}
// Payment succeeded
case "payment_intent.succeeded": {
const pi = event.data.object as Stripe.PaymentIntent;
await db.orders.update(
{ stripePaymentIntentId: pi.id },
Related in Sales & CRM
process-mapper
IncludedUse when a BizOps lead, COO, or process-improvement owner needs to document an end-to-end business process (procurement, employee onboarding, incident handoff, customer-onboarding, claims adjudication) in BPMN-style notation, measure cycle times by stage, surface where work spends most of its time waiting vs. being worked, and quantify the gap between processing time and total elapsed time. Pairs Lean / Six Sigma / Theory-of-Constraints canon with deterministic stdlib-only Python tools to produce a process map, a ranked bottleneck list (with severity + root-cause hypothesis), and a cycle-time analysis (P50, P90, value-add ratio, Little's-Law throughput). Distinct from sales-pipeline, system-reliability (SLO), and strategic-OKR work — this is tactical process documentation for internal operations.
payment-integration
IncludedIntegrate payments with SePay (VietQR), Polar, Stripe, Paddle (MoR subscriptions), Creem.io (licensing). Checkout, webhooks, subscriptions, QR codes, multi-provider orders.
customer-success-manager
IncludedMonitors customer health, predicts churn risk, and identifies expansion opportunities using weighted scoring models for SaaS customer success
sales-engineer
IncludedAnalyzes RFP/RFI responses for coverage gaps, builds competitive feature comparison matrices, and plans proof-of-concept (POC) engagements for pre-sales engineering. Use when responding to RFPs, bids, or proposal requests; comparing product features against competitors; planning or scoring a customer POC or sales demo; preparing a technical proposal; or performing win/loss competitor analysis. Handles tasks described as 'RFP response', 'bid response', 'proposal response', 'competitor comparison', 'feature matrix', 'POC planning', 'sales demo prep', or 'pre-sales engineering'.
customer-success-manager
IncludedMonitors customer health, predicts churn risk, and identifies expansion opportunities using weighted scoring models for SaaS customer success
sales-engineer
IncludedAnalyzes RFP/RFI responses for coverage gaps, builds competitive feature comparison matrices, and plans proof-of-concept (POC) engagements for pre-sales engineering. Use when responding to RFPs, bids, or proposal requests; comparing product features against competitors; planning or scoring a customer POC or sales demo; preparing a technical proposal; or performing win/loss competitor analysis. Handles tasks described as 'RFP response', 'bid response', 'proposal response', 'competitor comparison', 'feature matrix', 'POC planning', 'sales demo prep', or 'pre-sales engineering'.