个人微信小程序后台Ubuntu端部署+支付

Posted by ... on 2026年10月16日

wxminiapp-server 部署运维完整教程

适用环境:Ubuntu 20.04+(aarch64 / x86_64)· Node.js 22.5+ · systemd 本文档对应服务器路径:/opt/wxminiapp-server,服务地址 http://<服务器IP>:8787


一、目录文件说明(/opt/wxminiapp-server/ 下每个文件)

文件/目录 作用 能否删除
server.mjs 服务入口。把 Worker 代码包装成普通 Node HTTP 服务;读取 .env、注入 SQLite 数据层、启动 HTTP 监听。 ❌ 必需
db.mjs SQLite 数据层。用 Node 内置 node:sqlite 建表并实现所有数据读写(users / orders / products / logs / counters),等价于本地版 Cloudflare D1。 ❌ 必需
src/index.js 业务主代码(微信小程序后端 + /admin 后台)。包含全部路由、JWT、虚拟支付签名、后台页面。 ❌ 必需
.env 运行配置。端口、账号密码、密钥、微信/支付参数(见第五节)。 ❌ 必需
data/ 数据库目录。内含 wxminiapp.db(主库)、wxminiapp.db-shm、wxminiapp.db-wal(SQLite 日志文件)。 ❌ 必需,备份对象
run.log 运行日志(nohup/setsid 方式启动时写入)。 可删,会自动重建
README.md 项目说明文档。 ✅ 可删
tools/ 开发校验/迁移工具(check-admin.js 等)。 ✅ 运行不需要
schema.sql 旧版 SQL 说明(已废弃)。 ✅ 可删
wrangler.toml Cloudflare Workers 部署配置(仅 CF 云端用)。 ✅ 本地不需要

文件依赖关系:server.mjs → 读 .env + db.mjs + src/index.js(三者为运行最小集)。


二、环境要求

项目 要求 说明
操作系统 Ubuntu 20.04+ / Debian 系 其他 Linux 也行
CPU 架构 aarch64(ARM64)或 x86_64 下载对应 Node 包
Node.js 22.5+(需 --experimental-sqlite)或 23.4+/24+(免参数) 本项目零 npm 依赖
systemd 有(ps -p 1 显示 systemd) 开机自启需要
内存/磁盘 ≥512MB 内存、≥100MB 磁盘 实测 646MB 内存可跑

三、安装 Node.js(以 aarch64 / Ubuntu 20.04 为例)

3.1 检查环境

1
2
node --version        # 已装则跳过本节
uname -m              # aarch64 / x86_64

3.2 下载官方二进制(自动匹配最新 v22 LTS 的 ARM64 包)

1
2
cd /tmp
curl -sL -o node.tar.xz "https://nodejs.org/dist/latest-v22.x/$(curl -s https://nodejs.org/dist/latest-v22.x/SHASUMS256.txt | grep -oP 'node-v[0-9.]+-linux-arm64\.tar\.xz' | head -1)"

x86_64 机器把上面 URL 里的 linux-arm64 换成 linux-x64。

3.3 解压安装到 /usr/local

1
tar -xJf node.tar.xz -C /usr/local --strip-components=1

3.4 验证

1
2
node --version   # 应显示 v22.x.x
npm --version    # 应显示 10.x

本项目零 npm 依赖,不需要 npm install。

3.5 重要:如果你之前用 nvm 安装了 Node.js

如果你是通过 nvm install 装的 Node(而不是上面 3.2~3.3 的官方二进制解压方式),后面 systemd 开机自启会找不到 node,必须注意:

先查 node 实际路径:

1
2
3
4
which node
# 或者如果 which 没输出(nvm 环境未加载),用:
find /root/.nvm -name 'node' -type f 2>/dev/null
# 典型输出:/root/.nvm/versions/node/v22.23.3/bin/node

nvm 安装的 node 路径在 /root/.nvm/versions/node/<版本号>/bin/node,不是 /usr/local/bin/node。 systemd 不加载用户的 nvm 环境变量,直接写 node 或写 /usr/local/bin/node 都会报 203/EXEC 错误。

两种解决方式(任选其一):

方式一:改 systemd 服务文件里的 ExecStart 路径(推荐)

1
2
3
# 把下面命令里的路径替换成你 which node 查到的实际路径
sed -i 's|/usr/local/bin/node|/root/.nvm/versions/node/v22.23.3/bin/node|' /etc/systemd/system/wxminiapp.service
systemctl daemon-reload

