Skip to content

Guide โ€” Processor Deployment

Launch a Processor

This guide covers deploying the Stablecoin Stack as a production payment processor: deploying the settlement contract, configuring fees, funding the relayer, connecting all services, and onboarding your first merchant.

This is the operator path โ€” you are running infrastructure that other businesses (merchants) depend on. Take the security checklist at the end seriously.

Time to complete: Half a day for a staging deployment. Production hardening is an ongoing process.


Architecture overview

As a processor you operate all of these:

Merchants โ”€โ”€mTLSโ”€โ”€โ–บ Checkout Engine โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                                                   โ”‚
Wallets โ”€โ”€WSSโ”€โ”€โ–บ Wallet-Gateway โ”€โ”€โ–บ Broadcast Service โ”€โ”€โ–บ Relayer โ”€โ”€โ–บ Settlement Contract (on-chain)
                                                                   โ”‚
Explorer โ—„โ”€โ”€ Transfer History โ—„โ”€โ”€ on-chain events โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Settlement Contract is the only on-chain component. Everything else is off-chain infrastructure you control.


Prerequisites

  • A server environment with Docker and Docker Compose (production: Kubernetes or equivalent)
  • A funded account on your target EVM-compatible network (for the Relayer)
  • A domain with TLS certificates (Let's Encrypt or equivalent)
  • A PostgreSQL 15+ instance
  • A Redis 7+ instance
  • Basic familiarity with Docker and environment configuration

Step 1 โ€” Choose your network

The Stablecoin Stack works on any EVM-compatible network. Choose based on your target market's requirements:

Network Settlement speed Gas cost Suitable for
Polygon PoS ~2 seconds Very low High-volume, low-value payments
Ethereum mainnet ~12 seconds Higher High-value, institutional
Base ~2 seconds Very low Consumer payments
Any EVM testnet Variable Free Development and staging

Note the network's chain ID โ€” you will need it throughout.


Step 2 โ€” Deploy the Settlement Contract

Clone the reference implementation and run the deployment script:

git clone https://github.com/Stablecoin-Stack/reference-implementation
cd reference-implementation

# Install deployment dependencies
npm install

# Create deployment config
cp deploy/config.example.json deploy/config.json

Edit deploy/config.json:

{
  "network": {
    "rpcUrl": "https://polygon-rpc.com",
    "chainId": 137
  },
  "deployer": {
    "privateKey": "${DEPLOYER_PRIVATE_KEY}"
  },
  "contract": {
    "baseFeeAmount": "2500000",
    "maxAcquiringFee": 500,
    "acquiringPrice": "100000000",
    "feeRecipient": "0xYOUR_FEE_RECIPIENT_ADDRESS",
    "allowedTokens": [
      "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
      "0x1a13f4ca1d028320a707d99520abf482184ae2b4"
    ]
  }
}
Parameter Description
baseFeeAmount Flat fee on every payment, in token units. $2.50 USDC = 2500000 (6 decimals).
maxAcquiringFee Maximum fee percentage an acquirer can set. 500 = 5.00% in processor-defined units.
acquiringPrice One-time acquirer registration fee. $100 USDC = 100000000.
feeRecipient Wallet that receives accumulated processor fees.
allowedTokens Token contracts accepted for acquirer registration payments.

Deploy:

DEPLOYER_PRIVATE_KEY=0x... npm run deploy:contract -- --config deploy/config.json

The script outputs the contract address. Save this โ€” you need it for every other component.

Settlement Contract deployed: 0xABCD...1234
Transaction hash: 0x...
Block: 12345678

Verify the contract on the block explorer immediately:

npm run verify:contract -- \
  --address 0xABCD...1234 \
  --network polygon \
  --args deploy/config.json

Contract verification is not optional

A conformant deployment MUST have its contract source verified on the block explorer. Merchants, auditors, and regulators will check. An unverified contract is a red flag.


Step 3 โ€” Fund the Relayer

The Relayer account pays gas on behalf of every payer. It recovers costs through the base fee, but it must have a positive native token balance at all times.

# Generate a dedicated relayer account (never reuse a key)
node scripts/generate-relayer-key.js

# Output:
# Address: 0xRELAYER...
# Private key: 0x...  โ† store in your secrets manager

# Fund it with native tokens (MATIC for Polygon, ETH for Ethereum, etc.)
# Minimum recommended: enough to cover 10,000 transactions at current gas prices

Set up automated monitoring and top-up alerts. A Relayer with zero balance stops processing payments for all your merchants.


Step 4 โ€” Configure environment variables

Create your production .env from the template:

cp .env.production.example .env.production

Key variables to set:

# Network
CHAIN_ID=137
RPC_URL=https://polygon-rpc.com
SETTLEMENT_CONTRACT=0xABCD...1234

# Relayer (store in a secrets manager, not in the file)
RELAYER_PRIVATE_KEY=${SECRET_RELAYER_KEY}

# Database
DATABASE_URL=postgresql://user:password@db-host:5432/stablecoin_stack

# Redis
REDIS_URL=redis://redis-host:6379

# Gateway
GATEWAY_DOMAIN=gateway.your-processor.example
GATEWAY_PORT=443

# Checkout engine
CHECKOUT_DOMAIN=checkout.your-processor.example
WEBHOOK_SECRET_KEY=${SECRET_WEBHOOK_KEY}

# Certificate Authority (for mTLS merchant connections)
CA_CERT_PATH=/certs/ca.crt
CA_KEY_PATH=/certs/ca.key

# Confirmation depth before marking a charge as settled
CONFIRMATION_BLOCKS=12

# Basic Data Service
DATA_SERVICE_DOMAIN=data.your-processor.example

Step 5 โ€” Deploy the application services

Use the production Compose file or adapt it to your orchestration environment:

docker compose -f docker-compose.production.yml up -d

Services started:

Service Purpose
wallet-gateway WebSocket interface for wallet clients
broadcast-service Validates payloads, manages submission state
broadcast-submitter Holds relayer key, submits on-chain transactions
balance-and-history Real-time balance views and transfer notifications
transfer-history Indexes on-chain events, triggers charge reconciliation
core-checkout-engine Merchant-facing charge API
checkout-widget Public payment page (QR code delivery)
basic-data-server Public token + processor configuration endpoint
ca-server Internal CA for mTLS certificate issuance

Health checks:

curl https://gateway.your-processor.example/health
curl https://checkout.your-processor.example/health
curl https://data.your-processor.example/tokens

Step 6 โ€” Publish to the Basic Data Service

The Basic Data Service is the public directory that wallets use to discover your processor configuration. It must expose:

{
  "settlementContract": "0xABCD...1234",
  "chainId": 137,
  "walletGatewayUrl": "wss://gateway.your-processor.example/ws",
  "feeRecipient": "0xFEE...",
  "supportedTokens": [
    {
      "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
      "symbol": "USDC",
      "name": "USD Coin",
      "decimals": 6,
      "domainSeparator": "0x..."
    }
  ]
}

The domainSeparator for each token is computed from its EIP-712 domain. The deployment script outputs this for each configured token.


Step 7 โ€” Onboard your first merchant

Issue a test merchant certificate and API credentials:

# Issue a merchant certificate (for mTLS)
docker compose exec ca-server \
  issue-cert --merchant-id merchant-001 --common-name "Test Merchant"

# Output: merchant-001.crt, merchant-001.key

Send the merchant their certificate, private key, and the checkout API documentation. They integrate as described in Integrate Merchant Checkout.

Test the full flow end-to-end before going to production:

# Create a test charge via the checkout API
curl -X POST https://checkout.your-processor.example/api/v1/charges \
  --cert merchant-001.crt --key merchant-001.key \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": "1000000",
    "token": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "beneficiary": "0xMERCHANT_WALLET",
    "currency": "USDC",
    "orderId": "test-001"
  }'

