thinkphp整合微信支付,绝对可行

来源:互联网 发布:在线网络测速 编辑:程序博客网 时间:2024/06/05 17:15
http://www.inotcare.com/art/xinde/php/18.html
http://www.thinkphp.cn/code/1321.html
http://www.web-fish.com/program/php/856.html

thinkphp整合微信支付,绝对可行

  • _____卜在乎、卜解释
  • 2015-05-13
  • php
  • 围观4131次

上一篇文章是介绍了 ecshop 整合微信支付, 首先 ecshop 并不熟悉, 还有一些问题虽然解决了, 但是并不明白为什么, 这回用 thinkphp 整合 微信JSAPI 支付, 来特别说明一下(这个整合对微信JSAPI支付的原DEMO改动并不大)


这里 thinkphp使用的是3.2的版本, 框架系统目录改名为 _Core, 原先为 ThinkPHP


1, 首先, 去 https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmpl&lang=zh_CN 这上面下载  

下载 JS API 接口文档


将   这些文件 拷贝至 _Core/Library/Vendor/Wxpay/jsapi  目录下(没有 Wxpay 和 jsapi 目录的话请自行创建), 将 WxPayPubHelper 目录下的文件拷贝至 _Core/Library/Vendor/Wxpay/jsapi 目录下 , 

WxPay.pub.config.php 改名为 WxPaypubconfig.php 


2, 应用入口文件名改为 Hz , 并生成一个新的模块, 名为 Pay, 在 Pay/Controller/ 目录下新建一个控制器 WxjsapiController.class.php 文件


