Integrating Swiss Payment Providers: Twint, PostFinance, and Stripe
5 min read
Why Swiss Payment Integration Is Different
If you’ve built payment flows for US or UK markets, you might assume that Stripe alone covers everything. In Switzerland, that’s not the case. Swiss consumers expect to pay with Twint (the country’s dominant mobile payment app), PostFinance (used by millions of Swiss bank account holders), and credit/debit cards via Stripe or similar processors.
Offering all three isn’t a nice-to-have, it directly affects your conversion rate. A Swiss e-commerce site without Twint is like a German site without PayPal: you’re leaving money on the table.
This article covers how I approach multi-provider payment integration, the architecture decisions involved, and the gotchas I’ve learned the hard way.
Architecture: One Interface, Multiple Providers
The biggest mistake teams make is building provider-specific checkout flows. Instead, I abstract payments behind a single interface.
In a Laravel application, this looks like a PaymentGateway interface with methods like createPayment(), handleWebhook(), and refund(). Each provider (Stripe, Twint, PostFinance, Klarna) gets its own implementation class. The checkout controller doesn’t know or care which provider is being used, it just calls the interface.
This pattern gives you:
- Easy provider swapping: if a provider changes their API or you need to add a new one, only one class changes
- Consistent error handling: all providers return the same response shape
- Unified webhook processing: one endpoint, dispatching to the right handler based on the payload
Provider-Specific Considerations
Stripe is the most straightforward. Their PHP SDK is excellent, Stripe Checkout handles PCI compliance, and webhooks are well-documented. Use Payment Intents (not the legacy Charges API) and always verify webhook signatures.
Twint integration typically goes through a payment service provider (PSP) like Datatrans or Wallee, which acts as a middleman. Direct Twint integration is available but requires a contract with Twint AG and adds operational complexity. For most projects, going through a PSP is the pragmatic choice.
PostFinance also works through PSPs for e-commerce. The PostFinance Checkout API is usable but has more rigid requirements around redirect flows and status polling. Budget extra time for testing. Their sandbox environment can be temperamental.
Webhook Handling: The Hard Part
Payments are fundamentally asynchronous. A customer initiates a payment, you redirect them to the provider, and at some point a webhook tells you whether it succeeded. The challenge is handling this reliably.
Key patterns I follow:
-
Idempotent webhook handlers: Providers can (and do) send the same webhook multiple times. Every handler must be safe to run repeatedly without creating duplicate orders or charges.
-
Verify before trusting. Always verify webhook signatures. Never trust the payload alone. For Stripe, use
Webhook::constructEvent(). For PSP-based providers, verify the HMAC signature against your shared secret. -
Queue processing. Don’t process webhooks synchronously in the HTTP request. Accept the webhook, return a 200, and dispatch a queued job to handle the business logic. This prevents timeouts and gives you retry capability.
-
Status reconciliation. Build a scheduled job that polls each provider’s API for any payments that might have been missed by webhooks. This catches edge cases like network failures between the provider and your server.
Currency and Compliance
Switzerland uses the Swiss Franc (CHF), not the Euro. Make sure your payment setup handles CHF correctly. This sounds obvious, but I’ve seen projects where the default currency was set to EUR and nobody caught it until real transactions were failing.
For PCI compliance, never handle raw card data. Use Stripe Elements or Stripe Checkout to keep card numbers off your server entirely. Twint and PostFinance transactions go through redirects or their own SDKs, so PCI scope is minimal.
VAT handling in Switzerland differs from the EU. The standard rate is 8.1% (as of 2024), with reduced rates for certain goods. If you’re selling to EU customers from Switzerland, you may need to handle EU VAT as well. This is a business/legal decision, but your payment integration needs to support the correct tax calculations.
Testing in Production (Safely)
All three providers offer sandbox/test environments, but they don’t perfectly replicate production behavior. My approach:
- Thorough sandbox testing: cover the happy path, failed payments, expired sessions, and webhook retries
- Staged rollout: launch with a small percentage of real traffic first
- Monitoring: set up alerts for failed webhooks, payment mismatches, and unusual error rates
- Manual reconciliation: for the first week after launch, manually verify that every payment in your system matches the provider’s dashboard
Key Takeaways
- Abstract payments behind an interface, don’t couple your checkout to a single provider
- Budget extra integration time for Twint and PostFinance compared to Stripe
- Make webhook handlers idempotent and process them asynchronously
- Always handle CHF explicitly and verify your tax setup
- Monitor aggressively after launch
If you’re building an e-commerce platform for the Swiss market and need help with payment integration, get in touch or visit tedbin.com.