Step 8 โ€” Set fee parameters

Your fees are set on the Settlement Contract itself. To adjust after deployment:

import { ethers } from 'ethers';
import SettlementContractABI from './abis/SettlementContract.json';

const provider = new ethers.JsonRpcProvider(RPC_URL);
const admin    = new ethers.Wallet(ADMIN_PRIVATE_KEY, provider);
const contract = new ethers.Contract(SETTLEMENT_CONTRACT, SettlementContractABI, admin);

// Update base fee to $3.00
await contract.setBaseFeeAmount(ethers.parseUnits('3.00', 6));

// Update acquirer registration price to $150
await contract.setAcquiringPrice(ethers.parseUnits('150', 6));

Fee changes take effect immediately for new transactions. In-flight payments that have already been signed are not affected.


Step 9 โ€” Withdraw accumulated fees

Processor fees accumulate as an internal balance in the Settlement Contract. Withdraw to your fee recipient wallet:

// Check accumulated balance
const balance = await contract.getBalances(
  USDC_ADDRESS,
  [FEE_RECIPIENT_ADDRESS]
);
console.log(`Accumulated: $${ethers.formatUnits(balance[0], 6)}`);

// Withdraw
await contract.connect(feeRecipientSigner).withdraw(USDC_ADDRESS, withdrawAmount);

Production security checklist

Item Requirement
Settlement Contract source verified on block explorer MUST
Independent security audit of the contract MUST
Relayer private key in HSM or secrets manager MUST
Admin private key in hardware wallet or multi-sig MUST
Relayer balance monitoring with automated alerts MUST
Webhook secret rotatable without downtime MUST
Confirmation depth โ‰ฅ 12 blocks for mainnet RECOMMENDED
Database encrypted at rest RECOMMENDED
All services behind a WAF RECOMMENDED
Basic Data Service address matches deployed contract MUST
mTLS certificates issued by your CA (not self-signed) MUST
Incident response playbook documented RECOMMENDED

Monitoring

Recommended metrics to track:

# Relayer balance (alert below threshold)
# Transactions per minute (anomaly detection)
# Failed submissions rate
# Webhook delivery failure rate
# Gateway connected wallet count
# Confirmation depth distribution

Subscribe to the Event Explorer WebSocket from your monitoring system to track settlement in real time:

import { ExplorerClient } from '@stablecoin-stack/explorer-sdk';

const monitor = new ExplorerClient('wss://explorer.your-processor.example');
monitor.onSettlement((event) => {
  metrics.increment('settlements.total');
  metrics.gauge('settlements.fee', Number(event.fee));
});

Next steps