方式二:做软链接到 /usr/local/bin

1
2
ln -s /root/.nvm/versions/node/v22.23.3/bin/node /usr/local/bin/node
ln -s /root/.nvm/versions/node/v22.23.3/bin/npm /usr/local/bin/npm

建议:如果是全新部署,优先用上面 3.2~3.3 的官方二进制解压方式装到 /usr/local,省事不踩坑。已经用 nvm 装了也没关系,按上面改路径即可。


四、部署项目文件

4.1 创建目录

1
mkdir -p /opt/wxminiapp-server/src /opt/wxminiapp-server/data

4.2 上传文件(本地 → 服务器)

方式一:scp(在本地电脑执行)

1
2
scp server.mjs db.mjs root@<服务器IP>:/opt/wxminiapp-server/
scp src/index.js root@<服务器IP>:/opt/wxminiapp-server/src/

方式二:sftp(在本地电脑执行)

1
2
3
4
5
6
sftp root@<服务器IP>
cd /opt/wxminiapp-server
put server.mjs
put db.mjs
cd src
put src/index.js

4.3 语法检查(可选但建议)

1
2
cd /opt/wxminiapp-server
node --check server.mjs && node --check db.mjs && node --check src/index.js && echo ALL OK

五、配置 .env(关键!)

5.1 创建 .env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd /opt/wxminiapp-server
cat > .env <<'EOF'
PORT=8787
DB_PATH=./data/wxminiapp.db
APP_NAME=我的小程序
ADMIN_PREFIX=admin
JWT_EXPIRE=2592000
ADMIN_PAGE_SIZE=20
KV_MAX_SCAN=1000
XPAY_MOCK=1
ADMIN_USERNAME=admin
ADMIN_PASSWORD=请改成强密码
JWT_SECRET=请生成随机串
WX_APPID=
WX_SECRET=
XPAY_OFFER_ID=
XPAY_APP_KEY=
XPAY_NOTIFY_SIGN_KEY=
EOF

5.2 生成随机密钥

1
2
openssl rand -hex 24 | head -c 48    # 生成 JWT_SECRET(48 位)
openssl rand -base64 9 | tr -d '/+=' | head -c 12   # 生成后台密码

5.3 每个配置项含义

变量 必填 说明
PORT 否 监听端口,默认 8787
DB_PATH 否 SQLite 文件路径,默认 ./data/wxminiapp.db
APP_NAME 否 小程序显示名
ADMIN_PREFIX 否 后台路径前缀,默认 admin → /admin
JWT_EXPIRE 否 Token 有效期(秒),默认 2592000=30 天
ADMIN_PAGE_SIZE 否 后台每页条数,默认 20
KV_MAX_SCAN 否 后台列表最大扫描条数,默认 1000
XPAY_MOCK 否 1=模拟支付自测;0=真实支付(上线必须 0)
ADMIN_USERNAME 是 后台登录账号
ADMIN_PASSWORD 是 后台登录密码
JWT_SECRET 是 签名密钥,≥40 位随机串,泄漏=所有 token 可伪造
WX_APPID 真实上线 小程序 AppID。获取:微信公众平台 mp.weixin.qq.com → 开发管理 → 开发设置 → 开发者ID → 复制 AppID(AppID 公开可见)
WX_SECRET 真实上线 小程序 AppSecret。获取:同上 开发管理 → 开发设置 → 开发者ID → 点 AppSecret 右侧「生成/重置」后复制。注意:AppSecret 只完整显示一次,需立即保存;属敏感凭证,勿泄露
XPAY_OFFER_ID 真实支付 虚拟支付 OfferID
XPAY_APP_KEY 真实支付 虚拟支付现网 AppKey
XPAY_NOTIFY_SIGN_KEY 可选 消息推送验签密钥

改完 .env 后必须重启服务才生效。


六、启动与验证

6.1 前台启动(测试用)

1
2
cd /opt/wxminiapp-server
node --experimental-sqlite server.mjs

看到 wxminiapp-server on http://0.0.0.0:8787 即成功,Ctrl+C 停止。

6.2 后台常驻启动(推荐,SSH 断开不掉)

1
2
cd /opt/wxminiapp-server
setsid node --experimental-sqlite server.mjs > run.log 2>&1 < /dev/null &

