设为首页收藏本站

创业同盟军

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 6863|回复: 3
打印 上一主题 下一主题

ThinkPHP3.2.3封装微信支付V3接口

[复制链接]

25

主题

28

帖子

2491

积分

管理员

军长

Rank: 9Rank: 9Rank: 9

积分
2491
跳转到指定楼层
楼主
发表于 2015-10-11 16:48:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
前言

微信支付现在分为v2版和v3版,2014年9月10号之前申请的为v2版,之后申请的为v3版。V3版的微信支付没有paySignKey参数。本教程是基于V3版本,下面开始教程
首先是需要有已开通微信支付接口的认证服务号,这个相信大家都知道,开通之后微信会发一封邮件到你的邮箱,邮件的内容就是开发需要用到的一些接口信息了(包含账号密码之类的东西)。
【此类配置说明引用于:http://www.gouguoyin.cn/php/55.html
SDK下载地址https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmpl&lang=zh_CN
或点击直接下载https://res.wx.qq.com/paymchres/zh_CN/htmledition/download/bussiness-course3/wxm-payment-biz-api218f8e.zip

一、参数配置

1、 设置JS接口安全域名,点击设置 -> 公众号设置 -> 功能设置 -> JS接口安全域名 ->设置,弹出JS安全域名输入框,建议域名写上带www和和不带www的,如下图


2、设置授权回调页面域名,点击开发者中心 -> 接口权限表 -> 网页服务 -> 获取用户基本信息->修改(注意只有之前从未设置过时只有将鼠标放在获取用户基本信息上才会出现修改按钮),弹出授权回调页面域名:输入框,输入你的网站地址即可,如下图



3、设置微信安全支付目录,点击微信支付 -> 开发配置 -> 支付配置->支付授权目录。正式目录和测试目录都可以写上,测试目录需要把个人微信号先添加到白名单才能使用。安全支付目录正确格式是 http://域名/分组/控制器/(仅对ThinkPHP),如下图

4、进入公众平台,点击开发者中心->配置项->开发者ID,记下APPID和APPSECRET,如下图


进入商家平台,点击账户设置->账户信息->基本账户信息,记录下微信支付商户号MCHID,如下图

点击账户信息->API安全->API密匙->设置密匙,输入新的密匙KEY(注意一定要重置下密匙),并记录下密匙;




二、引入微信支付SDK包

1、用TP的小伙伴们可以用下载的包,也可以自行到官网下载SDK包放到TP的扩展目录里,具体目录:ThinkPHP\Library\Vendor\;

2、打开包里的WxPayPubConfig.php文件,填写上你的微信支付的接口信息就好啦,(注意:微信支付是不需要证书的,只有退款和红包才会使用证书)如下图【最好在配置文件中也写一份吧】


三、流程实现

参照链接http://www.cnblogs.com/txw1958/p/wxpayv3-jsapi.html
1. OAuth2.0授权
JSAPI 支付前需要调用 登录授权接口获取到用户的 Openid 。所以需要做一次授权,这次授权是不弹出确认框的。
其实质就是在用户访问
  1. http://www.fangbei.org/wxpay/js_api_call.php
复制代码
时跳转到
  1. https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx8888888888888888&redirect_uri=http://www.fangbei.org/wxpay/js_api_call.php&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
复制代码
js_api_call.php文件初始化方法
  1. //在类初始化方法中,引入相关类库
  2.     public function _initialize() {
  3.         // 如果签名出错,可能的原因就是所有的参数列表中不能有乱码,所以把这个页面设置下字符集即可!
  4.         header("Content-type:text/html;charset=utf-8");
  5.         // 引入重要的类库
  6.         vendor('WxPayPubHelper.WxPayPubHelper');
  7.     }
