适用场景

本文记录在 Ubuntu 22.04 上使用 Docker Compose 部署 Jeepay 的步骤。Jeepay 是一套开源支付系统,后端仓库是 jeequan/jeepay,前端仓库是 jeequan/jeepay-ui

官方 Compose 部署方式适合本地、内网测试、功能验证和二次开发环境。它会一次性启动 MySQL、Redis、RocketMQ、支付网关、运营平台、商户系统,以及三个前端入口。

不要把这套默认配置直接暴露到公网生产环境。默认数据库密码、默认管理账号、多个中间件端口和后台入口都需要在上线前重新设计访问边界。

官方参考文档:

前置条件

服务器环境:

  • Ubuntu 22.04
  • 建议至少 4 核 CPU、8 GB 内存
  • 已能访问 GitHub,或自行替换为 Gitee/GitCode 镜像仓库
  • 已安装 Docker Engine 和 Docker Compose Plugin

Jeepay 后端使用 JDK 17 构建,前端会在 Docker 构建阶段使用 Node.js 镜像构建,所以宿主机至少需要安装 gitopenjdk-17-jdkmaven

sudo apt update
sudo apt install -y git openjdk-17-jdk maven ca-certificates curl gnupg

验证 Java 和 Maven:

java -version
mvn -version

如果还没有安装 Docker,可以参考本站的 Docker 入门:Ubuntu 安装与基础配置,也可以按 Docker 官方文档安装:

sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

把当前用户加入 docker 组:

sudo usermod -aG docker "$USER"
newgrp docker

验证 Docker:

docker version
docker compose version

创建部署目录

官方 Compose 默认要求后端目录 jeepay 和前端目录 jeepay-ui 放在同一级目录下。本文统一放在 /opt/jeepay-open

sudo mkdir -p /opt/jeepay-open
sudo chown -R "$USER:$USER" /opt/jeepay-open
cd /opt/jeepay-open

拉取源码

本文固定使用后端 V3.2.9 标签。GitHub 上的标签名是大写 V3.2.9,也就是常说的 v3.2.9 版本。

后端仓库:

git clone --branch V3.2.9 --depth 1 https://github.com/jeequan/jeepay.git

前端仓库当前没有同名的 V3.2.9 标签,3.2 系列能看到的是 V3.2.0。如果只是按官方 Compose 拉起测试环境,可以先使用前端 V3.2.0 标签:

git clone --branch V3.2.0 --depth 1 https://github.com/jeequan/jeepay-ui.git

如果后续前端仓库发布了 V3.2.9 标签,建议把前端也同步固定到同名标签。不要在长期环境里一直跟随 master,因为 master 后续会变化,重新部署时拿到的代码可能和第一次部署不同。支付系统尤其不适合无意识升级。

查看 Compose 配置

进入后端目录:

cd /opt/jeepay-open/jeepay

官方 Compose 文件在后端仓库根目录:

ls -lh docker-compose.yml
docker compose config

默认端口如下:

组件宿主机端口
MySQL13306
Redis6380
RocketMQ NameServer9876
RocketMQ Broker109091091110912
支付网关 payment9216
运营平台后端 manager9217
商户系统后端 merchant9218
收银台前端9226
运营平台前端9227
商户系统前端9228

这些端口默认会绑定到宿主机所有网卡。如果服务器有公网 IP,务必用防火墙限制来源,或者改成只监听 127.0.0.1 后再通过 Nginx 反向代理暴露必要入口。

镜像版本说明

官方 docker-compose.yml 里有几类镜像需要分开理解:

  • jeepay-payment:latestjeepay-manager:latestjeepay-merchant:latestjeepay-ui-*.latest 是本地构建出来的业务镜像,真正决定代码版本的是你 checkout 的源码版本。
  • MySQL、Redis、RocketMQ、JDK 基础镜像默认指向华为云 SWR 公开仓库,方便国内环境拉取。
  • MYSQL_IMAGE 默认是 swr.cn-south-1.myhuaweicloud.com/jeepay/mysql:8,属于浮动大版本标签。测试环境可以接受,生产环境应在充分验证后固定到明确镜像标签或镜像 digest。

如果要覆盖前端目录位置,可以创建 .env

cat > .env <<'EOF'
UI_BASE_DIR=..
EOF

