支付宝手机网站即时交易 自己封装的类

  1 <?php
  2     /*
  3         支付宝封装类
  4         采用 rsa 生成签名模式
  5     */
  6     class Alipay {
  7         private $_config                     = array();
  8         private $_alipay_gateway_new         = 'https://mapi.alipay.com/gateway.do?'; //支付宝网关地址(新)
  9         private $_https_verify_url             = 'https://mapi.alipay.com/gateway.do?service=notify_verify&'; //校验用到
 10         private $_fix_config                   = array();
 11 
 12 
 13         public function __construct() {
 14             $this->_makeFixconfig();
 15         }
 16 
 17         //读取固定参数
 18         private function _makeFixconfig() {
 19             $this->_fix_config             = array(
 20                 'service'                 => 'alipay.wap.create.direct.pay.by.user',
 21                 '_input_charset'         => 'utf-8',
 22                 'sign_type'             => 'RSA',
 23                 'seller_id'             => '',
 24                 'private_key_path'         => VENDOR_PATH . '/Alipay/rsa_private_key.pem',//私密key
 25                 'ali_public_key_path'     => VENDOR_PATH . '/Alipay/alipay_public_key.pem',//公开key
 26                 'cacert'                 => VENDOR_PATH . '/Alipay/cacert.pem',//https请求用到验证
 27                 'transport'             => 'http',
 28                 'payment_type'             => 1,//固定参数
 29             );
 30         }
 31 
 32         /*
 33             config 需要参数
 34                 partner                         //合作身份者id,以2088开头的16位纯数字
 35                 notify_url                         //阿里处理后,异步处理url地址,http开头
 36                 return_url                         //阿里处理后,同步处理url地址
 37             
 38                 out_trade_no                     //订单号
 39                 subject                         //商品名称
 40                 total_fee                         //商品金额
 41                 
 42 
 43         */
 44         //生成配置文件,并请求
 45         public function run($config) {
 46             foreach ($config as $value) {
 47                 if(empty($value))
 48                     exit('缺少必要参数');
 49             }    
 50 
 51             $this->_fix_config['seller_id']         = $config['partner'];
 52 
 53             $this->_config                             = array_merge($this->_fix_config,$config);
 54 
 55             //签名,请求
 56             $this->_makeSign();
 57         }
 58 
 59 
 60         //生成签名信息,并发送请求
 61         private function _makeSign() {
 62             //获取签名需要的参数
 63             $param                 = array(
 64                     '_input_charset'    => trim($this->_config['_input_charset']),
 65                     'service'             => trim($this->_config['service']),
 66                     'partner'             => trim($this->_config['partner']),
 67                     'seller_id'            => trim($this->_config['seller_id']),
 68                     'payment_type'        => trim($this->_config['payment_type']),
 69                     'notify_url'        => trim($this->_config['notify_url']),
 70                     'return_url'        => trim($this->_config['return_url']),
 71                     'out_trade_no'        => trim($this->_config['out_trade_no']),
 72                     'subject'            => trim($this->_config['subject']),
 73                     'total_fee'            => trim($this->_config['total_fee']),                    
 74             );
 75 
 76             //对数组进行排序
 77             ksort($param);
 78             reset($param);
 79             
 80             //读取pem信息,生成key
 81             if(!file_exists($this->_config['private_key_path']) || !file_exists($this->_config['ali_public_key_path']))
 82                 exit('缺少pem必要参数,请检查');
 83 
 84             if(!function_exists('openssl_pkey_get_private'))
 85                 exit('不支持openssl');
 86 
 87             $query_string         = '';
 88             $query_string         = $this->_makeSignString($param);
 89 
 90             $private_key_info    = file_get_contents($this->_config['private_key_path']);
 91 
 92             $private_key_res     = openssl_pkey_get_private($private_key_info);
 93 
 94             $sign                 = '';
 95             openssl_sign($query_string, $sign, $private_key_res);
 96 
 97             openssl_free_key($private_key_res);
 98 
 99             //base64编码
100             $param['sign']                 = base64_encode($sign);
101 
102             //验证签名
103             // $this->_checkSign($query_string,$param['sign']);
104 
105             $param['sign_type']            = trim($this->_config['sign_type']);
106 
107             //触发表单请求,使用get模拟请求会失败
108             $this->_formPush($param,'确认');
109         }
110 
111         /*
112             --不能用模拟请求
113             发送get请求
114             $url         请求url地址
115         */
116         private function _makeGetHttp($url) {
117             $curl                 = curl_init($url);
118 
119             // curl_setopt($curl, CURLOPT_POST, 1);
120             curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
121             curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
122             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
123             curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
124             curl_setopt($curl, CURLOPT_CAINFO,$this->_fix_config['cacert']);//证书地址
125             curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
126             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
127             curl_setopt($curl,    CURLOPT_USERAGENT,"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"); 
128 
129             $result             = curl_exec($curl);
130 
131             $httpCode             = curl_getinfo($curl, CURLINFO_HTTP_CODE);
132             $curlErrNo             = curl_errno($curl);
133             $curlErr             = curl_error($curl);
134 
135             curl_close($curl);
136 
137             if ($httpCode == "0") 
138                    die("Curl error number:" . $curlErrNo . " , Curl error details:" . $curlErr . "\r\n");
139             else if ($httpCode != "200") 
140                    die("Http code:" . $httpCode .  " details:" . $result . "\r\n");
141               
142             return $result;
143         }
144 
145         /*
146             采用表单提交
147         */
148         private function _formPush($para_temp,$button_name) {
149             $sHtml         = '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>支付宝通信中...</title><link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css"></head><body>';
150 
151             $sHtml         .= '<div class="container-fluid"><div class="row" style="margin-top:200px;"><div class="col-xs-10 col-md-10 col-xs-offset-1 col-md-offset-1"><span class="btn btn-success btn-block">请稍后,连接支付宝...</span></div></div></div>';
152 
153             $sHtml         .= "<div style='display:none;'><form id='alipaysubmit' name='alipaysubmit' action='".$this->_alipay_gateway_new."_input_charset=".trim(strtolower($this->_config['_input_charset']))."' method='get'>";
154             while (list ($key, $val) = each ($para_temp)) {
155                 $sHtml    .= "<input type='hidden' name='".$key."' value='".$val."'/>";
156             }
157 
158             //submit按钮控件请不要含有name属性
159             $sHtml         = $sHtml."<input type='submit' value='".$button_name."'></form></div></body></html>";
160             
161             $sHtml         = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
162             
163 
164             echo $sHtml;
165         }
166 
167 
168         /*
169             构造签名字符串
170         */
171         private function _makeSignString($arr,$type=false) {
172             $tmp_str             = '';
173             foreach ($arr as $key => $value) {
174                 if($key == 'sign' || $key == 'sign_type') 
175                     continue;
176                 $tmp_str         .= $key . '=' . $value . '&';
177             }
178 
179             return trim($tmp_str,'&');
180         }
181 
182         /*
183             验证rsa签名
184             @param         $data 返回的get或者post的总数组,
185                         $sign get['sign'] 或者 post['sign']
186             @return     bool
187         */
188         public function checkSign($data,$sign) {
189 
190             //处理数据
191             $new_data                     = array();
192             foreach ($data as $key => $value) {
193                 if($key == 'sign' || $key == 'sign_type' || $key == '_URL_' || $value == '')
194                     continue;
195                 $new_data[$key]             = $value;
196             }
197 
198             ksort($new_data);
199             reset($new_data);
200 
201             $new_data_str                 = '';
202             $new_data_str                 = $this->_makeSignString($new_data);
203 
204             $ali_public_key_info         = file_get_contents($this->_fix_config['ali_public_key_path']);
205             $ali_public_key_res         = openssl_pkey_get_public($ali_public_key_info);
206             
207             $ali_public_key_verify        = openssl_verify($new_data_str, base64_decode($sign), $ali_public_key_res);
208             openssl_free_key($ali_public_key_res);
209 
210                if(!$ali_public_key_verify)
211                    return false;
212                return true;
213         }
214 
215 
216         /*
217             验证url来源是否为支付宝
218             @param             partner 合作账号
219                             notify_id get|post返回的
220         */
221         public function checkUrlFrom($partner,$notify_id) {
222             $url             = $this->_https_verify_url . 'partner=' . $partner . '&notify_id=' . $notify_id;
223             return $this->_makeGetHttp($url);
224         }
225 
226 
227 
228 
229 
230 
231 
232 
233 
234 
235 
236     }

