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.

End Customer pays
Stripe collects total charge
application_fee_amount deducted (commission)
Net amount → Tenant's Stripe Express account
Platform holds commission in balance
CommissionLedgerEntry created

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.

FieldTypeDescription
TenantIdGuidFK to the tenant. Unique — one Stripe account per tenant.
StripeAccountIdstring (max 255)Stripe Connect account ID (acct_xxx).
AccountTypeStripeAccountType enumExpress (recommended, Stripe-hosted dashboard) or Standard (full Stripe dashboard).
OnboardingStatusConnectOnboardingStatus enumLifecycle: NotStarted → Pending → Restricted → Complete.
ChargesEnabledboolWhether the account can accept charges (set by Stripe webhook).
PayoutsEnabledboolWhether the account can receive payouts (set by Stripe webhook).
OnboardingCompletedAtDateTime?When onboarding was fully completed (ChargesEnabled + PayoutsEnabled = true).
DisabledReasonstring? (max 1000)Reason if Stripe has restricted or disabled the account.
DefaultCurrencystring? (max 3)Default currency for this Connect account (e.g. "usd").
Countrystring? (max 2)Country code of the Connect account (e.g. "US", "GB").
CommissionRatedecimal? (5,4)Per-tenant commission rate override (0.00–1.00). Null = use edition or global default.
PayoutDelayDaysintPayout delay in days. Default 7 (industry standard for fraud protection).
LastPayoutAtDateTime?When the last payout was sent to this tenant's bank account.
TotalPayoutsAmountdecimal (18,2)Lifetime total payouts amount sent to this tenant.
TotalPayoutsCountintLifetime total number of payouts sent.
IsFullyOnboardedbool (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.

StatusMeaning
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.

TenantStripeAccount.CommissionRate (per-tenant override) — Per-tenant override — set by platform admin in the Stripe Connect admin panel.
if null →
Edition.ConnectCommissionRate (per-edition rate) — Per-edition rate — configured on the Edition entity via ConnectCommissionRate field.
if null →
ConnectPlatformSettings.GlobalCommissionRate (platform default) — Platform-wide default — stored in the ConnectPlatformSettings singleton row.
if missing →
0.10 hardcoded fallback (only if singleton row missing) — Hardcoded safety fallback — 10% — only used if the singleton row is missing.

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.

FieldTypeDefaultDescription
GlobalCommissionRatedecimal (5,4)0.10Platform-wide default commission rate (0–50%). Applied when no tenant override exists.
DefaultPayoutDelayDaysint7Default payout delay in days for newly created Connect accounts. Stripe minimum is 2.
ConnectEnabledbooltrueMaster switch — disables Stripe Connect platform-wide when set to false.
CommissionInvoiceThresholdAmountdecimal (10,2)50.00USD threshold triggering a mid-month commission invoice. Set 0 to disable threshold billing (monthly-only mode).
CommissionInvoiceGraceDaysint7Grace 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.

EntityPurpose
CommissionLedgerEntryOne row per payment. Stores TenantId, GrossAmount, CommissionAmount, CommissionRate, CommissionStatus, PaymentGatewayType, and GatewayTransactionId.
CommissionInvoiceMonthly (or threshold-triggered) invoice for non-Connect gateway commissions. Format: CINV-{YYYY}-{NNNNN}. Statuses: Draft → Finalized → Paid / Overdue.
ConnectPlatformSettingsSingleton 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.

TriggerWhen
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.

FieldTypeDescription
PromotionIdGuidFK to the EditionPromotion that was redeemed.
TenantIdGuidThe tenant that redeemed the promotion at activation time.
EmailHashstring (64)SHA-256 hex-encoded lowercase hash of the subscriber's email. Never stores raw email — used for FirstTimeOnly checks.
RedeemedAtDateTimeUTC 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 TypeTrigger Condition
WebhookDeadLetterA Stripe webhook event was permanently unprocessable after all retries.
StaleSessionCompletedA non-current checkout session completed (possible duplicate payment).
PromoFirstTimeViolationFirstTimeOnly promo redeemed by an email address with a prior redemption.
PermissionSyncFailedSubscriptionChangedEvent permission sync failed after all retries.
PromoSyncFailedStripe promo code sync failed after all retries.
ReaperSkippedPaidSignupReaper found a completed Stripe session but skipped it — handed to sweep job.
FieldTypeDescription
Typestring (max 100)Alert type identifier for admin panel filtering and runbook lookup.
Severityint1 = Info, 2 = Warning, 3 = Error, 4 = Critical.
Titlestring (max 300)Short human-readable title for the alert list view.
Detailstring? (max 2000)Detailed description including context identifiers.
RecommendedActionstring? (max 1000)Actionable runbook text shown inline in the admin panel (U17).
PayloadJsonstring?Raw JSON payload for debugging (e.g. full Stripe event body). Null when not applicable.
StatusAlertStatus enumOpen (1), Acknowledged (2), Resolved (3).
CreatedAtDateTimeUTC timestamp when the alert was created.
ResolvedAtDateTime?UTC timestamp when the alert was resolved. Null = not yet resolved.
ResolvedBystring? (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).

appsettings.jsonJSON
{
  "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.

MethodEndpointDescriptionAuth
POST/api/v1/connect/accountsRegister 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-linkGenerate a single-use Stripe Connect onboarding link for a tenant🔒 Required
GET/api/v1/connect/commission-ledgerList commission ledger entries (filterable by tenant, status, gateway)🔒 Required
GET/api/v1/connect/commission-invoicesList commission invoices (filterable by tenant, status, trigger)🔒 Required
POST/api/v1/connect/commission-invoices/generateManually trigger commission invoice generation for a tenant🔒 Required
GET/api/v1/connect/settingsGet the ConnectPlatformSettings singleton🔒 Required
PUT/api/v1/connect/settingsUpdate platform-wide Stripe Connect settings🔒 Required
GET/api/v1/connect/alertsList all operational alerts (filterable by type, status, severity)🔒 Required
PUT/api/v1/connect/alerts/{id}/resolveAcknowledge or resolve an operational alert with a resolution note🔒 Required