微信支付(API-v3)标准流程
Java(Spring Boot)下单示例(用官方 SDK,省心)(注意沙箱的密钥与生产不同);:后台校验回调签名,AES-GCM 解密交易资源,做。:按业务再扩展,依旧围绕「签名、拉起、回调、验签」。:你的服务器向微信「统一下单」接口发起请求,拿到。用于:前端失败但实际已扣款、或回调迟迟未到等。三、后端:统一下单(JSAPI/小程序)四、前端:拉起支付(小程序/Taro)一、整体时序(JSAPI
微信支付(API v3)标准流程
一、整体时序(JSAPI/小程序)
-
商户后台下单:你的服务器向微信「统一下单」接口发起请求,拿到
prepay_id。 -
前端拉起支付:小程序用
prepay_id组装参数,调用wx.requestPayment。 -
异步通知:支付成功后,微信回调你的
notify_url(带签名 + 加密资源)。 -
验签解密 & 业务落账:后台校验回调签名,AES-GCM 解密交易资源,做幂等更新订单状态;回 200。
-
(可选)主动查单/关单:容错用。
-
(可选)退款 & 退款回调:同样验签解密。
其它场景差异:
APP 支付:拉起步骤在 App SDK;
Native(扫码):你先拿
code_url生成二维码;H5(外部浏览器):走 H5 场景,微信外唤起。
二、名词 & 关键参数
-
商户号
mchid、AppIDappid、API v3 Key(32字节) -
商户私钥(RSA)+ 证书序列号:用于请求签名
-
微信平台证书:用于验签回调/应答
-
商户订单号
out_trade_no:你方唯一、做幂等键 -
金额单位:分(整数)
三、后端:统一下单(JSAPI/小程序)
HTTP:POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
请求体(示例):
{
"appid": "wx1234567890abcdef",
"mchid": "190000****",
"description": "VIP会员-月卡",
"out_trade_no": "ORDER_20250902_00001",
"notify_url": "https://your.domain/pay/wechat/notify",
"amount": { "total": 1999, "currency": "CNY" },
"payer": { "openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o" }
}
请求头需带 WECHATPAY2-SHA256-RSA2048 授权签名(含 Authorization、Wechatpay-Serial、Nonce、Timestamp 等)。
Java(Spring Boot)下单示例(用官方 SDK,省心)
推荐:
com.wechat.pay.java:wechatpay-java(API v3)
// 1) 初始化(建议单例)
CertificatesManager mgr = CertificatesManager.getInstance();
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchid, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8)
);
mgr.putMerchant(mchid, new WechatPay2Credentials(mchid, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes());
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchid, merchantSerialNumber, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
// 2) 统一下单(JSAPI)
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
String body = """
{
"appid":"%s","mchid":"%s","description":"VIP会员-月卡",
"out_trade_no":"%s","notify_url":"%s",
"amount":{"total":%d},"payer":{"openid":"%s"}
}
""".formatted(appid, mchid, outTradeNo, notifyUrl, total, openid);
httpPost.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
try (CloseableHttpResponse resp = httpClient.execute(httpPost)) {
String respStr = EntityUtils.toString(resp.getEntity());
// 解析得到 prepay_id
}
返回包含 prepay_id,例如:wx201410272009395522657a690389285100。
四、前端:拉起支付(小程序/Taro)
// 拿到服务端返回参数后(推荐由服务端生成这些签名参数再下发给前端)
Taro.requestPayment({
timeStamp: payParams.timeStamp, // 字符串
nonceStr: payParams.nonceStr,
package: `prepay_id=${payParams.prepayId}`,
signType: 'RSA',
paySign: payParams.paySign, // 服务端用商户私钥对 (appId,timeStamp,nonceStr,package) 组串签名
success: () => { /* 展示“支付处理中”,等待回调或主动查单 */ },
fail: (err) => { /* 失败/取消处理 */ }
})
强烈建议:这些参数由后端签好再发给前端,避免私钥泄露。
五、异步通知:验签 + 解密 + 幂等
-
回调地址:你在
notify_url配置的 HTTPS 接口 -
头部包含:
Wechatpay-Timestamp、Wechatpay-Nonce、Wechatpay-Signature、Wechatpay-Serial -
体内
resource使用 API v3 Key 进行 AES-256-GCM 解密
Java 验签解密(示意):
// 读取回调 Header
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String serial = request.getHeader("Wechatpay-Serial");
// 验签
String body = requestBodyAsString(request);
String message = timestamp + "\n" + nonce + "\n" + body + "\n";
boolean ok = verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
if (!ok) { return ResponseEntity.status(401).build(); }
// 解密
JsonNode root = objectMapper.readTree(body);
JsonNode res = root.get("resource");
String ciphertext = res.get("ciphertext").asText();
String nonceStr = res.get("nonce").asText();
String aad = res.get("associated_data").asText();
String plain = AesGcm.decryptToString(apiV3Key, nonceStr, ciphertext, aad);
// plain 内含交易信息(如 trade_state=SUCCESS, out_trade_no, transaction_id 等)
// 幂等更新:按 out_trade_no 原子落账;若已处理直接返回 200
return ResponseEntity.ok("{\"code\":\"SUCCESS\",\"message\":\"OK\"}");
回调会重试:只要你不是 200/
SUCCESS,微信会按策略重投。务必幂等。
六、主动查单 & 关单
-
查单:
GET /v3/pay/transactions/out-trade-no/{out_trade_no}?mchid=... -
关单:
POST /v3/pay/transactions/out-trade-no/{out_trade_no}/close
用于:前端失败但实际已扣款、或回调迟迟未到等。
七、退款 & 回调(API v3)
-
申请退款:
POST /v3/refund/domestic/refunds(用支付单transaction_id或out_trade_no) -
退款回调同样验签解密,按
out_refund_no幂等处理。
八、常见坑 & 最佳实践
-
证书与密钥:
-
-
商户私钥妥善保管;只在服务端使用。
-
定期轮换平台证书(AutoUpdateCertificatesVerifier 可自动拉取)。
-
-
时区/时间戳:统一用 UTC 秒级;签名串的换行与顺序严格一致。
-
金额单位:分(int);计算金额用整数或BigDecimal避免精度丢失。
-
重复回调/并发:以
out_trade_no做悲观/乐观锁或去重表。 -
安全:回调仅接受 HTTPS,且白名单校验 & CSRF 防御。
-
沙箱/联调:先走仿真/沙箱(注意沙箱的密钥与生产不同);线下全链路打通再切生产。
-
服务商模式(子商户):多一个
sub_mchid/sub_appid维度,签名流程相同。 -
合单/分账/电商收付通:按业务再扩展,依旧围绕「签名、拉起、回调、验签」。
九、极简落地清单
-
[ ] 在商户平台创建商户号、开通对应支付场景
-
[ ] 配置
appid、mchid、API v3 Key、下载商户私钥、保存证书序列号 -
[ ] 后端:打通统一下单 → 回调验签解密(幂等)
-
[ ] 前端:拿后端参数
requestPayment拉起 -
[ ] 容错:查单/关单
-
[ ] 退款链路与对账
更多推荐
所有评论(0)