嘔心之做:支付寶的手機網站支付接口的應用

因爲去年作手機Portl接口的工做,須要使用支付寶的支付,因而手機網站支付接口就成了首選。php

1.首先下載接口包html

 支付寶商家服務中心連接:https://b.alipay.com/login.htm?goto=https://b.alipay.com:443/newIndex.htm

  手機網站支付的產品介紹:https://b.alipay.com/order/productDetail.htm?productId=2013080604609688sql

  demo下載連接:https://doc.open.alipay.com/doc2/detail.htm?treeId=54&articleId=104511&docType=1   (請點擊關鍵字demo,進行下載)api

  

  解壓下載的文件能夠看到文件夾的結構以下圖:數組

  

  我使用的是RSA簽名方式,PHP-UTF-8的文件夾服務器

2.readme.txt的文檔說明框架

  紅色字體的文件是最重要的文件,也是必需的!curl


├lib┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈類文件夾
│ │
│ ├alipay_core.function.php ┈┈┈┈┈┈支付寶接口公用函數文件
│ │
│ ├alipay_notify.class.php┈┈┈┈┈┈┈支付寶通知處理類文件
│ │
│ ├alipay_submit.class.php┈┈┈┈┈┈┈支付寶各接口請求提交類文件
│ │
│ └alipay_rsa.function.php┈┈┈┈┈┈┈支付寶接口RSA函數文件

├log.txt┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈日誌文件

├alipay.config.php┈┈┈┈┈┈┈┈┈┈┈┈基礎配置類文件

├alipayapi.php┈┈┈┈┈┈┈┈┈┈┈┈┈┈支付寶接口入口文件

├notify_url.php ┈┈┈┈┈┈┈┈┈┈┈┈┈服務器異步通知頁面文件

├return_url.php ┈┈┈┈┈┈┈┈┈┈┈┈┈頁面跳轉同步通知文件

├key┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈私鑰公鑰文件夾(用法見下方※注意※)
│ │
│ ├rsa_private_key.pem┈┈┈┈┈┈┈┈┈商戶的私鑰文件
│ │
│ └alipay_public_key.pem┈┈┈┈┈┈┈┈支付寶的公鑰文件

├openssl┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈缺省dll文件(用法見下方※注意※)
│ │
│ ├libeay32.dll
│ │
│ ├ssleay32.dll
│ │
│ └php_openssl.dll

├cacert.pem ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈用於CURL中校驗SSL的CA證書文件

└readme.txt ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈使用說明文本


3. 把必需的文件整合到框架裏(我當時用的是thinkPHP框架)
  (1)在

 裏新建一個文件夾叫AliMobilePay,
      


     把上圖裏的4個文件拷貝到AliMobilePay文件夾裏,

      對以上文件進行重命名,異步

      alipay_core.function.php重命名爲:Corefunction.php函數

      alipay_notify.class.php重命名爲:Notify.php

      alipay_rsa.function.php重命名爲:Rsafunction.php

      alipay_submit.class.php重命名爲:Submit.php

      打開Notify.php,去掉一下代碼,

      require_once("alipay_core.function.php");
      require_once("alipay_rsa.function.php");

      一樣的道理去掉其餘3個文件裏的包含文件。

  (2)在根目錄下創建一個文件夾key
     在key文件夾裏放入商戶的私鑰文件、支付寶的公鑰文件、CA證書文件

        

       如何生成RSA密鑰:https://cshall.alipay.com/enterprise/help_detail.htm?help_id=474010&keyword=%C8%E7%BA%CE%C9%FA%B3%C9%B9%AB%CB%BD%D4%BF&sToken=s-5d0c889ac47741fd8094b26d4862696b&from=search&flag=0   (此文中描述的rsa_private_key.pem就是商家的私鑰文件)

         

      ◆商戶的私鑰
      一、不須要對剛生成的(原始的)私鑰作pkcs8編碼
      二、不須要去掉去掉「-----BEGIN RSA PRIVATE KEY-----」、「-----END RSA PRIVATE KEY-----」
      簡言之,只要維持用openssl工具剛生成出來的私鑰的內容便可。

      

      ◆支付寶公鑰
      一、須保留「-----BEGIN PUBLIC KEY-----」、「-----END PUBLIC KEY-----」這兩條文字。
      簡言之,支付寶公鑰只須要維持demo裏的原樣便可。

      

   (3)alipay_config.php 配置文件

      把alipay_config.php 配置文件整合到thinkPHP框架的配置文件裏

      

