Payments
Accept card payments on invoices with Stripe. Multi-provider architecture — Mollie and others slot in without workflow changes.
Let your customers pay invoices online with a credit or debit card. Workestra's payments pipeline is multi-provider from day one — Stripe is ready today, Mollie is on the way, and adding other gateways won't change how you send invoices or how they settle.
Payments is an opt-in capability. Workspaces without a configured provider still send invoices normally — the "Pay link" button and email CTA just don't appear.
What you get
- Hosted checkout. Customers click a pay link and land on the provider's secure checkout page. You never handle card data.
- Automatic invoice settlement. When a payment succeeds, the invoice flips to Paid (or Partial if the amount was less than the balance) within a second or two of the provider's webhook.
- Pay link anywhere. Every sent invoice exposes a "Pay link" button. Share it in an email, a chat, or include it automatically in the sent-invoice email.
- Ledger of every payment. Each successful charge is recorded on the invoice with the provider reference, amount, and timestamp.
- Encrypted credentials. Stripe and Mollie API keys are encrypted at rest with AES-256-GCM before they're stored. They're never displayed again after you save them — only masked.
- Idempotent webhooks + DLQ. Duplicate webhook events can't double-credit an invoice. Permanently failing events fall through to a dead-letter queue and are retried automatically.
- Audit-logged. Saving or rotating Stripe keys is recorded in the workspace audit log (key hash only — never the secret itself).
- Refund-ready. Full and partial refunds work via API today; a refund button on the invoice detail page lands in a follow-up release.
Providers
| Provider | Status | Coverage |
|---|---|---|
| Stripe | ✅ Available now | Global — all card types Stripe supports |
| Mollie | 🚧 Scaffolded | EU-first; implementation pending |
| Others | Extensible | Adyen, PayPal, etc. can be added without changing routes or UI |
Connecting Stripe
Setup takes ~5 minutes.
1. Get your Stripe keys
From the Stripe dashboard:
- Copy the Secret key (
sk_test_…in test mode,sk_live_…in production) - Copy the Publishable key (
pk_test_…/pk_live_…)
2. Add the webhook endpoint
In Stripe Developers → Webhooks → Add endpoint:
- URL:
https://<your-workspace-domain>/api/finance/payments/stripe/webhook - Events to listen for:
checkout.session.completedcheckout.session.async_payment_succeededcheckout.session.async_payment_failed
- After creating, reveal the Signing secret (
whsec_…)
3. Save the keys in Workestra
- Navigate to Finance → Settings → Payments
- Paste all three keys (secret, publishable, webhook)
- Pick Test or Live mode
- Toggle Offer card payment on invoices on
- Click Save Stripe
The workspace is now ready to accept card payments.
Use Test mode to verify the end-to-end flow with Stripe's test card 4242 4242 4242 4242. Switch to Live when you're ready for real charges.
Pay links on invoices
Once Stripe is connected, every Sent or Overdue invoice shows a Pay link button in the action bar.
Click it to:
- Generate the checkout URL
- Copy it to the clipboard for pasting into a message
- Open it in a new tab to verify the customer experience
The same URL is automatically included in the invoice email when you click Send.
What the customer sees
The payer clicks the link and lands on Stripe's hosted checkout page. They pay with a card. On success they're redirected to a branded confirmation page (/pay/success). If they cancel, they land on /pay/cancel. Neither page leaks workspace data.
Settlement flow
The moment Stripe confirms a payment, the webhook posts to your workspace:
Stripe → /api/finance/payments/stripe/webhook
↓
verifies signature
↓
records invoice_payments row
↓
recomputes invoice paid_amount
↓
flips invoice to Paid or PartialWebhook events are idempotent — Stripe can redeliver the same event and the invoice won't be double-credited. Every successful payment leaves an auditable record on the invoice (provider reference + event ID + amount).
Statuses
| Payment status | Meaning |
|---|---|
| Pending | Customer started checkout, no charge yet |
| Succeeded | Card was charged; invoice updated |
| Failed | Charge declined or error during checkout |
| Refunded | Full refund issued |
| Partially refunded | Partial refund issued |
| Cancelled | Customer abandoned the checkout |
Sending invoices with pay CTA
When Stripe is active:
- Open an invoice
- Click Send
- The email goes out with:
- The full invoice as a PDF attachment
- A prominent Pay online button
- The amount and due date up front
When Stripe is not active, the same email goes out with the PDF but without the pay button — no action required to disable anything.
Refunds
Full and partial refunds are supported at the API + service-layer level today. A first-class refund button on the invoice detail page is on the roadmap and ships in a follow-up release. In the meantime:
- Trigger the refund directly from the Stripe dashboard (or via the AI assistant — see AI Tools).
- Workestra's webhook records the refund automatically as a
refundedorpartially_refundedentry on the invoice's payment ledger. - The invoice's outstanding balance is recalculated; status flips back to Sent / Partial as needed.
Troubleshooting
Pay button missing on a sent invoice. Confirm the payment provider is toggled Active at /finance/settings?tab=payments. Inactive providers don't generate pay links.
Webhook signature failures. The signing secret in Workestra must match the one from Stripe's webhook endpoint configuration. If you regenerate it in Stripe, update Workestra immediately or webhooks will be rejected.
Invoice didn't flip to Paid. Check the Stripe dashboard → Developers → Events for the checkout.session.completed event. If it shows an HTTP error on delivery, the webhook URL or signing secret is wrong. Stripe retries automatically for ~3 days, so fixing the config usually recovers the settlement.
Removing a provider
To disconnect Stripe:
- Go to Finance → Settings → Payments
- Click Disconnect Stripe
- Confirm
The invoice Pay link button disappears immediately. Historical invoice_payments records stay intact — disconnecting the provider doesn't delete the payment ledger.
Next steps
- Invoicing — Send invoices with pay links
- Finance Settings — Configure defaults
- Credit Notes — Issue refunds & adjustments