4.4 KiB
Payments Setup (Hyperswitch + Mollie)
This guide explains how to configure Hyperswitch and Mollie for payment processing in Veza.
Overview
- Hyperswitch: Payment orchestration layer (self-hosted or cloud)
- Mollie: Payment processor (cards, iDEAL, etc.) configured in Hyperswitch
- Flow: Backend creates order → Hyperswitch creates payment → Frontend shows payment form → User pays via Mollie → Webhook updates order
Prerequisites
- Docker and Docker Compose
- Mollie account (https://www.mollie.com/dashboard)
- Hyperswitch API keys (from Control Center or self-hosted)
1. Start Hyperswitch (Local Development)
Hyperswitch runs as an optional Docker profile. Start it with:
docker compose --profile payments up -d
This starts:
hyperswitch_postgres– Hyperswitch databasehyperswitch– Hyperswitch router on port 18081
Verify:
curl http://localhost:18081/health
2. Hyperswitch Control Center
Option A: Hyperswitch Cloud (app.hyperswitch.io)
- Sign up at https://app.hyperswitch.io
- Create a merchant account
- Obtain API Key and Publishable Key from Settings → Developers
- Configure webhook URL:
https://your-domain.com/api/v1/webhooks/hyperswitch
Option B: Self-Hosted Control Center
If using a self-hosted Control Center, follow the Hyperswitch documentation to obtain API keys and configure webhooks.
3. Configure Mollie in Hyperswitch
- In Hyperswitch Control Center, go to Connectors
- Add Mollie
- Enter your Mollie API key:
- Test:
test_xxxfrom https://www.mollie.com/dashboard/developers/api-keys - Live:
live_xxxfor production
- Test:
Mollie test cards: https://docs.mollie.com/overview/testing
4. Backend Environment Variables
Add to veza-backend-api/.env:
# Hyperswitch
HYPERSWITCH_ENABLED=true
HYPERSWITCH_URL=http://localhost:18081
HYPERSWITCH_API_KEY=your_api_key_from_control_center
HYPERSWITCH_WEBHOOK_SECRET=whsec_xxx
# Checkout success redirect (used in return_url)
CHECKOUT_SUCCESS_URL=http://localhost:5173/purchases
For Docker, use http://hyperswitch:8080 as HYPERSWITCH_URL.
5. Frontend Environment Variables
Add to apps/web/.env.local:
VITE_HYPERSWITCH_PUBLISHABLE_KEY=pk_test_xxx
Use the publishable key from Hyperswitch Control Center (Settings → Developers).
6. Webhook Configuration
Hyperswitch must be able to reach your webhook endpoint:
- Local dev: Use a tunnel (ngrok, etc.) and set webhook URL to
https://your-tunnel.ngrok.io/api/v1/webhooks/hyperswitch - Production:
https://your-domain.com/api/v1/webhooks/hyperswitch
The webhook is public (no auth). Signature verification is done via HYPERSWITCH_WEBHOOK_SECRET.
7. Test Flow
- Start backend and frontend
- Add items to cart
- Go to checkout
- Fill billing details and click "Proceed to payment"
- Backend creates order and returns
client_secret - Hyperswitch payment form appears
- Use Mollie test card or iDEAL
- On success, webhook updates order and creates licenses
- User is redirected to
/purchases
8. Simulated Payments (No Hyperswitch)
When HYPERSWITCH_ENABLED=false or Hyperswitch is not configured:
- Orders are completed immediately (simulated payment)
- Licenses are created without real payment
- Useful for local development only — never use in production
9. Production Checklist
CRITICAL: Real payments require HYPERSWITCH_ENABLED=true. With false, orders complete without payment (dev/simulated only).
- Set
HYPERSWITCH_ENABLED=truein production - Use Mollie live API key
- Use Hyperswitch production keys (
pk_prd_,sk_prd_) - Set
CHECKOUT_SUCCESS_URLto production domain - Configure webhook with production URL
- Verify webhook signature in handler (Phase 7)
- Ensure
HYPERSWITCH_WEBHOOK_SECRETis set and kept secret - Set
VITE_HYPERSWITCH_PUBLISHABLE_KEYat build time for frontend
Troubleshooting
Hyperswitch not starting
- Check
hyperswitch_postgresis healthy - Ensure port 18081 is free
- See Hyperswitch logs:
docker compose logs hyperswitch
Payment form not showing
- Verify
VITE_HYPERSWITCH_PUBLISHABLE_KEYis set - Check backend returns
client_secretin CreateOrder response - Ensure
HYPERSWITCH_ENABLED=trueand API key are set
Webhook not received
- Ensure Hyperswitch can reach your webhook URL (no localhost in production)
- Check webhook secret matches
- Inspect backend logs for webhook errors