複製代碼
<?php
/**
 * Created by PhpStorm.
 * User: zhangxiaoliu
 * Date: 16/4/15
 * Time: 上午10:39
 */
//支付寶商家服務中心連接:https://b.alipay.com/login.htm?goto=https://b.alipay.com:443/newIndex.htm
return array(
    'ALIMOBILEPAY_CONFIG'=>array(
        //合做身份者id,以2088開頭的16位純數字, (合做身份者id的查看連接:https://b.alipay.com/order/pidAndKey.htm)
        'partner' => '2088XXXXXXXXXXXX',

        //收款支付寶帳號,與partner的值同樣
        'seller_id' => '2088XXXXXXXXXXXX',

        //商戶的私鑰(後綴是.pem)文件相對路徑
        'private_key_path'=> NEW_PORTAL_DOMAIN.'key/rsa_private_key.pem',

        //支付寶公鑰(後綴是.pem)文件相對路徑
        'ali_public_key_path'=> NEW_PORTAL_DOMAIN.'key/alipay_public_key.pem',

        //簽名方式 不需修改
        'sign_type' => strtoupper('RSA'),

        //字符編碼格式 目前支持 gbk 或 utf-8
        'input_charset'=> 'utf-8',

        //ca證書路徑地址,用於curl中ssl校驗
        'cacert' => NEW_PORTAL_DOMAIN.'key/cacert.pem',

        //訪問模式,根據本身的服務器是否支持ssl訪問,若支持請選擇https;若不支持請選擇http
        'transport' => 'http',

        //這裏是異步通知頁面url,提交到項目的Payment控制器的notifyurl方法;
        //需http://格式的完整路徑,不能加?id=123這類自定義參數
        'notify_url'=> NEW_PORTAL_DOMAIN.'portal.php/AliMobilePay/notify_url.php',

        //這裏是頁面跳轉通知url,提交到項目的Payment控制器的returnurl方法;
        //需http://格式的完整路徑,不能加?id=123這類自定義參數
        'return_url'=> NEW_PORTAL_DOMAIN.'portal.php/AliMobilePay/return_url.php',

        //支付成功跳轉到的頁面
        'successpage'=>NEW_PORTAL_DOMAIN.'portal.php/Success/index',
        //支付失敗跳轉到的頁面
        'errorpage'=>NEW_PORTAL_DOMAIN.'portal.php/Error/index',
        //商品展現地址
        'product_url'=>NEW_PORTAL_DOMAIN.'portal.php/Product/index',
    )
);
複製代碼

 

    (4)支付寶幫助中心

      https://cshall.alipay.com/enterprise/index.htm

4.調用支付寶接口

    (1)新建一個AliMobilePay控制器

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
<?php
namespace  Portal\Controller;
use  Common\Component\FilterComponent;
use  Portal\Service\LogPaycallbacksService;
use  Portal\Service\GuozhanOrderService;
use  Portal\Model\Pengwifi\Guozhan\OrderModel;
use  Portal\Service\TokenService;
use  Portal\Service\UserService;
use  Portal\Service\SetMotoRadiusService;
use  Common\Model\Radius\RadcheckModel;
/*
  * 購買上網卡的手機頁面支付寶接口
  */
class  AliMobilePayController  extends  CommonController{
     protected  $_order_model =null;
     protected  $_order_service =null;
     protected  $_token_service  = null;
     protected  $_Set_MotoRadius_service =null;
     protected  $_RadcheckModel =null;
     protected  $_log_pay_callbacks  = null;
 