默认同级目录结构是 /opt/jeepay-open/jeepay-ui,所以这里保持 .. 即可。

打包后端

Compose 构建后端镜像时会复制各模块 target 目录里的 JAR,所以第一次启动前必须先执行 Maven 打包:

cd /opt/jeepay-open/jeepay
mvn clean package -DskipTests

打包成功后确认 JAR 文件存在:

ls jeepay-payment/target/jeepay-payment.jar
ls jeepay-manager/target/jeepay-manager.jar
ls jeepay-merchant/target/jeepay-merchant.jar

启动 Jeepay

首次启动建议带上 --build,让 Compose 构建后端和前端镜像:

cd /opt/jeepay-open/jeepay
docker compose up -d --build

查看容器状态:

docker compose ps

如果机器配置较低,RocketMQ 和前端构建可能需要等一段时间。可以持续查看核心服务日志:

docker compose logs -f --tail=100 payment manager merchant

查看全部日志:

docker compose logs -f --tail=100

后续如果只是重启,不需要重新构建:

docker compose up -d

如果修改了后端代码,需要重新打包并构建镜像:

mvn clean package -DskipTests
docker compose up -d --build

访问验证

在服务器本机可以这样验证端口:

curl -I http://127.0.0.1:9227
curl -I http://127.0.0.1:9228
curl -I http://127.0.0.1:9226

浏览器访问:

http://<Ubuntu服务器IP>:9227
http://<Ubuntu服务器IP>:9228
http://<Ubuntu服务器IP>:9226

默认入口:

  • 运营平台:http://<Ubuntu服务器IP>:9227
  • 商户系统:http://<Ubuntu服务器IP>:9228
  • 收银台:http://<Ubuntu服务器IP>:9226

默认账号:

系统地址默认账号
运营平台http://<Ubuntu服务器IP>:9227jeepay / jeepay123
商户系统http://<Ubuntu服务器IP>:9228需要先在运营平台创建商户用户,默认密码 jeepay666

首次登录后应立即修改默认密码。不要把默认账号暴露在公网。

防火墙配置

如果只是内网测试,可以只允许可信网段访问前端端口。例如只允许 192.168.3.0/24 访问三个前端入口:

sudo ufw allow from 192.168.3.0/24 to any port 9226 proto tcp
sudo ufw allow from 192.168.3.0/24 to any port 9227 proto tcp
sudo ufw allow from 192.168.3.0/24 to any port 9228 proto tcp
sudo ufw status

不建议对公网开放这些端口:

  • 13306:MySQL
  • 6380:Redis
  • 9876109091091110912:RocketMQ
  • 921692179218:后端服务直连端口

如果要正式对外访问,建议使用 Nginx、HTTPS、固定域名、最小化端口暴露,并把数据库、Redis、MQ 保持在内网。

如果只是为了让支付宝沙箱回调 Jeepay,可以临时用 ngrok 暴露 payment 服务:

nohup ngrok http --url=maria-salic-lithographically.ngrok-free.dev 9216 \
  > /var/log/ngrok-jeepay-payment.log 2>&1 &

这里代理的是 9216 支付 API,不是前端页面。访问 https://maria-salic-lithographically.ngrok-free.dev/ 返回 404 是正常的,因为 payment 服务根路径没有首页;真正给支付宝用的是类似 https://maria-salic-lithographically.ngrok-free.dev/api/pay/notify/alipay 的回调路径。直接用浏览器或 curl 访问这个回调路径,如果没有带支付宝通知参数,通常会看到 order not exists,这反而说明请求已经打到 Jeepay payment 服务。

如果想通过 ngrok 访问页面,需要代理前端端口:9226 是收银台,9227 是运营平台,9228 是商户系统。但支付回调仍然应该指向 9216,不要把前端端口填到支付宝回调地址里。

接入 New-API:Jeepay + KitfoxPay

如果已经有 Jeepay 和 New-API,可以在中间增加 KitfoxPay 做适配。它对 New-API 暴露易支付接口,对下游调用 Jeepay API。这样 New-API 不需要改代码,只需要把易支付地址改成 KitfoxPay。

调用链路如下:

New-API
  -> KitfoxPay: 易支付接口 mapi.php / submit.php / api.php
  -> Jeepay: /api/pay/unifiedOrder 等接口
  -> KitfoxPay: /api/payment/notify
  -> New-API: /api/user/epay/notify 或 /api/subscription/epay/notify

