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:
The script outputs the contract address. Save this โ you need it for every other component.
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:
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:
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¶
- Register as an Acquirer โ โ register yourself as an acquirer on your own stack
- Use the Event Explorer โ โ build settlement dashboards and reconciliation tools
- SS-001 Formal Specification โ normative processor requirements