6.3 验证

1
2
3
4
5
curl http://127.0.0.1:8787/health
# 期望:{"ok":true,"app":"我的小程序","xpay_mock":true}

curl http://<服务器IP>:8787/health    # 内网/公网 IP 验证
curl -o /dev/null -w '%{http_code}\n' http://<服务器IP>:8787/admin   # 期望 302

七、停止 / 重启服务

先选一种管理方式,不要混用:

  • 未配 systemd(当前服务器状态):用本节 setsid 方式;
  • 已按第八节配好 systemd:日常一律用 systemctl restart/stop wxminiapp,回到本节用 setsid 会双进程抢端口。

7.1 停止服务

方式一:按 PID

1
2
ps aux | grep server.mjs | grep -v grep     # 找到 PID
kill <PID>

方式二:按名字(推荐)

1
pkill -f 'node --experimental-sqlite server.mjs'

方式三:systemd(如果已按第八节配置)

1
systemctl stop wxminiapp

7.2 重启服务

1
2
3
pkill -f 'node --experimental-sqlite server.mjs'; sleep 1
cd /opt/wxminiapp-server && setsid node --experimental-sqlite server.mjs > run.log 2>&1 < /dev/null &
sleep 2 && curl -s http://127.0.0.1:8787/health

注意:node --experimental-sqlite server.mjs 是 Node 22 的固定写法(SQLite 实验性开关);Node 24+ 可去掉该参数。


八、开机自启动(systemd)

前提:服务器需有 systemd(ps -p 1 显示 systemd 即可,Ubuntu 20.04 默认有)。 当前服务器状态提醒:本机目前服务仍是 setsid 方式运行(无开机自启)。下面的步骤是”切换 + 配置”的完整流程,照做后服务由 systemd 托管:开机自动拉起、崩溃 3 秒后自动重启。

8.1 创建服务文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > /etc/systemd/system/wxminiapp.service <<'EOF'
[Unit]
Description=wxminiapp-server (WeChat MiniApp backend)
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/wxminiapp-server
ExecStart=/usr/local/bin/node --experimental-sqlite server.mjs
Restart=always
RestartSec=3
EnvironmentFile=/opt/wxminiapp-server/.env
StandardOutput=append:/opt/wxminiapp-server/run.log
StandardError=append:/opt/wxminiapp-server/run.log

[Install]
WantedBy=multi-user.target
EOF

各字段说明:

字段 含义
ExecStart 启动命令。必须写 Node 绝对路径,路径用 which node 查出来的实际值填,不要照抄下面的 /usr/local/bin/node:
• 官方二进制解压装的 → 一般是 /usr/local/bin/node
• nvm 装的 → 一般是 /root/.nvm/versions/node/vXX.X.X/bin/node(见 3.5 节)
Node 22 固定带 --experimental-sqlite,Node 24+ 可去掉
WorkingDirectory 工作目录,必须是 /opt/wxminiapp-server(DB_PATH=./data/... 相对路径依赖它)
EnvironmentFile 自动注入 .env 全部变量(PORT/WX_APPID/JWT_SECRET 等),与手动 export 等效
Restart=always + RestartSec=3 进程退出/崩溃后 3 秒自动拉起
StandardOutput/Error 日志追加写入 run.log,与 setsid 方式日志一致

.env 里不要给值加引号(systemd 不支持引号包裹),本项目生成格式已满足。

8.2 先停掉旧的 setsid 进程(避免端口冲突)

1
2
pkill -f 'node --experimental-sqlite server.mjs'; sleep 1
ss -tlnp | grep 8787 || echo "PORT 8787 FREE"

如果不先停旧进程,systemctl start 会因端口被占而失败(8787 已被 setsid 进程持有)。

8.3 启用并启动

1
2
3
systemctl daemon-reload
systemctl enable wxminiapp        # 设置开机自启(重启后自动拉起)
systemctl start wxminiapp         # 立即启动

8.4 验证

1
2
3
4
5
systemctl status wxminiapp          # 期望:active (running)
systemctl is-enabled wxminiapp      # 期望:enabled
curl -s http://127.0.0.1:8787/health
# 期望:{"ok":true,"app":"我的小程序","xpay_mock":true}
journalctl -u wxminiapp -n 20       # 看 systemd 视角的启动日志

8.5 日常管理命令(systemd 方式)