调用案例:

  通知页面不能有阻拦,比如说要登录才能进入之类的

 1         /*
 2             测试支付宝
 3         */
 4         public function test_alipay() {
 5             //引入
 6             Vendor('Alipay.Alipay');
 7 
 8             //生成参数
 9             $param                 = array(
10                 'partner'         => C('PARTENR'),//2088开头的支付宝信息
11                 'notify_url'     => 'http://' . $_SERVER['HTTP_HOST'] . U('Leasegoods/alipay_notify_url'),//异步通知页面
12                 'return_url'     => 'http://' . $_SERVER['HTTP_HOST'] . U('Leasegoods/alipay_return_url'),//同步通知页面
13                 'out_trade_no'     => $this->_dealOrderNum(),//订单号
14                 'subject'         => 'lxd',//商品名称
15                 'total_fee'     => '0.01',//商品价格
16 
17             );
18 
19             $alipay_obj         = new Alipay();
20             $alipay_obj->run($param);
21         }

 通知页面验证:

  同步异步都类似,第一 校验签名;第二 校验来源;其余的就是业务之类的东西了

 1 public function alipay_return_url() {
 2             $info                 = serialize($_GET);
 3             
 4             $data                 = array();
 5             //判断状态
 6             if(isset($_GET) && $_GET['is_success'] == 'T') {
 7                 $order_index         = $_GET['out_trade_no'];
 8 
 9                 //校验签名
10                 Vendor('Alipay.Alipay');
11                 $alipay_obj         = new Alipay();
12                 $res                 = $alipay_obj->checkSign($_GET,$_GET['sign']);
13 
14                 if(!$res){
15                     $data['type']     = 'error';
16                     $data['info']   = '警告:服务器校验数据失败';
17                     $this->assign('data',$data);
18                     $this->display('info');die;
19                 }
20 
21                 //校验来源
22                 $res                 = null;
23                 $res                 = $alipay_obj->checkUrlFrom(C('PARTENR'),$_GET['notify_id']);
24                 if($res == 'false'){
25                     $data['type']     = 'error';
26                     $data['info']   = '错误:服务器校验来源错误';
27                     $this->assign('data',$data);
28                     $this->display('info');die;
29                 }
30 
31                 //记录流水
32                 $alipay_log_model     = M('alipay_log');
33                 $insert_res         = $alipay_log_model->add(array('order_index' => $order_index,'type' => 'return','info' => $info));
34                 if(!$insert_res){
35                     $data['type']     = 'error';
36                     $data['info']   = '警告:用户支付成功,服务器流水记录失败';
37                     $this->assign('data',$data);
38                     $this->display('info');die;
39                 }
40 
41 
42                 //接口请求成功
43                 if($_GET['trade_status'] == 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') {
44 
45                     //交易成功
46                     $data['type']     = 'success';
47                     $data['info']   = '成功:交易成功,请返回';
48                 }else {
49 
50                    
59                     //交易失败
60                     $data['type']     = 'error';
61                     $data['info']   = '失败:支付失败,请重试';
62                 }
63             }else {
64                 //接口请求失败
65                 $data['type']     = 'error';
66                 $data['info']   = '失败:服务器调用支付宝失败';
67             }
68             $this->assign('data',$data);
69             $this->display('info');
70         }

 

posted @ 2015-06-18 15:20  栋的博客  阅读(1142)  评论(2编辑  收藏  举报
深入理解php php扩展开发 docker mongodb