WxjsapiController.class.php 文件的代码如下,


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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<?php
namespacePay\Controller;
useThink\Controller;
classWxjsapiController extendsController {
 
    private$wxpayConfig;
    private$wxpay;
 
    publicfunction _initialize(){
        header("Content-type: text/html; charset=utf-8");
        vendor('Wxpay.jsapi.WxPaypubconfig');
        vendor('Wxpay.jsapi.WxPayPubHelper');
        vendor('Wxpay.jsapi.demo.log_');
        vendor('Wxpay.jsapi.SDKRuntimeException');
 
        $this->wxpayConfig = C('WXJSAPI_CONFIG');
 
        $paycnfT= M('pay_cnf');
        $this->wxpay = $paycnfT->where("P_C_Paytype = 'wxjsapi'")->find();
 
        $this->wxpayConfig['appid'] = $this->wxpay['P_C_Appid'];     // 微信公众号身份的唯一标识
        $this->wxpayConfig['appsecret'] = $this->wxpay['P_C_Secret'];// JSAPI接口中获取openid
        $this->wxpayConfig['mchid'] = $this->wxpay['P_C_Pid'];           // 受理商ID
        $this->wxpayConfig['key'] = $this->wxpay['P_C_Key'];         // 商户支付密钥Key
        $this->wxpayConfig['js_api_call_url'] = $this->get_url();
        $this->wxpayConfig['notifyurl'] = $this->wxpay['P_C_NotifyUrl'];
        $this->wxpayConfig['returnurl'] = $this->wxpay['P_C_ReturnUrl'];
 
        // 初始化WxPayConf_pub
        $wxpaypubconfig= new\WxPayConf_pub($this->wxpayConfig);
    }
 
    /**
     * 获取当前页面完整URL地址
     */
    privatefunction get_url() {
        $sys_protocal= isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443'? 'https://': 'http://';
        $php_self= $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
        $path_info= isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
        $relate_url= isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $php_self.(isset($_SERVER['QUERY_STRING']) ? '?'. $_SERVER['QUERY_STRING'] : $path_info);
        return$sys_protocal . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . $relate_url;
    }
 
    publicfunction index() {
    }
 
    /**
     *  获取openid
     */
    privatefunction get_openid() {
         $openid= $_COOKIE['apiopenid'];
 
        if(empty($openid)) {
            // 使用jsapi接口
            $jsApi= new\JsApi_pub();
 
            // 通过code获得openid
            if(!isset($_GET['code'])) {
                // 触发微信返回code码
                $url= $jsApi->createOauthUrlForCode(\WxPayConf_pub::$JS_API_CALL_URL);
                Header("Location: " . $url);
            }else{
                // 获取code码,以获取openid
                $code= $_GET['code'];
                $jsApi->setCode($code);
                $openid= $jsApi->getOpenId();
                setcookie('apiopenid',$openid, time() + 86400);
            }
        }
        return$openid;
    }
 
    /**
     *  支付
     */
    publicfunction pay() {
        if(isset($_SESSION['orderinfo']) && !empty($_SESSION['orderinfo']['orderid']) && !empty($_SESSION['orderinfo']['payprice'])) {
            $orderid= $_SESSION['orderinfo']['orderid'];
            $payprice= $_SESSION['orderinfo']['payprice'];
        }
 
        if(empty($orderid) || empty($payprice)) {
            die('订单参数不完整!');
        }
 
        // 1,获取openid
        $openid= $this->get_openid();
 
        // 2,使用统一支付接口
        $unifiedOrder= new\UnifiedOrder_pub();
 
        // 设置统一支付接口参数
        // 设置必填参数
        // appid已填,商户无需重复填写
        // mch_id已填,商户无需重复填写
        // noncestr已填,商户无需重复填写
        // spbill_create_ip已填,商户无需重复填写
        // sign已填,商户无需重复填写
        $unifiedOrder->setParameter("openid",$openid);
        $unifiedOrder->setParameter("body",$orderid);                          // 商品描述
        // 自定义订单号,此处仅作举例
        //$timeStamp = time();
        //$out_trade_no = \WxPayConf_pub::$APPID . $timeStamp;
        $out_trade_no= $orderid;
        //$out_trade_no = time();
        $unifiedOrder->setParameter("out_trade_no",$out_trade_no);             // 商户订单号
        $unifiedOrder->setParameter("total_fee",$payprice* 100);               // 总金额
        $unifiedOrder->setParameter("notify_url", \WxPayConf_pub::$NOTIFY_URL); // 通知地址
        $unifiedOrder->setParameter("trade_type","JSAPI");                     // 交易类型
        // 非必填参数,商户可根据实际情况选填
        //$unifiedOrder->setParameter("sub_mch_id", "XXXX");                 // 子商户号
        //$unifiedOrder->setParameter("device_info", "XXXX");                    // 设备号
        //$unifiedOrder->setParameter("attach", "XXXX");                     // 附加数据
        //$unifiedOrder->setParameter("time_start", "XXXX");                 // 交易起始时间
        //$unifiedOrder->setParameter("time_expire", "XXXX");                    // 交易结束时间
        //$unifiedOrder->setParameter("goods_tag", "XXXX");                      // 商品标记
        //$unifiedOrder->setParameter("openid", "XXXX");                     // 用户标识
        //$unifiedOrder->setParameter("product_id", "XXXX");                 // 商品ID
 
        $prepay_id= $unifiedOrder->getPrepayId();
 
        // 3,使用jsapi调起支付
        $jsApi= new\JsApi_pub();
        $jsApi->setPrepayId($prepay_id);
        $jsApiParameters= $jsApi->getParameters();
        // echo $jsApiParameters;
        $returnurl= \WxPayConf_pub::$RETURN_URL;
        $path=  dirname(__FILE__);
 
        $button= <<<EOT
        <html>
        <head>
        <meta http-equiv="content-type"content="text/html;charset=utf-8"/>
        <title>微信安全支付</title>
        </head>
        <body>
        <script type="text/javascript">
        // 调用微信JS api 支付
        functionjsApiCall(){
            WeixinJSBridge.invoke('getBrandWCPayRequest',{$jsApiParameters},function(res){
                    //WeixinJSBridge.log(res.err_msg);
                    alert(JSON.stringify({$jsApiParameters}));
                    alert(res.err_code+'调试信息:'+res.err_desc+res.err_msg);
                    if(res.err_msg.indexOf('ok')>0){
                        window.location.href='{$returnurl}';
                    }
                });
        }
 
        functioncallpay()
        {
            if(typeof WeixinJSBridge == "undefined"){
                if( document.addEventListener ){
                    document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
                }elseif (document.attachEvent){
                    document.attachEvent('WeixinJSBridgeReady', jsApiCall);
                    document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
                }
            }else{
                jsApiCall();
            }
        }
    </script>
    <button style="width:100%; height:40px; background:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;"type="button"onclick="callpay()">立即使用微信支付</button>
    </body>
    </html>
EOT;
        echo$button;
    }
 
    /**
     *  服务器异步通知页面路径
     */
    publicfunction Paynotify() {
        /**
         * 通用通知接口demo
         * ====================================================
         * 支付完成后,微信会把相关支付和用户信息发送到商户设定的通知URL,
         * 商户接收回调信息后,根据需要设定相应的处理流程。
         *
         * 这里举例使用log文件形式记录回调信息。
         */
        //include_once("./log_.php");
        //include_once("../WxPayPubHelper/WxPayPubHelper.php");
 
        // 使用通用通知接口
        $notify= new\Notify_pub();
 
        // 存储微信的回调
        $xml= $GLOBALS['HTTP_RAW_POST_DATA'];
        $notify->saveData($xml);
 
        // 验证签名,并回应微信。
        // 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,
        // 微信会通过一定的策略(如30分钟共8次)定期重新发起通知
        // 尽可能提高通知的成功率,但微信不保证通知最终能成功。
        if($notify->checkSign() == FALSE){
            $notify->setReturnParameter("return_code","FAIL");     // 返回状态码
            $notify->setReturnParameter("return_msg","签名失败");  // 返回信息
        }else{
            $notify->setReturnParameter("return_code","SUCCESS");  // 设置返回码
        }
        $returnXml= $notify->returnXml();
        echo$returnXml;
 
        //==商户根据实际情况设置相应的处理流程,此处仅作举例=======
 
        // 以log文件形式记录回调信息
        $log_= new\Log_();
        $log_name= THINK_PATH . "Library/Vendor/Wxpay/jsapi/demo/notify_url.log"// log文件路径
        $log_->log_result($log_name,"【接收到的notify通知】:\n". $xml. "\n");
 
        if($notify->checkSign() == TRUE) {
            if($notify->data["return_code"] == "FAIL") {
                // 此处应该更新一下订单状态,商户自行增删操作
                $log_->log_result($log_name,"【通信出错】:\n". $xml. "\n");
            }elseif($notify->data["result_code"] == "FAIL"){
                // 此处应该更新一下订单状态,商户自行增删操作
                $log_->log_result($log_name,"【业务出错】:\n". $xml. "\n");
            }else{
                // 此处应该更新一下订单状态,商户自行增删操作
                $order= $notify->getData();
                $orderid= $order["out_trade_no"];
                $log_->log_result($log_name,"【支付成功】:\n". $orderid. "\n");
 
                $shop= A('Wap/Shop');
                $shop->EndPay($orderid,'wxjsapi');
                $url= U('/Wap/Shop/orderList/type/2');
                header('Location:'. $url);
            }
 
            //商户自行增加处理流程,
            //例如:更新订单状态
            //例如:数据库操作
            //例如:推送支付完成信息
        }
    }
}
隐藏代码(双击代码内容快速复制)