     protected  function  afterInit() {
         parent::afterInit();
         vendor( 'AliMobilePay.Corefunction' );
         vendor( 'AliMobilePay.Rsafunction' );
         vendor( 'AliMobilePay.Notify' );
         vendor( 'AliMobilePay.Submit' );
         $this ->_order_model=  new  OrderModel();
         $this ->_order_service=  new  GuozhanOrderService();
         $this ->_log_pay_callbacks =  new  LogPaycallbacksService();
         $this ->_service =  new  UserService();
         $this ->_token_service =  new  TokenService();
         $this ->_RadcheckModel =  new  RadcheckModel();
         $this ->_Set_MotoRadius_service =  new  SetMotoRadiusService();
     }
 
 
     /**
      * 執行新增訂單
      */
     protected  function  _post(){
         if (isset( $this ->params[ 'name' ]) && ( $this ->params[ 'name' ]== "notify_url" )){
             $this ->notify_url( 'notify_url' );
             die ;
         }
         $this ->insert_order();
     }
 
 
     protected  function  _get(){
         /*
          *根據配置文件裏的路由規則:
          *':'.$var_controller.'/[:name]/[:action]'=>     ':1/_index?',   //匹配控制器後緊跟字符串,表示name
          * 例如:http://portal_v2.com/portal.php/Payment/Return.html
          * $notify_url會返回Return
          */
         $notify_url  = isset( $this ->params[ 'name' ]) ? FilterComponent::getString( $this ->params[ 'name' ]) :  'Unknown' ;
         switch ( $notify_url ){
             case  'return_url' :
                 $this ->return_url( $notify_url );
                 break ;
 
             default :
                 $this ->_log_pay_callbacks->update( array ( 'request_from' => 'Unknown' ), false);
                 exit ( 'Wrong request url' );
         }
     }
     
     //服務器異步通知頁面方法
     private  function  notify_url( $notify_url ){
         $alipay_config  = C( 'ALIMOBILEPAY_CONFIG' );
         //計算得出通知驗證結果
         $alipayNotify  new  \AlipayNotify( $alipay_config );
         $verify_result  $alipayNotify ->verifyNotify();
         if ( $verify_result ) { //驗證成功
             //商戶訂單號
             $order_sn  $this ->params[ 'out_trade_no' ];
             //支付寶交易號
             //$trade_no = $this->params['trade_no'];
             //交易狀態
             $trade_status  $this ->params[ 'trade_status' ];
             $this ->_log_pay_callbacks->update( array ( 'request_from' => $notify_url 'order_sn' => $order_sn 'response_status' => $trade_status ), false);
             if  (in_array( $trade_status , array ( 'TRADE_SUCCESS' , 'TRADE_FINISHED' ))) {
                 //判斷該筆訂單是否在商戶網站中已經作過處理
                 //若是沒有作過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序
                 //若是有作過處理,不執行商戶的業務程序
                  if (! $this ->checkorderstatus( $order_sn )){
                      $result = $this ->orderhandle( $order_sn );
                      if ( $result ==true){
                          echo  "success" ;
                      } else {
                          echo  "fail" ;
                      }
                  }
             } else {
                 echo  "fail" ;
             }
         } else  {
             //驗證失敗
             echo  "fail" ;
         }
     }
 
     //頁面跳轉同步通知
     private  function  return_url( $notify_url ){
         $alipay_config =C( 'ALIMOBILEPAY_CONFIG' );
         //計算得出通知驗證結果
         $alipayNotify  new  \AlipayNotify( $alipay_config );
         $verify_result  $alipayNotify ->verifyReturn();
         if ( $verify_result ) { //驗證成功
             //商戶訂單號
             $order_sn  $this ->params[ 'out_trade_no' ];
             //支付寶交易號
             //$trade_no = $this->params['trade_no'];
             //交易狀態
             $trade_status  $this ->params[ 'trade_status' ];
             $this ->_log_pay_callbacks->update( array ( 'request_from' => $notify_url 'order_sn' => $order_sn 'response_status' => $trade_status ), false);
             if  (in_array( $trade_status , array ( 'TRADE_SUCCESS' , 'TRADE_FINISHED' ))) {
                 //判斷該筆訂單是否在商戶網站中已經作過處理
                 //若是沒有作過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序
                 //若是有作過處理,不執行商戶的業務程序
                 if (! $this ->checkorderstatus( $order_sn )){
                     $result = $this ->orderhandle( $order_sn );
                     //——請根據您的業務邏輯來編寫程序(以上代碼僅做參考)——
                     if ( $result ==true){
                         header( "Location:" .C( 'ALIMOBILEPAY_CONFIG.successpage' )); //跳轉到配置項中配置的支付成功頁面;
                     } else {
                         header( "Location:" .C( 'ALIMOBILEPAY_CONFIG.errorpage' )); //跳轉到配置項中配置的支付失敗頁面;
                     }
                 }
             } else  {
                 header( "Location:" .C( 'ALIMOBILEPAY_CONFIG.errorpage' )); //跳轉到配置項中配置的支付失敗頁面;
             }
         } else  {
             //支付寶頁面「返回商戶」按鈕的連接,商品頁面
             header( "Location:" .C( 'ALIMOBILEPAY_CONFIG.product_url' ));
         }
     }
 