复制代码
以此来获得code参数,并根据code来获得授权access_token及openid,其实现的详细流程可参考 微信公众平台开发(71)OAuth2.0网页授权
在微信支付的Demo中,其代码为
  1. //使用jsapi接口
  2.         $jsApi = new \JsApi_pub();
  3.         
  4.         //=========  步骤1:网页授权获取用户openid  ============
  5.         // 首先应该判断用户的openid是否已经存在,在数据处理的时候,将openid进行保存
  6.         $openid  = get_user_info($user_id,'wx_openid');
  7.         if(!$openid){
  8.             //通过code获得openid
  9.             if (!isset($_GET['code']))
  10.             {
  11.                 //触发微信返回code码【必须将order_id的参数添加上去,不然,下次返回的时候,就没有order_id了】
  12.                 $url = $jsApi->createOauthUrlForCode($WxConfig['JS_API_CALL_URL'].'?order_id='.$order_id);
  13.                 Header("Location: $url");
  14.             }else
  15.             {
  16.                 //获取code码,以获取openid
  17.                 $code = $_GET['code'];
  18.                 $jsApi->setCode($code);
  19.                 $openid = $jsApi->getOpenId();
  20.             }
  21.         }
复制代码
这一步的最终结果就是获得了当前用户的openid
  1. ou9dHt0L8qkkI1foP-kj5x1mDWsM
复制代码
2. 统一支付
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口
统一支付中有些参数不需要填写,但是有些则需要填写
  1. //=========  步骤2:使用统一支付接口,获取prepay_id  ============
  2.         //使用统一支付接口
  3.         $unifiedOrder = new \UnifiedOrder_pub();
  4.         
  5.         //设置统一支付接口参数
  6.         //设置必填参数
  7.         //appid已填,商户无需重复填写
  8.         //mch_id已填,商户无需重复填写
  9.         //noncestr已填,商户无需重复填写
  10.         //spbill_create_ip已填,商户无需重复填写
  11.         //sign已填,商户无需重复填写
  12.         $unifiedOrder->setParameter("openid",$openid);//商品描述
  13.         $unifiedOrder->setParameter("body",$title);//商品描述
  14.         //自定义订单号,此处仅作举例
  15.         $unifiedOrder->setParameter("out_trade_no",$trade_no);//商户订单号
  16.         $unifiedOrder->setParameter("total_fee",$price);//总金额
  17.         $unifiedOrder->setParameter("notify_url",$WxConfig['NOTIFY_URL']);//通知地址
  18.         $unifiedOrder->setParameter("trade_type","JSAPI");//交易类型
  19.         //非必填参数,商户可根据实际情况选填
  20.         //$unifiedOrder->setParameter("sub_mch_id","XXXX");//子商户号
  21.         //$unifiedOrder->setParameter("device_info","XXXX");//设备号
  22.         //$unifiedOrder->setParameter("attach","XXXX");//附加数据
  23.         //$unifiedOrder->setParameter("time_start","XXXX");//交易起始时间
  24.         //$unifiedOrder->setParameter("time_expire","XXXX");//交易结束时间
  25.         //$unifiedOrder->setParameter("goods_tag","XXXX");//商品标记
  26.         //$unifiedOrder->setParameter("openid","XXXX");//用户标识
  27.         //$unifiedOrder->setParameter("product_id","XXXX");//商品ID
  28.         
  29.         // $prepay_id 获取不到的可能原因是,该订单已经支付,或者重复提交等原因,可以到该方法中打印进行查看
  30.         $prepay_id = $unifiedOrder->getPrepayId();
复制代码
3、JS API支付
前面的准备工作做好了以后,JS API根据prepay_id生成jsapi支付参数
代码如下
  1. //=========步骤3:使用jsapi调起支付============
  2.         $jsApi->setPrepayId($prepay_id);
  3.         
  4.         $jsApiParameters = $jsApi->getParameters();
  5.         
  6.         // 因为该方法可能会进行跳转,所以,不支持ajax获取信息,会产生跨域的问题,只能进行分配,但是,跳转
  7.         // 的页面不能为空白,可以尝试显示订单信息
  8.         $this->assign('order_info',array('order_no'=>$trade_no,'price'=>$order_info['price'],'title'=>$title));
  9.         $this->assign('jsApiParameters',$jsApiParameters);
  10.         $this->display('index');
复制代码

生成的json数据如下
  1. {
  2.     "appId": "wx8888888888888888",
  3.     "timeStamp": "1414411784",
  4.     "nonceStr": "gbwr71b5no6q6ne18c8up1u7l7he2y75",
  5.     "package": "prepay_id=wx201410272009395522657a690389285100",
  6.     "signType": "MD5",
  7.     "paySign": "9C6747193720F851EB876299D59F6C7D"
  8. }