Hz/Common/conf/config.php 配置如下, 只列出 C('WXJSAPI_CONFIG') 的配置


1
2
3
4
5
6
7
'MODULE_ALLOW_LIST'=> array('Pay'),
// Wxjsapi
        'WXJSAPI_CONFIG'=> array(
                'SSLCERT_PATH'=> __ROOT__ . THINK_PATH . 'Library/Vendor/Wxpay/jsapi/cacert/apiclient_cert.pem',       // 证书路径,注意应该填写绝对路径
                'SSLKEY_PATH'=> __ROOT__ . THINK_PATH . 'Library/Vendor/Wxpay/jsapi/cacert/apiclient_key.pem',         // 证书路径,注意应该填写绝对路径
                'CURL_TIMEOUT'=> 30
        ),
隐藏代码(双击代码内容快速复制)



WxPaypubconfig.php 的代码如下


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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php
/**
*   配置账号信息
*/
 
classWxPayConf_pub
{
    staticpublic $APPID;
    staticpublic $APPSECRET;
    staticpublic $MCHID;
    staticpublic $KEY;
    staticpublic $JS_API_CALL_URL;
    staticpublic $CURL_TIMEOUT;
    staticpublic $SSLCERT_PATH;
    staticpublic $SSLKEY_PATH;
    staticpublic $NOTIFY_URL;
    staticpublic $RETURN_URL;
 
    publicfunction __construct($wxpayconfig= array()) {
        self::$APPID= $wxpayconfig['appid'];
        self::$APPSECRET= $wxpayconfig['appsecret'];
        self::$MCHID= $wxpayconfig['mchid'];
        self::$KEY= $wxpayconfig['key'];
        self::$JS_API_CALL_URL= $wxpayconfig['js_api_call_url'];
        self::$CURL_TIMEOUT= $wxpayconfig['CURL_TIMEOUT'];
        self::$SSLCERT_PATH= $wxpayconfig['SSLCERT_PATH'];
        self::$SSLKEY_PATH= $wxpayconfig['SSLKEY_PATH'];
        self::$NOTIFY_URL= $wxpayconfig['notifyurl'];
        self::$RETURN_URL= $wxpayconfig['returnurl'];
    }
    /*
    //=======【基本信息设置】=====================================
    //微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
    const APPID = 'wx8888888888888888';
    //受理商ID,身份标识
    const MCHID = '18888887';
    //商户支付密钥Key。审核通过后,在微信发送的邮件中查看
    const KEY = '48888888888888888888888888888886';
    //JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看
    const APPSECRET = '48888888888888888888888888888887';
 
    //=======【JSAPI路径设置】===================================
    //获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
    const JS_API_CALL_URL = 'http://www.xxxxxx.com/demo/js_api_call.php';
 
    //=======【证书路径设置】=====================================
    //证书路径,注意应该填写绝对路径
    const SSLCERT_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_cert.pem';
    const SSLKEY_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_key.pem';
 
    //=======【异步通知url设置】===================================
    //异步通知url,商户根据实际开发过程设定
    const NOTIFY_URL = 'http://www.xxxxxx.com/demo/notify_url.php';
 
    //=======【curl超时设置】===================================
    //本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒
    const CURL_TIMEOUT = 30; */
}
隐藏代码(双击代码内容快速复制)



