在 UniApp 开发的微信小程序里接入微信支付,只需“小程序端负责调起、后端负责拿预支付单”,把两端各 3 个关键步骤跑通即可上线。下面给出 2025 年仍适用的完整流程、代码示例与常见坑位。
一、前置条件(必须先把“账号、证书、域名”配好)
- 小程序已开通微信支付(微信商户平台 → 产品中心 → 小程序支付)
- 在商户后台拿到 5 个核心参数:
mchId、apiV3Key、证书(apiclient_cert.pem / key)、appId、appSecret
(2025 年新申请默认就是 APIv3,不再用 v2) - 小程序后台配置「业务域名」「支付授权目录」:
必须是后端接收回调的公网地址,例如 https://pay.xxx.com/wx/callback
二、整体时序(一张图记牢)
小程序端 ——1.下单请求→ Java/Node 后端 ——2.统一下单→ 微信 ——3.prepay_id→ 后端
后端二次签名后 ——4.支付参数→ 小程序端 ——5.uni.requestPayment→ 微信客户端
用户输密完成 ——6.异步通知→ 后端更新订单
三、后端(以 SpringBoot + 微信支付 SDK 为例)
- 引入依赖(2025 稳定版)
xml
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.4.12</version>
</dependency>- 统一下单 + 二次签名(关键代码,可直接复制)
java
@PostMapping("/wx/createOrder")
public Map<String,String> createOrder(@RequestBody OrderDTO dto) throws Exception {
// 1. 先写一条业务订单,状态=PAYING
String outTradeNo = IdUtil.getSnowflake().nextIdStr();
saveOrder(outTradeNo, dto.getAmount());
// 2. 构建微信请求
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type","application/json; charset=utf-8");
String reqdata = JSONUtil.toJsonStr(MapUtil.builder()
.put("appid", "wx123456789") // 小程序 appId
.put("mchid", "1900000001") // 商户号
.put("description", dto.getBody())
.put("out_trade_no", outTradeNo)
.put("notify_url", "https://pay.xxx.com/wx/callback")
.put("amount", MapUtil.of("total", dto.getAmount()))
.put("payer", MapUtil.of("openid", dto.getOpenid()))
.build());
httpPost.setEntity(new StringEntity(reqdata, "utf-8"));
// 使用 SDK 的 RSA 自动签名
CloseableHttpResponse resp = wxPayClient.execute(httpPost);
String prepayId = JSONUtil.parseObj(EntityUtils.toString(resp.getEntity()))
.getStr("prepay_id");
// 3. 二次签名,返回给 uniapp 的 5 个参数
String timeStamp = String.valueOf(System.currentTimeMillis()/1000);
String nonceStr = IdUtil.fastSimpleUUID();
String packageStr = "prepay_id=" + prepayId;
String signStr = Stream.of("wx123456789", timeStamp, nonceStr, packageStr)
.collect(Collectors.joining("\n", "", "\n"));
String paySign = SignUtil.signRSA(signStr, merchantPrivateKey);
return MapUtil.builder()
.put("appId", "wx123456789")
.put("timeStamp", timeStamp)
.put("nonceStr", nonceStr)
.put("package", packageStr)
.put("signType", "RSA")
.put("paySign", paySign)
.build();
}- 回调验签 & 修改订单
java
@PostMapping("/wx/callback")
public String callback(HttpServletRequest req) throws Exception {
String body = IoUtil.read(req.getInputStream(), "utf-8");
// 微信 2025 仍是 XML,先解析
Map<String,String> map = WXPayUtil.xmlToMap(body);
boolean ok = WXPayUtil.isSignatureValid(map, apiV3Key, SignType.HMACSHA256);
if(ok && "SUCCESS".equals(map.get("result_code"))){
String outTradeNo = map.get("out_trade_no");
updateOrderPaid(outTradeNo); // 把订单状态改成 PAID
}
return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
}四、小程序端(UniApp)
- 获取用户 openid(第一次必须走
wx.login)
js
wx.login({
success({code}) {
uni.request({
url:`${baseUrl}/wx/getOpenid?code=${code}`,
success:res=>{ uni.setStorageSync('openid', res.data.openid) }
})
}
})- 下单并调起收银台
js
async function toPay(){
const openid = uni.getStorageSync('openid');
const res = await uni.request({
url: `${baseUrl}/wx/createOrder`,
method:'POST',
data:{ amount:1, body:'测试商品', openid }
});
const pay = res.data;
uni.requestPayment({
provider:'wxpay',
timeStamp: pay.timeStamp,
nonceStr: pay.nonceStr,
package: pay.package,
signType: pay.signType,
paySign: pay.paySign,
success:()=>{ uni.showToast({title:'支付成功'}) },
fail:(e)=>{ console.log('支付失败',e) }
});
}provider 必须写 'wxpay';package 必须是 prepay_id=xxx 这种格式,否则微信会提示 “验签失败”。