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