修改 WxPayPubHelper.php 文件,

(1) 155 行 curl_setopt($ch, CURLOP_TIMEOUT, $second);   CURLOP_TIMEOUT 修改为 CURLOPT_TIMEOUT

(2) 821 行 curl_setopt($ch, CURLOP_TIMEOUT, $this->curl_timeout);  CURLOP_TIMEOUT 修改为 CURLOPT_TIMEOUT

(3) 175 行 if($data) { curl_close($ch); return $data; }  注释 curl_close($ch); 这行

(4) 将所有的 WxPayConf_pub:: 替换为 WxPayConf_pub::$

到此 WxPayPubHelper.php 就修改完了


WxjsapiController.class.php  文件方法说明 :

_initialize() 方法是整合微信支付的数据库配置与config.php的微信支付配置, 并初始化 WxPaypubconfig.php 

pay() 方法, 发起支付的方法,  $openid = $this->get_openid();  可以先打印出 openid, 看是否正常获取

Paynotify() 回调方法, 支付成功后的回调


现在说一下 微信平台的配置

1, 修改 开发者中心->网页账号->修改为 当前域名

2, 修改 微信支付->开发配置->添加测试授权目录->http://当前域名/Pay/Wxjsapi/ , 并添加测试用的微信号


注意, thinkphp 配置 'URL_MODEL' => 2,  能直接以 http://域名/控制器名/方法名  这种形式来访问, 不需要 index.php


