Docker 一键部署加密支付网关:从零开始完整教程
在电商或 SaaS 项目中集成加密货币支付,很多人第一反应是接入 BitPay 或 Coinbase Commerce。但对于有一定技术能力的团队来说,自托管(Self-Hosted)方案在成本、安全性和品牌体验上都有显著优势。
本文将从零开始,手把手教你用 Docker 部署一个生产级的自托管加密支付网关,并集成到你的应用中。
环境准备
硬件要求
| 配置项 | 最低要求 | 推荐配置 |
|---|---|---|
| CPU | 1 核 | 2 核 |
| 内存 | 1GB | 4GB |
| 磁盘 | 20GB | 50GB (SSD) |
| 网络 | 公网 IP | 公网 IP + 域名 |
大部分云厂商的入门级 VPS(如阿里云轻量、腾讯云轻量、AWS Lightsail 等)都能满足需求。
软件要求
- 操作系统:Ubuntu 22.04 / Debian 12 / CentOS 8+(本文以 Ubuntu 22.04 为例)
- Docker:20.10+
- Docker Compose:v2+
安装 Docker
bash
# 更新包索引
sudo apt update
# 安装依赖
sudo apt install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 添加 Docker 仓库
echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 验证安装
sudo docker --version
sudo docker compose version
第一步:生成主种子(Master Seed)
种子短语是支付网关的根密钥,所有收款地址都由它派生。这一步必须在离线环境中完成。
方式一:使用离线工具生成
下载 iancoleman.io/bip39 的离线版本,在不联网的机器上生成 12 或 24 个单词的 BIP-39 种子:
abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
安全警告 :上面的种子是 BIP-39 测试向量,仅用于测试。生产环境绝不可使用。必须在离线、无网络的机器上生成真实种子。
方式二:使用命令行生成(离线)
bash
# 使用 openssl 生成 128 位随机数(12 词助记词)
openssl rand -hex 16
# 输出示例: 7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
# 然后使用 BIP-39 工具将熵转换为助记词(需要离线运行)
记录下生成的 12 或 24 个单词,将其安全保存(纸质备份 + 加密存储)。
第二步:创建项目目录
bash
mkdir -p ~/payment-gateway
cd ~/payment-gateway
第三步:配置环境变量
创建 .env 文件:
bash
cat > .env << 'EOF'
# 数据库
DB_ROOT_PASSWORD=your_secure_root_password_here
DB_PASSWORD=your_secure_db_password_here
# Redis
REDIS_PASSWORD=your_secure_redis_password_here
# 支付网关
XPAY_MASTER_SEED="your twelve word seed phrase generated offline"
XPAY_WEBHOOK_SECRET=whsec_your_webhook_secret_here
XPAY_API_KEY=xpay_your_api_key_here
XPAY_CHAINS=tron,eth,bsc,polygon
# 链配置(可选,默认使用公共 RPC)
# XPAY_TRON_NETWORK=mainnet
# XPAY_ETH_RPC_URL=https://eth.drpc.org
# XPAY_BSC_RPC_URL=https://bsc.drpc.org
# XPAY_POLYGON_RPC_URL=https://polygon.drpc.org
EOF
注意 :生产环境请将以上密码全部替换为强随机字符串。可以使用
openssl rand -base64 32生成。
第四步:编写 Docker Compose 文件
创建 docker-compose.yml:
yaml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: gateway-mysql
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: xpay_gateway
MYSQL_USER: xpay
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
networks:
- gateway-net
restart: always
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: gateway-redis
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
networks:
- gateway-net
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
gateway:
image: ghcr.io/xpaylabs/gateway:latest
container_name: xpay-gateway
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/xpay_gateway?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: xpay
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.cj.jdbc.Driver
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
SPRING_REDIS_PASSWORD: ${REDIS_PASSWORD}
XPAY_MASTER_SEED: ${XPAY_MASTER_SEED}
XPAY_WEBHOOK_SECRET: ${XPAY_WEBHOOK_SECRET}
XPAY_API_KEY: ${XPAY_API_KEY}
XPAY_CHAINS: ${XPAY_CHAINS}
# 可选:自定义区块链 RPC
# XPAY_TRON_GRID_URL: https://api.trongrid.io
# XPAY_ETH_RPC_URL: https://eth.drpc.org
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- gateway-net
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
mysql_data:
redis_data:
networks:
gateway-net:
driver: bridge
第五步:启动网关
bash
# 启动所有服务
sudo docker compose up -d
# 查看启动日志
sudo docker compose logs -f gateway
# 等待约 30-60 秒,看到类似以下日志表示启动成功:
# [INFO] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
# [INFO] [main] c.xpay.gateway.GatewayApplication : Started GatewayApplication in 25.4 seconds
第六步:验证网关运行状态
bash
# 测试网关 API 是否正常响应
curl -X GET http://localhost:8080/api/v1/health
# 预期返回:
# {"status":"UP","timestamp":"2025-01-15T10:30:00Z"}
创建一张测试发票验证功能:
bash
curl -X POST http://localhost:8080/api/v1/invoices \
-H "Content-Type: application/json" \
-H "X-API-Key: xpay_your_api_key_here" \
-d '{
"amount": "10.00",
"currency": "USD",
"chain": "tron",
"settlementAsset": "USDT",
"description": "测试订单 #001",
"metadata": {"orderId": "001"}
}'
成功后会返回类似:
json
{
"id": "inv_a1b2c3d4",
"amount": "10.00",
"currency": "USD",
"chain": "tron",
"settlementAsset": "USDT",
"depositAddress": "TXYZ1234...",
"status": "pending",
"checkoutUrl": "http://localhost:8080/checkout/inv_a1b2c3d4",
"createdAt": "2025-01-15T10:31:00Z",
"expiresAt": "2025-01-15T11:01:00Z"
}
第七步:配置 Nginx 反向代理(生产环境)
生产环境不应直接暴露网关端口。使用 Nginx 反向代理 + SSL 证书:
nginx
# /etc/nginx/sites-available/payments.example.com
server {
listen 443 ssl;
server_name payments.example.com;
ssl_certificate /etc/letsencrypt/live/payments.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/payments.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name payments.example.com;
return 301 https://$server_name$request_uri;
}
申请 SSL 证书:
bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d payments.example.com
第八步:集成到你的后端应用
Node.js/TypeScript 集成
bash
npm install @xpaylabs/node-sdk
typescript
import { XPayClient } from '@xpaylabs/node-sdk';
const client = new XPayClient({
apiKey: process.env.XPAY_API_KEY,
gatewayUrl: 'https://payments.example.com'
});
// 创建支付订单
app.post('/api/create-payment', async (req, res) => {
const { orderId, amount } = req.body;
const invoice = await client.createInvoice({
amount: amount.toString(),
currency: 'USD',
chain: 'tron',
settlementAsset: 'USDT',
description: `订单 #${orderId}`,
metadata: { orderId },
successUrl: `https://myshop.com/order/success/${orderId}`,
cancelUrl: `https://myshop.com/order/${orderId}`
});
res.json({
invoiceId: invoice.id,
checkoutUrl: invoice.checkoutUrl
});
});
// Webhook 处理支付确认
app.post('/api/webhooks/xpay',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['x-webhook-signature'];
const expected = crypto
.createHmac('sha256', process.env.XPAY_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (sig !== expected) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
if (event.type === 'invoice.confirmed') {
const { orderId } = event.data.metadata;
// 释放订单、发送商品、更新数据库
console.log(`订单 ${orderId} 支付确认`);
}
res.status(200).send('OK');
}
);
Java/Spring Boot 集成
xml
<dependency>
<groupId>io.xpay</groupId>
<artifactId>xpay-java-sdk</artifactId>
<version>0.1.0</version>
</dependency>
java
@Configuration
public class XPayConfig {
@Bean
public XPayClient xPayClient(@Value("${xpay.api-key}") String apiKey,
@Value("${xpay.gateway-url}") String gatewayUrl) {
return new XPayClient(apiKey, gatewayUrl);
}
}
@Service
public class PaymentService {
@Autowired
private XPayClient xPayClient;
public Invoice createPayment(String orderId, BigDecimal amount) {
CreateInvoiceRequest request = new CreateInvoiceRequest();
request.setAmount(amount.toString());
request.setCurrency("USD");
request.setChain("tron");
request.setSettlementAsset("USDT");
request.setDescription("订单 #" + orderId);
request.setSuccessUrl("https://myshop.com/order/success/" + orderId);
return xPayClient.createInvoice(request);
}
}
@RestController
public class WebhookController {
@PostMapping("/api/webhooks/xpay")
public ResponseEntity<String> handleWebhook(
@RequestBody String payload,
@RequestHeader("x-webhook-signature") String signature) {
String expected = HmacUtils.hmacSha256Hex(
webhookSecret, payload);
if (!expected.equals(signature)) {
return ResponseEntity.status(401).body("Invalid signature");
}
// 处理事件...
return ResponseEntity.ok("OK");
}
}
第九步:配置 Nginx 白标结账页面
网关自带白标结账页面。只需配置前端域名,客户看到的就是你的品牌:
yaml
# docker-compose.yml 中添加前端环境变量
services:
gateway:
environment:
XPAY_BRAND_NAME: "你的品牌名称"
XPAY_BRAND_LOGO_URL: "https://example.com/logo.png"
XPAY_SUPPORT_EMAIL: "support@example.com"
XPAY_PRIMARY_COLOR: "#2563eb"
运维与监控
查看日志
bash
# 实时日志
sudo docker compose logs -f --tail=100
# 查看特定服务的日志
sudo docker compose logs -f gateway
sudo docker compose logs -f mysql
备份数据库
bash
# 创建备份脚本 backup.sh
#!/bin/bash
BACKUP_DIR="/backups/gateway"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# 备份 MySQL
sudo docker compose exec -T mysql \
mysqldump -u root -p${DB_ROOT_PASSWORD} xpay_gateway \
> $BACKUP_DIR/gateway_$TIMESTAMP.sql
# 压缩备份
gzip $BACKUP_DIR/gateway_$TIMESTAMP.sql
# 保留最近 30 天备份,删除更早的
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
添加到 crontab 实现自动备份:
bash
0 3 * * * /root/payment-gateway/backup.sh
更新网关
bash
cd ~/payment-gateway
# 拉取最新镜像
sudo docker compose pull gateway
# 重新创建容器
sudo docker compose up -d --no-deps gateway
常见问题
1. 网关启动后立即退出
检查配置文件和数据库连接:
bash
# 查看完整错误日志
sudo docker compose logs gateway
# 常见原因:数据库连接失败、种子短语格式错误
# 确认 MySQL 服务健康:
sudo docker compose ps
2. 无法创建发票
bash
# 检查 API 密钥是否正确
curl -X POST http://localhost:8080/api/v1/invoices \
-H "Content-Type: application/json" \
-H "X-API-Key: your_key_here" \
-d '{"amount":"10","currency":"USD","chain":"tron","settlementAsset":"USDT"}'
# 如果返回 401,检查 .env 中的 XPAY_API_KEY 配置
3. 交易检测不到
bash
# 检查是否配置了正确的链
curl -X GET http://localhost:8080/api/v1/chains
# 确认使用的网络(主网/测试网)
# 测试网使用 TRON Shasta 网络:XPAY_TRON_NETWORK=shasta
4. Webhook 收不到回调
bash
# 确认 Webhook URL 可公网访问
# 确认 Webhook Secret 前后端一致
# 查看 Webhook 投递日志:
curl -X GET http://localhost:8080/api/v1/webhooks/logs?limit=10 \
-H "X-API-Key: your_key_here"
成本估算
部署完成后,运行成本如下:
| 项目 | 月成本 |
|---|---|
| VPS(2核4G) | ¥50-100 |
| 域名 | ¥5-10 |
| SSL 证书(Let's Encrypt) | 免费 |
| 区块链 RPC 费用 | $0-15 |
| 总计 | 约 ¥50-150/月 |
对比第三方网关(月流水 10 万美元情况下):
| 方案 | 月费用 | 年费用 |
|---|---|---|
| BitPay | 1,030-1,300 | 12,360-15,600 |
| Coinbase Commerce | $1,000 | $12,000 |
| 自托管 | ¥50-150 (~$7-21) | ¥600-1,800 (~$84-252) |
总结
本文从零开始完成了 Docker 部署加密支付网关的全流程:
- 环境准备:服务器、Docker 安装
- 密钥生成:离线 BIP-39 种子短语
- 配置部署:Docker Compose 一键启动
- 集成验证:API 创建发票 + Webhook 回调
- 生产加固:Nginx 反向代理、SSL、备份
整个部署过程大约需要 30 分钟,之后的维护主要是偶尔更新镜像和检查备份。对于月流水 1 万美元以上的商家,一年节省的费用足以覆盖数十倍的部署投入。
如果你正在寻找替代 BitPay 或 Coinbase Commerce 的自托管方案,这个部署流程是一个很好的起点。对于需要 TRON USDT 原生支持、多链架构和白标体验的团队,基于 Docker 的自托管网关是目前性价比最高的选择。