漫谈微信支付-公共号支付
本文对微信公共号支付的实现过程进行详细介绍。
概述
公共号支付主要应用于微信内H5页面上发起的微信支付流程,它相对于扫码支付和APP要复杂一些,而且在实际开发过程中遇到的问题相对较多。
实现过程
公共号支付与APP支付类似,都需要调起统一下单接口获得prepay_id,根据prepay_id等参数调起微信发起支付流程,最后微信后台将支付结果异步回调给商户后台。
◆ 准备工作
公共号支付需要提前在微信公共平台进行业务配置,包括设置支付授权目录、设置JS接口安全域名以及设置授权回调页面域名。
支付授权目录:
位置:微信支付——>开发配置——>公共号支付
[1] 所有使用公众号支付方式发起支付请求的链接地址,都必须在支付授权目录之下;
[2] 正式支付授权目录最多设置3个,测试授权目录最多设置1个,且域名必须通过ICP备案;
[3] 头部要包含http或https,须细化到二级或三级目录,以左斜杠“/”结尾。
业务中发起支付的页面地址必须在授权目录下,否则调用下单接口时会提示“当前页面的URL未注册”。
JS接口安全域名:
位置:微信支付——>公共号设置——>功能设置——>JS接口安全域名
设置JS接口安全域名后,公众号开发者可在该域名下调用微信开放的JS接口。
注意事项:
[1] 可填写三个域名,要求是一级或一级以上域名(例:qq.com,或者 www.qq.com ),需使用字母、数字及“-”的组合,不支持IP地址及端口号;
[2] 填写的域名须通过ICP备案的验证;
[3] 一个自然月内最多可修改并保存三次。
授权回调页面域名:
位置:微信支付——>接口权限——>网页授权获取用户基本信息
用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。
注意事项:
[1] 回调页面域名需使用字母、数字及“-”的组合,不支持IP地址及端口号。填写的域名需与实际回调URL中的域名相同;
[2] 填写的域名须通过ICP备案的验证。
获取用户授权时redirect_uri对应的URL必须在此域名下,否则回调的地址会无法打开。
◆ 统一下单
公共号支付统一下单接口需要传递openid参数,微信公共平台对于openid的解释如下:
在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。
公众号可根据以下接口来获取用户的openid,如需获取用户的昵称、头像、性别、所在城市、语言和关注时间,则需要用户授权。
参考信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
开发者如果需要将同一个用户在不同公众号下的openid统一为一个id来记录,可以参考以下接口:
参考信息:http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html
即openid其实是每个用户在不同公共号内的唯一标示,获取openid需要先经过用户同意授权,获取code,再通过code获取openid。
◆ 获取用户授权
微信提供了两种授权方式:snsapi_base和snsapi_userinfo。
snsapi_base:不弹出授权页面,直接跳转,只能获取用户openid;
snsapi_userinfo:弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息。
想要获取code,需要构造如下地址:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
其中SCOPE参数为snsapi_base或snsapi_userinfo,REDIRECT_URI为获取用户授权后跳转的地址。
此处需要在微信公共平台配置授权回调页面域名:微信公共平台——>接口权限——>网页授权获取用户基本信息,点击“修改”,在弹出的页面中填写授权回调页面域名,REDIRECT_URI所在的域名要与填写的回调页面域名相同,否则在进行用户授权时会提示redirect_uri错误。
获取code后,再请求以下链接获取openid:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
其中APPID为公共号唯一标示,SECRET为公共号APPSECRET。
有关如何获取用户openid的详细内容可以参考微信官网文档网页授权获取用户基本信息(PS:以上这是最新版的公共号开发文档,这是旧版的文档,建议查看新版文档)
微信公共号支付SDK文件JsApiPay类提供了获取用户openid的方法GetOpenid:
/** * * 通过跳转获取用户的openid,跳转流程如下: * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code * * @return 用户的openid */ public function GetOpenid() { //通过code获得openid if (!isset($_GET['code'])){ //触发微信返回code码 $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']); $url = $this->__CreateOauthUrlForCode($baseUrl); Header("Location: $url"); exit(); } else { //使用code码,以获取openid $code = $_GET['code']; $openid = $this->getOpenidFromMp($code); return $openid; } }
其中GetOpenidFromMp方法在官方SDK中存在错误,CURLOPT_TIMEOUT写错了:
/** * * 通过code从工作平台获取openid机器access_token * @param string $code 微信跳转回来带上的code * @return openid */ public function GetOpenidFromMp($code) { $url = $this->__CreateOauthUrlForOpenid($code); //初始化curl $ch = curl_init(); //设置超时 //curl_setopt($ch, CURLOP_TIMEOUT, 30); //CURLOP_TIMEOUT写错了!!!!应该是CURLOPT_TIMEOUT curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE); curl_setopt($ch, CURLOPT_HEADER, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0" && WxPayConfig::CURL_PROXY_PORT != 0){ curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST); curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT); } //运行curl,结果以json形式返回 $res = curl_exec($ch); curl_close($ch); //取出openid $data = json_decode($res,true); $this->data = $data; $openid = $data['openid']; return $openid; }
获取openid后就可以调用统一下单接口获得prepay_id。
◆H5调起微信支付
网页端调起支付API提供了前端调起微信发起支付的方法,此处需要特别注意:公共号支付调起微信支付的接口参数与APP支付调起微信支付的参数大小写不同!
APP拉起微信支付接口参数列表:
APP拉起微信支付
公共号拉起微信支付接口参数列表:
公共号拉起微信支付
可以发现变量名一列,公共号接口有的字母是大写的,生成签名是需要区分参数大小写的,所以这块一定要注意,否则在调用接口时你就会一直收到签名错误的提示。
签名生成成功了,但是在调用调起微信支付接口时你可能会收到提示”调用支付JSAPI缺少参数:timeStamp,但是明明已经传递该参数了,怎么还会提示缺少参数呢?是由timeStamp的类型引起的,上图的参数列表中timeStamp类型为String字符串类型,很多时候我们程序里直接使用time()生成时间戳是整数,因此在将timeStamp传递给该接口时需要转换为字符串类型。
如果是服务器端调用拉起微信支付接口,需要将WxPay.Data.php文件中WxPayJsApiPay的SetTimeStamp方法作相应修改:
/** * 设置支付时间戳 * @param string $value **/ public function SetTimeStamp($value) { //modify by yuri 2016/7/5 解决JSAPI微信支付提示“缺少timeStamp参数”问题 $this->values['timeStamp'] = (string)$value; }
测试开发时配置好测试授权目录,将微信号添加到测试白名单,H5调用拉起微信支付接口即可完成支付。
常见问题
◆支付场景非法
错误场景:前端调用拉起微信支付接口getBrandWCPayRequest报错。
原因分析:与开发用的APPID有关。JSAPI支付用到的APPID是从微信公告平台申请,APP支付用到的APPID是从微信开放平台申请,如果将APP支付的APPID用于JSAPI支付,就会收到该错误提示。
解决方法:不同的支付场景要使用相对应的APPID,不允许跨场景使用APPID支付。
◆您没有JSAPI支付权限
错误场景:后台调用unifiedorder统一下单接口报错。
原因分析:同“支付场景非法”
解决方法:同“支付场景非法”
◆不允许跨号支付
错误场景:在公共号内调用unifiedorder统一下单接口报错。
原因分析:公共号A申请开通了JSAPI支付,得到该公共号微信支付开发对应的APPID、KEY、APPSECRET,如果在公共号B内调用公共号A开发的微信支付接口,就会提示“不允许跨号支付”,微信侧限制了微信支付开发的相关接口只能在对应的公共号内调用。
解决方法:在微信JSAPI开发中配置的公共号内调用微信支付相关接口。
◆缺少openid
错误场景:后台调用unifiedorder统一下单接口报错。
原因分析:公共号支付的下单接口需要传递openid参数,为用户在商户appid下的唯一标识。调用统一下单接口时需要先获取用户openid。
解决方法:调用unifiedorder统一下单接口前,确保已经获取到用户openid。
◆缺少timeStamp
错误场景:前端调用拉起微信支付接口getBrandWCPayRequest报错。
原因分析:getBrandWCPayRequest参数列表中写明了timeStamp参数类型为String,如果传递的timeStamp值为整型,会产生该错误。
解决方法:将timeStamp的值显示转换为字符串类型。
◆签名失败
错误场景:前端调用拉起微信支付接口getBrandWCPayRequest报错。
原因分析:公共号支付拉起微信支付接口与APP支付拉起微信支付接口中参数的大小写不同,生成签名会区分大小写。
解决方法:生成签名要严格按照参数列表中变量名称进行生成,尤其注意参数大小写。
◆body参数长度有误
错误场景:后台调用unifiedorder统一下单接口报错。
原因分析:body参数长度太长,unifiedorder统一下单接口中body参数长度为String(128),即占用128个字节,一个汉字占用3个字节,一个字母或数字占用1个字节,所以如果全部为汉字的话,最多42个。
解决方法:必要时使用字符串截取函数限制body的长度。
◆URL未注册
错误场景:前端调用拉起微信支付接口getBrandWCPayRequest报错。
原因分析:授权目录配置问题,当前支付页与授权目录设置不匹配。
解决方法:详细解决方案可参考另一篇博文<漫谈微信支付-授权目录设置>
◆下单账号与支付账号不一致
错误场景:前端调用拉起微信支付接口getBrandWCPayRequest报错。前端调用拉起微信支付接口getBrandWCPayRequest,拉起微信账号A,但是在微信A中没有完成支付(例如:执行了取消操作),此时如果换另一个微信账号B,再对微信端同一个订单进行支付,拉起微信,此时会报该错
原因分析:微信端同一个订单号,限制了下单与支付账号必须使用同一个微信。在调用下单接口时传递了openid,在调用拉起微信支付接口时,如果当前用户的微信与下单微信不同,就会报该错。
解决方法:下单账号与支付账号必须使用同一个微信。
◆商户订单号重复
错误场景:后台调用unifiedorder统一下单接口报错。
原因分析:同一个订单,如果修改了商品描述或者商品价格,再用同一个订单号去调用统一下单接口就会报该错。
解决方法:重新生成订单。
◆订单已过期
错误场景:后台调用unifiedorder统一下单接口报错。
原因分析:下单有时效性。
解决方法:重新下单。
◆调用微信支付相关接口异常缓慢
微信支付SDK中所有最终请求微信服务器的出口均是经过WxPay.Api.php中的postXmlCurl接口:
/** * 以post方式提交xml到对应的接口url * @param string $xml 需要post的xml数据 * @param string $url url * @param bool $useCert 是否需要证书,默认不需要 * @param int $second url执行超时时间,默认30s * @throws WxPayException */ private static function postXmlCurl($xml, $url, $useCert = false, $second = 30) { $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); //如果有配置代理这里就设置代理 if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0" && WxPayConfig::CURL_PROXY_PORT != 0){ curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST); curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT); } curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //modify by yuri 2016/7/5 设置curl默认访问IPv4网络 curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); if($useCert == true){ //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH); } //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); //返回结果 if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); curl_close($ch); throw new WxPayException("curl出错,错误码:$error"); } }
因为CURL默认先解析IPV6,再解析IPV4,所以如果你的支付场景只有IPV4网络,则最好设置CURL默认访问IPV4,这样可以省去解析IPV6的时间,接口调用速度明显加快。
一、推荐使用迅雷或快车等多线程下载软件下载本站资源。
二、未登录会员无法下载,登录后可获得更多便利功能,若未注册,请先注册。
三、如果服务器暂不能下载请稍后重试!总是不能下载,请点我报错 ,谢谢合作!
四、本站大部分资源是网上搜集或私下交流学习之用,任何涉及商业盈利目的均不得使用,否则产生的一切后果将由您自己承担!本站将不对任何资源负法律责任.如果您发现本站有部分资源侵害了您的权益,请速与我们联系,我们将尽快处理.
五、如有其他问题,请加网站设计交流群(点击这里查看交流群 )进行交流。
六、如需转载本站资源,请注明转载来自并附带链接
七、本站部分资源为加密压缩文件,统一解压密码为:www.aizhanzhe.com
- 1CSS控制文字在Div最底部显示
- 2Thinkphp5如何配置IP+端口访问项目模块
- 3elementUI el-dialog弹框居中
- 4教你如何搭建及优化站点
- 5国内互联网视频行业运营分析
- 6service mysql start出错,mysql不能启动,解决mysql: unrecognized service错误
- 7CSS实现悬浮顶部的Div工具栏
- 8记一次Thinkphp5.1框架mysql数据库崩溃(SQLSTATE [08004] Too many connections)
- 9连接SQL Server数据库提示:Login failed for user 'sa'错误的解决方案
- 10Thinkphp3.2在centos7上设置计划任务的方法