total_fee 参数 最小为 1, 微信支付是以分 作为单位的

现在说明一下 微信支付中的错误:

1, appid and openid not match  这个其实不算是微信支付的错误, 主要是因为修改了微信支付的账号, 而原先的 openid 是采用 cookie 保存的, 先重新获取 openid, 并写入 cookie,  再重新开启 $openid = $_COOKIE['apiopenid']  , 这个问题一般不会碰到吧

2, 获取 openid 时的 redirect_uri 错误, 网页账号->修改域名, 带www与不带www 试下, 这个问题也没去详细了解, 现在没碰到这问题了

3, 点击  立即使用微信支付  没反应, 那么请查看参数是否完整, alert(JSON.stringify({$jsApiParameters}));   , 如果是 prepay_id 如果是空的话, 打开WxPayPubHelper.php  文件, 找到 getPrepayId() 方法, 打印 print_r($this->result);exit; 看看是什么错误

4, fail_no permission to execute 授权目录错误, 修改 微信支付->开发配置->支付测试里的授权目录

5, 最艹蛋的问题, fail_invalid_appid 这个弄了好久, 始终不行, 解决方法如下:

比如用户下订单后, 选择支付方式, 跳转到 Pay/Wxjsapi/pay 方法下, 莪这里是这么写的,

 $this->redirect(U('Pay/Wxjsapi/pay',array('payprice' => $order['S_O_Payprice'], 'orderid' => $order['S_O_OrderId']))); 比如 payprice 为 1, orderid 为 123456

传递了 订单号与支付金额, 生成的URL地址为  http://域名/Pay/Wxjsapi/pay/price/1/orderid/123456 , 但是这种跳转至 Pay/Wxjsapi/pay 方法下, 在发起支付的时候, 就会报这个错, 如果是这个地址, 那么微信支付的授权目录就得修改为 http://域名/Pay/Wxjsapi/pay/price, 到这里写不完整, 因为不知道对应的参数是什么, 所以, 在支付的时候传递 订单号 之类的参数, 莪使用的是存入 $_SESSION, 

$_SESSION['orderinfo']['orderid'] = $order['S_O_OrderId'];

$_SESSION['orderinfo']['payprice'] = $order['S_O_Payprice'];

$this->redirect(U('Pay/Wxjsapi/pay'), array());

然后在 pay() 方法 里获取 session, 这样的话, 用户支付时跳转的页面地址就变成 http://域名/Pay/Wxjsapi/pay  , 同样的, 授权目录只要修改为 http://域名/Pay/Wxjsapi/ 即可 , 而不是 http://域名/Pay/Wxjsapi/pay/ 否则会报 fail_no permission to execute 错误, 到此, 就解决了 fail_invalid_appid 这个错误

6, 回调地址的填写 http://域名/Pay/Wxjsapi/Paynotify 即可

7, 微信支付成功后, 点击完成按钮的跳转地址, 填写为你要跳转的地址即可,

    if(res.err_msg.indexOf('ok')>0){
        window.location.href='{$returnurl}';
    }

这里调用了

以上就是莪使用微信JS API支付时所碰到的问题, 现已解决, 可以成功使用微信支付


如果用户下订单后跳转至支付页面时, 需要带上一个参数的话, 比如 orderid ,  http://域名/Pay/Wxjsapi/pay/orderid/123456 那么, 授权目录改为 http://域名/Pay/Wxjsapi/pay/orderid/ 即可


0 0
原创粉丝点击