注意访问边界:

  • New-API 必须能访问 KitfoxPay 的对外地址。
  • Jeepay 必须能回调 KitfoxPay 的 /api/payment/notify
  • KitfoxPay 必须能访问 Jeepay 的支付网关地址。
  • New-API 的回调地址要是外部能访问的域名,不能只写 Pod、Service 或 127.0.0.1

准备 Jeepay 参数

登录 Jeepay 运营平台:

http://<Ubuntu服务器IP>:9227

在 Jeepay 里准备或确认这些信息:

商户号:mchNo
应用 ID:appId
商户私钥:privateKey
已启用的支付方式:wayCode

wayCode 很重要。KitfoxPay 当前不会把 alipaywxpay 转换成 Jeepay 的支付方式,而是直接把 New-API 传来的 payment_method 当成 Jeepay 的 wayCode 透传。也就是说,New-API 支付方式里的 type 要写成 Jeepay 已启用的 wayCode,例如 ALI_PCALI_WAPALI_QRWX_NATIVEWX_H5 等,具体以你在 Jeepay 应用里启用的支付接口为准。

部署 KitfoxPay

本文把 KitfoxPay 放在 /opt/kitfoxpay。KitfoxPay 当前没有 release tag,本文固定到写作时核对过的提交:

862c11bbe5387ded9df5ccf7772ca03ff286f058

部署:

sudo mkdir -p /opt/kitfoxpay
sudo chown -R "$USER:$USER" /opt/kitfoxpay
cd /opt/kitfoxpay

git clone https://github.com/kitfoxai/kitfoxpay.git .
git checkout 862c11bbe5387ded9df5ccf7772ca03ff286f058

如果 GitHub 访问慢,可以用 Gitee 镜像:

git clone https://gitee.com/kitfoxai/kitfoxpay.git .
git checkout 862c11bbe5387ded9df5ccf7772ca03ff286f058

如果要使用 Jeepay 的 ALI_QR,KitfoxPay 需要把 Jeepay 返回的 qr.alipay.com 链接渲染成二维码,所以先把二维码依赖写入 package.json

docker run --rm \
  -v "$PWD:/workspace" \
  -w /workspace \
  node:20-alpine \
  npm pkg set dependencies.qrcode='^1.5.4'

创建 Dockerfile:

cat > Dockerfile <<'EOF'
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install --omit=dev

COPY . .

EXPOSE 9219

CMD ["npm", "start"]
EOF

这里使用 node:20-alpine,固定了 Node 20 大版本,但仍然不是 digest 级别固定。KitfoxPay 仓库当前没有 package-lock.jsonnpm install 也会按依赖范围解析版本。生产环境建议先在测试环境生成并验证 package-lock.json,再构建镜像。

创建 KitfoxPay 配置

生成易支付密钥和管理后台密码:

EPAY_KEY=$(openssl rand -hex 24)
ADMIN_PASSWORD=$(openssl rand -base64 24)

echo "EPAY_KEY=$EPAY_KEY"
echo "ADMIN_PASSWORD=$ADMIN_PASSWORD"

创建 config.js

cat > config.js <<'EOF'
module.exports = {
  jeepay: {
    baseUrl: 'http://host.docker.internal:9216',
    mchNo: '替换成Jeepay商户号',
    appId: '替换成Jeepay应用ID',
    privateKey: '替换成Jeepay商户私钥'
  },

  epay: {
    pid: 'newapi',
    key: '替换成上一步生成的EPAY_KEY'
  },

  server: {
    host: '0.0.0.0',
    port: 9219,
    siteDomain: 'http://<Ubuntu服务器IP>:9219'
  },

  admin: {
    password: '替换成上一步生成的ADMIN_PASSWORD'
  }
};
EOF

如果 KitfoxPay 和 Jeepay 在同一台 Ubuntu 机器上,且 KitfoxPay 运行在单独 Docker Compose 里,baseUrl 可以先用:

http://host.docker.internal:9216

下面的 Compose 会加上 host.docker.internal:host-gateway,让 Linux Docker 容器能访问宿主机上的 9216 端口。

siteDomain 要写 New-API 和 Jeepay 都能访问的地址。内网测试可以先写:

http://192.168.3.213:9219

