All modules
Live

Paiements

La couche qui encaisse: Stripe Connect derrière chaque checkout, chaque abonnement, chaque facture.

The plumbing that gets you paid. A single boundary between Freelance OS and Stripe, resolved per workspace through Stripe Connect. Everything that takes money in the platform, product checkout, end-user billing, invoice payment, flows through this layer.

What it does for you

How it works

A port, not an SDK

The heart of the module is an interface, PaymentGateway. Every operation the app needs to take money is declared on it: create a customer, capture a card without charging, create a payment intent, open a hosted checkout, charge off-session, verify a webhook. Routes, crons and public pages depend on this interface, never on hard-coded stripe.* calls. Adding a provider means writing a class that implements the port and registering it, without touching anything else.

Stripe Connect per workspace

Each workspace connects its own Stripe account. The platform holds one secret key and acts on behalf of the connected account by passing its identifier (acct_...) on every Stripe call. The connected account secret is never stored: Connect Standard relies on the platform auth plus the account header. A workspace with no connected account falls back to the platform account (legacy mono-tenant behavior), but a new workspace must connect Stripe before it can take payments.

  • Account resolved from workspace_stripe_accounts, cached for 60 seconds
  • The connected flag says whether the workspace wired its own account or falls back to the platform
  • The connected account publishable key is what public pages use to mount Stripe Elements

What the gateway can do

The port covers the full lifecycle of a payment. Each operation returns a normalized, PSP-agnostic type, so the caller never writes Stripe-specific logic.

  • PSP customer: fetched or recreated if the stored id is dead
  • Card capture (SetupIntent): saves a payment method without a charge
  • On-session payment (PaymentIntent): the client pays on the page, with 3DS handled
  • Hosted checkout: the PSP hosts the page for a one-off payment, redirects to success or cancel
  • Off-session charge: debits a saved card without the client present, with an idempotency key against double charges
  • Webhook verification: checks the signature and returns a normalized event

Where the money comes in

The Payments layer has no screen of its own. It is consumed by the surfaces that sell. An invoice payment opens a branded checkout page that mounts Stripe Elements and keeps the client on your domain. A product checkout creates an order, applies shipping and coupons, charges through Connect. A subscription to a Collection bills the end-user on top. A booking deposit runs through the same path. On platform flows, a commission (application fee) can be taken on each session.

/pay/2026-014
Summary

Invoice 2026-014

Camille Roux

Amount due

€3,240.00

Paid
Card Preview
Number

4242 •••• •••• 4242

Expires

04 / 29

CVC

•••

Preview. No field can be typed in, no data is collected.

Click Pay to see the invoice page flip to paid.

One PSP written today

The design is provider-agnostic, but to be honest: only Stripe is actually implemented behind the port. PayPal, Mollie and Adyen are planned identifiers, not written gateways. A provider that is chosen but not implemented fails loudly, it never silently falls back to Stripe: taking money through the wrong PSP would be worse than not taking it. And until Stripe is connected at the workspace level, this whole layer stays inert.

Connected to

counsel Le paiement de facture ouvre une page de checkout brandée montée sur cette coucheproducts Le checkout produit crée une commande et encaisse via Stripe Connectbooking Les acomptes de réservation passent par le même gatewaycollections Les abonnements end-users à une app sont facturés par-dessus cette couche

Try Paiements in Freelance OS.

Free plan, no card. Test the module in real conditions.