复制代码
在微信浏览器中调试起js接口,代码如下
  1. <html>
  2. <head>
  3.     <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
  4.     <title>微信安全支付</title>
  5.     <script type="text/javascript">
  6.         //调用微信JS api 支付
  7.         function jsApiCall()
  8.         {
  9.             WeixinJSBridge.invoke('getBrandWCPayRequest', {$jsApiParameters}, function(res){
  10.                 if(res.err_msg == 'get_brand_wcpay_request:cancel') {
  11.                     
  12.                     // alert("您已取消了此次支付");
  13.                     window.location.href = ****
  14.                     return;
  15.                 } else if(res.err_msg == 'get_brand_wcpay_request:fail') {
  16.                     
  17.                     // alert("支付失败,请重新尝试");
  18.                     window.location.href = ****
  19.                     return;
  20.                 } else if(res.err_msg == 'get_brand_wcpay_request:ok') {
  21.                     
  22.                     // alert("支付成功!");
  23.                     window.location.href = ****;
  24.                 } else {
  25.                     // alert("未知错误"+res.err_msg);
  26.                     window.location.href = ****
  27.                     return;
  28.                 }
  29.             });
  30.         }

  31.         function callpay()
  32.         {
  33.             if (typeof WeixinJSBridge == "undefined"){
  34.                 if( document.addEventListener ){
  35.                     document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
  36.                 }else if (document.attachEvent){
  37.                     document.attachEvent('WeixinJSBridgeReady', jsApiCall);
  38.                     document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
  39.                 }
  40.             }else{
  41.                 jsApiCall();
  42.             }
  43.         }
  44.     </script>
  45. </head>
  46. <body>
  47.     </br></br></br></br>
  48.     <div align="center">
  49.         <button style="width:210px; height:30px; background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;" type="button" onclick="callpay()" >贡献一下</button>
  50.     </div>
  51. </body>
  52. </html>
复制代码
4、支付通知
支付成功后,通知接口中也将收到支付成功的xml通知
  1. <xml>
  2.   <appid><![CDATA[wx8888888888888888]]></appid>  
  3.   <bank_type><![CDATA[CFT]]></bank_type>  
  4.   <fee_type><![CDATA[CNY]]></fee_type>  
  5.   <is_subscribe><![CDATA[Y]]></is_subscribe>  
  6.   <mch_id><![CDATA[10012345]]></mch_id>  
  7.   <nonce_str><![CDATA[60uf9sh6nmppr9azveb2bn7arhy79izk]]></nonce_str>  
  8.   <openid><![CDATA[ou9dHt0L8qFLI1foP-kj5x1mDWsM]]></openid>  
  9.   <out_trade_no><![CDATA[wx88888888888888881414411779]]></out_trade_no>  
  10.   <result_code><![CDATA[SUCCESS]]></result_code>  
  11.   <return_code><![CDATA[SUCCESS]]></return_code>  
  12.   <sign><![CDATA[0C1D7F2534F1473247550A5A138F0CEB]]></sign>  
  13.   <sub_mch_id><![CDATA[10012345]]></sub_mch_id>  
  14.   <time_end><![CDATA[20141027200958]]></time_end>  
  15.   <total_fee>1</total_fee>  
  16.   <trade_type><![CDATA[JSAPI]]></trade_type>  
  17.   <transaction_id><![CDATA[1002750185201410270005514026]]></transaction_id>
  18. </xml>
