一丶 PHP后台生成预支付交易单,返回正确的预支付交易回话标识后
调起支付,根据文档拼接微信需要的参数,这里将需要的几个方法进行示例;
传输给微信的参数要组装成xml格式发送,传如参数数组!
public function ToXml($data=array()) { if(!is_array($data) || count($data) = 0) { return '数组异常'; } $xml = ""; foreach ($data as $key=$val) { if (is_numeric($val)){ $xml.="".$key."".$val.""; }else{ $xml.="".$key.""; } } $xml.=""; return $xml; }
生成随机字符串,微信所需参数。
function rand_code(){ $str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';//62个字符 $str = str_shuffle($str); $str = substr($str,0,32); return $str;}
这里是微信比较重要的一步了,这个方法会多次用到!生成签名
private function getSign($params) { ksort($params); //将参数数组按照参数名ASCII码从小到大排序 foreach ($params as $key = $item) { if (!empty($item)) { //剔除参数值为空的参数 $newArr[] = $key.'='.$item; // 整合新的参数数组 } } $stringA = implode("", $newArr); //使用 符号连接参数 $stringSignTemp = $stringA."key="."************************"; //拼接key // key是在商户平台API安全里自己设置的 $stringSignTemp = MD5($stringSignTemp); //将字符串进行MD5加密 $sign = strtoupper($stringSignTemp); //将所有字符转换为大写 return $sign; }
传递参数给微信,生成预支付订单!接收微信返回的数据,在反给APP端,APP端调用支付接口,完成支付 !
public function wx_pay() { $nonce_str = $this-rand_code(); //调用随机字符串生成方法获取随机字符串 $data['appid'] ='wxdbc5dc*******'; //appid $data['mch_id'] = '1493*****' ; //商户号 $data['body'] = "APP支付测试"; $data['spbill_create_ip'] = $_SERVER['HTTP_HOST']; //ip地址 $data['total_fee'] = 1; //金额 $data['out_trade_no'] = time().mt_rand(10000,99999); //商户订单号,不能重复 $data['nonce_str'] = $nonce_str; //随机字符串 $data['notify_url'] = ''; 接收支付后的通知,必须为能直接访问的网址,不能跟参数 $data['trade_type'] = 'APP'; //支付方式 //将参与签名的数据保存到数组 注意:以上几个参数是追加到$data中的,$data中应该同时包含开发文档中要求必填的剔除sign以外的所有数据 $data['sign'] = $this-getSign($data); //获取签名 $xml = $this-ToXml($data); //数组转xml //curl 传递给微信方 $url = ""; //header("Content-type:text/xml"); $ch = curl_init(); curl_setopt($ch,CURLOPT_URL, $url); if(stripos($url,"https://")!==FALSE){ curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); } else { curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 } //设置header curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_POST, TRUE); //传输文件 curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); //返回结果 if($data){ curl_close($ch); //返回成功,将xml数据转换为数组. $re = $this-FromXml($data); if($re['return_code'] != 'SUCCESS'){ json("201",'签名失败'); } else{ //接收微信返回的数据,传给APP! $arr =array( 'prepayid' =$re['prepay_id'], 'appid' = 'wxdbc5dc*****', 'partnerid' = '14937****', 'package' = 'Sign=WXPay', 'noncestr' = $nonce_str, 'timestamp' =time(), ); //第二次生成签名 $sign = $this-getSign($arr); $arr['sign'] = $sign; json('200','签名成功',$arr); } } else { $error = curl_errno($ch); curl_close($ch); json('201',"curl出错,错误码:$error"); } }
将xml数据转换为数组,接收微信返回数据时用到。
public function FromXml($xml) { if(!$xml){ echo "xml数据异常!"; } //将XML转为array //禁止引用外部xml实体 libxml_disable_entity_loader(true); $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $data; }
二 .支付成功后,会调用你填写的回调地址。
function wx_notify(){ //接收微信返回的数据数据,返回的xml格式 $xmlData = file_get_contents('php://input'); //将xml格式转换为数组 $data = $this-FromXml($xmlData); //用日志记录检查数据是否接受成功,验证成功一次之后,可删除。 $file = fopen('./log.txt', 'a+'); fwrite($file,var_export($data,true)); //为了防止假数据,验证签名是否和返回的一样。 //记录一下,返回回来的签名,生成签名的时候,必须剔除sign字段。 $sign = $data['sign']; unset($data['sign']); if($sign == $this-getSign($data)){ //签名验证成功后,判断返回微信返回的 if ($data['result_code'] == 'SUCCESS') { //根据返回的订单号做业务逻辑 $arr = array( 'pay_status' = 1, ); $re = M('order')-where(['order_sn'=$data['out_trade_no']])-save($arr); //处理完成之后,告诉微信成功结果! if($re){ echo '';exit(); } } //支付失败,输出错误信息 else{ $file = fopen('./log.txt', 'a+'); fwrite($file,"错误信息:".$data['return_msg'].date("Y-m-d H:i:s"),time()."rn"); } } else{ $file = fopen('./log.txt', 'a+'); fwrite($file,"错误信息:签名验证失败".date("Y-m-d H:i:s"),time()."rn"); }}
三、用PHP做微信支付签名错误,一般是什么问题
出现这种情况可能是您的网络不好,或者是没有遵循开发规范,导致数字签名失败
建议您更换更好的网络,同时根据开发规范调整后台代码。
四、其他配置
微信公众号JSAPI支付
一:配置参数
申请成功后,获取接口文件, 将所有文件放入项目根目录weixin下,在WxPay.ub.config.php中填入配置账户信息;
二:设置授权
开发者中心-网页服务-网页授权获取用户基本信息-修改;
“授权回调页面域名修改成你的域名地址即可,须保证网页授权已获得,不然会报redirect_uri 参数错误;
三:网页授权获取用户openid
js_api_call.php 请求文件中改动(所有传给微信的参数都在入口文件中接收)
$out_trade_no = $_GET[‘out_trade_no’]; //商户系统内部订单号 32个字符内
$total_fee = $_GET[‘total_fee’]*100; //订单总金额 单位为分,不能带小数点,所以须把价格乘以100,
不然获取prepay_id时会报错误(设置金额字段时最好设为小数点后2位)
p a ys t a t u s = g e tp a ys t a t u s ( pay_status = get_pay_status( paystatus=getpaystatus(out_trade_no);//查看订单支付状态
r e t u r nu r l = g e tr e t u r nu r l ( return_url = get_return_url( returnurl=getreturnurl(out_trade_no); //设置支付成功后跳转页面
//使用jsapi接口
$jsApi = new JsApi_pub();
//通过code获得openid
if (!isset($_GET[‘code’])) { //触发微信返回code码
//设置redirect_uri参数,返回code码地址,其实就是重定向到当前页面,获取用户code码参数
u r l = W x P a y C o n fp u b : : J SA P IC A L LU R L . " ? s h o w w x p a y t i t l e = 1 o u tt r a d en o = url = WxPayConf_pub::JS_API_CALL_URL."?showwxpaytitle=1out_trade_no= url=WxPayConfpub::JSAPICALLURL."?showwxpaytitle=1outtradeno=out_trade_nototal_fee=$total_fee";
$url = j s A p i − c r e a t e O a u t h U r l F o r C o d e ( jsApi-createOauthUrlForCode( jsApi−createOauthUrlForCode(url);
Header(“Location: $url”);
}else{ //获取code码,以获取openid
$code = $_GET[‘code’];
j s A p i − s e t C o d e ( jsApi-setCode( jsApi−setCode(code);
$openid = $jsApi-getOpenId(); //用户标识
}
再就是WxPayHubHelper.php文件 JsApi_pub()类下createOauthUrlForCode($redirectUrl)方法, 第二个参数
改为 u r l O b j [ " r e d i r e c tu r i " ] = u r l e n c o d e ( urlObj["redirect_uri"] = urlencode( urlObj["redirecturi"]=urlencode(redirectUrl); //对$url变量进行url编码 不然header重定向时无法获取订单号和金额
四:设置统一支付接口参数,获取prepay_id (预支付ID 微信生成的预支付 ID,用于后续接口调用中使用)
微信支付-开发配置-支付测试-测试授权目录和测试白名单添加
(支付授权目录需要精确到最细一级的目录,且在使用时,目录名称后直接加文件名,如ccc.com/weixin/)
确保你 WxPayPubHelper.php文件里 UnifiedOrder_pub 这个类的getPrepayId这个方法能正常使用
这一步的调试在 getPrepayId()内 var_dump($this-result); 就能看到错误代码;
posXml这个方法后调用了createXml这个方法
function postXml() {
$xml = $this-createXml();
//**在此处查看xml文件是否正常生成
include_once(“log_.php”);
$log = new Log_();
l o g − l o gr e s u l t ( " l o g . t x t " , log-log_result("log.txt", log−logresult("log.txt",xml);
//** 由于打印一些xml格式的文件只显示字符长度,不显示内容。于是用log的形式写在服务器上调试
$this-response = t h i s − p o s t X m l C u r l ( this-postXmlCurl( this−postXmlCurl(xml, t h i s − u r l , this-url, this−url,this-curl_timeout);
return $this-response;
}
保证xml这个变量格式如下;
参数正确,postXmlCurl才能正确提交,提交后就能得到正确的prepay_id,就能弹出支付框了
五:使用jsapi调起支付
根据prepay_id生成jsapi支付参数
j s A p i − s e t P r e p a y I d ( jsApi-setPrepayId( jsApi−setPrepayId(prepay_id);
$jsApiParameters = $jsApi-getParameters();
//调用微信JS api 支付
function jsApiCall() {
WeixinJSBridge.invoke(
‘getBrandWCPayRequest’,
,
function(res){
WeixinJSBridge.log(res.err_msg);
if(res.err_msg == “get_brand_wcpay_request:ok”) { //支付成功后
window.location.href=“” //跳转地址及订单操作,在异步页面也须处理订单,防止同步时失败
}else{
alert(“支付失败”+res.err_code+res.err_desc+res.err_msg);
}
}
);
}(每次请求callpay方法时须判断订单状态,防止重复提交)
六:通用通知接口 异步处理微信返回结果
支付完成后,notify.php中获取微信的回调
$xml = $GLOBALS[‘HTTP_RAW_POST_DATA’];
//须验证签名,并回应微信。
if($notify-checkSign() == TRUE) //签名验证通过并更新订单状态后
$notify-setReturnParameter(“return_code”,“SUCCESS”); //设置返回码,保证支付状态改变后才返回成功
//对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,
//微信会通过一定的策略(如30分钟共8次)定期重新发起通知,
$returnXml = $notify-returnXml();
echo $returnXml;(将xml数据返回微信 当return_code为SUCCESS时,不会再通知)
//当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,
如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,
以避免函数重入造成的数据混乱.
//判断是否在微信浏览器打开
function isWeiXin(){
var ua = window.navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) == ‘micromessenger’){
return true;
}else{
return false;
}
}
来源地址:https://blog.csdn.net/feixuewisdomlee/article/details/131447756