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 = ""; 235 foreach ($params as $key=>$val) 236 { 237 if (is_numeric($val)){ 238 $xml.="<".$key.">".$val."".$key.">"; 239 }else{ 240 $xml.="<".$key.">$val."]]>".$key.">"; 241 } 242 } 243 $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
返回错误参数和状态对官方的文档一个一个排查就可以了没几个问题
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
if($result['return_code'] == 'SUCCESS'){
$mweb_url= $result['mweb_url'];
}
这里返回的是一个支付连接,请求这个链接就可以激活微信进行支付*需要注意的是不能在pc上打开连接需要在手机上打开
这里需要注意不能使用 header("Location:$mweb_url");这样的方法直接请求连接,需要将返回的连接返回到前台尽心请求
前台代码不多说了就是一个js请求跳转后台获取的支付连接
Document
最后声明本博客非本人原创,也是借鉴他人自己在加以修改,欢迎转载评论!