分享微信h5支付经验

  1 <?php
  2 //use Flight;
  3     /**
  4      * 微信支付服务器端下单
  5      * 微信APP支付文档地址:  https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_6
  6      * 使用示例
  7      *  构造方法参数
  8      *      'appid'     =>  //填写微信分配的公众账号ID
  9      *      'mch_id'    =>  //填写微信支付分配的商户号
 10      *      'notify_url'=>  //填写微信支付结果回调地址
 11      *      'key'       =>  //填写微信商户支付密钥
 12      *  );
 13      *  统一下单方法
 14      *  $WechatAppPay = new wechatAppPay($options);
 15      *  $params['body'] = '商品描述';                   //商品描述
 16      *  $params['out_trade_no'] = '1217752501201407';   //自定义的订单号,不能重复
 17      *  $params['total_fee'] = '100';                   //订单金额 只能为整数 单位为分
 18      *  $params['trade_type'] = 'APP';                  //交易类型 JSAPI | NATIVE |APP | WAP 
 19      *  $wechatAppPay->unifiedOrder( $params );
 20      */
 21     class wechatAppPay
 22     {   
 23         //接口API URL前缀
 24         const API_URL_PREFIX = 'https://api.mch.weixin.qq.com';
 25         //下单地址URL
 26         const UNIFIEDORDER_URL = "/pay/unifiedorder";
 27         //查询订单URL
 28         const ORDERQUERY_URL = "/pay/orderquery";
 29         //关闭订单URL
 30         const CLOSEORDER_URL = "/pay/closeorder";
 31         //公众账号ID
 32         private $appid;
 33         //商户号
 34         private $mch_id;
 35         //随机字符串
 36         private $nonce_str;
 37         //签名
 38         private $sign;
 39         //商品描述
 40         private $body;
 41         //商户订单号
 42         private $out_trade_no;
 43         //支付总金额
 44         private $total_fee;
 45         //终端IP
 46         private $spbill_create_ip;
 47         //支付结果回调通知地址
 48         private $notify_url;
 49         //交易类型
 50         private $trade_type;
 51         //支付密钥
 52         private $key;
 53         //证书路径
 54         private $SSLCERT_PATH;
 55         private $SSLKEY_PATH;
 56         //所有参数
 57         private $params = array();
 58         public function __construct($appid, $mch_id, $notify_url, $key)
 59         {
 60             $this->appid = $appid;
 61             $this->mch_id = $mch_id;
 62             $this->notify_url = $notify_url;
 63             $this->key = $key;
 64         }
 65         /**
 66          * 下单方法
 67          * @param   $params 下单参数
 68          */
 69         public function unifiedOrder( $params ){
 70             $this->body = $params['body'];
 71             $this->out_trade_no = $params['out_trade_no'];
 72             $this->total_fee = $params['total_fee'];
 73             $this->trade_type = $params['trade_type'];
 74             $this->scene_info = $params['scene_info'];
 75             $this->nonce_str = $this->genRandomString();
 76             $this->spbill_create_ip = $_SERVER['REMOTE_ADDR'];
 77             $this->params['appid'] = $this->appid;
 78             $this->params['mch_id'] = $this->mch_id;
 79             $this->params['nonce_str'] = $this->nonce_str;
 80             $this->params['body'] = $this->body;
 81             $this->params['out_trade_no'] = $this->out_trade_no;
 82             $this->params['total_fee'] = $this->total_fee;
 83             $this->params['spbill_create_ip'] = $this->spbill_create_ip;
 84             $this->params['notify_url'] = $this->notify_url;
 85             $this->params['trade_type'] = $this->trade_type;
 86             $this->params['scene_info'] = $this->scene_info;
 87             //获取签名数据
 88             $this->sign = $this->MakeSign( $this->params );
 89             $this->params['sign'] = $this->sign;
 90             $xml = $this->data_to_xml($this->params);
 91             $response = $this->postXmlCurl($xml, self::API_URL_PREFIX.self::UNIFIEDORDER_URL);
 92             if( !$response ){
 93                 return false;
 94             }
 95             $result = $this->xml_to_data( $response );
 96             if( !empty($result['result_code']) && !empty($result['err_code']) ){
 97                 $result['err_msg'] = $this->error_code( $result['err_code'] );
 98             }
 99             return $result;
100         }
101         /**
102          * 查询订单信息
103          * @param $out_trade_no     订单号
104          * @return array
105          */
106         public function orderQuery( $out_trade_no ){
107             $this->params['appid'] = $this->appid;
108             $this->params['mch_id'] = $this->mch_id;
109             $this->params['nonce_str'] = $this->genRandomString();
110             $this->params['out_trade_no'] = $out_trade_no;
111             //获取签名数据
112             $this->sign = $this->MakeSign( $this->params );
113             $this->params['sign'] = $this->sign;
114             $xml = $this->data_to_xml($this->params);
115             $response = $this->postXmlCurl($xml, self::API_URL_PREFIX.self::ORDERQUERY_URL);
116             if( !$response ){
117                 return false;
118             }
119             $result = $this->xml_to_data( $response );
120             if( !empty($result['result_code']) && !empty($result['err_code']) ){
121                 $result['err_msg'] = $this->error_code( $result['err_code'] );
122             }
123             return $result;
124         }
125         /**
126          * 关闭订单
127          * @param $out_trade_no     订单号
128          * @return array
129          */
130         public function closeOrder( $out_trade_no ){
131             $this->params['appid'] = $this->appid;
132             $this->params['mch_id'] = $this->mch_id;
133             $this->params['nonce_str'] = $this->genRandomString();
134             $this->params['out_trade_no'] = $out_trade_no;
135             //获取签名数据
136             $this->sign = $this->MakeSign( $this->params );
137             $this->params['sign'] = $this->sign;
138             $xml = $this->data_to_xml($this->params);
139             $response = $this->postXmlCurl($xml, self::API_URL_PREFIX.self::CLOSEORDER_URL);
140             if( !$response ){
141                 return false;
142             }
143             $result = $this->xml_to_data( $response );
144             return $result;
145         }
146         /**
147          * 
148          * 获取支付结果通知数据
149          * return array
150          */
151         public function getNotifyData(){
152             //获取通知的数据
153             $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
154             //echo 123;die;
155             $data = array();
156             if( empty($xml) ){
157                 return false;
158             }
159             $data = $this->xml_to_data( $xml );
160             if( !empty($data['return_code']) ){
161                 if( $data['return_code'] == 'FAIL' ){
162                     return false;
163                 }
164             }
165             return $data;
166         }
167         /**
168          * 接收通知成功后应答输出XML数据
169          * @param string $xml
170          */
171         public function replyNotify(){
172             $data['return_code'] = 'SUCCESS';
173             $data['return_msg'] = 'OK';
174             $xml = $this->data_to_xml( $data );
175             echo $xml;
176             die();
177         }
178          /**
179           * 生成APP端支付参数
180           * @param  $prepayid   预支付id
181           */
182          public function getAppPayParams( $prepayid ){
183              $data['appid'] = $this->appid;
184              $data['partnerid'] = $this->mch_id;
185              $data['prepayid'] = $prepayid;
186              $data['package'] = 'Sign=WXPay';
187              $data['noncestr'] = $this->genRandomString();
188              $data['timestamp'] = time();
189              $data['sign'] = $this->MakeSign( $data ); 
190              return $data;
191          }
192         /**
193          * 生成签名
194          *  @return 签名
195          */
196         public function MakeSign( $params ){
197             //签名步骤一:按字典序排序数组参数
198             ksort($params);
199             $string = $this->ToUrlParams($params);
200             //签名步骤二:在string后加入KEY
201             $string = $string . "&key=".$this->key;
202             //签名步骤三:MD5加密
203             $string = md5($string);
204             //签名步骤四:所有字符转为大写
205             $result = strtoupper($string);
206             return $result;
207         }
208         /**
209          * 将参数拼接为url: key=value&key=value
210          * @param   $params
211          * @return  string
212          */
213         public function ToUrlParams( $params ){
214             $string = '';
215             if( !empty($params) ){
216                 $array = array();
217                 foreach( $params as $key => $value ){
218                     $array[] = $key.'='.$value;
219                 }
220                 $string = implode("&",$array);
221             }
222             return $string;
223         }
224         /**
225          * 输出xml字符
226          * @param   $params     参数名称
227          * return   string      返回组装的xml
228          **/
229         public function data_to_xml( $params ){
230             if(!is_array($params)|| count($params) <= 0)
231             {
232                 return false;
233             }
234             $xml = "<xml>";
235             foreach ($params as $key=>$val)
236             {
237                 if (is_numeric($val)){
238                     $xml.="<".$key.">".$val."</".$key.">";
239                 }else{
240                     $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
241                 }
242             }
243             $xml.="</xml>";
244             return $xml; 
245         }
246         /**
247          * 将xml转为array
248          * @param string $xml
249          * return array
250          */
251         public function xml_to_data($xml){  
252             if(!$xml){
253                 return false;
254             }
255             //将XML转为array
256             //禁止引用外部xml实体
257             libxml_disable_entity_loader(true);
258             $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);        
259             return $data;
260         }
261         /**
262          * 获取毫秒级别的时间戳
263          */
264         private static function getMillisecond(){
265             //获取毫秒的时间戳
266             $time = explode ( " ", microtime () );
267             $time = $time[1] . ($time[0] * 1000);
268             $time2 = explode( ".", $time );
269             $time = $time2[0];
270             return $time;
271         }
272         /**
273          * 产生一个指定长度的随机字符串,并返回给用户 
274          * @param type $len 产生字符串的长度
275          * @return string 随机字符串
276          */
277         private function genRandomString($len = 32) {
278             $chars = array(
279                 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
280                 "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
281                 "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
282                 "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
283                 "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
284                 "3", "4", "5", "6", "7", "8", "9"
285             );
286             $charsLen = count($chars) - 1;
287             // 将数组打乱 
288             shuffle($chars);
289             $output = "";
290             for ($i = 0; $i < $len; $i++) {
291                 $output .= $chars[mt_rand(0, $charsLen)];
292             }
293             return $output;
294         }
295         /**
296          * 以post方式提交xml到对应的接口url
297          * 
298          * @param string $xml  需要post的xml数据
299          * @param string $url  url
300          * @param bool $useCert 是否需要证书,默认不需要
301          * @param int $second   url执行超时时间,默认30s
302          * @throws WxPayException
303          */
304         private function postXmlCurl($xml, $url, $useCert = false, $second = 30){       
305             $ch = curl_init();
306             //设置超时
307             curl_setopt($ch, CURLOPT_TIMEOUT, $second);
308             curl_setopt($ch,CURLOPT_URL, $url);
309             curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
310             curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
311             //设置header
312             curl_setopt($ch, CURLOPT_HEADER, FALSE);
313             //要求结果为字符串且输出到屏幕上
314             curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
315             if($useCert == true){
316                 //设置证书
317                 //使用证书:cert 与 key 分别属于两个.pem文件
318                 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
319                 //curl_setopt($ch,CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH);
320                 curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
321                 //curl_setopt($ch,CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH);
322             }
323             //post提交方式
324             curl_setopt($ch, CURLOPT_POST, TRUE);
325             curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
326             //运行curl
327             $data = curl_exec($ch);
328             //返回结果
329             if($data){
330                 curl_close($ch);
331                 return $data;
332             } else { 
333                 $error = curl_errno($ch);
334                 curl_close($ch);
335                 return false;
336             }
337         }
338         /**
339           * 错误代码
340           * @param  $code       服务器输出的错误代码
341           * return string
342           */
343          public function error_code( $code ){
344              $errList = array(
345                 'NOAUTH'                =>  '商户未开通此接口权限',
346                 'NOTENOUGH'             =>  '用户帐号余额不足',
347                 'ORDERNOTEXIST'         =>  '订单号不存在',
348                 'ORDERPAID'             =>  '商户订单已支付,无需重复操作',
349                 'ORDERCLOSED'           =>  '当前订单已关闭,无法支付',
350                 'SYSTEMERROR'           =>  '系统错误!系统超时',
351                 'APPID_NOT_EXIST'       =>  '参数中缺少APPID',
352                 'MCHID_NOT_EXIST'       =>  '参数中缺少MCHID',
353                 'APPID_MCHID_NOT_MATCH' =>  'appid和mch_id不匹配',
354                 'LACK_PARAMS'           =>  '缺少必要的请求参数',
355                 'OUT_TRADE_NO_USED'     =>  '同一笔交易不能多次提交',
356                 'SIGNERROR'             =>  '参数签名结果不正确',
357                 'XML_FORMAT_ERROR'      =>  'XML格式错误',
358                 'REQUIRE_POST_METHOD'   =>  '未使用post传递参数 ',
359                 'POST_DATA_EMPTY'       =>  'post数据不能为空',
360                 'NOT_UTF8'              =>  '未使用指定编码格式',
361              ); 
362              if( array_key_exists( $code , $errList ) ){
363                 return $errList[$code];
364              }
365          }
366     } 

