***CodeIgniter集成微信支付(转)

微信支付Native扫码支付模式二之CodeIgniter集成篇

 http://www.cnblogs.com/24la/p/wxpay-native-qrcode-codeigniter.html
 

CI:3.0.5javascript

微信支付API类库来自:https://github.com/zhangv/wechat-payphp

请先看一眼官方场景及支付时序图:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5html

官方API列表:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1java

二维码生成类库:phpqrcodegit

走了几天的弯路,直到遇到Lamtin指点(热心网友),他说你既然是集成到CI为什么不本身写,我想了想是啊,为何我一直陷入官方sdk的漩涡里不能跳出来去看这件事,官方提供了API接口,你只须要去调用这些接口啊,post参数啊,是吧,后悔浪费了3天时间。为了避免让大家和我同样我把个人思路及代码发布出来,有什么问题能够留言。github

咱们为何使用三方支付类库?web

纵观微信支付的sdk或者其余的微信支付demo,或多或少的都是围绕官方API接口来写,增长些本身用的方法方便调用之类的,而若是我本身再去写这样的一个东西,第一可能组织很差,基础弱啊,第二可能须要花费大量时间,鉴于此我去寻找比较好用的别人封装的API类库好了,终于不负所望,真有,只能够这个类库几乎没有人用,不过真不错ajax

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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
class  WechatPay {
     const  TRADETYPE_JSAPI =  'JSAPI' ,TRADETYPE_NATIVE =  'NATIVE' ,TRADETYPE_APP =  'APP' ;
     const  URL_UNIFIEDORDER =  "https://api.mch.weixin.qq.com/pay/unifiedorder" ;
     const  URL_ORDERQUERY =  "https://api.mch.weixin.qq.com/pay/orderquery" ;
     const  URL_CLOSEORDER =  'https://api.mch.weixin.qq.com/pay/closeorder' ;
     const  URL_REFUND =  'https://api.mch.weixin.qq.com/secapi/pay/refund' ;
     const  URL_REFUNDQUERY =  'https://api.mch.weixin.qq.com/pay/refundquery' ;
     const  URL_DOWNLOADBILL =  'https://api.mch.weixin.qq.com/pay/downloadbill' ;
     const  URL_REPORT =  'https://api.mch.weixin.qq.com/payitil/report' ;
     const  URL_SHORTURL =  'https://api.mch.weixin.qq.com/tools/shorturl' ;
     const  URL_MICROPAY =  'https://api.mch.weixin.qq.com/pay/micropay' ;
     /**
      * 错误信息
      */
     public  $error  = null;
     /**
      * 错误信息XML
      */
     public  $errorXML  = null;
     /**
      * 微信支付配置数组
      * appid        公众帐号appid
      * mch_id       商户号
      * apikey       加密key
      * appsecret    公众号appsecret
      * sslcertPath  证书路径(apiclient_cert.pem)
      * sslkeyPath   密钥路径(apiclient_key.pem)
      */
     private  $_config ;
     /**
      * @param $config 微信支付配置数组
      */
     public  function  __construct( $config ) {
         $this ->_config =  $config ;
     }
     /**
      * JSAPI获取prepay_id
      * @param $body
      * @param $out_trade_no
      * @param $total_fee
      * @param $notify_url
      * @param $openid
      * @return null
      */
     public  function  getPrepayId( $body , $out_trade_no , $total_fee , $notify_url , $openid ) {
         $data  array ();
         $data [ "nonce_str" ]    =  $this ->get_nonce_string();
         $data [ "body" ]         =  $body ;
         $data [ "out_trade_no" ] =  $out_trade_no ;
         $data [ "total_fee" ]    =  $total_fee ;
         $data [ "spbill_create_ip" ] =  $_SERVER [ "REMOTE_ADDR" ];
         $data [ "notify_url" ]   =  $notify_url ;
         $data [ "trade_type" ]   = self::TRADETYPE_JSAPI;
         $data [ "openid" ]   =  $openid ;
         $result  $this ->unifiedOrder( $data );
         if  ( $result [ "return_code" ] ==  "SUCCESS"  &&  $result [ "result_code" ] ==  "SUCCESS" ) {
             return  $result [ "prepay_id" ];
         else  {
             $this ->error =  $result [ "return_code" ] ==  "SUCCESS"  $result [ "err_code_des" ] :  $result [ "return_msg" ];
             $this ->errorXML =  $this ->array2xml( $result );
             return  null;
         }
     }
     private  function  get_nonce_string() {
         return  substr ( str_shuffle ( "abcdefghijklmnopqrstuvwxyz0123456789" ),0,32);
     }
     /**
      * 统一下单接口
      */
     public  function  unifiedOrder( $params ) {
         $data  array ();
         $data [ "appid" ] =  $this ->_config[ "appid" ];
         $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
         $data [ "device_info" ] = (isset( $params [ 'device_info' ])&&trim( $params [ 'device_info' ])!= '' )? $params [ 'device_info' ]:null;
         $data [ "nonce_str" ] =  $this ->get_nonce_string();
         $data [ "body" ] =  $params [ 'body' ];
         $data [ "detail" ] = isset( $params [ 'detail' ])? $params [ 'detail' ]:null; //optional
         $data [ "attach" ] = isset( $params [ 'attach' ])? $params [ 'attach' ]:null; //optional
         $data [ "out_trade_no" ] = isset( $params [ 'out_trade_no' ])? $params [ 'out_trade_no' ]:null;
         $data [ "fee_type" ] = isset( $params [ 'fee_type' ])? $params [ 'fee_type' ]: 'CNY' ;
         $data [ "total_fee" ]    =  $params [ 'total_fee' ];
         $data [ "spbill_create_ip" ] =  $params [ 'spbill_create_ip' ];
         $data [ "time_start" ] = isset( $params [ 'time_start' ])? $params [ 'time_start' ]:null; //optional
         $data [ "time_expire" ] = isset( $params [ 'time_expire' ])? $params [ 'time_expire' ]:null; //optional
         $data [ "goods_tag" ] = isset( $params [ 'goods_tag' ])? $params [ 'goods_tag' ]:null;
         $data [ "notify_url" ] =  $params [ 'notify_url' ];
         $data [ "trade_type" ] =  $params [ 'trade_type' ];
         $data [ "product_id" ] = isset( $params [ 'product_id' ])? $params [ 'product_id' ]:null; //required when trade_type = NATIVE
         $data [ "openid" ] = isset( $params [ 'openid' ])? $params [ 'openid' ]:null; //required when trade_type = JSAPI
         $result  $this ->post(self::URL_UNIFIEDORDER,  $data );
         return  $result ;
     }
     private  function  post( $url $data , $cert  = false) {
         $data [ "sign" ] =  $this ->sign( $data );
         $xml  $this ->array2xml( $data );
         $ch  = curl_init();
         curl_setopt( $ch , CURLOPT_SSL_VERIFYPEER, false);
         curl_setopt( $ch , CURLOPT_SSL_VERIFYHOST, false);
         curl_setopt( $ch , CURLOPT_POST, 1);
         curl_setopt( $ch , CURLOPT_POSTFIELDS,  $xml );
         curl_setopt( $ch , CURLOPT_RETURNTRANSFER, true);
         curl_setopt( $ch , CURLOPT_URL,  $url );
         if ( $cert  == true){
             //使用证书:cert 与 key 分别属于两个.pem文件
             curl_setopt( $ch ,CURLOPT_SSLCERTTYPE, 'PEM' );
             curl_setopt( $ch ,CURLOPT_SSLCERT,  $this ->_config[ 'sslcertPath' ]);
             curl_setopt( $ch ,CURLOPT_SSLKEYTYPE, 'PEM' );
             curl_setopt( $ch ,CURLOPT_SSLKEY,  $this ->_config[ 'sslkeyPath' ]);
         }
         $content  = curl_exec( $ch );
         $array  $this ->xml2array( $content );
         return  $array ;
     }
     /**
      * 数据签名
      * @param $data
      * @return string
      */
     private  function  sign( $data ) {
         ksort( $data );
         $string1  "" ;
         foreach  ( $data  as  $k  =>  $v ) {
             if  ( $v  && trim( $v )!= '' ) {
                 $string1  .=  "$k=$v&" ;
             }
         }
         $stringSignTemp  $string1  "key="  $this ->_config[ "apikey" ];
         $sign  strtoupper (md5( $stringSignTemp ));
         return  $sign ;
     }
     private  function  array2xml( $array ) {
         $xml  "<xml>"  . PHP_EOL;
         foreach  ( $array  as  $k  =>  $v ) {
             if ( $v  && trim( $v )!= '' )
                 $xml  .=  "<$k><![CDATA[$v]]></$k>"  . PHP_EOL;
         }
         $xml  .=  "</xml>" ;
         return  $xml ;
     }
     private  function  xml2array( $xml ) {
         $array  array ();
         $tmp  = null;
         try {
             $tmp  = ( array ) simplexml_load_string( $xml );
         } catch (Exception  $e ){}
         if ( $tmp  &&  is_array ( $tmp )){
             foreach  $tmp  as  $k  =>  $v ) {
                 $array [ $k ] = (string)  $v ;
             }
         }
         return  $array ;
     }
     /**
      * 扫码支付(模式二)获取支付二维码
      * @param $body
      * @param $out_trade_no
      * @param $total_fee
      * @param $notify_url
      * @param $product_id
      * @return null
      */
     public  function  getCodeUrl( $body , $out_trade_no , $total_fee , $notify_url , $product_id ){
         $data  array ();
         $data [ "nonce_str" ]    =  $this ->get_nonce_string();
         $data [ "body" ]         =  $body ;
         $data [ "out_trade_no" ] =  $out_trade_no ;
         $data [ "total_fee" ]    =  $total_fee ;
         $data [ "spbill_create_ip" ] =  $_SERVER [ "SERVER_ADDR" ];
         $data [ "notify_url" ]   =  $notify_url ;
         $data [ "trade_type" ]   = self::TRADETYPE_NATIVE;
         $data [ "product_id" ]   =  $product_id ;
         $result  $this ->unifiedOrder( $data );
         if  ( $result [ "return_code" ] ==  "SUCCESS"  &&  $result [ "result_code" ] ==  "SUCCESS" ) {
             return  $result [ "code_url" ];
         else  {
             $this ->error =  $result [ "return_code" ] ==  "SUCCESS"  $result [ "err_code_des" ] :  $result [ "return_msg" ];
             return  null;
         }
     }
     /**
      * 查询订单
      * @param $transaction_id
      * @param $out_trade_no
      * @return array
      */
     public  function  orderQuery( $transaction_id , $out_trade_no ){
         $data  array ();
         $data [ "appid" ] =  $this ->_config[ "appid" ];
         $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
         $data [ "transaction_id" ] =  $transaction_id ;
         $data [ "out_trade_no" ] =  $out_trade_no ;
         $data [ "nonce_str" ] =  $this ->get_nonce_string();
         $result  $this ->post(self::URL_ORDERQUERY,  $data );
         return  $result ;
     }
     /**
      * 关闭订单
      * @param $out_trade_no
      * @return array
      */
     public  function  closeOrder( $out_trade_no ){
         $data  array ();
         $data [ "appid" ] =  $this ->_config[ "appid" ];
         $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
         $data [ "out_trade_no" ] =  $out_trade_no ;
         $data [ "nonce_str" ] =  $this ->get_nonce_string();
         $result  $this ->post(self::URL_CLOSEORDER,  $data );
         return  $result ;
     }
     /**
      * 申请退款 - 使用商户订单号
      * @param $out_trade_no 商户订单号
      * @param $out_refund_no 退款单号
      * @param $total_fee 总金额(单位:分)
      * @param $refund_fee 退款金额(单位:分)
      * @param $op_user_id 操做员帐号
      * @return array
      */
     public  function  refund( $out_trade_no , $out_refund_no , $total_fee , $refund_fee , $op_user_id ){
         $data  array ();
         $data [ "appid" ] =  $this ->_config[ "appid" ];
         $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
         $data [ "nonce_str" ] =  $this ->get_nonce_string();
         $data [ "out_trade_no" ] =  $out_trade_no ;
         $data [ "out_refund_no" ] =  $out_refund_no ;
         $data [ "total_fee" ] =  $total_fee ;
         $data [ "refund_fee" ] =  $refund_fee ;
         $data [ "op_user_id" ] =  $op_user_id ;
         $result  $this ->post(self::URL_REFUND,  $data ,true);
         return  $result ;
     }
     /**
      * 申请退款 - 使用微信订单号
      * @param $out_trade_no 商户订单号
      * @param $out_refund_no 退款单号
      * @param $total_fee 总金额(单位:分)
      * @param $refund_fee 退款金额(单位:分)
      * @param $op_user_id 操做员帐号
      * @return array
      */
     public  function  refundByTransId( $transaction_id , $out_refund_no , $total_fee , $refund_fee , $op_user_id ){
         $data  array ();
         $data [ "appid" ] =  $this ->_config[ "appid" ];
         $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
         $data [ "nonce_str" ] =  $this ->get_nonce_string();
         $data [ "transaction_id" ] =  $transaction_id ;
         $data [ "out_refund_no" ] =  $out_refund_no ;
         $data [ "total_fee" ] =  $total_fee ;
         $data [ "refund_fee" ] =  $refund_fee ;
         $data [ "op_user_id" ] =  $op_user_id ;
         $result  $this ->post(self::URL_REFUND,  $data ,true);
         return  $result ;
     }
     /**
      * 下载对帐单
      * @param $bill_date 下载对帐单的日期,格式:20140603
      * @param $bill_type 类型
      * @return array
      */
     public  function  downloadBill( $bill_date , $bill_type  'ALL' ){
         $data  array ();
         $data [ "appid" ] =  $this ->_config[ "appid" ];
         $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
         $data [ "bill_date" ] =  $bill_date ;
         $data [ "bill_type" ] =  $bill_type ;
         $data [ "nonce_str" ] =  $this ->get_nonce_string();
         $result  $this ->post(self::URL_DOWNLOADBILL,  $data );
         return  $result ;
     }
     /**
      * 获取js支付使用的第二个参数
      */
     public  function  get_package( $prepay_id ) {
         $data  array ();
         $data [ "appId" ] =  $this ->_config[ "appid" ];
         $data [ "timeStamp" ] = time();
         $data [ "nonceStr" ]  =  $this ->get_nonce_string();
         $data [ "package" ]   =  "prepay_id=$prepay_id" ;
         $data [ "signType" ]  =  "MD5" ;
         $data [ "paySign" ]   =  $this ->sign( $data );
         return  $data ;
     }
     /**
      * 获取发送到通知地址的数据(在通知地址内使用)
      * @return 结果数组,若是不是微信服务器发送的数据返回null
      *          appid
      *          bank_type
      *          cash_fee
      *          fee_type
      *          is_subscribe
      *          mch_id
      *          nonce_str
      *          openid
      *          out_trade_no    商户订单号
      *          result_code
      *          return_code
      *          sign
      *          time_end
      *          total_fee       总金额
      *          trade_type
      *          transaction_id  微信支付订单号
      */
     public  function  get_back_data() {
         $xml  file_get_contents ( "php://input" );
         $data  $this ->xml2array( $xml );
         if  ( $this ->validate( $data )) {
             return  $data ;
         else  {
             return  null;
         }
     }
     /**
      * 验证数据签名
      * @param $data 数据数组
      * @return 数据校验结果
      */
     public  function  validate( $data ) {
         if  (!isset( $data [ "sign" ])) {
             return  false;
         }
         $sign  $data [ "sign" ];
         unset( $data [ "sign" ]);
         return  $this ->sign( $data ) ==  $sign ;
     }
     /**
      * 响应微信支付后台通知
      * @param $return_code 返回状态码 SUCCESS/FAIL
      * @param $return_msg  返回信息
      */
     public  function  response_back( $return_code = "SUCCESS" $return_msg =null) {
         $data  array ();
         $data [ "return_code" ] =  $return_code ;
         if  ( $return_msg ) {
             $data [ "return_msg" ] =  $return_msg ;
         }
         $xml  $this ->array2xml( $data );
         print  $xml ;
     }
}

1、注意:此类库集成到ci咱们要更名WechatPay改成Wechatpay让他符ci类库规范,并且文件名也要改保持统一性json

2、把Wechatpay.php放在application\libraries文件夹内,将证书之类的,日志文件之类的放置在和wechatpay.php同级目录下便可,固然能够随便放api

3、将微信配置信息,商户号、appid、AppSecret、API key、证书位置等信息放在wxpay_config.php文件中,放在application\config目录中

wxpay_config.php代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php defined( 'BASEPATH' ) OR  exit ( 'No direct script access allowed' );
/**
  * Created by PhpStorm.
  * User: sxq
  * Date: 2016-04-20
  * Time: 16:59
  */
$config [ 'appid' ] =  '你的公众号appid' ;
 
$config [ 'mch_id' ] =  '你的商户号' ;
 
$config [ 'apikey' ] =  '你的APIkey' ;
 
$config [ 'appsecret' ] =  "你的AppSecret" ;
 
$config [ 'sslcertPath' ] =  APPPATH. 'libraries/cert/apiclient_cert.pem' ;
 
$config [ 'sslkeyPath' ] = APPPATH. 'libraries/cert/apiclient_key.pem' ;

 

4、phpqrcode文件,这份文件在微信官方sdk中,使用文件有phpqrcode文件夹和qrcode.php也一同放置在application\libraries文件夹内

 

5、日志文件log.php,这份文件在微信官方sdk中也一同放置在application\libraries文件夹内

1
2
3
4
5
6
require_once  (APPPATH. 'libraries/log.php' );
//初始化日志
$logHandler new  CLogFileHandler(APPPATH. "logs/" . date ( 'Y-m-d' ). '.log' );
Log::Init( $logHandler , 15);
//我在控制器最顶部加了这个实例化,日志文件放在了application/logs文件夹
//调用方式:log::debug("输出信息");简单记录执行信息方便调试

  

6、配置信息写完后,那么在控制器里调用吧(满满的全是干货)

咱们首先按照常规的加载配置信息代码同样去加载微信配置信息,最后再加载三方类库wechatpay.php

1
2
3
4
5
6
7
8
9
$this ->load->config( 'wxpay_config' );
$wxconfig [ 'appid' ]= $this ->config->item( 'appid' );
$wxconfig [ 'mch_id' ]= $this ->config->item( 'mch_id' );
$wxconfig [ 'apikey' ]= $this ->config->item( 'apikey' );
$wxconfig [ 'appsecret' ]= $this ->config->item( 'appsecret' );
$wxconfig [ 'sslcertPath' ]= $this ->config->item( 'sslcertPath' );
$wxconfig [ 'sslkeyPath' ]= $this ->config->item( 'sslkeyPath' );
//因为此类库构造函数须要传参,咱们初始化类库就传参数给他吧
$this ->load->library( 'Wechatpay' , $wxconfig );

  这步基础信息配置完毕,接下来咱们须要构造统一下单API接口参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
       $param [ 'body' ]= "商品名称(自行看文档具体填什么)" ;
       $param [ 'attach' ]= "我有个参数要传我就穿了个id过来,这里不要有空格避免出错" ;
       $param [ 'detail' ]= "我填了商品名称加订单号" ;
       $param [ 'out_trade_no' ]= "商户订单号" ;
       $param [ 'total_fee' ]= "金额,记得乘以100,微信支付单位默认分" ; //如$total_fee*100
       $param [ "spbill_create_ip" ] = $_SERVER [ 'REMOTE_ADDR' ]; //客户端IP地址
       $param [ "time_start" ] =  date ( "YmdHis" ); //请求开始时间
       $param [ "time_expire" ] = date ( "YmdHis" , time() + 600); //请求超时时间
       $param [ "goods_tag" ] = urldecode( $productname ); //商品标签,自行填写
       $param [ "notify_url" ] = base_url(). "home/wxnotify" ; //自行定义异步通知url
       $param [ "trade_type" ] =  "NATIVE" ; //扫码支付模式二
       $param [ "product_id" ] =  $order ->productid; //正好有产品id就传了个,看文档说本身定义
//调用统一下单API接口
       $result = $this ->wechatpay->unifiedOrder( $param );          //这里能够加日志输出,log::debug(json_encode($result));
//成功(return_code和result_code都为SUCCESS)就会返回含有带支付二维码连接的数据
       if  (isset( $result [ "code_url" ]) && ! empty ( $result [ "code_url" ])) { />             //二维码图片连接
           $data [ 'wxurl' ] =  $result [ "code_url" ];
//这里传递商户订单号到扫码视图,是由于我想作跳转,根据商户号去查询订单是否支付成功,若是成功了就跳转,定时轮询微信服务器(这个谁有好的方法能够分享给我啊,表示感谢啦)
           $data [ 'orderno' ] =  $out_trade_no ;
           $this ->load->view( 'home/pay' $data );
       }

  

pay.php扫码视图页面代码以下:这部分代码来自(https://github.com/Alpha2016/wxpay)

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
<?php  if (isset( $wxurl )&&! empty ( $wxurl )){?>
     <div  class = "bgcolor" >
         <div  class = "container" >
             <div  class = "panel" >
                 <div  class = "panel-heading" >
                     <ol  class = "breadcrumb" >
                         <li><a href= "<?php echo base_url().'home';?>" >首页</a><span  class = 'divider' >></span></li>
                         <li  class = "active active-tab" ><span><?php  echo  "二维码支付" ;?></span></li>
                     </ol>
                 </div>
                 <div  class = "panel-body" >
                     <div  class = "page-header" >二维码支付</div>
                     <div  class = "text-danger center-block text-center" >
                         <input type= "hidden"  id= "orderno"  value= "<?php echo $orderno;?>" />
                         <img alt= "扫码支付"  src= "<?php echo base_url().'home/qrcode?data='.urlencode($wxurl);?>"  style= "width:200px;height:200px;" />
                     </div>
                 </div>
             </div>
         </div>
     </div>
<?php }?>
<script>
     // 每半秒请求一次数据,而后判断,跳转,增长用户友好性
     $( function (){
         orderno = $( '#orderno' ).val();
         start = self.setInterval( "checkstatus(orderno)" , 500);
     });
 
     function  checkstatus(order_no){
         if (order_no == undefined || order_no ==  '' ){
             window.clearInterval(start);
         }
         else {
             $.ajax({
                 url: "<?php echo base_url().'home/queryorder';?>" ,
                 type: 'POST' ,
                 dataType: 'json' ,
                 data:{orderno:orderno},
                 success: function (msg){
                     if (msg.trade_state ==  "SUCCESS" ) {
                         window.clearInterval(start);
                         alert( '支付成功' );
                         location.href =  "<?php echo base_url().'home/myorder';?>" ;
                     }
                 }
             });
         }
     }
</script>

  其实核心在二维码连接如何转换成二维码图片和如何定时轮询支付结果

1
<?php  echo  base_url(). 'home/qrcode?data=' .urlencode( $wxurl );?>这句是调用phpqrcode类库<br>轮询方法代码:<br>该部分在home控制器下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function  queryorder()
{
     $this ->load->config( 'wxpay_config' );
     $wxconfig [ 'appid' ]= $this ->config->item( 'appid' );
     $wxconfig [ 'mch_id' ]= $this ->config->item( 'mch_id' );
     $wxconfig [ 'apikey' ]= $this ->config->item( 'apikey' );
     $wxconfig [ 'appsecret' ]= $this ->config->item( 'appsecret' );
     $wxconfig [ 'sslcertPath' ]= $this ->config->item( 'sslcertPath' );
     $wxconfig [ 'sslkeyPath' ]= $this ->config->item( 'sslkeyPath' );
     $this ->load->library( 'Wechatpay' , $wxconfig );
     $out_trade_no  $_POST [ 'orderno' ];      //调用查询订单API接口
     $array  $this ->wechatpay->orderQuery( '' , $out_trade_no );
     echo  json_encode( $array );
}

那么二维码类库调用在这里

1
2
3
4
5
6
function  qrcode()
{
     require_once (APPPATH. 'libraries/phpqrcode/phpqrcode.php' );
     $url  = urldecode( $_GET [ "data" ]);
     QRcode::png( $url );
}

那么二维码生成支付图片完成,支付轮询也完成了,该如何去处理业务逻辑呢?

先说明下,这部分有个弊端,若是客户一直不支付那么他就一直轮询,能够自行设置个有效期,我没有设置。若是在轮询处处理业务逻辑怎么样?能够的,可是也有个问题若是客户直接关掉了,你来不及处理的业务怎么办?因此还要确保不掉单,还须要再微信异步通知url那里处理下业务

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
     //微信异步通知
     function  wxnotify()
     {
//$postStr = file_get_contents("php://input");//由于不少都设置了register_globals禁止,不能用$GLOBALS["HTTP_RAW_POST_DATA']     //这部分困扰了很久用上面这种一直接受不到数据,或者接受了解析不正确,最终用下面的正常了,有哪位愿意指点的能够告知一二
         $xml  $GLOBALS [ 'HTTP_RAW_POST_DATA' ]; //这个须要开启;always_populate_raw_post_data = On
         $this ->load->config( 'wxpay_config' );
         $wxconfig [ 'appid' ]= $this ->config->item( 'appid' );
         $wxconfig [ 'mch_id' ]= $this ->config->item( 'mch_id' );
         $wxconfig [ 'apikey' ]= $this ->config->item( 'apikey' );
         $wxconfig [ 'appsecret' ]= $this ->config->item( 'appsecret' );
         $wxconfig [ 'sslcertPath' ]= $this ->config->item( 'sslcertPath' );
         $wxconfig [ 'sslkeyPath' ]= $this ->config->item( 'sslkeyPath' );
         $this ->load->library( 'Wechatpay' , $wxconfig );
         libxml_disable_entity_loader(true);
         $array = json_decode(json_encode(simplexml_load_string( $xml 'SimpleXMLElement' , LIBXML_NOCDATA)), true);
         log::debug( $xml );
         log::debug(json_encode( $array ));
         if ( $array !=null)
         {
             $out_trade_no  $array [ 'out_trade_no' ];
             $trade_no  $array [ 'transaction_id' ];
             $data [ 'orderid' ]= $array [ 'attach' ];
             $this ->load->model( 'payorder' );
             $payinfo  $this ->payorder->GetPayorder( array ( 'orderno'  =>  $out_trade_no ));
             if  (! $payinfo ) {
                 $data [ 'orderno' ] =  $out_trade_no ;
                 $data [ 'money' ] =  $array [ 'total_fee' ];
                 $data [ 'tradeno' ] =  $trade_no ;
                 $rs = $this ->payorder->AddPayorder( $data );
                 if ( $rs >0)
                 {             //告知微信我成功了
                     $this ->wechatpay->response_back();
                 } else {             //告知微信我失败了继续发
                     $this ->wechatpay->response_back( "FAIL" );
                 }
             } else {
                 $this ->wechatpay->response_back();
             }
         }
     }

  

花了5天的时间去研究这个类型的微信支付,花了半天的时间去梳理知识点,总体感受就是若是API接口少,又有成熟类库本身去集成吧


微信支付JSAPI模式及退款CodeIgniter集成篇

 http://www.cnblogs.com/24la/p/wxpay-jsapi-refund.html
 

微信支付接口文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

首先你得知道这个jsapi是不能离开微信进行调用支付的,明白了这个道理咱们好下手,页面是在微信内显示并经过jsapi调用微信支付组件进行支付。

能够看看咱们上一篇文章,主要是Native扫码支付模式二

咱们仍然继续使用wechatpay.php这个支付集成类,简单方便好理解,不过若是应用jsapi的话这个类有个bug

在咱们构造jsapi须要的参数时有个时间戳,咱们用time()生成的,会报微信支付调用JSAPI缺乏参数:timeStamp

修改以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
  * 获取js支付使用的第二个参数
  */
public  function  get_package( $prepay_id ) {
     $data  array ();
     $data [ "appId" ] =  $this ->_config[ "appid" ];
       //改动地方,把它变成字符串
     $time =time();
     $data [ "timeStamp" ] =  "\"" . $time . "\"" ;
     $data [ "nonceStr" ]  =  $this ->get_nonce_string();
     $data [ "package" ]   =  "prepay_id=$prepay_id" ;
     $data [ "signType" ]  =  "MD5" ;
     $data [ "paySign" ]   =  $this ->sign( $data );
     return  $data ;
}

 其实这个方法就是获取jsapi的支付参数了

1、微信JSAPI支付

不能忘记配置受权目录,调用jsapi我是在http://xxx.com/index.php/home下我配置了这个

首先咱们仍是要调用统一下单接口,获取咱们要的参数(若是此类的配置放置位置等不会的请参考上篇文章),此为pay方法,在调用统一下单接口的时候咱们须要知道须要哪些参数

一、要获取openid,这个我是项目用了一个微信API的类库,https://github.com/dodgepudding/wechat-php-sdk,主要是用了这里面的方法

此项目有朋友专门的对接了CodeIgniter框架的扩展类库,能够直接用,目录结构,咱们直接上代码吧

1
2
3
4
5
6
public  function  __construct()
{
     parent::__construct();
     $this ->load->library( 'CI_Wechat' ); //因为个人项目是时刻都跟微信绑在一块儿,因此直接加载在构造函数里了,不用每一个方法都加载了。
     $this ->load->library( 'pagination' );
}

 CI_Model内容你们看下上面的类库源码,还有里面如何配置的,下面咱们看看如何获取openid

 

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
function  oauthurl()
{
     $oauth_url  $this ->ci_wechat->getOauthRedirect(base_url() .  'index.php/home/oauth' , 1);
     header( 'Location: '  $oauth_url );
     exit ();
}
 
function  oauth()
{
     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  {
         $json  $this ->ci_wechat->getOauthAccessToken();
         $openid  $json [ 'openid' ];
         //注册用户,成功后能够抢单
         //return $this->_isRegistered($_SESSION['user']['openid']);
         return  $openid ;
 
     }
}

 以上两个方法就是获取openid的,获取以后我是保存在session里的,我每一个页面都判断是否获取了openid若是没有获取直接

1
$this ->session->set_userdata( 'openid' $this ->oauth());

 这样保证一直能获得openid

二、构造JSAPI支付所需参数(统一下单的参数构造)

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
$this ->load->model( 'publist' ); //获取订单信息
$pub  $this ->publist->GetList( array ( 'id'  =>  $_SESSION [ 'orderid' ]));
//微信支付配置的参数配置读取
$this ->load->config( 'wxpay_config' );
$wxconfig [ 'appid' ]= $this ->config->item( 'appid' );
$wxconfig [ 'mch_id' ]= $this ->config->item( 'mch_id' );
$wxconfig [ 'apikey' ]= $this ->config->item( 'apikey' );
$wxconfig [ 'appsecret' ]= $this ->config->item( 'appsecret' );
$wxconfig [ 'sslcertPath' ]= $this ->config->item( 'sslcertPath' );
$wxconfig [ 'sslkeyPath' ]= $this ->config->item( 'sslkeyPath' );
$this ->load->library( 'Wechatpay' , $wxconfig );
//商户交易单号
$out_trade_no  $pub ->listno;
$total_fee = $pub ->fee;
$openid = $_SESSION [ 'openid' ];
$param [ 'body' ]= "黑人牙膏" ;
$param [ 'attach' ]= $pub ->id;
$param [ 'detail' ]= "黑人牙膏-" . $out_trade_no ;
$param [ 'out_trade_no' ]= $out_trade_no ;
$param [ 'total_fee' ]= $total_fee *100;
$param [ "spbill_create_ip" ] = $_SERVER [ 'REMOTE_ADDR' ];
$param [ "time_start" ] =  date ( "YmdHis" );
$param [ "time_expire" ] = date ( "YmdHis" , time() + 600);
$param [ "goods_tag" ] =  "黑人牙膏" ;
$param [ "notify_url" ] = base_url(). "index.php/home/notify" ;
$param [ "trade_type" ] =  "JSAPI" ;
$param [ "openid" ] =  $openid ;
 
//统一下单,获取结果,结果是为了构造jsapi调用微信支付组件所需参数
$result = $this ->wechatpay->unifiedOrder( $param );
 
//若是结果是成功的咱们才能构造所需参数,首要判断预支付id
 
if  (isset( $result [ "prepay_id" ]) && ! empty ( $result [ "prepay_id" ])) {
     //调用支付类里的get_package方法,获得构造的参数
     $data [ 'parameters' ]=json_encode( $this ->wechatpay->get_package( $result [ 'prepay_id' ]));
     $data [ 'notifyurl' ]= $param [ "notify_url" ];
     $data [ 'fee' ]= $total_fee ;
     $data [ 'pubid' ]= $_SESSION [ 'orderid' ];
 
     $this ->load->view( 'home/header' );
     //要有个页面将以上数据传递过去并展现给用户
     $this ->load->view( 'home/pay' $data );
     $this ->load->view( 'home/footer' );
}       

 三、支付页面,views视图pay.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
<?php
$jsApiParameters  $parameters ; //参数赋值
?>
 
<script type= "text/javascript" >
     //调用微信JS api 支付
     function  jsApiCall()
     {
         WeixinJSBridge.invoke(
             'getBrandWCPayRequest' ,
             <?php  echo  $jsApiParameters ; ?>,
             function (res){
                 WeixinJSBridge.log(res.err_msg);
                 if (res.err_msg ==  "get_brand_wcpay_request:ok"  ){
                     $.alert( '支付成功' );
                     //我在这里选择了前台只要支付成功将单号传递更新数据
                     $.ajax({
                         url: '<?php  echo $notifyurl.' / '.$pubid;?>' ,
                         dataType: 'json' ,
                         success :  function (ret){
                             if (ret==1){
                                 //成功后返回个人订单页面
                                 location.href= "<?php echo base_url().'index.php/home/myorder';?>" ;
                             }
                         }
                     });
                 } else
                 {
                     //$.alert('支付失败');
                 }
                 //alert(res.err_code+res.err_desc+res.err_msg);
             }
         );
     }
 
     function  callpay()
     {
         if  (typeof WeixinJSBridge ==  "undefined" ){
             if ( document.addEventListener ){
                 document.addEventListener( 'WeixinJSBridgeReady' , jsApiCall, false);
             } else  if  (document.attachEvent){
                 document.attachEvent( 'WeixinJSBridgeReady' , jsApiCall);
                 document.attachEvent( 'onWeixinJSBridgeReady' , jsApiCall);
             }
         } else {
             jsApiCall();
         }
     }
</script>
<div  class = "hd" >
     <h1  class = "page_title" >支付佣金</h1>
     <p  class = "page_desc" >请认真核对佣金金额</p>
</div>
<div  class = "weui_cells" >
     <div  class = "weui_cell" >
         <div  class = "weui_cell_hd weui_cell_primary" >
             该笔订单支付金额为<span style= "color:#f00;font-size:50px" ><?php  echo  $fee ; ?></span>元钱
         </div>
     </div>
</div>
<button  class = "weui_btn weui_btn_primary"  type= "button"  onclick= "callpay()"  >当即支付</button>

 以上代码能够用微信web开发者工具,使用方式本身看看吧,有了这个工具调试再也不难

四、支付成功跳转页面,咱们看notify方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function  notify()
{
     $id  $this ->uri->segment(3);
     if  (isset( $_SESSION [ 'openid' ])) {
         $this ->load->model( 'publist' ); //更新业务逻辑
         $rs  $this ->publist->UpdateList( array ( 'id'  =>  $id 'feestatus'  => 1));
         if  ( $rs  > 0) {
             echo  1;
             exit ;
         else  {
             echo  0;
             exit ;
         }
     }
}

 这样咱们的支付流程就完全走完了。

2、当咱们支付完以后,有些单子能够退单的,如何将款项也退回呢

以上场景要弄明白了

咱们申请退款须要参数有哪些?咱们看看支付类里的退款方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
  * 申请退款 - 使用商户订单号
  * @param $out_trade_no 商户订单号
  * @param $out_refund_no 退款单号
  * @param $total_fee 总金额(单位:分)
  * @param $refund_fee 退款金额(单位:分)
  * @param $op_user_id 操做员帐号
  * @return array
  */
public  function  refund( $out_trade_no , $out_refund_no , $total_fee , $refund_fee , $op_user_id ){
     $data  array ();
     $data [ "appid" ] =  $this ->_config[ "appid" ];
     $data [ "mch_id" ] =  $this ->_config[ "mch_id" ];
     $data [ "nonce_str" ] =  $this ->get_nonce_string();
     $data [ "out_trade_no" ] =  $out_trade_no ;
     $data [ "out_refund_no" ] =  $out_refund_no ;
     $data [ "total_fee" ] =  $total_fee ;
     $data [ "refund_fee" ] =  $refund_fee ;
     $data [ "op_user_id" ] =  $op_user_id ;
     $result  $this ->post(self::URL_REFUND,  $data ,true);
 
     return  $result ;
}

 商户订单号,商户提供的退单号,付款金额,退款金额(不能退的比实际付款的多),操做员(通常商户号)

控制器内写退款方法

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
//申请退款
function  refund( $id = "" )
{
     if ( $id == "" ){
         //方便我手动调用退单
         $id  $this ->uri->segment(3);
     }
     if  (isset( $id ) &&  $id  !=  "" ) {
         $this ->load->model( 'publist' );
         //一、取消订单能够退款。二、失败订单能够退款
         $pub  $this ->publist->GetList( array ( 'id'  =>  $id ));
         if  ( $pub ->liststatus == 3 ||  $pub ->liststatus == 4) {
             $listno  $pub ->listno;
             $fee  $pub ->fee * 100;
 
             $this ->load->config( 'wxpay_config' );
             $wxconfig [ 'appid' ]= $this ->config->item( 'appid' );
             $wxconfig [ 'mch_id' ]= $this ->config->item( 'mch_id' );
             $wxconfig [ 'apikey' ]= $this ->config->item( 'apikey' );
             $wxconfig [ 'appsecret' ]= $this ->config->item( 'appsecret' );
             $wxconfig [ 'sslcertPath' ]= $this ->config->item( 'sslcertPath' );
             $wxconfig [ 'sslkeyPath' ]= $this ->config->item( 'sslkeyPath' );
             $this ->load->library( 'Wechatpay' , $wxconfig );
 
             if  (isset( $listno ) &&  $listno  !=  "" ) {
                 $out_trade_no  $listno ;
                 $total_fee  $fee ;
                 $refund_fee  $fee ;
                 //自定义商户退单号
                 $out_refund_no = $wxconfig [ 'mch_id' ]. date ( "YmdHis" );
                     $result = $this ->wechatpay->refund( $out_trade_no , $out_refund_no , $total_fee , $refund_fee , $wxconfig [ 'mch_id' ]);
 
                 log::DEBUG(json_encode( $result ));
                 if  (isset( $result [ "return_code" ]) &&  $result [ "return_code" ]= "SUCCESS" &&isset( $result [ "result_code" ]) &&  $result [ "result_code" ]= "SUCCESS" ) {
                     echo  "<script>$.toast('退款成功')</script>" ;
                 }
                 //佣金状态更改成已退款
                 $this ->publist->UpdateList( array ( 'id' => $id , 'liststatus' =>3, 'listoutno' => $out_refund_no ));
                 redirect( 'home/myorder' );
             }
         }
     }
}

 试试就行了,很快就能够接到退款消息

以上是这几天摸索出来的东西,分享给你们。

 
其余参考文章:

CI集成JSApi微信支付

http://blog.sina.com.cn/s/blog_14955e3b70102wvmj.html

相关文章
相关标签/搜索