分享微信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>
 
最后聲明本博客非本人原創,也是借鑒他人自己在加以修改,歡迎轉載評論!

 


免責聲明!

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



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