<?php namespace weixinpayApp; include 'wechatAppPay.php'; class wxh5{ //$data 金额和订单号 public function wxh5Request($data){ $appid = 'wxdf************'; $mch_id = '*********';//商户号 $key = '32位申请时自己设置的';//商户key $notify_url = "https://www.gujia.la/wxnativepay";//回调地址 $wechatAppPay = new \wechatAppPay($appid, $mch_id, $notify_url, $key); $params['body'] = '估价啦'; //商品描述 $params['out_trade_no'] = $data['oid']; //自定义的订单号 $params['total_fee'] = '1'; //订单金额 只能为整数 单位为分 $params['trade_type'] = 'MWEB'; //交易类型 JSAPI | NATIVE | APP | WAP $params['scene_info'] = '{"h5_info": {"type":"Wap","wap_url": "https://www.diugou.com","wap_name": "估价啦"}}'; $result = $wechatAppPay->unifiedOrder( $params ); $url = $result['mweb_url'].'&redirect_url=https%3A%2F%2Fwww.gujia.la ';//redirect_url 是支付完成后返回的页面 return $url; } }

调用实例wxh5.php

返回错误参数和状态对官方的文档一个一个排查就可以了没几个问题
 
if($result['return_code'] == 'SUCCESS'){
  $mweb_url= $result['mweb_url'];
}
这里返回的是一个支付连接,请求这个链接就可以激活微信进行支付*需要注意的是不能在pc上打开连接需要在手机上打开
 
这里需要注意不能使用 header("Location:$mweb_url");这样的方法直接请求连接,需要将返回的连接返回到前台尽心请求
前台代码不多说了就是一个js请求跳转后台获取的支付连接
 
 
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
window.location.href="<?php echo $mweb_url;?>";
</script>
</body>
</html>
 
最后声明本博客非本人原创,也是借鉴他人自己在加以修改,欢迎转载评论!

猜你喜欢

转载自www.cnblogs.com/zhangzhijian/p/8855721.html