最近在折微信公众号内H5用JSAPI调用微信支付,境内服务商版支付,微信支付给出的官方文档以及SDK不够详细,导至咱们走了一些弯路,把他分享出来,我这边主要是用PHP开发,因此未加说的话示例都是PHP代码javascript
微信的官方文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=7_1php
1.服务商模式下调用统一下单html
独立商户模式统一下单:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1java
服务商模式下统一下单:https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_1json
统一下单与与独立商户模式之间有一点点修改,两个参数,sub_mch_id,sub_appid,原来的openid 参数改成sub_openidapi
**
* 文件:WxPayPubHelper.php * 统一支付接口类 * Class UnifiedOrder_pub */ class UnifiedOrder_pub extends Wxpay_client_pub { public function __construct() { //设置接口连接 $this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //设置curl超时时间 $this->curl_timeout = WxPayConf_pub::getParams('curl_timeout'); } /** * 生成接口参数xml * @return string|void */ public function createXml() { try { //检测必填参数 if ($this->parameters["out_trade_no"] == null) { throw new SDKRuntimeException("缺乏统一支付接口必填参数out_trade_no!" . "<br>"); } elseif ($this->parameters["body"] == null) { throw new SDKRuntimeException("缺乏统一支付接口必填参数body!" . "<br>"); } elseif ($this->parameters["total_fee"] == null) { throw new SDKRuntimeException("缺乏统一支付接口必填参数total_fee!" . "<br>"); } elseif ($this->parameters["notify_url"] == null) { throw new SDKRuntimeException("缺乏统一支付接口必填参数notify_url!" . "<br>"); } elseif ($this->parameters["trade_type"] == null) { throw new SDKRuntimeException("缺乏统一支付接口必填参数trade_type!" . "<br>"); } elseif ($this->parameters["trade_type"] == "JSAPI" && $this->parameters["openid"] == NULL ) { throw new SDKRuntimeException("统一支付接口中,缺乏必填参数openid!trade_type为JSAPI时,openid为必填参数!" . "<br>"); } /************************************服务商模式修改***********************************/ if(WxPayConf_pub::getParams('sub_mchid')){ //服务商模式 $this->parameters["sub_mch_id"] = WxPayConf_pub::getParams('sub_mchid'); $this->parameters["sub_appid"] = WxPayConf_pub::getParams('sub_appid'); $this->parameters["sub_openid"] = $this->parameters["openid"]; unset($this->parameters["openid"]);//去掉原来的openid } /************************************服务商模式修改结束***********************************/
$this->parameters["appid"] = WxPayConf_pub::getParams('appid');//公众帐号ID $this->parameters["mch_id"] = WxPayConf_pub::getParams('mchid');//商户号 $this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];//终端ip $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串 $this->parameters["sign"] = $this->getSign($this->parameters);//签名 return $this->arrayToXml($this->parameters); } catch (SDKRuntimeException $e) { die($e->errorMessage()); } }
2.服务商模式下JSAPI调用微信支付微信
官方给出H5调用API支付文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=7_7&index=6 app
而咱们以前一直立商户模式,用的是JSSDK :https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.htmlcurl
调试统一下单时还算比较顺利,由于获取prepay_id不成功无非就是参数的问题,而且返回给咱们的错误也很是详细,能够很快定位问题。但是这个JSAPI可就把我弄惨了,先看代码(javascript)ide
// 微信支付调用方法 function weixinpayFun() {
var buildformUrl = 'http://local.test/pay/getparams?orderid=订单号';//获取微信支付相关参数 $.post(buildformUrl, {}, function (res) { $.hideLoading(); if (res.status) { var wxpayParam = res.data; // 发起一个支付请求 wx.chooseWXPay({ timestamp: wxpayParam.timeStamp, nonceStr: wxpayParam.nonceStr, package: wxpayParam.package, signType: wxpayParam.signType, paySign: wxpayParam.paySign, trigger: function (res) { }, complete: function (res) { }, success: function (res) { $.alert('支付成功'); }, cancel: function (res) { $.alert('支付已取消'); }, fail: function (res) { alert(JSON.stringify(res)); $.alert('支付失败'); } }); } else { $.alert('微信支付失败!'); } }, 'json'); }
由这段代码能够看到,咱们是经过Ajax方式调用微信支付统一下单并获取到JSAPI的支付参数的,这在独立商户模式下是没有问题的,而且是使用了很长一段时间。
改用服务商模式后,奇怪的问题就来了,每次请求从日志中看到统一下单是成功的,也就是说个人参数与签名也都是没有问题的,可是返回给个人错误见下图
从各方了解到,出现这个错误的缘由是签名错误,因而我就各类各样的调整签名参数,把sub_appid ,appid 换来换去,或者一块儿结合起试,都不行。中间有咨询过微信支付的技术人员,对方也是帮忙分析了一下确认是签名错误,但根据他要求提供了相关参数检查后签名是正确的。最终也是没有彻底搞定。
今天上午由于要处理另一个问题,公众号从公众平台受权会引发独立商户支付出错,彻底调不出支付界面,(由于以前咱们都是让客户填写url,token appid,appsecret之类的参数的,如今改为了公众平台受权) 发现咱们在一个初始的地方作了个JSSDK的初始化主要用于调用分享 ,见下面的代码
<script> window.onload = function onload() { jwx.initialize({ appId: '{$sys_params.jsapi_param.appId}', timestamp: '{$sys_params.jsapi_param.timestamp}', nonceStr: '{$sys_params.jsapi_param.nonceStr}', signature: '{$sys_params.jsapi_param.signature}' }, function () { jwx.setShareData({ title: '分享标题', desc: '描述', link: "分享的连接", imgUrl: '分享显示的图片' }); if ("function" === typeof jwxCallback) { jwxCallback(); } }); }; </script>
因为是受权因此没有了appsecret这个参数,致使没法正常获取JSAPI初始化参数,通过一番代码修改,终于受权的公众号能够正常使用独立商户的微信支付了,改完忽然想起,中间咨询微信支付的技术大神有提到过config文件中的nonceStr和timestamp,由此想到会不会是这个JSAPI初始参数用的appid与appsecret 用的是子商户的appid和appsecret引发的呢?把他换成服务商的试试呢,这一试可不就把问题给解决了.
3.总结
微信支付使用JSAPI发起支付时要注意: JSAPI初始化参数与调用统一下单时的参数(appid,appsecret)要一致,不然会出现莫名的签名错误