《HarmonyOS NEXT 鸿蒙应用接入微信支付实战(2026最新版)》
非常适合写成一篇 CSDN 爆款长文。
建议标题:
《HarmonyOS NEXT 鸿蒙应用接入微信支付实战(2026最新版)——从申请到支付成功,全流程详解(含完整源码)》
由于内容超过 2 万字,我会按照 CSDN 可以直接复制发布的格式,一章一章完整输出,每章都包含:
- 原理讲解
- 接入流程
- 官方SDK解析
- ArkTS代码
- 服务端交互
- 微信支付参数
- 回调处理
- 常见坑
- 企业项目最佳实践
- 面试考点
目前微信支付已提供 HarmonyOS OpenSDK,并支持 HarmonyOS NEXT 应用接入,但前提仍需要完成微信开放平台应用审核、商户配置以及服务端统一下单等流程。
《HarmonyOS NEXT 鸿蒙应用接入微信支付实战(2026最新版)》
第一章 HarmonyOS NEXT 为什么不能直接调用微信支付?
很多刚接触鸿蒙开发的同学都会有一个疑问:
Android 调用微信支付不是很简单吗?
鸿蒙为什么感觉复杂很多?
其实并不是鸿蒙复杂,而是微信支付本身就是一个客户端+服务器共同完成的支付体系。
整个支付流程如下:
用户点击立即支付
│
▼
HarmonyOS APP
│
▼
自己的业务服务器
│
▼
微信支付统一下单
│
▼
返回prepay_id
│
▼
服务器生成支付参数
│
▼
HarmonyOS SDK
│
▼
拉起微信
│
▼
用户确认支付
│
▼
微信服务器
│
▼
支付完成
│
▼
通知服务器
│
▼
服务器通知APP
很多新人以为:
点击一个SDK就能支付。
实际上:
真正决定支付成功的是服务器。
客户端只是:
- 拉起微信
- 展示支付页面
- 接收支付结果
真正签名的是服务器。
第二章 微信支付整体架构
整个支付包含四部分:
HarmonyOS App
↓
业务服务器
↓
微信支付服务器
↓
微信客户端
分别承担不同职责。
App负责
例如:
点击购买
↓
请求服务器
↓
获取支付参数
↓
调用SDK
↓
等待回调
App 不参与:
- 签名
- 商户号
- API Key
这些全部不能放客户端。
否则:
一分钟就被反编译。
服务器负责
服务器主要做:
创建订单
例如:
订单号
金额
商品信息
用户ID
保存数据库。
调用微信统一下单
服务器请求:
https://api.mch.weixin.qq.com/
生成:
prepay_id
然后:
再次签名。
返回:
partnerId
prepayId
nonceStr
timestamp
sign
客户端只负责拿这些数据。
第三章 开通微信支付需要哪些账号
很多人开发一天,
最后发现:
没有账号……
所以先准备。
需要:
一、微信开放平台
作用:
用于:
HarmonyOS应用授权。
二、微信商户平台
作用:
真正收钱。
需要:
商户号
API证书
APIv3 Key
三、HarmonyOS应用
已经:
AGC创建完成
拥有:
Bundle Name
例如:
com.demo.mall
四、HarmonyOS AppID
例如:
App Identifier
微信后台需要填写。
HarmonyOS 的 Bundle ID 与 Identifier(AppID) 都需要在微信开放平台完成配置和审核后,才能正常调起微信支付。
第四章 下载微信 HarmonyOS SDK
目前官方已经提供:
HarmonyOS SDK。
安装:
{
"dependencies": {
"@tencent/wechat_open_sdk":"1.0.11"
}
}
然后:
Sync Project
即可。
SDK 中主要包含:
WXApi
PayReq
SendReq
AuthReq
ShareReq
支付主要使用:
PayReq
HarmonyOS 项目通常通过 @tencent/wechat_open_sdk 引入微信 OpenSDK,版本以官方最新发布为准。
第五章 创建微信管理类
企业项目一般不会直接在页面调用 SDK。
而是封装:
WXManager
例如:
export class WXManager {
static getInstance(): WXManager {
return new WXManager()
}
register() {
}
pay() {
}
}
这样:
所有页面:
WXManager.getInstance().pay()
即可。
后续:
登录
分享
支付
收藏
全部统一管理。
《HarmonyOS NEXT 鸿蒙应用接入微信支付实战(2026最新版)》
第六章 集成微信 OpenSDK
上一篇我们已经完成了:
- 微信开放平台申请
- 微信商户平台配置
- SDK导入
下面正式开始编码。
整个流程如下:
应用启动
│
▼
初始化微信SDK
│
▼
注册AppID
│
▼
判断微信是否安装
│
▼
调用服务器
│
▼
统一下单
│
▼
拉起微信支付
│
▼
支付完成
│
▼
回调APP
企业项目通常都会把所有微信能力统一封装。
例如:
common
├── manager
│ ├── WXManager.ets
│ ├── PayManager.ets
│
├── network
│
├── model
│
└── utils
这样以后:
- 微信登录
- 微信分享
- 微信支付
全部都在一个地方维护。
第七章 初始化微信SDK
建议在应用启动时初始化。
例如:
// EntryAbility.ets
import { WXApi } from '@tencent/wechat_open_sdk'
export default class EntryAbility extends UIAbility {
onCreate() {
WXApi.registerApp({
appId: "wx1234567890abcdef"
})
}
}
这里的 AppID:
不是
BundleName
也不是
Harmony App ID
而是:
微信开放平台申请得到的:
wxxxxxxxxxxxxx
例如:
wx5f88c66xxxxxxxx
很多新人第一次都会填错。
初始化建议封装:
export default class WXManager {
private static instance: WXManager = new WXManager()
static getInstance() {
return this.instance
}
init() {
WXApi.registerApp({
appId: AppConfig.WX_APPID
})
}
}
以后:
WXManager.getInstance().init()
即可。
第八章 判断微信是否安装
正式支付之前,
一定先检查:
微信有没有安装。
例如:
const installed = await WXApi.isWXAppInstalled()
if (!installed) {
promptAction.showToast({
message: "请先安装微信"
})
return
}
企业项目一般都会封装:
async checkWX(): Promise<boolean> {
const ok = await WXApi.isWXAppInstalled()
if (!ok) {
promptAction.showToast({
message: "未检测到微信"
})
}
return ok
}
支付时:
if (!await checkWX()) {
return
}
即可。
第九章 判断微信版本是否支持支付
不仅需要安装。
还需要:
支持当前支付SDK。
例如:
const support = await WXApi.isWXAppSupportAPI()
if (!support) {
promptAction.showToast({
message: "微信版本过低"
})
return
}
这一点很多人会忽略。
尤其:
企业内网设备。
微信版本几年没更新。
支付直接失败。
推荐统一封装:
async canPay(): Promise<boolean> {
const install = await WXApi.isWXAppInstalled()
if (!install) {
return false
}
const support = await WXApi.isWXAppSupportAPI()
return support
}
第十章 点击支付按钮
页面一般都是:
立即购买
按钮。
例如:
Button("立即支付")
.onClick(()=>{
this.pay()
})
支付函数:
async pay() {
}
不要:
直接调用微信。
第一步:
请求自己的服务器。
第十一章 请求服务器创建订单
支付流程:
点击支付
↓
请求服务器
↓
服务器创建订单
↓
返回订单号
↓
统一下单
↓
返回支付参数
↓
客户端拉起微信
例如:
interface PayResponse {
partnerId:string
prepayId:string
nonceStr:string
timeStamp:string
sign:string
}
客户端:
let result = await Http.post<PayResponse>(
"/pay/create",
{
orderId:this.orderId
}
)
服务器返回:
{
"partnerId":"1900000109",
"prepayId":"wx22112233",
"nonceStr":"abcdef",
"timeStamp":"1712345678",
"packageValue":"Sign=WXPay",
"sign":"4A9F3C..."
}
这里所有字段均由服务端生成并签名,客户端不要自行拼接或计算签名。
第十二章 为什么不能客户端签名?
很多新人喜欢:
const sign = md5(...)
这是错误的。
因为:
商户API Key:
xxxxxxxxxxxxxxxxxx
绝不能出现在客户端。
否则:
任何人:
apk
↓
反编译
↓
拿到key
↓
伪造订单
因此:
客户端:
永远不能:
生成sign
生成prepay_id
调用微信统一下单
全部:
服务器完成。
客户端:
只负责:
请求接口
↓
获得参数
↓
拉起微信
第十三章 PayReq 对象详解
服务器返回数据后。
开始创建:
PayReq
对象。
这是微信支付:
最重要的对象。
结构如下:
class PayReq{
appId:string
partnerId:string
prepayId:string
packageValue:string
nonceStr:string
timeStamp:string
sign:string
}
每个字段:
都不能写错。
下面详细解释。
appId
例如:
wx5f88xxxxxxxx
微信开放平台申请。
不是:
BundleName
也不是:
包名
partnerId
例如:
1900000109
商户号。
来自:
微信支付后台。
prepayId
统一下单接口返回。
例如:
wx201410272009395522657a690389285100
一个订单:
一个 prepayId。
不能重复使用。
packageValue
固定:
Sign=WXPay
不要:
写成:
WXPay
也不要:
package
否则:
支付失败。
nonceStr
随机字符串。
服务器生成。
例如:
ABCD123456EFG
每次:
都不同。
timeStamp
时间戳。
例如:
1720501000
注意:
是:
秒级时间戳。
不是:
毫秒
否则:
签名失败。
sign
最终签名。
服务器生成。
客户端:
禁止修改。
第十四章 发起微信支付
经过前面的准备,我们已经拿到了服务器返回的支付参数。
下面开始真正拉起微信支付。
整个流程如下:
点击立即支付
│
▼
请求业务服务器
│
▼
服务器统一下单
│
▼
返回支付参数
│
▼
构建 PayReq
│
▼
调用微信SDK
│
▼
跳转微信
│
▼
用户确认支付
真正调用微信支付,其实只需要一个对象:
PayReq
第十五章 构建 PayReq
假设服务器返回:
{
"appId":"wx123456789",
"partnerId":"1900000109",
"prepayId":"wx202607061111",
"packageValue":"Sign=WXPay",
"nonceStr":"abcdefg",
"timeStamp":"1720233333",
"sign":"B8C78B8E0Fxxxxxxx"
}
客户端开始构建:
import { PayReq } from '@tencent/wechat_open_sdk'
let req = new PayReq()
req.appId = result.appId
req.partnerId = result.partnerId
req.prepayId = result.prepayId
req.packageValue = result.packageValue
req.nonceStr = result.nonceStr
req.timeStamp = result.timeStamp
req.sign = result.sign
整个对象不要修改任何字段。
尤其:
sign
必须使用服务器返回的数据。
企业项目推荐写法
建议封装:
private buildPayReq(data: PayResponse): PayReq {
let req = new PayReq()
req.appId = data.appId
req.partnerId = data.partnerId
req.prepayId = data.prepayId
req.packageValue = data.packageValue
req.nonceStr = data.nonceStr
req.timeStamp = data.timeStamp
req.sign = data.sign
return req
}
以后:
let req = this.buildPayReq(result)
即可。
第十六章 调用微信支付
真正支付只有一句:
await WXApi.sendReq(req)
例如:
async pay() {
let result = await Http.post<PayResponse>("/pay/create")
let req = this.buildPayReq(result)
await WXApi.sendReq(req)
}
执行以后:
系统会自动:
HarmonyOS APP
↓
微信
↓
支付页面
无需自己跳转。
sendReq 返回值
很多人误认为:
await WXApi.sendReq()
返回:
支付成功
实际上不是。
它返回的是:
是否成功拉起微信
例如:
const ok = await WXApi.sendReq(req)
如果:
false
说明:
微信没有打开。
可能原因:
- 微信未安装
- AppID错误
- SDK初始化失败
- 请求参数非法
真正支付成功:
需要等待:
微信回调。
第十七章 微信支付回调流程
微信支付:
不是同步返回。
而是:
APP
↓
拉起微信
↓
用户支付
↓
微信服务器
↓
微信客户端
↓
回调HarmonyOS
所以:
不能写:
await WXApi.sendReq()
支付成功
这是错误的。
必须监听:
支付回调。
官方流程
sendReq()
↓
微信
↓
支付完成
↓
WXEntryAbility
↓
APP
所以:
需要新增:
WXEntryAbility
它负责:
接收微信返回。
第十八章 创建 WXEntryAbility
微信所有功能:
都会回调这里。
例如:
entry
↓
wxapi
↓
WXEntryAbility.ets
目录:
entry
├── src
│
├── wxapi
│ ├── WXEntryAbility.ets
这是官方推荐目录。
代码:
import { WXApi } from '@tencent/wechat_open_sdk'
export default class WXEntryAbility extends UIAbility {
onCreate() {
}
}
真正重要的是:
处理:
支付结果
第十九章 监听微信支付结果
微信SDK:
会返回:
PayResp
例如:
WXApi.onResp((resp)=>{
})
收到:
resp.errCode
不同值:
代表不同结果。
例如:
0
表示:
支付成功。
-1
表示:
支付失败。
-2
表示:
用户取消。
企业一般封装:
WXApi.onResp((resp)=>{
switch(resp.errCode){
case 0:
this.paySuccess()
break
case -1:
this.payFail()
break
case -2:
this.payCancel()
break
}
})
第二十章 支付成功后的正确处理
很多新人:
收到:
errCode == 0
立即:
提示:
支付成功
其实:
这是一个经典错误。
真正正确流程:
微信返回成功
↓
查询服务器
↓
服务器查询微信订单
↓
确认支付成功
↓
更新订单状态
↓
返回APP
↓
展示支付成功
为什么?
因为:
客户端:
不能作为最终依据。
例如:
网络异常。
服务器:
还没有收到:
支付通知。
所以:
正确流程:
支付完成
↓
APP请求
/pay/query
↓
服务器查询微信
↓
确认订单状态
↓
SUCCESS
↓
APP提示支付成功
查询订单接口
例如:
GET /pay/query?orderId=123456
服务器:
返回:
{
"code":0,
"status":"SUCCESS"
}
客户端:
let result = await Http.get<OrderStatus>(
"/pay/query",
{
orderId:this.orderId
}
)
if(result.status=="SUCCESS"){
promptAction.showToast({
message:"支付成功"
})
}
企业项目推荐封装
不要把支付代码写在页面里面。
推荐:
pay
│
├── PayManager
├── WXManager
├── OrderManager
├── PayCallback
├── PayResult
页面:
PayManager.pay(orderId)
即可。
PayManager:
负责:
创建订单
↓
统一下单
↓
微信支付
↓
监听回调
↓
查询订单
↓
通知页面
页面:
完全不用关心微信SDK。
这也是大型商城项目最常见的架构。
企业支付时序图
HarmonyOS APP
│
│点击支付
▼
Business Server
│
│统一下单
▼
WeChat Pay Server
│
│返回prepay_id
▼
Business Server
│
│支付参数
▼
HarmonyOS APP
│
│WXApi.sendReq()
▼
WeChat Client
│
用户确认支付
│
▼
WeChat Server
│
支付通知(Notify)
▼
Business Server
│
更新订单状态(SUCCESS)
│
▼
HarmonyOS APP
查询订单最终状态
│
▼
展示支付成功
第二十一章 微信支付统一下单
很多人第一次接微信支付,会认为:
APP
↓
微信
↓
支付成功
实际上真正流程如下:
HarmonyOS APP
│
│ 点击支付
▼
业务服务器
│
│ 创建订单
▼
微信支付统一下单
│
│ 返回 prepay_id
▼
业务服务器
│
│ RSA签名
▼
HarmonyOS APP
│
│ WXApi.sendReq()
▼
微信客户端
可以看到:
真正请求微信的是:
业务服务器
不是客户端。
为什么必须服务器统一下单?
原因非常简单。
服务器拥有:
商户号(MchId)
API证书
APIv3Key
商户私钥
这些东西:
绝不能放到客户端。
否则:
APK
↓
反编译
↓
获得商户密钥
↓
伪造支付
所以:
统一下单接口:
永远只能服务器调用。
第二十二章 服务端创建订单
支付第一步:
不是微信。
而是:
自己的数据库。
例如:
order_id
user_id
goods_id
price
status
create_time
状态:
UNPAID
创建成功以后。
再去:
调用微信。
千万不要:
微信成功以后再创建订单。
否则:
支付成功。
数据库没有订单。
后期无法恢复。
企业流程:
点击支付
↓
数据库创建订单
↓
订单状态:
UNPAID
↓
微信统一下单
↓
返回prepay_id
↓
返回客户端
第二十三章 微信支付签名(RSA)
这是微信支付最重要的一章。
微信支付 V3:
采用:
RSA-SHA256
而不是以前:
MD5
签名字符串:
例如:
AppID
TimeStamp
NonceStr
PrepayId
按照固定格式:
AppID
TimeStamp
NonceStr
prepay_id=xxxx
然后:
服务器:
商户私钥
↓
RSA签名
↓
Base64编码
得到:
sign
客户端:
永远不要:
自己生成。
很多新人:
喜欢:
sign = md5(...)
这是 V2 的写法。
HarmonyOS 接入:
现在基本都是:
微信支付 V3
所以:
不要再使用:
MD5。
第二十四章 返回客户端的数据
服务器一般返回:
{
"appId":"wx123456",
"partnerId":"1900000109",
"prepayId":"wx111222333",
"packageValue":"Sign=WXPay",
"nonceStr":"abcdefg",
"timeStamp":"172000000",
"sign":"xxxxxxxxxxxxxxxx"
}
客户端:
收到以后:
什么都不要改。
直接:
PayReq
↓
WXApi.sendReq()
即可。
企业项目:
一般都会:
统一返回:
{
"code":0,
"msg":"success",
"data":{
...
}
}
不要:
直接返回:
PayReq。
方便以后:
增加字段。
例如:
{
"orderNo":"20260706001",
"expireTime":"30分钟",
"payData":{
}
}
第二十五章 微信支付通知(Notify)
很多开发者:
认为:
客户端:
收到:
支付成功
订单:
就是成功。
实际上:
企业:
全部依赖:
Notify。
流程:
用户付款
↓
微信服务器
↓
Notify
↓
业务服务器
↓
修改订单
↓
SUCCESS
这里:
微信会主动:
POST:
你的:
/pay/notify
接口。
例如:
POST /pay/notify
Body:
{
"resource":{
}
}
服务器:
收到以后:
第一件事情:
不是:
更新数据库。
而是:
验签。
第二十六章 为什么一定要验签?
因为:
任何人:
都可以:
POST:
你的:
/pay/notify
如果:
没有:
验签。
别人:
发送:
{
"trade_state":"SUCCESS"
}
你的订单:
全部:
变成:
支付成功。
所以:
第一步:
必须:
微信平台证书
↓
验签
↓
成功
↓
解析数据
↓
修改订单
千万不要:
跳过。
第二十七章 更新订单状态
收到:
Notify:
以后。
服务器:
更新:
数据库。
例如:
status
↓
SUCCESS
然后:
保存:
transaction_id
微信订单号。
例如:
420000221220260706123456789
以后:
退款:
查询:
全部需要:
它。
建议:
数据库:
增加:
transaction_id
pay_time
payer
notify_json
方便:
以后:
查问题。
第二十八章 为什么不要相信客户端?
这是:
所有支付系统:
最重要的一句话。
客户端永远不可信。
例如:
APP:
收到:
支付成功
但是:
服务器:
没有:
Notify。
怎么办?
正确流程:
APP
↓
收到成功
↓
请求
/pay/query
↓
服务器查询订单
↓
SUCCESS
↓
展示支付成功
如果:
服务器:
还是:
UNPAID
客户端:
继续:
轮询。
例如:
2秒
↓
4秒
↓
8秒
↓
停止
不要:
一直:
请求。
企业项目支付状态机
建议:
订单:
采用:
UNPAID
↓
PAYING
↓
SUCCESS
↓
REFUNDING
↓
REFUND
↓
CLOSED
不要:
只有:
支付成功
支付失败
否则:
退款:
无法处理。
企业级支付架构
HarmonyOS App
│
▼
OrderController
│
▼
PayManager
│
┌────────────────┴────────────────┐
▼ ▼
OrderService WXPayService
│ │
│ ▼
│ 微信统一下单
│ │
▼ ▼
MySQL Database 微信支付平台
▲ │
│ ▼
└──────────── Notify 回调 ─────────┘
这种分层有几个好处:
- 职责清晰:订单逻辑与支付逻辑解耦。
- 方便扩展:后续接入支付宝、云闪付,只需新增对应的支付服务。
- 易于测试:订单模块可以独立进行单元测试。
- 降低耦合:控制器无需关注具体支付平台实现。
企业级最佳实践(重点)
在真实商城项目中,建议遵循以下原则:
| 模块 | 推荐做法 |
|---|---|
| 下单 | 先创建本地订单,再调用微信统一下单 |
| 支付 | 所有签名均由服务端完成 |
| 回调 | 必须验证微信签名后再更新订单 |
| 查询 | 客户端收到支付成功后,再向服务端确认订单状态 |
| 幂等 | Notify 回调必须支持重复通知,不可重复扣库存或发货 |
| 日志 | 保存完整请求、响应及回调日志,方便排查问题 |
| 超时 | 未支付订单定时关闭,释放库存 |
| 安全 | API Key、私钥、证书绝不下发到客户端 |
第二十九章 微信退款(Refund)
退款不是简单调用一个接口。
一个完整退款流程如下:
用户申请退款
│
▼
客服审核
│
▼
业务服务器
│
▼
微信退款接口
│
▼
微信退款处理中
│
▼
退款成功通知
│
▼
更新退款状态
退款同样需要服务器完成。
客户端只能:
点击申请退款
然后:
POST
/refund/apply
即可。
为什么退款不能客户端完成?
退款需要:
商户私钥
API证书
APIv3 Key
客户端不能拥有这些。
否则:
任何人:
退款100万元
都可以伪造请求。
第三十章 数据库退款设计
建议:
新增:
refund_order
例如:
id
refund_no
order_no
transaction_id
refund_fee
refund_reason
status
create_time
update_time
其中:
退款状态:
WAITING
PROCESSING
SUCCESS
FAIL
不要:
直接:
退款成功
因为:
微信退款:
可能需要:
几分钟。
甚至:
几十分钟。
为什么退款需要独立表?
很多新人:
喜欢:
order
↓
增加
refund_status
其实:
不好。
因为:
一个订单:
可能:
退款一次
退款两次
部分退款
全部退款
所以:
退款:
必须:
单独:
建表。
第三十一章 查询订单
很多企业:
每天都会:
主动查询订单。
为什么?
因为:
Notify:
可能:
丢失。
例如:
微信
↓
Notify
↓
网络异常
↓
服务器没收到
怎么办?
服务器:
定时:
查询微信。
流程:
订单
↓
PAYING
↓
QueryOrder
↓
SUCCESS
↓
修改订单
因此:
建议:
支付成功以后:
仍然:
查询一次。
企业轮询策略
例如:
支付完成
↓
2秒
↓
查询
↓
失败
↓
4秒
↓
查询
↓
失败
↓
8秒
↓
查询
↓
16秒
↓
结束
不要:
while(true)
疯狂:
查询。
否则:
微信:
限制:
接口。
第三十二章 超时关闭订单
商城:
每天:
都会:
大量:
未付款订单。
例如:
下单
↓
库存锁定
↓
30分钟没人付款
怎么办?
需要:
关闭。
流程:
订单
↓
UNPAID
↓
30分钟
↓
CloseOrder
↓
释放库存
企业:
一般:
定时任务:
例如:
每5分钟
扫描:
status=UNPAID
然后:
调用:
CloseOrder
接口。
为什么一定关闭?
否则:
库存:
永远:
锁住。
例如:
100台手机。
有人:
全部:
下单。
一直:
不付款。
别人:
全部:
买不了。
第三十三章 微信支付错误码解析
这是企业开发最容易踩坑的地方。
下面列举几个最常见的错误。
1. APPID_MCHID_NOT_MATCH
APPID和商户号不匹配
原因:
微信开放平台
绑定错误
2. INVALID_REQUEST
请求参数:
非法。
例如:
金额
为空
订单号重复
3. SIGN_ERROR
签名:
错误。
通常:
RSA
私钥
错误
或者:
时间戳:
错误。
4. ORDERNOTEXIST
订单:
不存在。
可能:
order_no
写错
或者:
订单:
已经:
关闭。
5. USERPAYING
最经典。
表示:
用户:
还在输入密码
不要:
提示:
支付失败。
应该:
继续:
查询。
企业建议
所有:
错误码:
统一:
封装。
例如:
export enum PayError{
SUCCESS=0,
USER_CANCEL=-2,
WX_NOT_INSTALL=-3,
NETWORK_ERROR=-4,
SIGN_ERROR=-5
}
页面:
不用:
判断:
微信:
各种:
错误。
第三十四章 支付日志
企业:
一定:
打印:
完整日志。
建议:
保存:
请求时间
请求参数
响应数据
Notify
异常
耗时
例如:
PayRequest
↓
PayResponse
↓
Notify
↓
QueryOrder
方便:
以后:
查问题。
千万不要打印:
API Key
Private Key
证书
日志:
也会:
泄露。
第三十五章 多商户架构
很多公司:
有:
多个品牌。
例如:
商城A
商城B
商城C
对应:
商户A
商户B
商户C
怎么办?
建议:
数据库:
增加:
merchant
例如:
merchant_id
appid
mchid
private_key
serial_no
支付:
根据:
merchant_id
自动:
切换。
不要:
写死。
企业架构
Order
↓
MerchantService
↓
MerchantConfig
↓
WXPayService
以后:
增加:
100个商户。
不用:
改代码。
第三十六章 企业级支付模块设计
建议:
项目:
目录:
pay
│
├── controller
│ PayController
│
├── service
│ PayService
│ RefundService
│ NotifyService
│
├── manager
│ WXPayManager
│
├── config
│ MerchantConfig
│
├── dto
│
├── vo
│
├── entity
│
├── mapper
│
└── task
CloseOrderTask
HarmonyOS 客户端:
建议:
pay
├── PayManager
├── WXManager
├── PayRepository
├── PayApi
├── PayModel
├── PayResult
├── PayListener
└── PayEvent
页面:
永远:
不要:
出现:
WXApi.sendReq()
而是:
PayManager.pay(orderId)
所有:
微信SDK:
全部:
隐藏。
以后:
如果:
改:
支付宝。
页面:
一行:
不用:
修改。
企业支付生命周期
创建订单
│
▼
待支付(UNPAID)
│
▼
调用微信统一下单
│
▼
拉起微信支付
│
▼
支付中(PAYING)
│
▼
收到 Notify
│
▼
支付成功(SUCCESS)
│
▼
发货(DELIVERED)
│
▼
完成(FINISHED)
退款流程:
SUCCESS
│
▼
申请退款
│
▼
退款中(REFUNDING)
│
▼
退款成功(REFUNDED)
企业开发避坑总结(重点)
| 问题 | 正确做法 |
|---|---|
| 客户端签名 | ❌ 禁止,必须服务端签名 |
| 直接相信客户端支付成功 | ❌ 必须查询服务端订单状态 |
| Notify 不验签 | ❌ 必须验签 |
| 重复处理 Notify | ❌ 必须做幂等处理 |
| 一个订单允许重复支付 | ❌ 下单前检查订单状态 |
| 支付成功立即发货 | ❌ 等 Notify + 订单状态确认 |
| API Key 放客户端 | ❌ 永远不要下发 |
| 支付日志记录密钥 | ❌ 日志脱敏 |
第三十七章 商城支付系统整体架构
一个成熟的商城支付系统,不仅仅是调用微信支付接口,而是多个业务模块协同工作。
典型架构如下:
HarmonyOS APP
│
点击"立即支付"
│
▼
OrderController
│
▼
OrderService
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
CouponService InventoryService MemberService
│ │ │
└──────────────┬──────┴──────────────┬──────┘
▼ ▼
PriceCalculator PromotionEngine
│
▼
PaymentService
│
▼
WeChatPayService
│
▼
微信支付平台
可以看到:
支付只是商城系统中的一个环节。
真正复杂的是:
- 商品价格计算
- 优惠券计算
- 活动计算
- 库存锁定
- 支付确认
- 发货
第三十八章 为什么支付前要锁库存?
假设:
库存:
iPhone 17 Pro
库存:1
现在:
两个用户:
同时点击:
立即支付
如果:
支付成功以后:
才扣库存。
结果:
库存:-1
发生超卖。
所以:
正确流程:
点击支付
│
▼
锁库存(LOCK)
│
▼
创建订单
│
▼
微信支付
│
▼
支付成功
│
▼
正式扣库存
如果:
30分钟:
未支付。
则:
释放库存
库存状态建议
AVAILABLE(可售)
↓
LOCKED(锁定)
↓
SOLD(已售)
↓
RELEASE(释放)
不要:
只有:
库存--
否则:
秒杀一定出问题。
第三十九章 秒杀订单支付设计
秒杀:
最大的特点:
不是支付。
而是:
并发。
例如:
库存:100
100000人
同时点击
如果:
全部:
进入支付。
数据库:
直接:
崩溃。
所以:
第一步:
不是支付。
而是:
抢资格。
流程:
点击秒杀
↓
Redis判断库存
↓
获得资格
↓
创建订单
↓
微信支付
不是:
点击支付
↓
微信支付
↓
库存不足
否则:
用户:
已经付款。
商品:
没库存。
秒杀订单状态
建议:
WAIT_QUEUE
↓
WAIT_PAY
↓
SUCCESS
↓
CLOSE
而不是:
未支付
已支付
第四十章 拼团支付设计
拼团:
不是:
支付成功。
订单:
结束。
例如:
2人成团
流程:
A支付
↓
等待
↓
B支付
↓
拼团成功
↓
发货
所以:
订单:
增加:
GROUP_WAIT
状态。
例如:
UNPAID
↓
SUCCESS
↓
GROUP_WAIT
↓
GROUP_SUCCESS
↓
DELIVER
很多新人:
支付成功。
直接:
发货。
这是错误的。
第四十一章 优惠券计算
企业:
绝不会:
客户端:
计算:
价格。
例如:
客户端:
100
↓
优惠券20
↓
支付80
如果:
用户:
修改:
APP。
变成:
支付1元
怎么办?
因此:
所有价格:
服务器:
重新计算。
客户端:
只能:
显示。
企业价格计算流程
商品价格
↓
会员折扣
↓
满减
↓
优惠券
↓
积分抵扣
↓
运费
↓
最终金额
全部:
服务器:
完成。
HarmonyOS:
只是:
展示。
第四十二章 混合支付设计
很多商城:
支持:
余额
+
微信
例如:
订单:
100元
余额:
30元
则:
微信:
支付:
70元
流程:
订单100
↓
余额30
↓
剩余70
↓
微信支付70
↓
完成
千万不要:
客户端:
自己:
计算:
70。
必须:
服务器:
返回。
例如:
{
"orderAmount":100,
"balancePay":30,
"wechatPay":70
}
第四十三章 支付密码设计
余额支付:
一般:
需要:
支付密码。
流程:
输入密码
↓
服务器验证
↓
余额扣款
↓
成功
不要:
客户端:
MD5
密码
↓
上传
建议:
使用:
HTTPS
+
RSA
+
Token
服务器:
验证。
支付密码:
不要:
保存在:
客户端。
第四十四章 支付风控设计
真正的大厂:
支付:
都有:
风控。
例如:
一分钟
支付50次
直接:
拦截。
例如:
凌晨3点
支付20万元
需要:
二次验证。
建议:
增加:
RiskService
流程:
支付请求
↓
RiskService
↓
正常
↓
微信支付
风险:
则:
短信验证
人脸验证
短信验证码
再:
支付。
风控规则
例如:
同IP
↓
一分钟
↓
100次支付
拒绝。
或者:
同设备
↓
连续退款
↓
冻结账号
这些:
都是:
支付系统:
必须:
考虑的。
第四十五章 HarmonyOS 支付模块最终架构
建议:
客户端:
最终目录:
entry
│
├── common
│ Logger.ets
│ HttpClient.ets
│
├── pay
│ PayManager.ets
│ WXManager.ets
│ PayRepository.ets
│ PayApi.ets
│ PayModel.ets
│ PayListener.ets
│ PayEvent.ets
│ PayResult.ets
│
├── order
│ OrderManager.ets
│
├── user
│
├── coupon
│
├── promotion
│
└── pages
OrderPage.ets
页面:
真正代码:
只有:
Button("立即支付")
.onClick(async () => {
await PayManager.getInstance().pay(this.orderNo)
})
页面:
永远:
不知道:
微信SDK。
所有:
微信:
支付宝:
银联:
全部:
隐藏。
以后:
增加:
支付宝
Apple Pay
云闪付
数字人民币
页面:
不用:
修改。
企业支付时序图(完整)
用户点击支付
│
▼
HarmonyOS APP
│
▼
创建订单
│
▼
锁库存
│
▼
计算优惠
│
▼
统一下单
│
▼
微信支付
│
▼
支付成功
│
▼
Notify
│
▼
更新订单
│
▼
扣减库存
│
▼
生成物流
│
▼
消息通知
│
▼
完成订单
企业项目支付开发 Checklist
上线前建议逐项检查:
| 检查项 | 是否必须 |
|---|---|
| 微信 SDK 初始化 | ✅ |
| AppID、商户号配置正确 | ✅ |
| 服务端统一下单 | ✅ |
| RSA-SHA256 签名 | ✅ |
| 客户端不保存密钥 | ✅ |
| Notify 验签 | ✅ |
| Notify 幂等处理 | ✅ |
| 客户端二次查询订单 | ✅ |
| 超时关闭订单 | ✅ |
| 锁库存机制 | ✅ |
| 支持退款 | ✅ |
| 支付日志与监控 | ✅ |
| 异常告警 | ✅ |
| 压力测试 | ✅ |
第四十六章 项目目录设计
一个好的支付项目,从目录开始。
HarmonyOS 客户端
建议采用如下目录:
entry
│
├── common
│ ├── http
│ ├── utils
│ ├── constants
│ ├── storage
│ └── logger
│
├── network
│ ├── ApiClient.ets
│ ├── ApiConfig.ets
│ └── Interceptor.ets
│
├── pay
│ ├── PayManager.ets
│ ├── WXManager.ets
│ ├── PayRepository.ets
│ ├── PayApi.ets
│ ├── model
│ └── callback
│
├── order
│
├── cart
│
├── user
│
├── pages
│
└── components
很多初学者喜欢把所有 .ets 文件都放在一个目录中,项目规模稍大后维护成本会迅速上升。建议从一开始就按业务模块拆分。
第四十七章 服务端目录设计
Spring Boot 推荐采用经典三层架构,并按支付业务拆分模块:
mall-payment
├── controller
│ PayController
│ RefundController
│
├── service
│ PayService
│ RefundService
│ NotifyService
│
├── manager
│ WXPayManager
│
├── config
│ WXPayConfig
│
├── mapper
│
├── entity
│
├── dto
│
├── vo
│
├── task
│
└── utils
如果未来需要接入:
- 支付宝
- 云闪付
- PayPal
只需要新增对应的 Manager,而不用修改控制层。
第四十八章 数据库设计
支付相关通常至少包含以下几张核心表:
t_order 订单表
t_pay_order 支付流水表
t_refund_order 退款流水表
t_notify_log 回调日志
t_pay_log 支付日志
其中:
订单表
CREATE TABLE t_order (
id BIGINT PRIMARY KEY,
order_no VARCHAR(64),
user_id BIGINT,
total_amount DECIMAL(10,2),
pay_amount DECIMAL(10,2),
status VARCHAR(20),
create_time DATETIME,
update_time DATETIME
);
这里建议保留:
- 商品总金额(
total_amount) - 实际支付金额(
pay_amount)
便于统计优惠金额。
支付流水表
很多项目直接把支付信息放进订单表,这是不推荐的。
建议独立:
t_pay_order
例如:
transaction_id
mchid
appid
pay_time
trade_state
这样后续:
- 多次支付
- 补单
- 对账
都会方便很多。
第四十九章 HarmonyOS 创建订单
点击:
立即支付
不要马上调微信。
第一步:
调用:
POST /api/order/create
HarmonyOS:
async createOrder() {
const result = await OrderApi.create({
goodsId: this.goodsId,
count: this.count
})
return result.orderNo
}
服务器:
返回:
{
"code":0,
"data":{
"orderNo":"202607060000001"
}
}
注意:
此时:
订单状态:
UNPAID
还没有调用微信。
第五十章 获取支付参数
创建订单以后:
客户端:
请求:
POST /api/pay/create
参数:
{
"orderNo":"202607060000001"
}
服务器:
内部流程:
检查订单
↓
检查库存
↓
检查金额
↓
微信统一下单
↓
生成签名
↓
返回客户端
返回:
{
"code":0,
"data":{
"appId":"wx******",
"partnerId":"******",
"prepayId":"******",
"packageValue":"Sign=WXPay",
"nonceStr":"******",
"timeStamp":"******",
"sign":"******"
}
}
HarmonyOS:
直接:
PayReq
↓
sendReq()
第五十一章 封装 PayManager
企业项目不要在页面写支付逻辑。
推荐:
export class PayManager {
async pay(orderNo:string){
// 1.获取支付参数
// 2.构建PayReq
// 3.调用微信
// 4.等待回调
// 5.查询订单
}
}
页面:
只有:
await PayManager.getInstance().pay(orderNo)
即可。
所有支付细节:
全部隐藏。
第五十二章 支付结果监听
支付完成以后:
统一:
交给:
PayCallback
例如:
export interface PayCallback{
onSuccess()
onFail()
onCancel()
}
页面:
实现:
class OrderPage implements PayCallback{
}
这样:
以后:
增加:
支付宝。
接口:
不用改。
第五十三章 支付状态同步
很多开发者:
收到:
微信:
成功。
立即:
跳:
支付成功
实际上:
企业:
一般:
先:
支付完成
↓
Loading
↓
查询订单
↓
SUCCESS
↓
支付成功
HarmonyOS:
例如:
while(true){
const result = await queryOrder()
if(result.status=="SUCCESS"){
break
}
}
但是:
不要:
无限循环。
建议:
最多:
查询:
5~6次。
采用:
指数退避:
2秒
↓
4秒
↓
8秒
↓
16秒
↓
停止
第五十四章 支付模块事件总线
支付:
通常:
多个页面:
需要:
监听。
例如:
订单页
购物车
我的订单
首页
建议:
使用:
EventHub。
例如:
eventHub.emit("PAY_SUCCESS", orderNo)
其它页面:
eventHub.on("PAY_SUCCESS",()=>{
})
这样:
支付完成:
购物车:
自动:
刷新。
订单:
自动:
刷新。
不用:
页面:
互相:
调用。
第五十五章 企业支付完整时序图
HarmonyOS App
│
▼
创建订单
│
▼
OrderService
│
▼
MySQL 保存订单
│
▼
统一下单
│
▼
微信支付平台
│
▼
返回 prepay_id
│
▼
HarmonyOS 拉起微信
│
▼
用户确认支付
│
▼
微信 Notify
│
▼
NotifyService
│
▼
更新订单状态
│
▼
HarmonyOS 查询订单
│
▼
支付成功
│
▼
刷新订单页面
整个过程中:
客户端只负责:
- 创建订单请求
- 获取支付参数
- 拉起微信
- 查询支付状态
- 更新 UI
所有涉及:
- 签名
- 金额计算
- 库存校验
- 订单更新
- 回调处理
全部由服务端完成。
企业开发经验总结
很多支付 Bug 都集中在以下几个方面:
| 问题 | 推荐方案 |
|---|---|
| 重复点击支付 | 按钮防抖 + 服务端幂等 |
| 重复 Notify | 数据库唯一索引 + 幂等锁 |
| 超时未支付 | 定时关闭订单 |
| 支付成功未发货 | 以 Notify 为准,不以客户端为准 |
| 查询频繁 | 指数退避轮询 |
| 多端登录 | 支付结果通过事件同步 |
| 日志排查困难 | 全链路 TraceId |
第五十六章 sendReq() 返回 true,但没有拉起微信
这是 HarmonyOS 接入微信支付最常见的问题之一。
很多开发者认为:
let result = await WXApi.sendReq(req)
if (result) {
// 支付成功
}
实际上:
sendReq() 返回 true,只表示请求已经成功发送给微信 SDK。
并不表示:
- 微信已经打开
- 用户已经支付
- 支付一定成功
真正流程:
sendReq()
↓
SDK接收请求
↓
尝试启动微信
↓
微信验证参数
↓
打开支付页面
任何一步失败,都可能导致:
没有拉起微信
排查顺序
建议按照下面顺序检查:
① 微信是否安装
await WXApi.isWXAppInstalled()
② SDK是否初始化
例如:
WXApi.registerApp(AppID)
是否执行。
建议:
应用启动立即初始化。
不要:
点击支付才初始化。
③ AppID是否一致
很多企业:
开发环境:
wx123
正式:
wx456
如果:
后台:
返回:
正式 AppID。
客户端:
初始化:
测试 AppID。
微信:
直接拒绝。
④ BundleName是否配置错误
HarmonyOS:
BundleName:
必须:
与微信开放平台:
一致。
例如:
com.demo.mall
不要:
com.demo.mall.test
企业建议
支付前:
统一检查:
checkWX()
↓
checkVersion()
↓
checkRegister()
↓
sendReq()
不要:
直接:
sendReq()
第五十七章 为什么 errCode=0 订单还是未支付?
很多新人:
看到:
errCode=0
立即:
支付成功
这是企业最严重的 Bug。
正确流程:
微信返回成功
↓
Notify
↓
服务器更新订单
↓
客户端查询订单
↓
SUCCESS
↓
支付成功
为什么?
例如:
微信支付完成
↓
服务器网络异常
↓
Notify失败
数据库:
还是:
UNPAID
如果:
客户端:
直接:
提示:
支付成功。
订单:
永远:
未支付。
正确方案
errCode=0
↓
queryOrder()
↓
SUCCESS
↓
支付成功
第五十八章 Notify 为什么收到两次?
很多开发者:
发现:
/pay/notify
收到:
2次
3次
10次
其实:
这是微信:
正常机制。
例如:
第一次:
Notify
↓
服务器超时
微信:
认为:
失败。
继续:
发送。
直到:
收到:
HTTP 200
才停止。
所以:
Notify:
必须:
幂等。
例如:
order_status
已经:
SUCCESS
直接:
return
不要:
再次:
扣库存。
不要:
再次:
发货。
第五十九章 为什么重复发货?
例如:
Notify:
第一次:
SUCCESS
发货。
第二次:
Notify:
再次:
SUCCESS
又:
发货。
怎么办?
建议:
增加:
delivery_status
例如:
WAIT
↓
DELIVERING
↓
SUCCESS
发货:
之前:
判断:
SUCCESS?
如果:
已经:
成功。
直接:
结束。
更推荐
数据库:
增加:
UNIQUE(transaction_id)
保证:
重复通知:
不会:
重复处理。
第六十章 为什么支付成功库存还是负数?
很多商城:
这样:
写:
支付成功
↓
库存--
如果:
Notify:
重复:
两次。
库存:
100
↓
99
↓
98
其实:
卖了一件。
库存:
减少:
两件。
正确:
SUCCESS?
↓
已处理?
↓
return
库存:
只扣:
一次。
推荐:
库存:
采用:
CAS
或者
Redis Lua
保证:
原子。
第六十一章 用户连续点击支付怎么办?
很多用户:
连续:
点击:
立即支付
结果:
服务器:
创建:
5个订单。
怎么办?
HarmonyOS:
建议:
按钮:
this.loading=true
支付:
结束:
恢复。
例如:
Button("立即支付")
.enabled(!this.loading)
服务端:
更加重要。
例如:
订单:
已经:
UNPAID
再次:
支付。
直接:
返回:
原订单。
不要:
重新:
创建。
第六十二章 用户支付过程中退出微信怎么办?
例如:
微信
↓
输入密码
↓
返回APP
订单:
到底:
成功了吗?
不能:
猜。
建议:
返回:
APP:
立即:
查询:
GET
/pay/query
如果:
PAYING
继续:
轮询。
企业建议
增加:
订单:
PAYING
状态。
不要:
只有:
UNPAID
SUCCESS
第六十三章 微信支付超时怎么办?
例如:
用户:
打开:
支付页面。
30分钟:
没付款。
怎么办?
微信:
订单:
会:
失效。
建议:
客户端:
收到:
ORDER_CLOSED
提示:
订单已失效
请重新下单
不要:
继续:
支付。
第六十四章 为什么支付成功没有收到 Notify?
原因:
通常:
以下几个:
公网无法访问
HTTPS错误
证书错误
防火墙
接口超时
DNS错误
企业:
上线:
第一件事:
不是:
测试:
支付。
而是:
测试:
Notify。
例如:
微信
↓
POST Notify
↓
服务器
↓
HTTP200
是否:
正常。
建议:
Notify:
日志:
永久:
保存。
例如:
notify_json
notify_time
headers
ip
以后:
查问题:
非常:
方便。
第六十五章 企业上线 Checklist(第一版)
正式上线前,请至少完成以下检查:
| 检查项 | 是否完成 |
|---|---|
| 微信 AppID 配置正确 | ✅ |
| 商户号配置正确 | ✅ |
| API V3 Key 配置 | ✅ |
| 商户私钥加载成功 | ✅ |
| 平台证书下载成功 | ✅ |
| Notify 地址公网可访问 | ✅ |
| Notify 验签通过 | ✅ |
| Notify 幂等处理完成 | ✅ |
| 订单状态流转正确 | ✅ |
| 超时关闭订单 | ✅ |
| 客户端二次确认订单 | ✅ |
| 支付日志记录 | ✅ |
| TraceId 全链路跟踪 | ✅ |
| Redis 锁防止重复支付 | ✅ |
| 压测通过 | ✅ |
第六十六章 HarmonyOS NEXT 接入微信支付兼容性问题
HarmonyOS NEXT 与 Android 最大的区别之一,就是它已经不再兼容 Android APK,也没有 Android 的 Activity、Intent 等机制。因此,在迁移支付能力时,很多 Android 的经验不能直接照搬。
下面是企业项目中最常见的问题。
问题一:支付页面无法正常回调
现象:
微信支付完成
↓
没有返回应用
↓
应用一直停留在后台
原因可能包括:
- 回调 Ability 配置错误
- 应用未正确注册微信 AppID
- 回调 Ability 未导出
- Bundle Name 与微信开放平台配置不一致
建议检查:
EntryAbility
↓
WXEntryAbility
↓
AppScope
↓
module.json5
整个注册流程是否完整。
问题二:不同设备表现不一致
例如:
Mate70
正常支付
↓
Mate60
无法拉起微信
通常需要排查:
- 微信版本是否一致
- HarmonyOS NEXT 系统版本是否一致
- 微信 SDK 是否为最新版本
- OpenSDK 是否支持当前系统版本
企业建议:
建立设备兼容性测试矩阵,例如:
| 品牌 | 系统版本 | 微信版本 | 是否通过 |
|---|---|---|---|
| Mate60 | HarmonyOS NEXT 5.0 | 最新版 | ✅ |
| Mate70 | HarmonyOS NEXT 5.1 | 最新版 | ✅ |
| Pura70 | HarmonyOS NEXT 5.1 | 最新版 | ✅ |
| Mate X5 | HarmonyOS NEXT 5.1 | 最新版 | ✅ |
第六十七章 DevEco Studio 调试支付技巧
很多开发者第一次调试微信支付时:
日志非常少。
建议统一封装:
Logger.info("开始创建订单")
Logger.info("统一下单完成")
Logger.info("调用sendReq")
Logger.info("收到微信回调")
Logger.info("开始查询订单")
不要直接:
console.info(...)
推荐建立统一日志工具:
Logger
↓
Console
↓
File
↓
上传服务器
这样:
线上问题:
可以快速定位。
日志建议增加 TraceId
例如:
订单号:
202607060001
↓
TraceId:
f8ab-23ce-998d
以后:
所有:
接口:
统一:
携带:
X-Trace-Id
例如:
创建订单
↓
统一下单
↓
Notify
↓
查询订单
全部:
可以串起来。
第六十八章 多环境配置方案
企业:
一般:
至少:
四套环境。
dev
↓
test
↓
pre
↓
prod
不要:
代码:
这样:
const AppId="wx123456"
建议:
配置中心:
PayConfig
↓
开发环境
↓
测试环境
↓
生产环境
HarmonyOS:
例如:
PayConfig.getAppId()
而不是:
写死。
配置建议
config
├── dev.json
├── test.json
├── pre.json
└── prod.json
支付:
自动:
读取。
第六十九章 微信支付证书自动更新
很多企业:
支付:
突然:
全部:
失败。
原因:
平台证书
过期
解决方案:
不要:
人工:
更新。
建议:
后台:
每天:
自动:
检查:
微信平台证书
是否:
更新。
流程:
定时任务
↓
检查平台证书
↓
发现更新
↓
下载新证书
↓
热更新
避免:
凌晨:
全部:
支付失败。
第七十章 支付性能优化
很多项目:
一个支付:
耗时:
3秒
5秒
8秒
其实:
真正:
耗时:
通常:
在:
服务器。
建议:
拆解:
耗时:
创建订单
30ms
↓
统一下单
200ms
↓
数据库
20ms
↓
Redis
5ms
↓
返回客户端
统计:
每一步:
耗时。
不要:
只统计:
总耗时。
推荐监控
例如:
PayMonitor
↓
createOrder
↓
unifiedOrder
↓
notify
↓
queryOrder
每一步:
都有:
耗时。
第七十一章 高并发支付设计
例如:
618。
一分钟:
10万单
怎么办?
不要:
同步:
全部:
完成。
建议:
订单
↓
消息队列
↓
支付中心
↓
微信
例如:
RabbitMQ
或者
Kafka
订单:
异步:
处理。
客户端:
等待:
支付参数。
企业架构
Order
↓
MQ
↓
PayService
↓
WXPay
解耦:
订单:
支付。
第七十二章 支付监控平台
企业:
一般:
都有:
支付大屏。
例如:
实时:
显示:
支付成功率
↓
QPS
↓
退款数
↓
平均耗时
↓
异常数
建议:
Grafana:
例如:
今日支付:
10000
成功:
9988
失败:
12
发现:
异常:
立即:
报警。
第七十三章 支付告警系统
例如:
一分钟:
Notify:
失败:
100次。
怎么办?
立即:
通知:
运维。
建议:
接入:
短信
企业微信
钉钉
邮件
例如:
支付成功率
<95%
报警。
不要:
等:
用户:
投诉。
第七十四章 企业支付架构演进
小项目:
通常:
APP
↓
SpringBoot
↓
微信
几十万用户:
以后:
建议:
APP
↓
Gateway
↓
Order
↓
PayCenter
↓
WXPay
再大:
例如:
千万:
用户。
建议:
APP
↓
API Gateway
↓
Order Center
↓
Payment Center
↓
MQ
↓
Notify Center
↓
Message Center
↓
Inventory Center
支付:
独立:
成为:
支付中心。
方便:
支付宝、
微信、
云闪付、
Apple Pay、
数字人民币
统一:
管理。
第七十五章 企业级支付架构(最终版)
HarmonyOS APP
│
▼
API Gateway
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Order Center User Center Coupon Center
│
▼
Payment Center
│
┌───────┼────────┐
▼ ▼ ▼
WeChat Alipay UnionPay
│
▼
Notify Center
│
▼
Message Queue
│
▼
Inventory / Logistics / Message
这种架构有几个优势:
- 支付渠道统一:微信、支付宝、云闪付使用统一接口。
- 高扩展性:新增支付渠道几乎不影响业务层。
- 高可用:支付中心可独立扩容、独立部署。
- 便于监控:支付链路集中管理。
经过大量项目实践,推荐遵循以下原则:
| 场景 | 推荐方案 |
|---|---|
| 支付入口 | PayManager 统一管理 |
| 支付渠道 | 策略模式(Strategy) |
| 支付回调 | 事件总线(EventHub) |
| 支付状态 | 服务端统一维护 |
| 订单查询 | 指数退避轮询 |
| 日志 | TraceId + 全链路日志 |
| 配置 | 多环境配置中心 |
| 高并发 | MQ 解耦支付流程 |
| 告警 | 实时监控 + 自动通知 |
第76章 企业级 PayManager 完整设计
在很多公司的项目中,页面(Page) 是绝对不能直接调用微信 SDK 的。
很多初学者会这样写:
Button("立即支付")
.onClick(async () => {
const payData = await Http.post("/pay/create")
const req = new PayReq()
req.appId = payData.appId
req.partnerId = payData.partnerId
...
await WXApi.sendReq(req)
})
虽然能够运行,但是存在很多问题:
- 页面代码过于臃肿
- 无法复用
- 后续接入支付宝需要修改所有页面
- 难以单元测试
- 不方便统一日志和异常处理
因此,企业项目一般会采用如下结构:
PayManager
│
├── PayRepository
├── WXManager
├── PayDispatcher
├── PayEventCenter
└── OrderRepository
页面只负责:
await PayManager.getInstance().pay(orderNo)
第77章 PayManager 职责划分
一个优秀的 PayManager 不应该只有一个 pay() 方法,而应该承担整个支付生命周期的管理。
例如:
export class PayManager {
async createOrder() {}
async createPay() {}
async startWechatPay() {}
async queryPayResult() {}
async cancelPay() {}
}
每一个方法都有独立职责:
createOrder()
│
▼
创建业务订单
createPay()
│
▼
获取微信支付参数
startWechatPay()
│
▼
拉起微信
queryPayResult()
│
▼
确认订单状态
cancelPay()
│
▼
取消支付流程
这样后续维护成本会低很多。
第78章 Repository 模式
企业项目一般不会让 PayManager 直接请求网络。
推荐增加:
PayRepository
负责所有支付接口。
例如:
export default class PayRepository {
async createPay(orderNo: string) {
return await PayApi.createPay(orderNo)
}
async queryOrder(orderNo: string) {
return await PayApi.queryOrder(orderNo)
}
}
这样:
如果以后:
HTTP 框架:
Axios
↓
OkHttp
↓
自研HTTP
页面和业务层都不需要修改。
第79章 WXManager 封装
所有微信 SDK 调用,都建议集中管理。
例如:
export class WXManager {
async register() {}
async sendPayRequest() {}
async isWXInstalled() {}
async isSupportPay() {}
}
支付时:
await WXManager.getInstance().sendPayRequest(req)
不要在多个页面重复:
WXApi.sendReq(...)
统一封装的好处:
- 统一异常处理
- 统一日志
- 统一版本兼容
- 后续 SDK 升级只改一处
第80章 EventHub 支付事件设计
支付完成后,通常需要通知多个页面。
例如:
支付成功
│
├── 订单页刷新
├── 首页刷新
├── 购物车刷新
├── 我的订单刷新
└── 用户积分刷新
如果页面之间直接互相调用,耦合会越来越高。
推荐定义统一事件:
export enum PayEvent {
PAY_SUCCESS,
PAY_CANCEL,
PAY_FAIL
}
支付成功:
eventHub.emit(PayEvent.PAY_SUCCESS, orderNo)
任何页面:
eventHub.on(PayEvent.PAY_SUCCESS, (orderNo) => {
})
即可监听。
第81章 支付状态机设计
不要使用:
支付成功
支付失败
两个状态。
建议完整生命周期:
CREATED
│
▼
WAIT_PAY
│
▼
PAYING
│
▼
SUCCESS
│
▼
FINISHED
异常流程:
WAIT_PAY
│
├── CANCEL
│
├── CLOSE
│
└── REFUND
建议定义枚举:
export enum PayStatus {
CREATED,
WAIT_PAY,
PAYING,
SUCCESS,
FINISHED,
CANCEL,
CLOSED,
REFUNDING,
REFUNDED
}
所有页面统一使用,不要写 "SUCCESS"、"PAID" 之类的字符串。
第82章 企业级异常处理
不要:
try{
}catch(e){
}
什么都不做。
建议统一异常:
PayException
例如:
export class PayException {
code:number
message:string
}
统一错误码:
| Code | 含义 |
|---|---|
| 1001 | 微信未安装 |
| 1002 | 微信版本过低 |
| 1003 | 订单不存在 |
| 1004 | 统一下单失败 |
| 1005 | 签名失败 |
| 1006 | 订单已关闭 |
| 1007 | 订单已支付 |
这样 UI 可以根据错误码做不同提示,而不是只弹一个“支付失败”。
第83章 日志系统设计
建议整个支付链路都有统一日志。
例如:
[Pay]
创建订单
↓
统一下单
↓
拉起微信
↓
收到回调
↓
查询订单
↓
完成
推荐封装:
PayLogger.info()
PayLogger.warn()
PayLogger.error()
而不是项目里到处都是:
console.info(...)
日志中建议包含:
- TraceId
- OrderNo
- UserId(脱敏)
- 耗时
- 错误码
- 当前支付阶段
第84章 支付模块单元测试
支付模块也是可以测试的。
例如:
模拟:
创建订单成功
↓
统一下单失败
或者:
微信未安装
甚至:
Notify 延迟 10 秒
通过 Mock PayRepository 和 WXManager,可以覆盖大部分业务逻辑,而无需真实调用微信 SDK。
建议重点测试:
- 重复点击支付
- 网络超时
- 查询订单失败
- 用户取消支付
- 回调重复触发
第85章 企业级客户端 SDK 最终结构
最终推荐结构如下:
pay
├── api
│ PayApi.ets
│
├── repository
│ PayRepository.ets
│
├── manager
│ PayManager.ets
│ WXManager.ets
│
├── callback
│ PayCallback.ets
│
├── event
│ PayEvent.ets
│
├── model
│ PayRequest.ets
│ PayResponse.ets
│ PayResult.ets
│
├── exception
│ PayException.ets
│
├── logger
│ PayLogger.ets
│
└── utils
PayUtils.ets
整个客户端对外暴露的接口尽量保持简单:
await PayManager.getInstance().pay(orderNo)
第86章 企业级支付模块初始化
经过前面的章节,我们已经完成了支付架构设计。
现在开始正式搭建支付模块。
建议采用独立业务模块:
entry
├── common
├── network
├── order
├── user
├── pay
│ ├── api
│ ├── manager
│ ├── model
│ ├── repository
│ ├── callback
│ ├── event
│ ├── exception
│ ├── utils
│ └── logger
└── pages
这样支付相关代码全部集中管理,不会散落在页面中。
为什么支付要独立模块?
支付通常会被多个业务复用:
- 商品购买
- VIP 会员
- 课程购买
- 充值
- 打赏
- 活动报名
如果每个页面都写一套支付逻辑,维护成本会越来越高。
因此推荐统一入口:
PayManager
所有业务都调用同一个支付模块。
第87章 支付配置中心
不要在代码中直接写:
微信 AppID
商户号
服务器地址
推荐统一配置:
PayConfig
负责:
- 支付环境
- 支付渠道
- 请求超时时间
- 日志等级
- 回调地址(由服务端管理)
配置中心的职责是读取配置,而不是保存任何敏感密钥。
多环境切换
建议支持:
dev
test
pre
prod
每个环境使用不同:
- App 配置
- 服务端地址
- 日志级别
避免开发环境误调用生产服务。
第88章 支付生命周期
企业支付建议定义统一生命周期:
初始化
│
▼
创建订单
│
▼
获取支付参数
│
▼
发起支付
│
▼
等待回调
│
▼
查询订单
│
▼
完成
所有阶段建议统一记录日志,方便定位问题。
第89章 Repository 与 Manager 的职责
建议职责明确:
Repository:
负责:
- 请求服务端接口
- 数据转换
- 网络异常处理
Manager:
负责:
- 支付流程控制
- 调用微信 SDK
- 回调处理
- 状态同步
- 事件通知
这样网络层和业务层完全解耦。
第90章 页面如何调用支付?
页面应保持尽可能简单。
推荐流程:
点击立即支付
│
▼
PayManager
│
▼
Repository
│
▼
服务端
│
▼
微信支付
页面只需要关心:
- 显示 Loading
- 响应支付结果
- 刷新订单
而无需了解支付实现细节。
本章小结
本章完成了企业级支付模块初始化设计,包括:
- 支付模块目录规划
- 配置中心设计
- 多环境管理
- 支付生命周期
- Repository 与 Manager 分层
- 页面调用规范
第91章 服务端统一下单接口设计
客户端点击"立即支付"后,第一步不是调用微信,而是请求自己的业务服务器。
整体流程如下:
HarmonyOS App
│
│ POST /api/pay/create
▼
业务服务器
│
│ 校验订单
│ 校验金额
│ 校验库存
▼
微信支付统一下单
│
▼
返回预支付信息
│
▼
业务服务器签名
│
▼
HarmonyOS App
统一下单接口建议保持职责单一:
- 校验订单是否合法
- 判断订单是否已支付
- 调用微信支付统一下单
- 返回客户端发起支付所需参数
不要在这个接口中处理:
- 发货
- 扣积分
- 修改订单状态为成功
这些都应该等待支付结果确认后再执行。
第92章 创建订单接口设计
建议支付前先创建业务订单。
例如:
POST /api/order/create
请求:
{
"goodsId": 10001,
"count": 2,
"couponId": 8
}
返回:
{
"code": 0,
"data": {
"orderNo": "202607060001"
}
}
订单创建成功后:
数据库状态:
WAIT_PAY
而不是:
SUCCESS
为什么要先创建订单?
如果:
先微信支付
↓
再创建订单
那么:
一旦:
数据库:
异常。
用户:
已经:
付款。
订单:
不存在。
企业:
几乎:
无法恢复。
所以:
必须:
创建订单
↓
统一下单
↓
支付
第93章 幂等设计
支付系统:
最重要:
两个字:
幂等
例如:
用户:
连续点击:
立即支付
十次。
不能:
创建:
十个订单。
建议:
数据库:
增加:
business_no
或者:
request_id
作为:
幂等键。
流程:
收到请求
↓
检查request_id
↓
存在
↓
直接返回
↓
不存在
↓
创建订单
Notify 也要幂等
微信:
可能:
重复:
通知。
例如:
SUCCESS
↓
SUCCESS
↓
SUCCESS
业务:
只能:
处理:
一次。
建议:
更新订单:
采用:
update t_order
set status='SUCCESS'
where order_no=?
and status='WAIT_PAY'
如果:
更新:
0 行。
说明:
已经:
处理。
直接:
返回。
第94章 订单状态流转
建议:
完整状态:
CREATED
│
▼
WAIT_PAY
│
▼
PAYING
│
▼
SUCCESS
│
▼
DELIVERING
│
▼
FINISHED
异常:
WAIT_PAY
├── CLOSED
├── CANCELLED
└── REFUNDING
不要:
只有:
0
1
两个状态。
后期:
维护:
非常困难。
第95章 回调后的业务处理
微信通知:
支付成功。
第一件事情:
不是:
发货。
正确流程:
Notify
↓
验签
↓
更新订单
↓
记录支付流水
↓
发送MQ消息
↓
库存系统
↓
积分系统
↓
物流系统
↓
消息中心
这样:
支付:
与:
业务:
彻底:
解耦。
为什么使用 MQ?
例如:
支付成功。
如果:
同步:
调用:
库存
↓
积分
↓
优惠券
↓
短信
其中:
短信:
超时。
整个:
支付:
都会:
变慢。
所以:
推荐:
Notify
↓
MQ
↓
库存服务
↓
积分服务
↓
消息服务
异步:
消费。
第96章 支付流水设计
订单:
不能:
代替:
支付流水。
建议:
独立:
t_pay_order
记录:
- 商户号
- 微信交易号
- 支付金额
- 支付时间
- 支付状态
- 原始返回数据(必要时脱敏存储)
这样:
后期:
对账:
退款:
都会:
方便很多。
第97章 对账机制
每天:
建议:
执行:
下载微信账单
↓
比对数据库
↓
发现差异
↓
自动补偿
↓
人工处理
不要:
认为:
Notify:
100%
可靠。
任何:
支付系统:
都需要:
每日:
对账。
第98章 支付安全建议
企业:
上线:
至少:
做到:
以下:
几点:
- 全站 HTTPS
- 服务端验签
- 不在客户端保存商户密钥
- 敏感日志脱敏
- 支付接口限流
- 防重放(Nonce、时间戳等按协议要求处理)
- 风险订单二次验证
第99章 上线前压测
建议:
模拟:
1000
↓
5000
↓
10000
↓
50000
并发:
支付。
观察:
- 数据库
- Redis
- MQ
- CPU
- 内存
- 平均响应时间
提前:
发现:
瓶颈。
更多推荐




所有评论(0)