Skip to content

REX Pay — Deployment Guide

This guide covers production deployment of REX Pay (Regen Exchange), a non-custodial crypto payment processing platform. It is intended for system administrators and DevOps engineers.


Table of Contents

  1. Prerequisites
  2. What You Need — No API Keys Required for DOGE/LTC
  3. Production Setup
  4. Chain Node Setup
  5. Scaling
  6. Monitoring
  7. Backup and Recovery
  8. Environment Variables Reference

1. Prerequisites

Software

Requirement Minimum Version Notes
Docker Engine 24+ Required for all services
Docker Compose v2.20+ Plugin version (docker compose) preferred
Git Any To clone the repository

Hardware (Production Recommendations)

Component Minimum Recommended
CPU 4 cores 8+ cores
RAM 8 GB 16 GB
Disk (OS + app) 20 GB 50 GB SSD
Disk (DOGE mainnet) 60 GB 100 GB SSD
Disk (LTC mainnet) 120 GB 180 GB SSD
Network 10 Mbps 100 Mbps

Both blockchain nodes require dedicated SSD storage separate from the OS volume. A single disk setup is possible but not recommended for production.

Domain and TLS

  • A domain pointed at your server (rexpay.co is planned for Q2 2026).
  • A TLS certificate — Let's Encrypt via Certbot is the standard free option.
  • A reverse proxy (Nginx) to terminate TLS and route traffic to Docker containers.

2. What You Need

REX Pay is designed to minimize third-party dependencies and API costs. The table below summarizes exactly what external services are needed and their cost.

Chain / Service Node Required External API Cost Notes
Dogecoin (DOGE) Dogecoin Core (included in Docker) None Free Runs its own full node. No key needed.
Litecoin (LTC) Litecoin Core (included in Docker) None Free Runs its own full node. No key needed.
USDT TRC-20 None TronGrid Free tier No node. Requires TronGrid API (free tier is sufficient for low volume). Key recommended for production. Get key at https://www.trongrid.io/
Exchange rates None CoinGecko Free Public API for DOGE/LTC to fiat conversion. No key needed for the free tier (300 req/min).
PostgreSQL Included in Docker None Free
Redis Included in Docker None Free

Banking and licensing: REX Pay is non-custodial — crypto goes directly to the merchant's own wallet. The platform never holds customer funds. No money transmitter license is required for non-custodial operation. No bank account is needed.


3. Production Setup

3.1 Clone the Repository

git clone git@github.com:ReGenNow/REXPay.git /opt/rexpay
cd /opt/regen-pay

3.2 Create the Environment File

Copy the example and fill in production values:

cp .env.example .env
# Edit with your values:
nano .env

The critical values to set before first boot:

# 1. Generate a Django secret key (use a long random string)
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_urlsafe(50))")

# 2. Generate the Fernet encryption key (CRITICAL — back this up)
REGEN_PAY_ENC_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")

echo "SECRET_KEY=$SECRET_KEY"
echo "REGEN_PAY_ENC_KEY=$REGEN_PAY_ENC_KEY"

Update .env with both values.

CRITICAL: The REGEN_PAY_ENC_KEY encrypts all merchant xpubs, addresses, and webhook secrets in the database. If this key is lost, all encrypted data is permanently unrecoverable. Back it up to a secure secrets manager immediately.

3.3 Configure Required Environment Variables

Minimum production .env values:

# Django
SECRET_KEY=<generated above — long random string>
DEBUG=false
ALLOWED_HOSTS=rexpay.co,www.rexpay.co

# Database
DATABASE_URL=postgres://regenpay:<strong_password>@postgres:5432/regenpay

# Redis
REDIS_URL=redis://redis:6379/0
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0

# Encryption (CRITICAL — back this up)
REGEN_PAY_ENC_KEY=<generated above>
FIELD_ENCRYPTION_KEY=<same value as REGEN_PAY_ENC_KEY>

# CORS
CORS_ALLOWED_ORIGINS=https://rexpay.co,https://www.rexpay.co,https://pay.rexpay.co,https://api.rexpay.co,https://app.rexpay.co,https://docs.rexpay.co

# Public URLs
PUBLIC_API_BASE_URL=https://api.rexpay.co
PUBLIC_CHECKOUT_BASE_URL=https://pay.rexpay.co
SITE_URL=https://rexpay.co