如果 New-API 已经走公网域名和 HTTPS,KitfoxPay 也建议放到 HTTPS 域名后面,例如:

https://kitfoxpay.example.com

创建 KitfoxPay Compose

cat > compose.yaml <<'EOF'
services:
  kitfoxpay:
    build:
      context: .
      dockerfile: Dockerfile
    image: kitfoxpay:862c11b
    container_name: kitfoxpay
    restart: unless-stopped
    ports:
      - "9219:9219"
    volumes:
      - ./config.js:/app/config.js
    extra_hosts:
      - "host.docker.internal:host-gateway"
EOF

启动:

docker compose up -d --build
docker compose ps
docker compose logs -f --tail=100

验证 KitfoxPay:

curl http://127.0.0.1:9219/api/health

正常会返回 JSON,并能看到 Jeepay 的 baseUrlmchNoappId 等配置摘要。

管理界面:

http://<Ubuntu服务器IP>:9219

config.js 挂载为可写文件,KitfoxPay 的 Web 管理界面保存配置时会写回这个文件。这个后台不要直接暴露到公网;如果必须公网访问,至少要放到 HTTPS 和访问控制后面,并使用强密码。

配置 New-API

登录 New-API 管理后台,在系统设置或支付设置中配置易支付参数。不同 New-API 版本界面名称可能略有差异,核心字段对应关系如下:

New-API 字段填写内容
易支付接口地址 / PayAddress / epay_urlhttp://<KitfoxPay地址>:9219
易支付商户 ID / EpayId / pidnewapi
易支付商户密钥 / EpayKey / keyconfig.js 里的 epay.key
自定义回调地址 / CustomCallbackAddressNew-API 对外访问地址,例如 https://k8s-ai.example.com
支付方式 / PayMethodstype 使用 Jeepay 的 wayCode

这里特别容易填错的是 CustomCallbackAddress。它必须是 New-API 自己的对外地址,不是 KitfoxPay 地址。例如 New-API 访问地址是:

https://ai.jihw.top

那么 CustomCallbackAddress 就填:

https://ai.jihw.top

不要填成:

http://<KitfoxPay地址>:9219

否则 New-API 创建订单时传给 KitfoxPay 的 notify_url 会变成 KitfoxPay 自己,支付成功后余额不会正确回写到 New-API。

支付方式示例:

[
  {
    "name": "支付宝扫码",
    "icon": "SiAlipay",
    "type": "ALI_QR"
  }
]

这里的 ALI_QR 只是支付宝扫码示例。实际要填 Jeepay 里当前商户应用已经配置并启用的 wayCode。如果仍然使用 New-API 默认的 alipaywxpay,KitfoxPay 会把它们原样传给 Jeepay,通常会导致 Jeepay 下单失败。

使用 ALI_QR 扫码支付

ALI_PC 适合浏览器跳转测试,Jeepay 会返回支付宝 PC 收银台地址。ALI_QR 则不同,Jeepay 返回的是 https://qr.alipay.com/... 这类扫码链接,这个链接应该生成二维码让用户用支付宝沙箱 App 扫码。如果 KitfoxPay 直接把浏览器重定向到这个链接,支付宝通常会落到类似下面的提示页:

https://render.alipay.com/p/yuyan/180020040001212700/?cid=wap_dc

所以使用 ALI_QR 时,要改 KitfoxPay 的 epay.js:引入 qrcode,并让 _generatePayFormALI_QR 时返回二维码 HTML,而不是自动跳转。

先在文件顶部增加:

const QRCode = require('qrcode');

再把生成表单的位置改成 await

const formHtml = await this._generatePayForm(payUrl, epayParams);

然后把 _generatePayForm 改成异步函数。核心逻辑如下,非 ALI_QR 的支付方式继续保留原来的自动跳转逻辑:

async _generatePayForm(payUrl, params) {
  if (!payUrl) {
    return '';
  }

  const isQrPay = params.type === 'ALI_QR' || payUrl.includes('qr.alipay.com');

  if (isQrPay) {
    const qrDataUrl = await QRCode.toDataURL(payUrl, {
      errorCorrectionLevel: 'M',
      margin: 2,
      width: 260
    });

    return `<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>支付宝扫码支付</title>
</head>
<body>
  <h2>支付宝扫码支付</h2>
  <p>请使用支付宝沙箱 App 扫码完成支付</p>
  <img src="${qrDataUrl}" alt="支付宝支付二维码">
  <p>${params.money || ''} 元</p>
  <p>${params.out_trade_no || ''}</p>
  <a href="${payUrl}">无法扫码时打开支付链接</a>
</body>
</html>`;
  }

  return `<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="refresh" content="0;url=${payUrl}">
  <title>正在跳转到支付页面...</title>
</head>
<body>
  <p>正在跳转到支付页面...</p>
  <script>
    window.location.href = ${JSON.stringify(payUrl)};
  </script>
</body>
</html>`;
}

