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


微信支付Native掃碼支付模式二之CodeIgniter集成篇

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

CI:3.0.5

微信支付API類庫來自:https://github.com/zhangv/wechat-pay

請先看一眼官方場景及支付時序圖:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

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

二維碼生成類庫:phpqrcode

走了幾天的彎路,直到遇到Lamtin指點(熱心網友),他說你既然是集成到CI為何不自己寫,我想了想是啊,為什么我一直陷入官方sdk的漩渦里不能跳出來去看這件事,官方提供了API接口,你只需要去調用這些接口啊,post參數啊,是吧,后悔浪費了3天時間。為了不讓你們和我一樣我把我的思路及代碼發布出來,有什么問題可以留言。

我們為什么使用三方支付類庫?

縱觀微信支付的sdk或者其他的微信支付demo,或多或少的都是圍繞官方API接口來寫,增加些自己用的方法方便調用之類的,而如果我自己再去寫這樣的一個東西,第一可能組織不好,基礎弱啊,第二可能需要花費大量時間,鑒於此我去尋找比較好用的別人封裝的API類庫好了,終於不負所望,真有,只可以這個類庫幾乎沒有人用,不過真不錯

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 ;
     }
}

一、注意:此類庫集成到ci我們要改名WechatPay改為Wechatpay讓他符ci類庫規范,而且文件名也要改保持統一性

二、把Wechatpay.php放在application\libraries文件夾內,將證書之類的,日志文件之類的放置在和wechatpay.php同級目錄下即可,當然可以隨便放

三、將微信配置信息,商戶號、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' ;

 

四、phpqrcode文件,這份文件在微信官方sdk中,使用文件有phpqrcode文件夾和qrcode.php也一同放置在application\libraries文件夾內

 

五、日志文件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("輸出信息");簡單記錄執行信息方便調試

  

六、配置信息寫完后,那么在控制器里調用吧(滿滿的全是干貨)

我們首先按照常規的加載配置信息代碼一樣去加載微信配置信息,最后再加載三方類庫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的支付參數了

一、微信JSAPI支付

不能忘記配置授權目錄,調用jsapi我是在http://xxx.com/index.php/home下我配置了這個

首先我們還是要調用統一下單接口,獲取我們要的參數(如果此類的配置放置位置等不會的請參考上篇文章),此為pay方法,在調用統一下單接口的時候我們需要知道需要哪些參數

1、要獲取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

2、構造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' );
}       

 3、支付頁面,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開發者工具,使用方式自己看看吧,有了這個工具調試不再難

4、支付成功跳轉頁面,我們看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 ;
         }
     }
}

 這樣我們的支付流程就徹底走完了。

二、當我們支付完之后,有些單子可以退單的,如何將款項也退回呢

以上場景要弄明白了

我們申請退款需要參數有哪些?我們看看支付類里的退款方法

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' );
         //1、取消訂單可以退款。2、失敗訂單可以退款
         $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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM