Was macht Stripe Connect & Abonnement-Abrechnung?
Zwei Geldflüsse auf einem Stripe-Konto. Ein Marktplatz belastet den Käufer einmal und verteilt die Erlöse an mehrere Verkäufer (Connect, getrennte Zahlungen und Überweisungen). Ein Plattform-Abonnement belastet diese Verkäufer mit einer wiederkehrenden Gebühr off-session (Billing). Sie teilen sich einen Client, ein Webhook-Paar und einen Geldtyp.
Die Erkenntnis, die alles prägt: Die Zahlung des Käufers und die Auszahlungen an die Verkäufer schlagen unabhängig voneinander fehl. Bis die Abrechnung ausgeführt wird, ist das Geld bereits bewegt; daher darf eine abgelehnte Überweisung niemals die Zahlung oder den Webhook zum Scheitern bringen und muss später erneut ausführbar sein, ohne jemanden doppelt zu bezahlen.
Wann verwendet werden
- Eine Zahlung wird auf mehrere Verkäufer aufgeteilt, wobei die Plattform eine Provision erhält.
- Verkäufer onboarden zu Connect und werden verzögert ausgezahlt (Treuhand, Reserven, KYC).
- Die Plattform berechnet ihren eigenen Verkäufern eine wiederkehrende Gebühr von einer gespeicherten Karte.
- Ein Connected Account sieht gesund aus, hat aber noch nie Geld erhalten.
Wann NICHT verwenden
| Nicht dies | Stattdessen verwenden |
|---|---|
| Einzel-Verkäufer-Checkout, ohne Aufteilung | Stripe Checkout / Payment Element direkt |
| PayPal Marketplace Onboarding + Auszahlungen | Eine PayPal-Fähigkeit; nur die Nahtstellen sind gemeinsam |
| Karten-UI, Payment Element Styling | Ihr Designsystem; diese Fähigkeit ist serverseitig |
Architektur
kunde ──zahlung (plattform-konto, transfer_group = orderId)──▶ plattform-guthaben
│ abrechnung-fan-out (webhook ODER read-path-sync; belegter claim)
┌────────────┴──┬────────────────────────┬────────────────┐
▼ ▼ ▼ ▼
überweisung:verkäufer überweisung:partner treuhand-reserve rollende-reserve
(source_transaction = die zahlung) (release_at) (% einbehalten)
│ │ release-treuhand-cron
▼ ▼
connected guthaben ◀── wiederholen-überweisungen auszahlbar-guthaben ──▶ auszahlung (KYC/risiko-beschränkt)
(nicht finanzierte beine)
plattform ──off_session PaymentIntent (kunde + gespeicherte zahlungsmethode)──▶ abonnement-rechnung
dunning: 3 versuche, 3 tage abstand, dann sperren
Kritische Fakten
- Getrennte Zahlungen und Überweisungen, niemals Destination Charges. Eine Zahlung
hat genau ein
destination; ein Multi-Verkäufer-Warenkorb benötigt N Überweisungen unter einertransfer_group, daher istapplication_fee_amountnicht nutzbar. - Eine Überweisung ohne
source_transactionbezieht sich auf das verfügbare Guthaben und wird abgelehnt, während die Zahlung noch abgerechnet wird; bei einem jungen Plattform-Konto ist das jede Überweisung. Dieses eine Flag ermöglicht es, eine Aufteilung zu finanzieren, während die Zahlung noch abgerechnet wird. - Stripe-Überweisungen sind grenzüberschreitend nur innerhalb US/CA/UK/EEA/CH möglich. Eine Plattform außerhalb dieser Regionen zahlt nur an Connected Accounts im eigenen Land. Stripe erzwingt dies zum Zeitpunkt der Überweisung, nicht beim Onboarding; daher wenden die Vorlagen die Regionsregel beim Onboarding an.
- Connect-Events haben ein anderes Signing-Secret als Plattform-Events. Zwei Endpunkte, zwei Secrets, eine URL – gegen beide verifizieren.
- Das Land eines Connected Accounts ist unveränderlich – falsches Land bedeutet erneutes Onboarding, keine Korrektur.
- Geld ist niemals ein Float –
numeric(19,4)-Strings durchgängig,bigint-Mathematik, Minor-Units nur an der SDK-Grenze.
Harte Regeln
Lassen Sie niemals zu, dass eine fehlgeschlagene Überweisung die Abrechnung zum Scheitern bringt. Erfassen Sie das Bein als nicht finanziert und fahren Sie fort. Die Zahlung war bereits erfolgreich, daher muss auch der Webhook erfolgreich sein; der Wiederholungs-Cron finanziert das Bein später.
Behandeln Sie niemals einen doppelten Webhook-Insert als "bereits bearbeitet". Der Insert ist ein Claim; die Wiederholung nach einem Absturz während der Verarbeitung führt die Handler erneut aus, sodass jedes Event genau einmal vollständig ausgeführt wird.
Senden Sie niemals eine Überweisung erneut, ohne Stripe zu fragen, ob sie bereits existiert. Ein als nicht finanziert erfasstes Bein, weil die Antwort verloren ging, wird doppelt bezahlt, sobald das 24-Stunden-Idempotenz-Fenster von Stripe verstrichen ist.
Leiten Sie die Exklusivität der Abrechnung niemals von einem Status-Flag ab, das am Ende geschrieben wird. Zwei Aufrufer erreichen die Abrechnung routinemäßig; ein atomarer, belegter Claim stellt sicher, dass Bestand und Überweisungen einmalig stattfinden.
Buchen Sie niemals mehr zurück als den verbleibenden Spielraum einer Überweisung. Gestapelte Rückbuchungen (Gateway-Gebühr, dann eine Rückerstattung) werden nach dem ursprünglichen Betrag abgelehnt.
Schnellstart
- Modellieren Sie das Geld – money.md, data-model.md, store.md.
- Client, duale Webhook-Secrets, Umgebung – stripe-adapter.md.
- Onboarding von Accounts; zuerst die Regionsregel prüfen – connect-accounts.md.
- Empfangen von Events idempotent – webhooks.md.
- Abrechnung: Claim, Fan-Out, Treuhand, Reserve – settlement.md.
- Konvergenz: Beine wiederholen, Gebühren abgleichen – reconciliation.md.
- Abrechnung der Mieter – subscriptions.md; ausführen mit operations.md.
Passen Sie es an Ihre App an mit adaptation.md; der Prüfpfad ist in provenance.md dokumentiert.
Anpassungsvertrag
| Nahtstelle | Die Fähigkeit liefert | Der Host liefert |
|---|---|---|
| Domain-Entitäten | Tenant (Verkäufer-Org), Order, VendorOrder, Partner | Eigene Vokabeln |
| Tenant-Bereich | Eine serverseitig abgeleitete tenantId | Org / Workspace / Verkäufer |
| Auth-Guard | Eine Adapter-Signatur pro Route | Clerk, NextAuth, Supabase, benutzerdefiniert |
| Datenzugriff | Ein PaymentsStore-Contract + SQL | Eigenes ORM / SDK |
| Audit + Benachrichtigungen | Aufrufstellen, Event-Namen, Payload-Formen | Eigene Audit-Tabelle / Outbox |
| Hintergrundarbeit | Vier Crons und ihr Rhythmus | Eigener Scheduler |
| Validierung | Ein Schema-Formular pro Route-Body | zod / valibot / yup |
| UI | Nichts – diese Fähigkeit ist serverseitig | Alles |
Referenzverzeichnis
| Szenario | Trigger-Schlüsselwörter | Referenz |
|---|---|---|
| Warum dieses Geldmodell | separate charges and transfers, destination charge, application_fee_amount, transfer_group, ledger | architecture.md |
| Tabellen, Spalten, RLS | schema, migration, payment_intents, transfers, escrow_holds, webhook_events | data-model.md |
| Der Datenzugriffsvertrag | store, repository, atomic claim, conditional update, ORM | store.md |
| Beträge und Rundung | numeric, bigint, minor units, distribute, largest remainder, cents | money.md |
| Client- und Adapter-Code | stripeClient, apiVersion, constructEvent, whsec_, PaymentIntent, refund, payout | stripe-adapter.md |
| Onboarding, Account-Zustand | Express, Accounts v2, account.updated, charges_enabled, acct_, country, region | connect-accounts.md |
| Sicheres Empfangen von Events | idempotency, duplicate, replay, raw body, signature, invalid_signature | webhooks.md |
| Aufteilen einer bezahlten Bestellung | settlement, fan-out, source_transaction, escrow, reserve, single-flight | settlement.md |
| Beheben von nicht gelandeten Zahlungen | retry, unfunded, backoff, adopt, gateway fee, clawback, reversal, headroom | reconciliation.md |
| Wiederkehrende Plattformgebühren | SetupIntent, off_session, saved card, dunning, invoice, past_due, suspend | subscriptions.md |
| Einrichtung, Umgebung, Crons, Debugging | STRIPE_SECRET_KEY, webhook endpoint, stripe listen, cron, "merchant never paid" | operations.md |
| Anpassung an diese App | adapt, host probe, rename, ORM, auth guard, port | adaptation.md |
| Der Prüfpfad | provenance, deviation, kept, added, unverified | provenance.md |