Paozhu v1.17.0:重构 WebPay,并将 MQTT 与多渠道支付纳入 C++ Web 开发

2026-09-28 26 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

Paozhu v1.17.0 把重点放在两类常见的业务能力上:消息通信与网络支付。新版加入 MQTT,并重构 WebPay 模块;支付侧已经在公网环境测试支付宝、微信支付 V2/V3、小程序支付和网站微信 Native 支付。对于希望用 C++ 直接构建业务后台、又不想额外拼装大量第三方组件的团队,这次更新补上了重要的一环。

WebPay 重构解决的不是“发起请求”这么简单

支付接入表面上只是调用接口,实际工程里至少包含以下几条链路:

  • 创建支付订单并保存商户侧订单号;
  • 根据网站、App 或小程序场景选择支付方式;
  • 验证异步通知的签名和证书;
  • 处理重复通知,保证订单更新幂等;
  • 主动查询订单状态,处理超时和通知丢失;
  • 保存支付流水,但避免记录私钥、完整用户标识等敏感信息。

Paozhu v1.17.0 重构 WebPay,并覆盖支付宝、微信 V2/V3、小程序与 Native 支付,意味着开发者可以围绕统一的 Web 应用组织这些流程。微信 Native 支付尤其适合桌面网站:后端生成支付信息,前端展示二维码,用户通过微信扫码完成付款。根据发布说明,这种方式无需年费,适合支付频率不高的网站。

不过,“低频次”并不等于可以弱化安全设计。即使一天只有几笔订单,回调验签、金额核对和幂等更新也必须完整实现。

从 conf/webpay.conf 管好支付凭据

发布说明指出支付配置可参考 conf/webpay.conf。实际字段应以当前版本仓库中的示例为准,下面给出的是一个可改造的配置组织方式,并非对框架字段名的逐项复刻。把占位符替换为支付平台分配的真实值,同时不要将真实密钥提交到 Git。

# conf/webpay.conf
# 示例结构:字段名请按 Paozhu v1.17.0 自带配置调整

[wechat_v3]
enabled = true
mchid = YOUR_WECHAT_MCH_ID
appid = YOUR_WECHAT_APP_ID
serial_no = YOUR_MERCHANT_CERT_SERIAL
api_v3_key = YOUR_32_BYTE_API_V3_KEY
private_key_file = /etc/paozhu/keys/wechat_merchant_private_key.pem
notify_url = https://pay.example.com/payment/wechat/v3/notify

[wechat_v2]
enabled = false
mchid = YOUR_WECHAT_MCH_ID
appid = YOUR_WECHAT_APP_ID
api_key = YOUR_WECHAT_V2_API_KEY
notify_url = https://pay.example.com/payment/wechat/v2/notify

[wechat_miniprogram]
enabled = true
appid = YOUR_MINIPROGRAM_APP_ID
secret = YOUR_MINIPROGRAM_SECRET

[alipay]
enabled = true
app_id = YOUR_ALIPAY_APP_ID
private_key_file = /etc/paozhu/keys/alipay_app_private_key.pem
alipay_public_key_file = /etc/paozhu/keys/alipay_public_key.pem
notify_url = https://pay.example.com/payment/alipay/notify
return_url = https://www.example.com/orders/result

配置落地后,至少完成文件权限与版本库排除设置:

sudo install -d -m 700 /etc/paozhu/keys
sudo install -m 600 ./secrets/wechat_merchant_private_key.pem \
  /etc/paozhu/keys/wechat_merchant_private_key.pem
sudo install -m 600 ./secrets/alipay_app_private_key.pem \
  /etc/paozhu/keys/alipay_app_private_key.pem

cat >> .gitignore <<'EOF'
conf/webpay.local.conf
secrets/
*.pem
EOF

生产环境更推荐把非敏感参数放在配置文件中,把私钥和 API 密钥交给 Secret 管理系统,再在进程启动时挂载为只读文件。日志中只保留商户订单号、支付渠道、状态和平台流水号的脱敏形式。

回调处理要围绕验签、核对与幂等展开

支付平台的异步通知不能被当作普通表单请求。可靠的处理顺序应当是:读取原始请求体、验证平台签名、解析订单、核对金额与商户身份、执行幂等更新,然后返回平台要求的成功响应。

下面是与具体 Paozhu API 解耦的 C++伪项目示例。接口名称是假设的,需要替换为 v1.17.0 WebPay 模块和数据库组件的真实调用,但控制流程可以直接沿用:

#include <string>

struct HttpResponse {
    int status;
    std::string content_type;
    std::string body;
};

