Stripe Connect
Marketplace payment splitting via Stripe Connect Express — tenant accounts, commission rate resolution chain, ledger entries, commission invoicing, promotion redemption, and operational alerts.
Stripe Connect enables SCRIPE's marketplace payment splitting model. When a tenant processes a user payment, the platform automatically deducts a commission via Stripe's application_fee_amount and routes the net amount to the tenant's Stripe Express account. This page covers the full domain model, commission resolution chain, and operational tooling.
What Is Stripe Connect?
Stripe Connect is Stripe's multi-party payment infrastructure. In SCRIPE, it powers the B2B2C marketplace: tenants sell plans to their end-users, Stripe routes payments, and SCRIPE's commission engine deducts the platform fee automatically on each charge without requiring tenant cooperation.
Payment Split Architecture
Every user payment flows through Stripe, which instantly splits it between the tenant and the platform based on the resolved commission rate.
TenantStripeAccount Entity
One TenantStripeAccount row exists per tenant. It tracks the Stripe account ID, onboarding lifecycle, charge/payout capability, commission rate override, and cumulative payout statistics.
| Field | Type | Description |
|---|---|---|
| TenantId | Guid | FK to the tenant. Unique — one Stripe account per tenant. |
| StripeAccountId | string (max 255) | Stripe Connect account ID (acct_xxx). |
| AccountType | StripeAccountType enum | Express (recommended, Stripe-hosted dashboard) or Standard (full Stripe dashboard). |
| OnboardingStatus | ConnectOnboardingStatus enum | Lifecycle: NotStarted → Pending → Restricted → Complete. |
| ChargesEnabled | bool | Whether the account can accept charges (set by Stripe webhook). |
| PayoutsEnabled | bool | Whether the account can receive payouts (set by Stripe webhook). |
| OnboardingCompletedAt | DateTime? | When onboarding was fully completed (ChargesEnabled + PayoutsEnabled = true). |
| DisabledReason | string? (max 1000) | Reason if Stripe has restricted or disabled the account. |
| DefaultCurrency | string? (max 3) | Default currency for this Connect account (e.g. "usd"). |
| Country | string? (max 2) | Country code of the Connect account (e.g. "US", "GB"). |
| CommissionRate | decimal? (5,4) | Per-tenant commission rate override (0.00–1.00). Null = use edition or global default. |
| PayoutDelayDays | int | Payout delay in days. Default 7 (industry standard for fraud protection). |
| LastPayoutAt | DateTime? | When the last payout was sent to this tenant's bank account. |
| TotalPayoutsAmount | decimal (18,2) | Lifetime total payouts amount sent to this tenant. |
| TotalPayoutsCount | int | Lifetime total number of payouts sent. |
| IsFullyOnboarded | bool (computed) | True if Status = Complete AND ChargesEnabled AND PayoutsEnabled. Not persisted. |
Onboarding Status Lifecycle
Tenant accounts go through a Stripe-managed KYC/identity verification process before they can accept charges or receive payouts.
| Status | Meaning |
|---|---|
| NotStarted (0) | Tenant has not started Stripe Connect onboarding. |
| Pending (1) | Onboarding link generated; awaiting Stripe KYC/identity verification. |
| Restricted (2) | Partial onboarding — Stripe requires additional documents or information. |
| Complete (3) | Fully verified — ChargesEnabled and PayoutsEnabled are both true. |
Commission Rate Resolution Chain
The effective commission rate is resolved from most-specific to most-general. The first non-null value in the chain wins.
ConnectPlatformSettings (Singleton)
A single row (ID: 00000001-0000-0000-0000-000000000001) stores platform-wide Connect defaults. Always access via ConnectPlatformSettings.SingletonId — never insert a second row.
| Field | Type | Default | Description |
|---|---|---|---|
| GlobalCommissionRate | decimal (5,4) | 0.10 | Platform-wide default commission rate (0–50%). Applied when no tenant override exists. |
| DefaultPayoutDelayDays | int | 7 | Default payout delay in days for newly created Connect accounts. Stripe minimum is 2. |
| ConnectEnabled | bool | true | Master switch — disables Stripe Connect platform-wide when set to false. |
| CommissionInvoiceThresholdAmount | decimal (10,2) | 50.00 | USD threshold triggering a mid-month commission invoice. Set 0 to disable threshold billing (monthly-only mode). |
| CommissionInvoiceGraceDays | int | 7 | Grace period (days) before an overdue commission invoice triggers tenant suspension. |
Commission Ledger & Invoicing
Three entities form the commission accounting system. For Stripe Connect payments, commissions are collected instantly via application_fee_amount. For non-Connect gateways (PayPal, Paymob), commissions are tracked in CommissionLedgerEntry and billed monthly or on threshold.
| Entity | Purpose |
|---|---|
| CommissionLedgerEntry | One row per payment. Stores TenantId, GrossAmount, CommissionAmount, CommissionRate, CommissionStatus, PaymentGatewayType, and GatewayTransactionId. |
| CommissionInvoice | Monthly (or threshold-triggered) invoice for non-Connect gateway commissions. Format: CINV-{YYYY}-{NNNNN}. Statuses: Draft → Finalized → Paid / Overdue. |
| ConnectPlatformSettings | Singleton row storing platform-wide defaults. Always accessed via ConnectPlatformSettings.SingletonId. |
Commission Invoice Triggers
CommissionInvoice rows are generated by one of three triggers, configurable in ConnectPlatformSettings.
| Trigger | When |
|---|---|
| Monthly (0) | Scheduled 1st-of-month rollup via background job. |
| Threshold (1) | Unbilled commission exceeded CommissionInvoiceThresholdAmount (e.g. $50). |
| Manual (2) | Super admin manually triggered invoice generation from the admin panel. |
Promotion Redemption (FirstTimeOnly Enforcement)
PromotionRedemption records each promotional code usage at signup activation. Because SCRIPE creates a new Stripe Customer per signup, Stripe's first_time_transaction flag is unreliable. SCRIPE instead stores a SHA-256 hash of the subscriber's email to enforce FirstTimeOnly promotions locally for up to 12 months.
| Field | Type | Description |
|---|---|---|
| PromotionId | Guid | FK to the EditionPromotion that was redeemed. |
| TenantId | Guid | The tenant that redeemed the promotion at activation time. |
| EmailHash | string (64) | SHA-256 hex-encoded lowercase hash of the subscriber's email. Never stores raw email — used for FirstTimeOnly checks. |
| RedeemedAt | DateTime | UTC timestamp when the promotion was redeemed at checkout. |
Operational Alerts
OperationalAlert is a dead-letter table for events requiring human review. Every alert also triggers an ops notification email via the email outbox. Alerts are visible in the admin panel where operators can acknowledge and resolve them inline.
| Alert Type | Trigger Condition |
|---|---|
| WebhookDeadLetter | A Stripe webhook event was permanently unprocessable after all retries. |
| StaleSessionCompleted | A non-current checkout session completed (possible duplicate payment). |
| PromoFirstTimeViolation | FirstTimeOnly promo redeemed by an email address with a prior redemption. |
| PermissionSyncFailed | SubscriptionChangedEvent permission sync failed after all retries. |
| PromoSyncFailed | Stripe promo code sync failed after all retries. |
| ReaperSkippedPaidSignup | Reaper found a completed Stripe session but skipped it — handed to sweep job. |
| Field | Type | Description |
|---|---|---|
| Type | string (max 100) | Alert type identifier for admin panel filtering and runbook lookup. |
| Severity | int | 1 = Info, 2 = Warning, 3 = Error, 4 = Critical. |
| Title | string (max 300) | Short human-readable title for the alert list view. |
| Detail | string? (max 2000) | Detailed description including context identifiers. |
| RecommendedAction | string? (max 1000) | Actionable runbook text shown inline in the admin panel (U17). |
| PayloadJson | string? | Raw JSON payload for debugging (e.g. full Stripe event body). Null when not applicable. |
| Status | AlertStatus enum | Open (1), Acknowledged (2), Resolved (3). |
| CreatedAt | DateTime | UTC timestamp when the alert was created. |
| ResolvedAt | DateTime? | UTC timestamp when the alert was resolved. Null = not yet resolved. |
| ResolvedBy | string? (max 256) | Username or admin ID of who resolved the alert. |
Configuration
Stripe Connect requires two webhook secrets: the standard webhook secret for SaaS subscription events, and a Connect webhook secret for account-level events (charges, payouts, account.updated).
{
"Stripe": {
"SecretKey": "sk_live_...",
"PublishableKey": "pk_live_...",
"WebhookSecret": "whsec_...",
"ConnectWebhookSecret": "whsec_connect_..."
}
}API Endpoints
Connect management endpoints are restricted to super-admin roles. Tenant-facing onboarding links are generated per-tenant and are single-use.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/v1/connect/accounts | Register a new Stripe Connect Express account for a tenant | 🔒 Required |
| GET | /api/v1/connect/accounts/{tenantId} | Get the Stripe account status and onboarding details for a tenant | 🔒 Required |
| POST | /api/v1/connect/accounts/{tenantId}/onboarding-link | Generate a single-use Stripe Connect onboarding link for a tenant | 🔒 Required |
| GET | /api/v1/connect/commission-ledger | List commission ledger entries (filterable by tenant, status, gateway) | 🔒 Required |
| GET | /api/v1/connect/commission-invoices | List commission invoices (filterable by tenant, status, trigger) | 🔒 Required |
| POST | /api/v1/connect/commission-invoices/generate | Manually trigger commission invoice generation for a tenant | 🔒 Required |
| GET | /api/v1/connect/settings | Get the ConnectPlatformSettings singleton | 🔒 Required |
| PUT | /api/v1/connect/settings | Update platform-wide Stripe Connect settings | 🔒 Required |
| GET | /api/v1/connect/alerts | List all operational alerts (filterable by type, status, severity) | 🔒 Required |
| PUT | /api/v1/connect/alerts/{id}/resolve | Acknowledge or resolve an operational alert with a resolution note | 🔒 Required |