     //在線交易訂單支付處理函數
     //函數功能:根據支付接口傳回的數據判斷該訂單是否已經支付成功;
     //返回值:若是訂單已經成功支付,返回true,不然返回false;
     private  function  checkorderstatus( $order_sn ){
         $status = $this ->_order_model->where( "order_sn='$order_sn'" )->getField( 'order_status' );
         if ( $status  == OrderModel::ORDER_STATUS_PAYED){
             return  true;
         } else {
             return  false;
         }
     }
     //處理訂單函數
     //更新訂單狀態,寫入訂單支付後返回的數據
     private  function  orderhandle( $order_sn ){
         try {
             //開啓事務
             $this ->_order_model->startTrans();
             $data [ 'order_status' ]=OrderModel::ORDER_STATUS_PAYED;
             $affected_row = $this ->_order_model->where( "order_sn='$order_sn'" )->save( $data );
             $find = $this ->_order_model->where( "order_sn='$order_sn'" )->field( 'location_id,goods_id,mobile,goods_number' )->find();
             //根據goods_id查找card_name對應的上網時長
             $goods_model =M( 'goods' );
             $card_model =M( 'card' );
             $card_name = $goods_model ->where( "id={$find['goods_id']}" )->getField( 'card_name' );
             $duration = $card_model ->where( "location_id={$find['location_id']} and card_name='$card_name'" )->order( 'id desc' )->getField( 'duration' );
             $incre_time =( $find [ 'goods_number' ]) *  $duration ;
             $user_model =M( 'user' );
             $mobile = $find [ 'mobile' ];
             $user_info = $user_model ->where( "user_name='{$mobile}'" )->field( 'id,end_time' )->find();
             $affected_row2 = $user_model ->where( "user_name='{$mobile}'" )->setInc( 'usable_time' , $incre_time );
             //若是end_time 大於當前的時間戳就累計,不然就更新:使用當前時間戳 加上 $incre_time
             if ( $user_info [ 'end_time' ] >= time()){
                 $user_model ->where( "user_name='{$mobile}'" )->setInc( 'end_time' , $incre_time );
             } else {
                 $update_data [ 'end_time' ]=time()+ $incre_time ;
                 $user_model ->where( "user_name='{$mobile}'" )->save( $update_data );
             }
             if ( empty ( $affected_row )){
                 $this ->_log_pay_callbacks->setException(L( 'ERROR_FAILED_UPDATE_ORDER' ),  $this ->_log_pay_callbacks->getException( 'code' ));
                 throw  new  \Exception();
             }
             if ( empty ( $affected_row2 )){
                 $this ->_log_pay_callbacks->setException(L( 'ERROR_FAILED_UPDATE_USABLETIME' ),  $this ->_log_pay_callbacks->getException( 'code' ));
                 throw  new  \Exception();
             }
             //提交更新
             if ( $affected_row  &&  $affected_row2 ) {
                 $this ->_order_model->commit();
                 return  true;
             }
         } catch (\Exception  $e ){
             $this ->_order_model->rollback();
             return  false;
         }
     }
 
