最近几日对于h5的支付做了调试,发现调用其实非常简单,这里提示几点在调试过程中出现的错误
1 config中的 key 是微信商户的key ,填写的时候需要注意。如果填写的是微信公众号的key,在其他传递的参数都是正确的情况下,爆出的是签名错误。
2 前端调用如果是h5页面,使用 window.location.href = 返回的链接 直接做链接跳转 ,后可加 redirect_url 参数,指定在支付成功后的跳转页面。但是加参数的跳转链接 需要使用 encodeURI 做处理,如图
3 如果是前后端分离,前端使用的是vue的话 建议前端在支付部分直接做 完全性的 h5页面处理 进行链接跳转,请勿在页面中使用任何有关vue的东西,这是比较简单的针对vue 微信h5支付 做处理的方式
至于vue直接处理微信h5支付 前端尝试未通过,有兴趣的可以联合前端查看处理下,也欢迎反馈问题,这里会非常感谢,并标明提供的相关信息。
以上就是微信h5支付在处理过程中需要注意的几点,而相对于支付宝的h5,虽然同微信h5都是直接根据返回的链接做跳转 但是不需要做什么麻烦的处理 相对来说还是比较方便的
之前在csdn 上,为了方便修改更新现在移动到这里,那边的会直接删除
从工作至今四年 终于将自己封装的写个差不多了 其中有借鉴各种支付方式 可有雷同 但一些内容为自己整理封装后的效果 希望多多提议
目前测试通过无误的有 app wx 其他间隔时间过长 不太确定 会在之后不断调整 力求完美
<?php namespace pay; /** * 微信支付 */ class Wxpay { // 需了解一个应用对应一个appid 需要多个共存时就需要多个设置 private $config = [ 'app_appid'=>'*********', 'wx_appid'=>'******', 'mobile_appid'=>'*****', 'mch_id'=>'*********', 'key'=>'******', ]; // 设置证书 退款时需要 private $cert=[ 'apiclient_cert'=>'', 'apiclient_key'=>'', ]; private $appid=''; private $calltype = 'app';//调用端口 app wx mobile pc wx_small private $unifiedorder = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//微信支付接口 private $refund = 'https://api.mch.weixin.qq.com/secapi/pay/refund';//微信退款提交接口 private $refundquery = 'https://api.mch.weixin.qq.com/pay/refundquery';//微信退款查询接口 public function __construct($payTypeInfo=[]){ if($payTypeInfo) { foreach ($payTypeInfo as $k => $val) { if($k=='calltype') { $this->calltype=$val['val']!=''?$val['val']:$this->calltype; }else{ $this->config[$k]=$val['val']; } } } $appname=$this->calltype.'_appid'; $this->appid=$this->config[$appname]; // 线上 linux // $this->cert['apiclient_cert']=EXTEND_PATH.'pay/wxcert/apiclient_cert.pem'; // $this->cert['apiclient_key']=EXTEND_PATH.'pay/wxcert/apiclient_key.pem'; // 本地 // $this->cert['apiclient_cert']=EXTEND_PATH.'pay\wxcert\apiclient_cert.pem'; // $this->cert['apiclient_key']=EXTEND_PATH.'pay\wxcert\apiclient_key.pem'; } public function index($data) { // 端口检测 $res=$this->calltypeCheck($data); if($res['code']==0){ return $res; } // 初次提交进行数据判断 // FLog($data); $res=$this->wxresult($this->postdata($data),$this->config['key']); FLog($res); if($res['return_code']=='FAIL') { return ['code'=>0,'msg'=>$res['return_msg']]; } if($res['result_code']=='FAIL') { return ['code'=>0,'msg'=>$res['err_code'].' '.$res['err_code_des']]; } $calltype=$this->calltype; return $this->$calltype($data,$res); } // 微信公众号 public function wx($data,$res) { $newjsdata=[ 'appId'=>$this->appid, 'nonceStr'=>'"'.rand(1000,9999).'"', 'package'=>"prepay_id=".$res['prepay_id'],// 'signType'=>"MD5", 'timeStamp'=>'"'.time().'"', 'paySign'=>'', ]; $newjsdata=getsign($newjsdata,$this->config['key'],'paySign','key','1'); return ['code'=>1,'msg'=>'请支付','data'=>['str'=>$newjsdata]]; } // 微信小程序 public function wx_small($data,$res) { $nonceStr=(string)rand(1000,9999); $timeStamp=(string)time(); $newjsdata=[ 'appId'=>$this->appid, 'nonceStr'=>$nonceStr, 'package'=>"prepay_id=".$res['prepay_id'],// 'signType'=>"MD5", 'timeStamp'=>$timeStamp, 'paySign'=>'', ]; $newjsdata=getsign($newjsdata,$this->config['key'],'paySign','key',1); $returndata['time']=$timeStamp; $returndata['order']=$newjsdata; return ['code'=>1,'msg'=>'请支付','data'=>$returndata]; } // app支付 public function app($data,$res) { $newdata=[ 'appid'=>$res['appid'], 'partnerid'=>$res['mch_id'], 'prepayid'=>$res['prepay_id'], 'package'=>"Sign=WXPay",// 'noncestr'=>rand(1000,9999), 'timestamp'=>time(), ]; $newdata=getsign($newdata,$this->config['key'],'sign','key','1'); // 外壳app的操作 // $buff = ""; // foreach ($newdata as $k => $v) { // if($v != "" && !is_array($v)){ // $buff .= $k . "=" . $v . "&"; // } // } // $buff = trim($buff, "&"); // $url='http://www.ruidao888.com/home/order/index.html?type=wxpay&'.$buff; return ['code'=>1,'msg'=>'请支付','data'=>['type'=>'app','str'=>$newdata]]; } public function mobile($data,$res) { //$web_link='http://pnceshi.zzyuyou.com/web/index/app_download'; //$url=$res['mweb_url'].'&redirect_url='.urlencode($web_link); //以上两行为测试支付成功后指定返回的页面,一般写在前端比较好,这里只做事例查看 $url=$res['mweb_url']; return ['code'=>1,'msg'=>'请支付','data'=>['type'=>'app','url'=>$url]]; } public function postdata($data) { $trade_type=$this->calltypeD('trade_type'); $postdata=[ 'appid'=>$this->appid, 'mch_id'=>$this->config['mch_id'], 'nonce_str'=>rand(10000,99999), 'body'=>$data['subject'], 'attach'=>$data['attach'],//对什么表进行操作 'out_trade_no'=>$data['pay_sn'], 'total_fee'=>$data['total']*100, 'spbill_create_ip'=>request()->ip(), 'notify_url'=>$data['notify_url'], 'trade_type'=>$trade_type,// ]; $postdata=$this->ReArr_postdata($postdata,$data); // dump($postdata); // die; return $postdata; } // 重组数据 public function ReArr_postdata($postdata,$data) { if($this->calltype=='wx' || $this->calltype=='wx_small') { /*微信公众号小程序必须*/ $postdata['openid']=$data['openid']; }else if($this->calltype=='app') { // 正常字段就行 }else if($this->calltype=='mobile') { $ccD=cc('web_link,web_name'); $scene_info=[ 'type'=>'Wap',//场景类型 'wap_url'=>$ccD['web_link'],//WAP网站URL地址 'wap_name'=>$ccD['web_name'], ]; //好像之前跟现在的不太一样 不过对号入座,改成这个也是没有问题的 $h5_info=[ 'h5_info'=>$scene_info ]; $postdata['scene_info']=json_encode($h5_info); }else if($this->calltype=='jsapi') { $ccD=cc('web_link,web_name'); $scene_info=[ 'type'=>'Wap',//场景类型 'wap_url'=>$ccD['web_link'],//WAP网站URL地址 'wap_name'=>$ccD['web_name'], ]; $postdata['scene_info']=json_encode($scene_info); } $postdata['body']=trim($postdata['body']); $postdata['total_fee']=(int)$postdata['total_fee']; return $postdata; } // 根据类型获取想要的信息 public function calltypeD($field) { /** * JSAPI--JSAPI支付(或小程序支付) 测试通过 * NATIVE--Native支付 * APP--app支付 测试通过 * MWEB--H5支付 * MICROPAY--付款码支付 */ $D=[ 'wx'=>['trade_type'=>'JSAPI'], 'wx_small'=>['trade_type'=>'JSAPI'],//小程序 'app'=>['trade_type'=>'APP'], 'mobile'=>['trade_type'=>'MWEB'], ]; return isset($D[$this->calltype])?$D[$this->calltype][$field]:''; } // 端口检测 public function calltypeCheck($data) { // 对配置信息进行检测 if($this->appid=='') { return ['code'=>0,'msg'=>'请先配置 appid']; } if($this->config['mch_id']=='') { return ['code'=>0,'msg'=>'请先配置 mch_id']; } if($this->config['key']=='') { return ['code'=>0,'msg'=>'请先配置 key']; } if($this->calltype=='wx' && (!isset($data['openid']) || $data['openid']=='')) { return ['code'=>0,'msg'=>'缺少微信openid,请先获取']; } return ['code'=>1,'msg'=>'检测通过']; } // 退款申请并查询 public function refund_and_query($data) { $res=$this->refund($data); if($res['code']==0) { return $res; } $res2=$this->refundquery($res['refund_data']); return $res2; } // 退款申请 public function refund($data) { $out_refund_no = date('YmdHis').rand(1000, 9999);//订单号每次随机 $postdata=[ 'appid'=>$this->appid, 'mch_id'=>$this->config['mch_id'], 'nonce_str'=>rand(10000,99999), 'out_trade_no'=>$data['out_trade_no'], 'out_refund_no'=>$out_refund_no, 'total_fee'=>$data['total_fee']*100, 'refund_fee'=>$data['refund_fee']*100, ]; $res=$this->wxresult_refund($postdata,$this->config['key'],$this->cert); // echo var_dump($res); // die; if($res['return_code']=='FAIL') { return ['code'=>0,'msg'=>$res['return_msg']]; } if($res['result_code']=='FAIL') { return ['code'=>0,'msg'=>$res['err_code'].' '.$res['err_code_des']]; } return ['code'=>1,'msg'=>'申请成功','refund_data'=>$res]; } // 退款查询 public function refundquery($data) { // 查询是否退款成功 $s_postdata=[ 'appid'=>$this->appid, 'mch_id'=>$this->config['mch_id'], 'nonce_str'=>rand(10000,99999), 'sign_type'=>'MD5', 'refund_id'=>$data['refund_id'],//退款单号 ]; $res2=$this->wxresult_refund($s_postdata,$this->config['key'],$this->cert,$this->refundquery); if($res2['return_code']=='FAIL') { return ['code'=>0,'msg'=>$res2['return_msg']]; } if($res2['result_code']=='FAIL') { return ['code'=>0,'msg'=>$res2['err_code'].' '.$res2['err_code_des']]; } return ['code'=>1,'msg'=>'修改成功','refund_data'=>$res2]; } private function wxresult_refund($data,$key,$cert,$url='') { $url=$url==''?$this->refund:$url; return postXmlSSLCurl(arrayToXml(getsign($data,$key)),$cert,$url); } private function wxresult($data,$key) { return $this->wxCur(arrayToXml(getsign($data,$key))); } private function wxCur($data,$url="") { $url=$url==''?$this->unifiedorder:$url; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $response=curl_exec($ch); if($response) { curl_close($ch); return json_decode(json_encode(simplexml_load_string($response, 'simplexmlelement', LIBXML_NOCDATA)),true); }else{ $error = curl_errno($ch); // echo "curl出错,错误码:$error"."<br>"; // echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>"; curl_close($ch); return ['return_code'=>'FAIL','return_msg'=>"curl出错,错误码:$error"]; } } } function postXmlSSLCurl($xml,$cert,$url,$second=30) { // echo '2222222222222'; // dump($xml); // die; $ch = curl_init(); //超时时间 curl_setopt($ch,CURLOPT_TIMEOUT,$second); //这里设置代理,如果有的话 //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8'); //curl_setopt($ch,CURLOPT_PROXYPORT, 8080); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); //设置header curl_setopt($ch,CURLOPT_HEADER,FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE); //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 //默认格式为PEM,可以注释 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, $cert['apiclient_cert']); //默认格式为PEM,可以注释 curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY, $cert['apiclient_key']); //post提交方式 curl_setopt($ch,CURLOPT_POST, true); curl_setopt($ch,CURLOPT_POSTFIELDS,$xml); $response=curl_exec($ch); if($response) { curl_close($ch); return json_decode(json_encode(simplexml_load_string($response, 'simplexmlelement', LIBXML_NOCDATA)),true); }else{ $error = curl_errno($ch); // echo "curl出错,错误码:$error"."<br>"; // echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>"; curl_close($ch); return ['return_code'=>'FAIL','return_msg'=>"curl出错,错误码:$error"]; } } function getsign($data,$key,$newkey='sign',$linkstr='key',$ksorts=1) { if($ksorts==1){ ksort($data); } $buff = ""; foreach ($data as $k => $v) { if($k != $newkey && $v != "" && !is_array($v)){ $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); $buff=$buff."&".$linkstr."=".$key; $sign=strtoupper(MD5($buff)); $data[$newkey]=$sign; return $data; } // 签名验证 对比 // function makeSign($data) // { // $config_key='z4YoADhVnknugpG8BN2S4bvJZIV9s7QC'; // // 去空 // $data=array_filter($data); // //签名步骤一:按字典序排序参数 // ksort($data); // //将数组转成url形式 // $string_a=http_build_query($data); // $string_a=urldecode($string_a); // //签名步骤二:在string后加入KEY // $string_sign_temp=$string_a."&key=".$config_key; // //签名步骤三:MD5加密 // $sign = md5($string_sign_temp); // // 签名步骤四:所有字符转为大写 // $result=strtoupper($sign); // return $result; // } function arrayToXml($arr) { // dump($arr); // die; $xml = "<xml>"; foreach ($arr as $key=>$val) { $xml.="<".$key.">".$val."</".$key.">"; } $xml.="</xml>"; return $xml; }
在规矩中有自己的不规矩,走出不一样的路才是属于自己的路