HttpResponse wechatNotify(const HttpRequest& request) {
    const std::string rawBody = request.rawBody();

    // 必须使用原始请求体和微信请求头验签,不能先修改 JSON 内容。
    if (!webpay.wechatV3().verifyNotification(
            rawBody,
            request.header("Wechatpay-Timestamp"),
            request.header("Wechatpay-Nonce"),
            request.header("Wechatpay-Signature"),
            request.header("Wechatpay-Serial"))) {
        return {401, "application/json", R"({"code":"INVALID_SIGNATURE"})"};
    }

    const auto notice = webpay.wechatV3().decryptNotification(rawBody);

    // 服务端读取订单,不能相信回调里未经核对的业务数据。
    auto order = orderRepository.findByOrderNo(notice.outTradeNo);
    if (!order) {
        return {404, "application/json", R"({"code":"ORDER_NOT_FOUND"})"};
    }

    if (order->amountFen != notice.amountFen ||
        order->merchantId != notice.merchantId) {
        return {400, "application/json", R"({"code":"ORDER_MISMATCH"})"};
    }

    // 数据库层应对支付流水号建立唯一索引,并在事务中更新订单。
    orderRepository.markPaidIdempotently(
        notice.outTradeNo,
        notice.transactionId,
        notice.paidAt);

    return {200, "application/json", R"({"code":"SUCCESS"})"};
}

这里有三个容易踩坑的点:

  1. 不要只看 HTTP 来源地址。 回调真实性必须依赖平台规定的签名或证书验证。
  2. 不要直接把订单改成已支付。 先核对商户号、应用 ID、订单号、币种和金额。
  3. 不要假设通知只来一次。 网络重试会产生重复通知,数据库更新必须幂等。

还可以在测试环境用 curl 检查路由、状态码和日志链路是否正常。下面的签名是故意无效的,只应用于确认服务会拒绝伪造通知:

curl -i -X POST 'https://pay-test.example.com/payment/wechat/v3/notify' \
  -H 'Content-Type: application/json' \
  -H 'Wechatpay-Timestamp: 1700000000' \
  -H 'Wechatpay-Nonce: local-test' \
  -H 'Wechatpay-Serial: TEST' \
  -H 'Wechatpay-Signature: INVALID' \
  --data '{"id":"fake-notification","event_type":"TRANSACTION.SUCCESS"}'

预期结果应是 401 或项目约定的验签失败响应,而不是订单状态发生变化。真正的联调应使用支付平台提供的沙箱或公网测试流程。

MQTT 更适合承接支付之后的异步动作

来源只明确了 v1.17.0 新增 MQTT,并未给出具体客户端 API,因此不宜假设框架中的类名与调用方式。工程上可以把 MQTT 用在支付成功后的事件分发,而不是让支付回调同步执行开票、发货、短信和统计等耗时任务。

一种可实践的事件结构如下:

{
  "event_id": "pay_20250308_000001",
  "event_type": "payment.succeeded",
  "order_no": "ORDER_20250308_10001",
  "channel": "wechat_native",
  "amount_fen": 9900,
  "occurred_at": "2025-03-08T10:30:00Z"
}

如果本地已有 MQTT Broker 和 mosquitto-clients,可以用下面的命令验证主题与消费者。请修改 Broker 地址和凭据:

export MQTT_HOST='127.0.0.1'
export MQTT_PORT='1883'

# 终端一:订阅支付成功事件
mosquitto_sub -h "$MQTT_HOST" -p "$MQTT_PORT" \
  -t 'shop/payment/succeeded' -q 1 -v

# 终端二:发布测试事件
mosquitto_pub -h "$MQTT_HOST" -p "$MQTT_PORT" \
  -t 'shop/payment/succeeded' -q 1 \
  -m '{"event_id":"pay_test_001","event_type":"payment.succeeded","order_no":"ORDER_TEST_001","channel":"wechat_native","amount_fen":9900}'

支付回调与 MQTT 发布之间仍有一致性风险:如果数据库提交成功,但发布消息失败,下游就收不到事件。可采用事务消息表(Outbox)解决:支付事务只更新订单并写入待发布事件,再由独立任务重试发送 MQTT 消息。消费者也要按 event_id 去重,因为 QoS 1 保证的是“至少一次”,不是“恰好一次”。

升级前的落地清单

Paozhu v1.17.0 适合希望在 C++ Web 项目中集中处理页面、接口、支付与消息通信的团队。升级或接入时建议逐项确认:

  • 对照新版 conf/webpay.conf 迁移配置,不照搬旧版字段;
  • 分离支付宝、微信 V2、微信 V3和小程序的应用及商户参数;
  • 私钥使用独立文件或 Secret 服务保存,并限制读取权限;
  • 在公网 HTTPS 地址上配置支付通知路由;
  • 为商户订单号和平台流水号建立唯一约束;
  • 覆盖重复通知、错误金额、无效签名和通知乱序测试;
  • MQTT 启用认证、TLS、主题 ACL 与持久会话策略;
  • 用 Outbox 和消费端去重连接支付事务与异步事件。

WebPay 的价值不只是减少几段 HTTP 请求代码,而是把支付协议、配置和 Web 生命周期放进同一套工程结构。MQTT 则为支付之后的异步业务提供了出口。两者结合时,真正决定系统可靠性的仍然是密钥管理、验签、幂等和消息一致性。


相关推荐