修改后重建 KitfoxPay:

cd /opt/kitfoxpay
docker compose up -d --build
docker compose logs --tail=100

再次从 New-API 发起充值时,如果支付方式是 ALI_QR,页面应显示二维码;如果是支付宝沙箱环境,要用支付宝开放平台提供的沙箱版支付宝 App 扫码。

默认情况下,扫码页不会在用户支付成功后自动跳转。原因是 ALI_QR 是“另一个设备扫码支付”:支付宝完成支付后走异步通知链路,浏览器里打开的二维码页不会天然收到这个结果。要自动跳回 New-API,需要让二维码页轮询 KitfoxPay 的订单状态,查到支付成功后再跳转到 New-API 传来的 return_url

不要把 epay.key 写进浏览器页面里去调 api.php?act=order。更稳妥的做法是在 KitfoxPay 增加一个只查询订单状态的公开接口,例如在 index.js 里加:

app.get('/api/payment/status', async (req, res) => {
  try {
    const outTradeNo = req.query.out_trade_no || '';
    const tradeNo = req.query.trade_no || '';

    if (!outTradeNo && !tradeNo) {
      return res.status(400).json({ code: -1, msg: 'missing order id', paid: false });
    }

    const order = await jeepay.queryOrder({
      payOrderId: tradeNo,
      mchOrderNo: outTradeNo
    });
    const state = String(order.state || '');

    res.json({
      code: 1,
      msg: 'success',
      paid: state === '2',
      closed: ['3', '4', '6'].includes(state),
      state,
      trade_no: order.payOrderId || '',
      out_trade_no: order.mchOrderNo || ''
    });
  } catch (error) {
    res.json({ code: -1, msg: error.message || 'query failed', paid: false });
  }
});

然后在 ALI_QR 的二维码 HTML 里增加轮询脚本:

<div id="pay-status">等待支付结果...</div>

<script>
  (function () {
    var orderNo = "${params.out_trade_no || ''}";
    var returnUrl = "${params.return_url || ''}";
    var attempts = 0;

    async function checkPayment() {
      attempts += 1;
      try {
        var query = new URLSearchParams({ out_trade_no: orderNo });
        var response = await fetch('/api/payment/status?' + query.toString(), { cache: 'no-store' });
        var result = await response.json();

        if (result.paid) {
          window.location.href = returnUrl || '/';
          return;
        }

        if (result.closed) {
          document.getElementById('pay-status').textContent = '订单已关闭,请重新创建订单';
          return;
        }
      } catch (error) {
        // 查询失败时继续轮询,避免临时网络问题中断支付流程。
      }

      if (attempts < 180) {
        window.setTimeout(checkPayment, 3000);
      }
    }

    if (orderNo) {
      window.setTimeout(checkPayment, 3000);
    }
  })();
</script>

上面这段 HTML 是说明写法。实际放进 epay.js 模板字符串时,orderNoreturnUrl 建议用 JSON.stringify(params.out_trade_no || '')JSON.stringify(params.return_url || '') 输出,避免引号或特殊字符破坏脚本。

如果 New-API 返回类似下面的错误:

{"code":-1,"msg":"Jeepay API 错误: 系统:java.io.IOException ... 504 Gateway Time-out"}

先看 KitfoxPay 日志里支付宝接口的 method。如果是 alipay.trade.precreate,并且 HTML 里出现 openapi-sandbox.dl.alipaydev.com504 Gateway Time-out,说明 Jeepay 已经请求到了支付宝沙箱,但是支付宝沙箱网关临时超时。可以等待几十秒后重新创建订单;同时用下面命令确认服务器能访问沙箱网关:

curl -k -I --connect-timeout 10 --max-time 20 \
  https://openapi-sandbox.dl.alipaydev.com/gateway.do