复制代码
数据接收代码:
  1. /**
  2.      * 获取返回的数据
  3.      */
  4.     public function notify()
  5.     {
  6.         //使用通用通知接口
  7.         $notify = new \Notify_pub();
  8.    
  9.         //存储微信的回调
  10.         $xml    = $GLOBALS['HTTP_RAW_POST_DATA'];
  11.         $notify->saveData($xml);
  12.    
  13.         //验证签名,并回应微信。
  14.         //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,
  15.         //微信会通过一定的策略(如30分钟共8次)定期重新发起通知,
  16.         //尽可能提高通知的成功率,但微信不保证通知最终能成功。
  17.         
  18.         if($notify->checkSign() == FALSE){
  19.             $notify->setReturnParameter("return_code","FAIL");//返回状态码
  20.             $notify->setReturnParameter("return_msg","签名失败");//返回信息
  21.         }else{
  22.             $notify->setReturnParameter("return_code","SUCCESS");//设置返回码
  23.         }
  24.         
  25.         // 返回给微信端的处理结果,下面就不用再返回了,仅作数据处理就可以了
  26.         // 返回示例:<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>
  27.         $returnXml = $notify->returnXml();
  28.         echo $returnXml;
  29.         
  30.         // 获取返回结果
  31.         $result = $notify->getData();
  32.                
  33.         //==商户根据实际情况设置相应的处理流程,此处仅作举例=======
  34.                 // demo文件中是以日志的形式写到服务器上面,我选择的是保存到数据库里面,所以,这段代码我先注释掉,你可以根据自己的喜好进行选择
  35.         //以log文件形式记录回调信息
  36. //                $log_ = new Log_();
  37. //                $log_name="./notify_url.log";//log文件路径
  38. //                $log_->log_result($log_name,"【接收到的notify通知】:\n".$xml."\n");

  39.                 // 验证签名
  40.                 if($notify->checkSign() == TRUE)
  41.                 {
  42. //                        if ($notify->data["return_code"] == "FAIL") {
  43. //                                //此处应该更新一下订单状态,商户自行增删操作
  44. //                                $log_->log_result($log_name,"【通信出错】:\n".$xml."\n");
  45. //                        }
  46. //                        elseif($notify->data["result_code"] == "FAIL"){
  47. //                                //此处应该更新一下订单状态,商户自行增删操作
  48. //                                $log_->log_result($log_name,"【业务出错】:\n".$xml."\n");
  49. //                        }
  50. //                        else{
  51. //                                //此处应该更新一下订单状态,商户自行增删操作
  52. //                                $log_->log_result($log_name,"【支付成功】:\n".$xml."\n");
  53. //                        }
  54.                
  55.             // 验证通过,将时间进行转化,原本的时间格式为:20151009233115
  56.             $add_time  = date('Y-m-d H:i:s',strtotime($result['time_end']));
  57.             // 是否交易成功
  58.             $parameter = array(
  59.                     "transaction_id" => $result['transaction_id'],  // 商户订单编号;
  60.                     "trade_no"       => $result['out_trade_no'],    // 支付宝交易号;
  61.                     "total_fee"      => $result['total_fee']/100,   // 交易金额;
  62.                     "trade_status"   => $result['out_trade_no'],    // 交易状态
  63.                     "trade_type"     => $result['trade_type'],      // 交易类型
  64.                     "openid"         => $result['openid'],          // 用户的openId。
  65.                     "add_time"       => $add_time,                  // 通知的发送时间。
  66.                     'post_data'      => serialize($result),         // 返回过来的数据
  67.                     'ip'             => get_client_ip()             // 用户登陆的IP地址
  68.             );
  69.             // 具体的数据处理流程我就不多说了,根据自己的喜好吧
  70.             $res   = orderdeal_wx($parameter);
  71.         }
  72.     }
复制代码

回复

使用道具 举报

0

主题

1

帖子

26

积分

新手上路

Rank: 1

积分
26
沙发
发表于 2015-10-17 08:39:29 | 只看该作者
不看不知道,看了才知道,好帖












回复 支持 反对

使用道具 举报

0

主题

16

帖子

59

积分

注册会员

Rank: 2

积分
59
板凳
发表于 2015-10-21 16:06:32 | 只看该作者
希望大家踊跃发言,我顶先
回复 支持 反对

使用道具 举报

0

主题

16

帖子

76

积分

注册会员

Rank: 2

积分
76
地板
发表于 2015-10-27 06:45:10 | 只看该作者
不错的贴,太喜欢了,大家都顶啊












回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|创业同盟军 ( 京ICP备15035328号-2  

GMT+8, 2024-9-8 10:11 , Processed in 0.073910 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表