# Dogecoin (mainnet RPC port is 22555)
DOGE_NETWORK=mainnet
DOGE_RPC_HOST=rexpay_dogecoind
DOGE_RPC_PORT=22555
DOGE_RPC_USER=<choose a strong rpc user>
DOGE_RPC_PASSWORD=<choose a strong rpc password>
DOGE_RPC_WALLET=rexpay_watch

# Litecoin (mainnet RPC port is 9332)
LTC_NETWORK=mainnet
LTC_RPC_HOST=rexpay_litecoind
LTC_RPC_PORT=9332
LTC_RPC_USER=<choose a strong rpc user>
LTC_RPC_PASSWORD=<choose a strong rpc password>
LTC_RPC_WALLET=rexpay_watch

# TRON / USDT TRC-20
TRON_NETWORK=mainnet
TRONGRID_API_KEY=<your key from trongrid.io — optional for low volume>

3.4 Update PostgreSQL Password in docker-compose.yml

Update the postgres service environment in docker-compose.yml to match the password set in DATABASE_URL:

postgres:
  environment:
    - POSTGRES_USER=regenpay
    - POSTGRES_PASSWORD=<strong_password>
    - POSTGRES_DB=regenpay

3.5 Update Dogecoin and Litecoin Node RPC Credentials

In docker-compose.yml, update the dogecoind and litecoind command arguments to match the RPC user and password set in .env:

dogecoind:
  command: >
    dogecoind
      -mainnet
      -server
      -rpcuser=<your_rpc_user>
      -rpcpassword=<your_rpc_password>
      -rpcallowip=172.16.0.0/12
      -rpcbind=0.0.0.0
      -rpcport=8332
      -txindex=1
      -disablewallet=0

For production, restrict -rpcallowip to the Docker internal network range rather than 0.0.0.0/0.

3.6 Start All Services

docker compose up -d

Check that all services started:

docker compose ps
docker compose logs --tail=30

3.7 Run Initial Database Migration

The API container runs python manage.py migrate automatically on startup. You can also run it manually:

docker compose exec api python manage.py migrate

3.8 Collect Static Files

docker compose exec api python manage.py collectstatic --noinput

3.9 Create the First Merchant

REX Pay ships with management commands to bootstrap merchants and API keys:

# Create a merchant account
docker compose exec api python manage.py create_merchant \
  --name "My Store" \
  --email "merchant@example.com"

# Create API keys (test and live) for the merchant
docker compose exec api python manage.py create_apikey \
  --merchant-email "merchant@example.com" \
  --mode test

docker compose exec api python manage.py create_apikey \
  --merchant-email "merchant@example.com" \
  --mode live

The full key is shown only once at creation time. Store it securely.

3.10 Nginx Reverse Proxy with TLS

Install Nginx and Certbot on the host (outside Docker):

apt install nginx certbot python3-certbot-nginx
certbot --nginx -d rexpay.co -d www.rexpay.co

Example Nginx configuration for the API:

server {
    listen 443 ssl;
    server_name api.rexpay.co;

    ssl_certificate /etc/letsencrypt/live/rexpay.co/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rexpay.co/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 443 ssl;
    server_name pay.rexpay.co;

    ssl_certificate /etc/letsencrypt/live/rexpay.co/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rexpay.co/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

4. Chain Node Setup

4.1 Dogecoin Core

Image: dogecoin/dogecoin:latest

Testnet RPC port: 44555 Mainnet RPC port: 8332

Testnet vs Mainnet:

Setting Testnet Mainnet
-testnet flag Present Absent (default)
RPC port 44555 8332
Address prefix n D
Disk usage ~1 GB ~50 GB
Sync time ~30 minutes ~2-4 hours
Coins Worthless test coins Real DOGE

Switching from testnet to mainnet in docker-compose.yml:

dogecoind:
  command: >
    dogecoind
      -mainnet
      -server
      -rpcport=8332
      ...

And in .env:

DOGE_NETWORK=mainnet
DOGE_RPC_PORT=8332

Watch-only wallet: The system automatically creates and loads a watch-only wallet named regenpay_watch (configured via DOGE_RPC_WALLET). This wallet tracks all derived payment addresses without holding any private keys.

Volume: Dogecoin blockchain data is stored in the dogecoin_data Docker volume. Do not delete this volume — full resync takes hours.

4.2 Litecoin Core

Image: litecoin/litecoin-core:latest

Testnet RPC port: 19332 Mainnet RPC port: 9332

Testnet vs Mainnet:

Setting Testnet Mainnet
-testnet flag Present Absent
RPC port 19332 9332
Address prefixes m, n, tltc1 L, M, ltc1
Disk usage ~2 GB ~100 GB
Sync time ~1 hour ~4-8 hours
Default confirmations required 3 3

Switching to mainnet in docker-compose.yml:

litecoind:
  command: >
    litecoind
      -server
      -rpcport=9332
      ...

And in .env:

LTC_NETWORK=mainnet
LTC_RPC_PORT=9332

4.3 USDT TRC-20 (TronGrid API — No Node Required)

REX Pay uses TronGrid's HTTP API to monitor USDT TRC-20 transfers. No local TRON node is required.

Free tier: Sufficient for development and low-volume production. No API key needed to make basic requests.

Production key: Recommended to avoid rate limits. Obtain at https://www.trongrid.io/

Endpoints used: - /v1/accounts/{address}/transactions/trc20 — poll inbound USDT transfers per address - /v1/transactions/{txid}/events — retrieve transaction details - /wallet/getnowblock — health check

Mainnet vs Testnet (Nile):

Setting Mainnet Testnet (Nile)
TRON_NETWORK mainnet testnet
API URL https://api.trongrid.io https://nile.trongrid.io
USDT contract TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t TXLAQ63Xg1NAzckPwKHvzw7CSEmLMEqcdj

5. Scaling

5.1 API Workers

The api service runs Gunicorn with 2 workers by default. For production under load, increase the worker count:

api:
  command: >
    sh -c "python manage.py migrate &&
           gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 4 --worker-class gthread --threads 2"

A common rule of thumb is (2 * CPU_cores) + 1 workers.

5.2 Per-Chain Celery Workers

Each chain runs in an independent Celery worker consuming a dedicated queue:

Worker Queue Dependency
celery-doge doge Dogecoin Core node
celery-ltc ltc Litecoin Core node
celery-tron usdt_trc20 TronGrid API
celery-webhooks webhooks None
celery-beat (scheduler) All queues

Each worker can be scaled independently. To add concurrency for a busy chain:

celery-doge:
  command: celery -A core worker -Q doge -l info --concurrency=4

5.3 PostgreSQL Tuning

For production, tune PostgreSQL via environment variables or a custom postgresql.conf. Key settings for a 16 GB RAM server:

max_connections = 100
shared_buffers = 4GB
effective_cache_size = 12GB
maintenance_work_mem = 1GB
checkpoint_completion_target = 0.9
wal_buffers = 64MB
default_statistics_target = 100
random_page_cost = 1.1        # SSD
effective_io_concurrency = 200  # SSD
work_mem = 10MB

Mount a custom config:

postgres:
  volumes:
    - ./postgres.conf:/etc/postgresql/postgresql.conf
    - postgres_data:/var/lib/postgresql/data
  command: postgres -c config_file=/etc/postgresql/postgresql.conf

5.4 Redis Configuration

Redis is used for both Celery broker and Django cache. For production, enable persistence:

redis:
  command: redis-server --appendonly yes --maxmemory 2gb --maxmemory-policy allkeys-lru
  volumes:
    - redis_data:/data

5.5 Multiple API Instances (Load Balancing)

The API is stateless. To run multiple instances behind a load balancer:

  1. Remove port mapping from the api service in docker-compose.yml.
  2. Add an nginx or traefik service as the load balancer.
  3. Scale: docker compose up -d --scale api=3

6. Monitoring

6.1 Health Check Endpoints

Endpoint Purpose Returns unhealthy on
GET /health/ Overall system health (DB, Redis, chain providers) DB failure → 503
GET /health/ready/ Kubernetes readiness probe DB unreachable → 503
GET /health/live/ Kubernetes liveness probe Always 200 if process is alive

Example /health/ response:

{
  "status": "healthy",
  "checks": {
    "database": "ok",
    "cache": "ok",
    "chains": {
      "DOGE": {
        "core": { "name": "dogecoin_core", "healthy": true },
        "fallback": { "enabled": false, "name": null, "healthy": null },
        "using_fallback": false,
        "degraded": false
      }
    }
  }
}

A status of degraded means the system is operational but a chain provider is using a fallback or experiencing issues. unhealthy (503) means the database is down and the system cannot function.

6.2 Celery Monitoring

View worker status:

docker compose exec celery-doge celery -A core inspect active
docker compose exec celery-doge celery -A core inspect stats

View beat schedule:

docker compose exec celery-beat celery -A core inspect scheduled

6.3 Beat Schedule Summary

Task Schedule Queue Purpose
watch_all_chains Every 10 seconds doge Scan chains for new transactions
update_confirmations Every 30 seconds doge Update confirmation counts
check_expired Every 60 seconds doge Expire unpaid invoices
retry_failed_webhooks Every 60 seconds webhooks Retry failed webhook deliveries
cleanup_old_intents Sundays at 3:00 AM doge Log old terminal-state intents for archival

6.4 Log Aggregation

Stream all service logs:

docker compose logs -f

Stream a specific service:

docker compose logs -f celery-doge
docker compose logs -f api

For production, forward logs to a centralized system (Loki, CloudWatch, Datadog, etc.) by configuring Docker's logging driver in docker-compose.yml:

services:
  api:
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

7. Backup and Recovery

7.1 PostgreSQL Backups

Daily backup script:

#!/bin/bash
BACKUP_DIR=/backups/postgres
DATE=$(date +%Y-%m-%d-%H%M%S)
mkdir -p "$BACKUP_DIR"

docker compose exec -T postgres pg_dump \
  -U regenpay \
  -d regenpay \
  --format=custom \
  --compress=9 \
  > "$BACKUP_DIR/regenpay-$DATE.dump"

# Retain 30 days
find "$BACKUP_DIR" -name "*.dump" -mtime +30 -delete

Restore:

docker compose exec -T postgres pg_restore \
  -U regenpay \
  -d regenpay \
  --clean \
  < /backups/postgres/regenpay-<date>.dump

7.2 Chain Node Data Volumes

The Dogecoin and Litecoin blockchain data lives in Docker named volumes (dogecoin_data, litecoin_data). These are large and slow-changing. Options:

  1. No backup needed if you are willing to re-sync from genesis on disaster recovery (hours, not days for DOGE/LTC).
  2. Snapshot the volume using Docker or the host's filesystem snapshot feature.
  3. Use a separate fast disk mounted as the volume path, then snapshot that disk.

To find the volume path on the host:

docker volume inspect regen-pay_dogecoin_data
# Look for "Mountpoint"

7.3 Redis Backup

Redis persistence is handled by the appendonly yes configuration (see Section 5.4). The AOF file is stored in the redis_data volume. Back it up like any file.

For Celery's usage, Redis data loss is acceptable — tasks will be re-queued on the next beat tick (within 10-60 seconds). Rate limit counters and cache will reset harmlessly.

7.4 Encryption Key Backup — CRITICAL

The REGEN_PAY_ENC_KEY (Fernet key) encrypts all sensitive merchant data in the database:

  • Merchant xpubs (extended public keys for address derivation)
  • Settlement addresses
  • Webhook signing secrets

If this key is lost, ALL encrypted data in the database is permanently unrecoverable. Merchant configurations cannot be restored.

Required backup procedure:

  1. Store the key in at minimum two separate locations (e.g., a password manager plus an offline printed copy in a safe).
  2. Rotate it only with a planned key-rotation migration — never change it without migrating the encrypted data first.
  3. Include it in your secrets management system (HashiCorp Vault, AWS Secrets Manager, etc.).

8. Environment Variables Reference

Core Django Settings

Variable Default Required Description
SECRET_KEY dev-secret-key-change-in-production Yes Django secret key. Must be long, random, and unique per environment.
DEBUG true Yes Set to false in production.
ALLOWED_HOSTS localhost,127.0.0.1,regenpay.local Yes Comma-separated list of allowed hostnames.

Database

Variable Default Required Description
DATABASE_URL postgres://regenpay:regenpay@localhost:5432/regenpay Yes PostgreSQL connection URL.

Redis and Celery

Variable Default Required Description
REDIS_URL redis://localhost:6379/0 Yes Redis connection URL. Used for cache and session.
CELERY_BROKER_URL redis://localhost:6379/0 Yes Celery message broker URL.
CELERY_RESULT_BACKEND redis://localhost:6379/0 Yes Celery result backend URL.

CORS

Variable Default Required Description
CORS_ALLOWED_ORIGINS http://localhost:3000,http://127.0.0.1:3000 Yes Comma-separated list of allowed CORS origins.

Encryption

Variable Default Required Description
REGEN_PAY_ENC_KEY (empty) Yes Fernet key for encrypting xpubs, addresses, and webhook secrets. Generate with python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())".
FIELD_ENCRYPTION_KEY (empty) Yes Must be set to the same value as REGEN_PAY_ENC_KEY.

