纯前端实现H5支付

参考 H5支付文档 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1

1.发送请求所需要的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 
微信后台 设置的key
怎么拿到key https://jingyan.baidu.com/article/c1465413f093870bfcfc4c82.html
*/
let key = "ORM1NEK98XXXXXX3JJAITO6EB14W274ZH";
/* 支付必要参数 */
let h5Obj = {
appid: "wxddxx8xx48c19xxxsdsbdcb", //appid
body: "xx-商品", //商品描述
mch_id: "1579347061", //商户号
nonce_str: randomString(), //随机字符串,不长于32位。
notify_url: "https://www.baidu.com/", //接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
out_trade_no: randomString(), // 订单号
spbill_create_ip: ip, //用户的ip地址 本地不影响 127.0.0.1
total_fee: total, //价格
trade_type: "MWEB", // h5为MWEB 固定
scene_info: '{"h5_info":{"type":"Wap","wap_url":"支付的域名www.baidu.com","wap_name":"订单"}}', // 信息
};

2.用户IP地址获取

1
2
// 这里使用的是souhu的 发送GET请求到 保存IP即可
let url = 'https://pv.sohu.com/cityjson?ie=utf-8'

3.如何进行sign签名?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1排序(ASCII)
let reStr = sortStr(h5Obj, false, key);
// 2 签名方法 获取签名
let sign = nodeMd5(reStr).toUpperCase();
// 3设置要发送的xml参数;sign放在后面
let formData = `<xml>
<appid>${h5Obj.appid}</appid>
<body>${h5Obj.body}</body>
<mch_id>${h5Obj.mch_id}</mch_id>
<nonce_str>${h5Obj.nonce_str}</nonce_str>
<notify_url>${h5Obj.notify_url}</notify_url>
<out_trade_no>${h5Obj.out_trade_no}</out_trade_no>
<spbill_create_ip>${h5Obj.spbill_create_ip}</spbill_create_ip>
<total_fee>${h5Obj.total_fee}</total_fee>
<trade_type>${h5Obj.trade_type}</trade_type>
<scene_info>${h5Obj.scene_info}</scene_info>
<sign>${sign}</sign>
</xml>`;
return {
h5Obj,
formData,
};

(1).排序方法(根据ASCII进行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* 这里放一个通用方法 字节小程序也可以使用 */
/* params为排序对象 , ttKey是字节小程序key ,h5Key微信h5key */
const sortStr = (params, ttKey, h5Key) => {
const paramKeys = Object.keys(params).sort();
let signStr = "";
paramKeys.forEach((key) => {
let value = params[key];
// 空值,risk_info, sign 不参与签名
if (!value || ["risk_info", "sign"].indexOf(key) >= 0) {
return;
}
if (signStr.length > 0) {
signStr += "&";
}
if (typeof value === "object") {
value = JSON.stringify(value);
}
signStr += key + "=" + value;
});
if (ttKey) {
signStr += `${ttKey}`;
return signStr;
}
if (h5Key) {
signStr += "&key=" + h5Key;
return signStr;
}
};
export default sortStr;

(2)签名方法node版(如果不能使用 后面有js版)

1
2
3
4
5
6
7
const crypto = require("crypto");
const createSign = (signStr) => {
const md5 = crypto.createHash("md5");
const sign = md5.update(signStr).digest("hex");
return sign;
};
export default createSign;

(3)随机字符方法

1
2
3
4
5
6
7
8
9
10
11
12
/* 随机字符串32 */
function randomString() {
/* 去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1 */
let chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
let maxPos = chars.length;
let pwd = "";
for (let i = 0; i < 32; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
export default randomString;

4.封装支付方法 (vue可用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//  ASCII 码排序
import sortStr from "../MD5/sortStr/sortStr";
// md5 加密
// import wxMD5 from "../MD5/md5";
import nodeMd5 from "../MD5/nodeMd5";
// 32位随机串
import randomString from "../randomString/randomString";

let h5PayFn = {
/* 下单 */
h5Pay(total = 100, ip = "127.0.0.1") {
// ...h5obj
}
}
export default h5PayFn;
1
2
3
4
5
// 微信h5支付
import h5PayFn from "./H5pay";
Vue.prototype.$utils = {
...h5PayFn,
};

5.使用

1
2
3
4
5
6
7
8
9
10
/* 这里使用的是 flyio 不要忘记ip地址  */ 
let { h5Obj, formData } = this.$utils.h5Pay(100, this.ip);
// 生成的订单号
let out = h5Obj.out_trade_no;
// 下单
this.$utils.flypost("https://api.mch.weixin.qq.com/pay/unifiedorder", formData)
.then((res) => {
console.log(res);
// 得到最后结果 mweb_url 进行跳转即可
}).catch()

6.常见报错

https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4

1.商家存在未配置的参数,请联系商家解决

一般是因为微信后台未配置支付的域名 , 这个一定要注意 ,有的可能一个微信绑定多个商户号! ,要确保配置正确

jsMD5排序
MD5加密JS排序

仅供参考