Skip to content

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 + Redis

Setting up a new merchant

Via admin dashboard

  1. Log in at /admin
  2. Navigate to MerchantsCreate Merchant
  3. Fill in:
    • Name — internal label
    • Webhook URL — where payment events are sent
  4. 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 Found

Default 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.

Released under the BSL 1.1 License.