Multi-merchant
PayWarden supports running multiple merchants from a single deployment. Each merchant has their own API key, webhook configuration, and isolated order history.
Use cases
- SaaS platforms — each of your customers is a merchant
- Agency setups — manage payments for multiple clients
- Multi-brand businesses — separate payment streams per brand
How it works
Admin Dashboard
│
├── Merchant A (api_key_prefix: pw_live_aaaa)
│ └── Orders: A1, A2, A3...
│
├── Merchant B (api_key_prefix: pw_live_bbbb)
│ └── Orders: B1, B2, B3...
│
└── Merchant C (api_key_prefix: pw_live_cccc)
└── Orders: C1, C2, C3...
Shared:
- HD wallet (single vault.enc)
- Address index pool (globally sequential)
- Chain Watcher (monitors all pending addresses)
- PostgreSQL + RedisSetting up a new merchant
Via admin dashboard
- Log in at
/admin - Navigate to Merchants → Create Merchant
- Fill in:
- Name — internal label
- Webhook URL — where payment events are sent
- Copy the generated API key (shown once only)
Via admin API
bash
curl -X POST https://pay.yourdomain.com/api/v1/admin/merchants \
-H "Cookie: paywarden_admin=<jwt>" \
-H "Content-Type: application/json" \
-d '{
"name": "Shop B",
"webhook_url": "https://shopb.com/webhooks/paywarden"
}'Merchant API key usage
Merchants use their API key to create and query their own orders:
bash
# Merchant A creates an order
curl -X POST https://pay.yourdomain.com/api/v1/payments \
-H "X-API-Key: pw_live_aaaa_..." \
-d '{"amount": "50.00", "currency": "USDT", "external_id": "a_order_1"}'
# Merchant B cannot see Merchant A's orders
curl https://pay.yourdomain.com/api/v1/payments/ord_abc \
-H "X-API-Key: pw_live_bbbb_..."
# → 404 Not FoundDefault merchant
On first setup, PayWarden creates a default merchant using the API_KEY from your .env. This merchant is used in single-tenant mode and for backwards compatibility.
Limitations in self-hosted mode
In the self-hosted version, all merchants share:
- One vault — single seed phrase, single xpub
- One address pool — globally sequential index, not per-merchant
This means:
- If the vault is compromised, all merchants' funds are at risk
- Address indices are not predictably tied to a single merchant
For full merchant isolation (separate vaults per merchant), see PayWarden Cloud.
Webhook routing
Each merchant receives webhooks only for their own orders, signed with their own webhook_secret:
Order created by Merchant A (webhook_url: https://a.com/hook)
│
▼ payment.confirmed
PayWarden signs with Merchant A's webhook_secret
│
▼
POST https://a.com/hook
X-PayWarden-Signature: sha256=<hmac(body, merchant_a_secret)>Merchant B never receives Merchant A's events.