1
2
3
4
5
systemctl restart wxminiapp   # 重启(改代码/改 .env 后用它)
systemctl stop wxminiapp      # 停止
systemctl disable wxminiapp   # 取消开机自启(服务仍可手动 start)
systemctl status wxminiapp    # 查看运行状态
journalctl -u wxminiapp -f    # 实时跟踪日志

注意:一旦交给 systemd 管理,日常重启请用 systemctl restart wxminiapp,不要再混用 setsid 启动(会双进程抢端口)。

8.6 排错

现象 处理
systemctl start 报端口占用 先 pkill -f 'node --experimental-sqlite server.mjs' 再 start
start 后立即变 failed journalctl -u wxminiapp -n 30 看报错(多为语法错误/路径错误)
status=203/EXEC 或日志报 No such file or directory node 路径不对。which node 查实际路径,改服务文件里的 ExecStart(见 3.5 节)
启动成功但改 .env 不生效 必须 systemctl restart wxminiapp
想完全移除 systemd 服务 systemctl disable --now wxminiapp && rm /etc/systemd/system/wxminiapp.service

九、修改端口 / 新增 API

9.1 修改端口

改 .env 里的 PORT:

1
2
sed -i 's/^PORT=.*/PORT=9000/' /opt/wxminiapp-server/.env
systemctl restart wxminiapp        # 或按 7.2 手动重启

监听地址已固定在 0.0.0.0(server.mjs 内),改端口即所有网卡生效。注意云服务器需在安全组/防火墙放行新端口。

9.2 新增 API 接口(示例:加一个 GET /api/hello)

编辑 src/index.js,第 1 步:在路由区(约第 120 行 /health 附近)加一行路由:

1
  if (p === '/api/hello' && request.method === 'GET') return apiHello(request, env);

第 2 步:在文件里任意位置(建议放在其他 api 函数旁)加处理函数:

1
2
3
async function apiHello(request, env) {
  return json({ ok: true, msg: 'hello from wxminiapp-server', app: env.APP_NAME }, 200);
}

第 3 步:语法检查 + 重启 + 验证:

1
2
3
4
5
node --check src/index.js && echo SYNTAX OK
pkill -f 'node --experimental-sqlite server.mjs'; sleep 1
cd /opt/wxminiapp-server && setsid node --experimental-sqlite server.mjs > run.log 2>&1 < /dev/null &
curl http://127.0.0.1:8787/api/hello
# 期望:{"ok":true,"msg":"hello from wxminiapp-server","app":"我的小程序"}

若用 systemd 管理,重启改为 systemctl restart wxminiapp。

9.3 新增 POST 接口(带 JSON 请求体示例)

路由行:

1
  if (p === '/api/echo' && request.method === 'POST') return apiEcho(request, env);

处理函数:

1
2
3
4
async function apiEcho(request, env) {
  const body = await request.json().catch(() => ({}));
  return json({ ok: true, received: body }, 200);
}

测试:

1
curl -X POST http://127.0.0.1:8787/api/echo -H 'Content-Type: application/json' -d '{"a":1}'

十、常见问题排查

现象 原因 解决
内网 IP 访问不通 监听只绑 127.0.0.1 server.mjs 中 listen 地址改为 '0.0.0.0',重启
SyntaxError Node 版本太旧 装 Node 22.5+
node:sqlite 找不到 缺 --experimental-sqlite 用 node --experimental-sqlite server.mjs
端口被占用 有旧进程 pkill -f server.mjs 后重启
后台改配置不生效 未重启 重启服务(见第七节)
服务进程莫名消失 SSH 会话回收 用 setsid 启动,或用 systemd(第八节)
/api/xpay/* 返回 unauthorized 需登录态/token 属正常鉴权,先 /api/auth/login

十一、备份与升级

备份数据库

1
cp /opt/wxminiapp-server/data/wxminiapp.db /opt/backup-$(date +%F).db

定时备份(crontab)

1
2
3
crontab -e
# 每天 3 点备份,保留 7 天
0 3 * * * cp /opt/wxminiapp-server/data/wxminiapp.db /opt/backup/wxminiapp-$(date +\%F).db && find /opt/backup -name 'wxminiapp-*.db' -mtime +7 -delete

升级代码

1
2
# 重新上传 server.mjs / db.mjs / src/index.js 后:
node --check src/index.js && systemctl restart wxminiapp