只要 ALI_PC 能正常下单,ALI_QR 偶发这个 504 通常不是 KitfoxPay 的签名或二维码渲染问题。

如果 New-API 是 HTTPS 站点,而 PayAddress 使用 http://<KitfoxPay地址>:9219,浏览器提交支付表单时可能会遇到 HTTP/HTTPS 混合内容限制或不安全表单提示。内网测试可以先接受;正式使用建议给 KitfoxPay 也配置 HTTPS 域名,然后把 PayAddress 改成 HTTPS 地址。

如果 New-API 部署在 Kubernetes,可以先从集群里测试它是否能访问 KitfoxPay:

KITFOXPAY_URL="http://192.168.3.213:9219"

kubectl -n new-api run kitfoxpay-test \
  --rm -it --restart=Never \
  --image=curlimages/curl:8.10.1 \
  -- "$KITFOXPAY_URL/api/health"

如果 New-API 是多副本,修改支付配置后建议重启一次:

kubectl -n new-api rollout restart deploy/new-api
kubectl -n new-api rollout status deploy/new-api --timeout=300s

验证支付链路

  1. 在 New-API 前台用普通用户发起一笔小额充值。
  2. New-API 应跳转到 KitfoxPay,再由 KitfoxPay 创建 Jeepay 订单。
  3. 在 KitfoxPay 日志里查看下单和通知:
cd /opt/kitfoxpay
docker compose logs -f --tail=100
  1. 在 Jeepay 运营平台查看支付订单。
  2. 支付完成后,确认 New-API 用户余额增加。

如果 New-API 没有收到回调,优先检查三处地址:

  • KitfoxPay server.siteDomain 是否是 Jeepay 能访问的地址。
  • New-API CustomCallbackAddress 是否是 KitfoxPay 能访问的地址。
  • 防火墙或 Ingress 是否允许 /api/payment/notify/api/user/epay/notify/api/subscription/epay/notify 这些回调路径。

KitfoxPay 常用维护

cd /opt/kitfoxpay

docker compose ps
docker compose logs -f --tail=100
docker compose restart
docker compose down
docker compose up -d

备份配置:

cp config.js config.js.$(date +%F).bak

升级 KitfoxPay 前先备份 config.js,再在测试环境确认 New-API 支付、Jeepay 下单和异步通知都正常。

修改配置后重启

后端配置文件在后端仓库的 conf 目录下:

conf/payment/application.yml
conf/manager/application.yml
conf/merchant/application.yml

修改配置后重启对应服务:

docker compose restart payment
docker compose restart manager
docker compose restart merchant

