首先来看一下官方给出的支付流程。
- 用户在小程序下单。小程序请求商户服务器进行下单。
- 商户系统调用小程序的官方api接口,获取用户openid。
- 然后调用生成订单api。生成商户订单。
- 调用统一下单API接口,换回预付单信息。
- 将组合数据再次签名返回给小程序,并调用支付接口。
- 用户确认后支付。调用支付接口。返回支付结果。
- 用户进行展示。
1、模拟用户下单。调用统一下单api,统一下单api需要的必填参数有:小程序的ID(appid),商户号(mch_id),随机字符串(nonce_str),签名(sign),商品描述(body),商户订单号(out_trade_no),标价金额(total_fee),终端IP(spbill_create_ip),通知地址(notify_url),交易类型(trade_type)。
先整理出需要的数据
#提示:一下所有敏感信息如appid,secret,mch_id都经过处理。
$appid = "wx7e229199f3363a7f"; //这里是小程序的appid
$mch_id = "8512312591"; //添加商户号id
$nonce_str = "wtg9lanlmsj";//由微信小程序js随机数生成算法生成 Math.random().toString(36).substr(2,30);
$body = "某某某公司充值——10元";
$out_trade_no = "201903210001";//订单号由商户自定义生成,但需要唯一。
$total_fee = "1000";//金额这边单位为分。
$spbill_create_ip = "127.0.0.1";
$notify_url = "127.0.0.1/api/pay.php";
$trade_type = "JSAPI";
$openid = "jsWua5ZnPGLpsksnUa1efdcLMaI"; //这边openid在小程序调用login接口 获取js_code然后用js_code调用code2Session获取opnid
/* sign参数需要一些处理。首先官方文档这样说
假设传送的参数如下:
appid: wxd930ea5d5a258f4f
mch_id: 10000100
device_info: 1000
body: test
nonce_str: ibuaiVcKdpRxkhJA
第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:
stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
第二步:拼接API密钥:
stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" //注:key为商户平台设置的密钥key
sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7" //注:MD5签名方式
sign=hash_hmac("sha256",stringSignTemp,key).toUpperCase()="6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6" //注:HMAC-SHA256签名方式
*/
#我们拼接字符串
$stringA = "appid=$appid&body=$body&mch_id=$mch_id&nonce_str=$nonce_str¬ify_url=$notify_url&openid=$openid&out_trade_no=$out_trade_no&spbill_create_ip=$spbill_create_ip&total_fee=$total_fee&trade_type=$trade_type"; //这里要把所有要传递的参数都按照ascll码排序
$stringSignTemp = $stringA."&key=8749b9e658d71dcbd33c3dce5afad7f6"; //这里key是商户秘钥
$sign = strtoupper(md5($stringSignTemp));
所有数据准备好之后。生成xml请求字符串。并且请求
$xmldata = "<xml>
<appid>$appid</appid>
<mch_id>$mch_id</mch_id>
<nonce_str>$nonce_str</nonce_str>
<sign>$sign</sign>
<body>$body</body>
<out_trade_no>$out_trade_no</out_trade_no>
<total_fee>$total_fee</total_fee>
<spbill_create_ip>$spbill_create_ip</spbill_create_ip>
<notify_url>$notify_url</notify_url>
<trade_type>$trade_type</trade_type>
<openid>$openid</openid>
</xml>";
$curl = curl_init();//初始化一个curl会话 curl_setopt($curl,CURLOPT_URL,"https://api.mch.weixin.qq.com/pay/unifiedorder");//设置地址
curl_setopt($curl,CURLOPT_POST,1);//设置发送方式为post
curl_setopt($curl,CURLOPT_POSTFIELDS,$xmldata);//设置要发送的数据
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);//设置返回的信息以字符串输出
$return_xml = curl_exec($curl);//执行
curl_close($curl);//关闭并释放资源
libxml_disable_entity_loader(true); //禁止引用外部xml实体
$value_array = json_decode(json_encode(simplexml_load_string($return_xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);//先把xml转换为simplexml对象,再把simplexml对象转换成 json,再将 json 转换成数组。
#接下来打印输出一下获取的结果。
echo "<pre>";
print_r($value_array);
输出结果为
返回了我们接下来需要的几个值。
如果报错参照官方文档下错误码修改。我在最初写的时候提示签名错误。微信支付接口签名校验工具可以有效看出签名算法的问题出在哪里。
2、接下来将数据再次签名
首先准备数据。由官方文档可知,签名字段有:小程序ID(appId)、时间戳(timeStamp)、随机串(nonceStr)、数据包(package)、签名方式(signType)。
签名方式与之前签名方式相同,依照ascll码排序按照&name=value的格式拼接字符串。
var d = new Date() //创建date对象
let timeStamp = d.getTime().toString(); //获取时间戳
nonce = Math.random().toString(36).substr(2, 30) 生成随机字段。
//连同上一个接口调用返回到prepay_id等,拼接stringA字符串。
let stringA = "appId=" + app.globalData.appid + "&nonceStr=" + nonce + "&package=prepay_id=" + res.data.data.prepay_id + "&signType=MD5" +"&timeStamp=" + timeStamp //appid我保存在了全局变量中 这里注意package的值为 "prepay_id=*****"并不只是prepay_id的值
//接下来连接商户key 同上一个接口 key为商户秘钥
let signTemp = stringA +"&key=7YROQ23666IQlNAX16HD0xyGxNiuJ112R"
//然后进行签名加密
let paySign = utilMd5.hexMD5(signTemp).toUpperCase()//进行md5加密
//原生js中没有md5加密算法 下面附教程
在数据准备好后,调用官方支付接口。
wx.requestPayment({
timeStamp: timeStamp,
nonceStr: nonce,
package: 'prepay_id='+res.data.data.prepay_id,
signType: 'MD5',
paySign: paySign,
success: function(res) {
console.log(res)
},
fail: function(res) {},
complete: function(res) {},
})
},
fail: function(res) {},
complete: function(res) {},
})
调用成功后发现小程序弹出支付窗口。输入口令后支付成功。
若出现缺少参数total_fee则检查统一下单接口各个值是否正确,订单号是否重复。
在二次签名加密时也有检验工具
最后php接收微信后台返回的支付状态,更新订单状态。