Dogecoin Core RPC

Variable Default Required Description
DOGE_NETWORK testnet Yes mainnet or testnet.
DOGE_RPC_HOST localhost Yes Hostname of the Dogecoin Core node. Use dogecoind inside Docker.
DOGE_RPC_PORT 44555 Yes RPC port. 44555 for testnet, 8332 for mainnet.
DOGE_RPC_USER rpcuser Yes RPC username. Must match node's -rpcuser argument.
DOGE_RPC_PASSWORD rpcpassword Yes RPC password. Must match node's -rpcpassword argument.
DOGE_RPC_TIMEOUT 30.0 No RPC call timeout in seconds.
DOGE_RPC_WALLET regenpay_watch No Watch-only wallet name. Auto-created on first start.

Litecoin Core RPC

Variable Default Required Description
LTC_NETWORK testnet Yes mainnet or testnet.
LTC_RPC_HOST litecoind Yes Hostname of the Litecoin Core node. Use litecoind inside Docker.
LTC_RPC_PORT 19332 Yes RPC port. 19332 for testnet, 9332 for mainnet.
LTC_RPC_USER rpcuser Yes RPC username. Must match node's -rpcuser argument.
LTC_RPC_PASSWORD rpcpassword Yes RPC password.
LTC_RPC_TIMEOUT 30.0 No RPC call timeout in seconds.
LTC_RPC_WALLET regenpay_watch No Watch-only wallet name. Auto-created on first start.