如果改了 MQ 类型,不只要改 conf/*.yml,还要同步修改 docker-compose.yml 中的 MQ 服务。官方默认使用 RocketMQ。

常用维护命令

进入部署目录:

cd /opt/jeepay-open/jeepay

查看容器:

docker compose ps

查看日志:

docker compose logs -f --tail=100 payment
docker compose logs -f --tail=100 manager
docker compose logs -f --tail=100 merchant

重启单个服务:

docker compose restart payment
docker compose restart manager
docker compose restart merchant

停止全部服务:

docker compose stop

重新启动:

docker compose start

停止并删除容器、网络,但保留数据卷:

docker compose down

备份和恢复

Compose 使用 Docker named volume 保存 MySQL、Redis、RocketMQ 数据。重点要备份 MySQL。

备份 Jeepay 数据库:

cd /opt/jeepay-open/jeepay
docker exec jeepay-mysql mysqldump -uroot -prootroot jeepaydb > jeepaydb_$(date +%F).sql

恢复前先确认目标环境,避免覆盖已有数据:

cat jeepaydb_2026-06-22.sql | docker exec -i jeepay-mysql mysql -uroot -prootroot jeepaydb

这里使用的是官方 Compose 默认数据库密码 rootroot。如果你已经修改过密码,要同步替换命令里的密码。

卸载

停止并删除容器、网络,保留数据卷:

cd /opt/jeepay-open/jeepay
docker compose down

连同 Docker volume 一起删除:

docker compose down -v

down -v 会删除 MySQL、Redis、RocketMQ 等数据卷。执行前务必确认已经备份数据库。

如果还要删除源码目录:

sudo rm -rf /opt/jeepay-open

常见注意事项

前端目录找不到

如果启动时报错找不到 jeepay-ui,先确认目录结构:

find /opt/jeepay-open -maxdepth 2 -type d | sort

应该类似:

/opt/jeepay-open
├── jeepay
└── jeepay-ui

如果前端目录不在后端同级目录,编辑 /opt/jeepay-open/jeepay/.env

UI_BASE_DIR=/实际存放前端仓库的父目录

注意这里填的是包含 jeepay-ui 的父目录,不是 jeepay-ui 目录本身。

后端镜像构建失败

如果出现找不到 JAR 的错误,通常是没有先打包后端:

cd /opt/jeepay-open/jeepay
mvn clean package -DskipTests
docker compose up -d --build

exec format error

如果构建后端镜像时报错:

exec /bin/sh: exec format error

一般是服务器 CPU 架构和基础镜像架构不匹配。例如宿主机是 x86_64,但后端 Dockerfile 默认使用的 swr.cn-south-1.myhuaweicloud.com/jeepay/eclipse-temurin:17-jre 实际拉到了 linux/arm64 镜像,构建时执行 /bin/sh 就会失败。

先确认宿主机架构和当前基础镜像架构:

uname -m
docker info --format '{{.Architecture}}'
docker pull swr.cn-south-1.myhuaweicloud.com/jeepay/eclipse-temurin:17-jre
docker image inspect swr.cn-south-1.myhuaweicloud.com/jeepay/eclipse-temurin:17-jre \
  --format '{{.Os}}/{{.Architecture}}'

如果宿主机架构和基础镜像架构不一致,建议用 docker-compose.override.yml 覆盖有问题的镜像,不直接改官方 docker-compose.yml。下面这个覆盖文件做两件事:

  • MySQL 改用 Docker Hub 官方 mysql:8.0 多架构镜像。
  • 三个后端服务的 JDK 基础镜像改用 eclipse-temurin:17-jre-jammy 多架构镜像。
cat > docker-compose.override.yml <<'EOF'
services:
  mysql:
    image: mysql:8.0

  payment:
    build:
      args:
        BASE_IMAGE: eclipse-temurin:17-jre-jammy

  manager:
    build:
      args:
        BASE_IMAGE: eclipse-temurin:17-jre-jammy

  merchant:
    build:
      args:
        BASE_IMAGE: eclipse-temurin:17-jre-jammy
EOF

也可以把同样的内容手动写成 YAML:

services:
  mysql:
    image: mysql:8.0

  payment:
    build:
      args:
        BASE_IMAGE: eclipse-temurin:17-jre-jammy

  manager:
    build:
      args:
        BASE_IMAGE: eclipse-temurin:17-jre-jammy

  merchant:
    build:
      args:
        BASE_IMAGE: eclipse-temurin:17-jre-jammy

然后重新构建:

docker compose build --no-cache payment manager merchant
docker compose up -d --build

如果 MySQL 已经用错误架构镜像启动失败,并且是刚初始化的测试环境,可以删除失败容器和数据卷后再启动:

docker compose rm -sf mysql
docker volume rm jeepay_mysql
docker compose up -d

docker volume rm jeepay_mysql 会删除 MySQL 数据,只适合确认没有有效数据的新部署环境。

如果仍然有 RocketMQ、Redis 或前端镜像的架构问题,再看对应服务是否需要 platform: linux/amd64 或替换为支持 ARM64 的镜像。用 amd64 模拟运行可以解决一部分兼容问题,但性能和稳定性不如直接使用当前架构镜像。

端口被占用

检查端口占用:

sudo ss -lntp | grep -E '13306|6380|9876|10909|10911|10912|9216|9217|9218|9226|9227|9228'

如果宿主机已有 MySQL、Redis 或其他服务占用端口,可以修改 docker-compose.yml 左侧的宿主机端口。例如:

ports:
  - "23306:3306"

冒号右侧是容器内端口,通常不要改;冒号左侧是宿主机端口,可以按需要调整。

默认密码风险

官方 Compose 默认包含这些测试密码:

  • MySQL root 密码:rootroot
  • MySQL 业务用户:jeepay / jeepay
  • 运营平台:jeepay / jeepay123
  • 商户用户默认密码:jeepay666

这些默认值只适合本地或内网测试。只要环境会被多人访问,至少要修改数据库密码、后台账号密码,并限制后台入口访问来源。