Skip to content
Invoke Technologies
All insights

Five lessons from building payments on Stripe Connect

Webhooks as the source of truth, idempotent reconciliation, fee math you can explain, ledgers from day one, and the local-development gotchas nobody warns you about.

Invoke Technologies · · 2 min read

Stripe makes taking a payment easy. Building a platform - where money flows from your users’ customers to your users, with a fee in between - takes more care. Here’s what we’ve learned building on Stripe Connect.

1. The webhook is the source of truth, not the browser

When a checkout completes in the browser, it’s tempting to mark the payment successful right there. Don’t. The browser can close, the network can drop, and payments can succeed or fail asynchronously. Record the payment as pending, and let a signature-verified webhook such as payment_intent.succeeded move it to succeeded.

2. Make reconciliation idempotent

Stripe retries webhooks, and events can arrive more than once. Your handler should check the current state before acting, and apply the payment’s side effects - updating totals, writing ledger entries - in a single database transaction, exactly once.

3. Keep the fee math small, central, and tested

Platform fees accumulate special cases quickly: a default rate, per-customer overrides, promotions, tips that offset fees. Put the resolution logic in one well-named class with thorough unit tests, and make sure the numbers shown on your pricing page come from the same rules.

4. Start a ledger on day one

A Payment table tells you what happened at Stripe. A ledger tells you what happened to each account’s balance: gross donation in, platform fee out, later refunds and payouts. Adding one after the fact means back-filling history from payment records; adding it at the start is a few extra rows per transaction.

5. Local development has sharp edges

  • stripe listen needs an explicit --events list on current CLI versions.
  • The webhook signing secret changes every time you restart the listener - update your local secret and restart the API.
  • If your handler reads the full object from the event, you need classic “snapshot” events, not thin events.

None of these are hard once you know them. All of them can cost an afternoon if you don’t.

Need a hand?

We design and build payment flows and multi-party platforms on Stripe. See our payments integration services or get in touch.

Want help putting this into practice?

Tell us what you’re building. We’ll reply within one business day with honest next steps - even if that means pointing you somewhere else.