TRON / USDT TRC-20

Variable Default Required Description
TRON_NETWORK mainnet Yes mainnet or testnet (Nile).
TRONGRID_API_KEY (empty) No TronGrid API key. Optional for low volume; recommended for production. Get at https://www.trongrid.io/

Rate Service (CoinGecko)

Variable Default Required Description
COINGECKO_API_URL https://api.coingecko.com/api/v3 No CoinGecko base URL.
RATE_CACHE_TTL_SECONDS 300 No How long to cache exchange rates in Redis (seconds).

Webhook Delivery

Variable Default Required Description
WEBHOOK_MAX_RETRIES 8 No Maximum delivery attempts per event.
WEBHOOK_RETRY_SCHEDULE 60,300,1800,7200,18000,36000,86400 No Comma-separated retry delays in seconds.
WEBHOOK_JITTER_PERCENT 15 No Jitter percentage applied to retry delays.
WEBHOOK_TIMESTAMP_TOLERANCE 300 No Seconds of tolerance for webhook signature timestamp validation.

Rate Limiting

Variable Default Required Description
RATE_LIMIT_LIVE_PER_MINUTE 60 No Requests per minute for live API keys.
RATE_LIMIT_TEST_PER_MINUTE 120 No Requests per minute for test API keys.
RATE_LIMIT_CHECKOUT_PER_MINUTE 30 No Requests per minute for unauthenticated checkout polling (per IP).

Public URLs

Variable Default Required Description
PUBLIC_API_BASE_URL http://localhost:8000 Yes Externally reachable API base URL. Used to construct links.
PUBLIC_CHECKOUT_BASE_URL http://localhost:3000 Yes Externally reachable checkout frontend base URL. Used for checkout_url in API responses.
SITE_URL http://localhost:8000 No Main site URL.

Idempotency

Variable Default Required Description
IDEMPOTENCY_KEY_TTL_HOURS 24 No How long idempotency keys are valid (hours).

Fallback Provider (Optional)

Variable Default Required Description
FALLBACK_PROVIDER none No External fallback for DOGE if Core is unhealthy. Options: none, sochain.
FALLBACK_PROVIDER_ENABLED false No Enable the fallback provider.
DOGE_EXPLORER_API_URL https://sochain.com/api/v2 No SoChain API base URL (if fallback enabled).
DOGE_EXPLORER_API_KEY (empty) No SoChain API key (if required).