     private  function  insert_order(){
         $gw_id  = isset( $this ->params[ 'gw_id' ]) ? FilterComponent::get( $this ->params[ 'gw_id' ]) :  '' ;
         if  ( empty ( $gw_id )) {
             exit ( '400_EMPTY_GWID' );
         }
         $router =M( 'router' );
         $location_id = $router ->where( "gw_id='$gw_id'" )->getField( 'supplier_location_id' );
         $goods_number  = isset( $this ->params[ 'goods_number' ]) ? FilterComponent::get( $this ->params[ 'goods_number' ], 'int' ) :  '' ;
         if  ( empty ( $goods_number )) {
             exit ( '400_EMPTY_GOODSNUMBER' );
         }
         $mobile  = isset( $this ->params[ 'mobile' ]) ? FilterComponent::get( $this ->params[ 'mobile' ]) :  '' ;
         if  (!preg_match( '/^1[0-9]{10}$/' , $mobile )) {
             exit ( '400_ERROR_MOBILE' );
         }
         $user =M( 'user' );
         //查詢充值號碼是否存在
         $user_name = $user ->where( "user_name='$mobile'" )->getField( 'user_name' );
         if (! $user_name ){
             exit ( '400_EMPTY_USERNAME' );
         }
         $goods_id  = isset( $this ->params[ 'goods_id' ]) ? FilterComponent::get( $this ->params[ 'goods_id' ], 'int' ) :  '' ;
         if  ( empty ( $goods_id )) {
             exit ( '400_EMPTY_GOODSID' );
         }
 
         $goods =M( 'goods' );
         $unit_price = $goods ->where( "id=$goods_id" )->getField( 'unit_price' );
         $this ->params[ 'WIDtotal_fee' ]= $unit_price  $goods_number ;
 
         $data [ 'location_id' ]= $location_id ;
         $data [ 'mobile' ]= $mobile ;
         $data [ 'goods_id' ]= $goods_id ;
         $data [ 'goods_type' ]=1; //1表明充值卡
         $data [ 'goods_number' ]= $goods_number ;
         $data [ 'total_price' ]= $this ->params[ 'WIDtotal_fee' ];
         $data [ 'pay_type' ]=OrderModel::PAY_TYPE_ALIPAY; //支付寶
         //執行添加操做
         $insert_id = $this ->_order_service->update( $data ,false);
//        var_dump($this->_order_service->getError());
//        var_dump($this->_order_service->model->getError());
//        var_dump($this->_order_service->model->getlastsql());die;
         if ( $insert_id ){
             $this ->params[ 'WIDout_trade_no' ]= $this ->_order_model->where( "id=$insert_id" )->getField( 'order_sn' );
             /**************************請求參數**************************/
             //支付類型
             $payment_type  "1" ;
             //必填,不能修改
 
             //商戶訂單號
             $out_trade_no  $this ->params[ 'WIDout_trade_no' ];
             //商戶網站訂單系統中惟一訂單號,必填
 
             $this ->params[ 'WIDsubject' ]= 'pengwifi_card' ;
             //訂單名稱
             $subject  $this ->params[ 'WIDsubject' ];
             //必填
 
             //付款金額
             $total_fee  $this ->params[ 'WIDtotal_fee' ];
             //必填
 
             //$this->params['WIDshow_url']=trim(C('ALIMOBILEPAY_CONFIG.product_url'));
             $this ->params[ 'WIDshow_url' ]= $_SERVER [ 'HTTP_REFERER' ];
             //商品展現地址
             $show_url  $this ->params[ 'WIDshow_url' ];
             //必填,需以http://開頭的完整路徑,例如:http://www.商戶網址.com/myorder.html
 
             //訂單描述
             $body  $this ->params[ 'WIDbody' ];
             //選填
 
             //超時時間
             $it_b_pay  $this ->params[ 'WIDit_b_pay' ];
             //選填
 
             //錢包token
             $extern_token  $this ->params[ 'WIDextern_token' ];
             //選填
 
             /************************************************************/
             //構造要請求的參數數組,無需改動
             $parameter  array (
                 "service"  =>  "alipay.wap.create.direct.pay.by.user" ,
                 "partner"  => trim(C( 'ALIMOBILEPAY_CONFIG.partner' )),
                 "seller_id"  => trim(C( 'ALIMOBILEPAY_CONFIG.seller_id' )),
                 "payment_type"   =>  $payment_type ,
                 "notify_url"     => trim(C( 'ALIMOBILEPAY_CONFIG.notify_url' )),
                 "return_url"     => trim(C( 'ALIMOBILEPAY_CONFIG.return_url' )),
                 "out_trade_no"   =>  $out_trade_no ,
                 "subject"    =>  $subject ,
                 "total_fee"  =>  $total_fee ,
                 "show_url"   =>  $show_url ,
                 "body"   =>  $body ,
                 "it_b_pay"   =>  $it_b_pay ,
                 "extern_token"   =>  $extern_token ,
                 "_input_charset"     => trim( strtolower (C( 'input_charset' )))
             );
 
             $alipay_config =C( 'ALIMOBILEPAY_CONFIG' );
 
             //創建請求
             $alipaySubmit  new  \AlipaySubmit( $alipay_config );
             //創建請求,以表單HTML形式構造(默認),經測試post方法不行
             $html_text  $alipaySubmit ->buildRequestForm( $parameter , "get" "確認" );
 
             echo  $html_text ;
         } else {
             echo  'fail' ;
         }
     }
}

若是您閱讀過此文章有所收穫,請爲我頂一個,若是文章中有錯誤的地方,歡迎指出。

相互學習,共同進步!

相關文章
相關標籤/搜索