2023年12月11日发(作者:各大手机处理器排行榜)
微信H5支付
微信H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在商品展示页确认使用微信支付时,商户发起本服务唤起微信客户端进
行支付。主要用于触屏版的手机浏览器请求微信支付的场景,可以方便的从外部浏览器唤醒微信支付。
支付流程
1. 用户在商品页完成下单并使用微信支付进行支付
2. 由商户后台向微信支付发起下单请求即调用统一下单接口
3. 统一下单接口返回支付相关参数给商户后台,商户通过专用链接调起微信支付中间页面。
4. 在中间页进行H5权限校验以及安全性检测
5. 若支付成功商户后台会收到微信端的异步通知
6. 用户在微信支付收银平台完成支付或取消支付并返回商品页面
7. 商品在展示页引导用户主动发起支付结果的查询
8. 商户后台判断是否 接收到微信端的支付结果通知,若没有后台调用订单查询接口确定订单状态。
9. 展示最终的订单支付结果给用户
支付流程
开发流程
1. 用户下单时选择微信支付
2. 商户进行业务逻辑处理并调用微信统一下单接口,微信H5交易类型为
trade_type=MWEB
。
3. 调用下单接口成功时,微信会返回包含支付跳转URL等相关参数,商户通过参数
mweb_url
调起支付中间页。
4. 在中间页微信会进行H5权限的校验
5. 支付成功,微信会向商户发送异步结果通知。
统一下单接口
class Wechat
{
/**
* 组建签名
* @param array $params 请求参数
* @param string $key 秘钥
*/
public static function getSign($params, $key)
{
foreach ($params as $k=>$v) {
if (!$v) {
unset($params[$k]);
}
}
ksort($params);
$paramStr = '';
foreach ($params as $k => $v) {
$paramStr = $paramStr . $k . '=' . $v . '&';
}
$paramStr = $paramStr . 'key='.$key;
$sign = strtoupper(md5($paramStr));
return $sign;
}
/**
* 将数组转为XML
* @param array $params 支付请求参数
*/
public static function getXml($params)
{
if(!is_array($params)|| count($params) <= 0) {
return false;
}
$xml = "
foreach ($params as $key=>$val) {
if (is_numeric($val)) {
$xml.="<".$key.">".$val."".$key.">";
} else {
$xml.="<".$key.">".$key.">";
}
}
$xml.="";
return $xml;
}
/*官方统一下单*/
public static function unifiedOrder($appid, $mch_id, $key, $total_fee, $out_trade_no, $body, $notify_url, $wap_url)
{
$spbill_create_ip = $_SERVER["REMOTE_ADDR"]; //获得用户设备IP
$nonce_str=MD5($out_trade_no);//随机字符串
$trade_type = 'MWEB';//交易类型 微信H5交易
$scene_info ='{"h5_info":{"type":"Wap","wap_url":"'.$wap_url.'","wap_name":"支付"}}';//场景信息 必要参数
$signA ="appid=$appid&body=$body&mch_id=$mch_id&nonce_str=$nonce_str¬ify_url=$notify_url&out_trade_no=$out_trade_no&scene_info=$scene_info&spbill_
//拼接字符串 注意顺序微信有个测试网址 顺序按照他的来 直接点下面的校正测试 包括下面XML 是否正确
$strSignTmp = $signA."&key=$key";
// MD5 后转换成大写
$sign = strtoupper(MD5($strSignTmp));
//拼接成XML格式 *XML格式文件要求非常严谨不能有空格这点一定要注意
$post_data="
";
";
$url = "/pay/unifiedorder";//微信下单接口连接不用更改
$headers = array();
$headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
$headers[] = 'Connection: Keep-Alive';
$headers[] = 'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3';
$headers[] = 'Accept-Encoding: gzip, deflate';
$headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20100101 Firefox/22.0';
$dataxml = self::httpPost($url,$post_data,$headers);//传参调用curl请求
$objectxml = (array)simplexml_load_string($dataxml,'SimpleXMLElement',LIBXML_NOCDATA); //将微信返回的XML 转换成数组
//支付跳转URL
$mweb_url = "";
if($objectxml['return_code'] == 'SUCCESS'){
$mweb_url= $objectxml['mweb_url'];
}
return $mweb_url;//跳转后台获取的支付连接
}
/**
* 获取微信支付中间页deepLink参数
* @param string $url 微信返回的mweb_url
* @param string $ip 用户端IP
*/
public static function getDeeplink($url, $ip)
{
$headers = array("X-FORWARDED-FOR:$ip", "CLIENT-IP:$ip");
ob_start();
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_HTTPHEADER , $headers );
curl_setopt ($ch, CURLOPT_REFERER, "");
curl_setopt( $ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; Android 6.0.1; OPPO R11s Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Ch
curl_exec($ch);
curl_close ($ch);
$out = ob_get_contents();
ob_clean();
$a = preg_match('/weixin:wap.*/',$out, $str);
if ($a) {
return substr($str[0], 0, strlen($str[0])-1);
} else {
return '';
}
}
public static function httpPost($url='',$post_data=array(),$header=array(),$timeout=30) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
public static function getClientIp($type = 0) {
$type = $type ? 1 : 0;
$ip = 'unknown';
if ($ip !== 'unknown') return $ip[$type];
if($_SERVER['HTTP_X_REAL_IP']){//nginx 代理模式下,获取客户端真实 IP
$ip=$_SERVER['HTTP_X_REAL_IP'];
}elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的 ip
$ip = $_SERVER['HTTP_CLIENT_IP'];
$ip = $_SERVER['HTTP_CLIENT_IP'];
}elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown',$arr);
if(false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
}elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的 ip 地址
}else{
$ip=$_SERVER['REMOTE_ADDR'];
}
// IP 地址合法验证
$long = sprintf("%u",ip2long($ip));
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
}
请求参数
请求微信统一下单接口
appid
微信公众号iD
mch_id
账户号
nonce_str
随机字符串,不长于32位
sign
签名
body
商品描述
out_trade_no
商户订单号,不长于32位
total_fee
总金额,以分为单位
spbill_create_ip
用户端请求支付时的IP
notify_url
异步通知回调地址,必须是可直接访问地址,不能携带参数
trade_type
交易类型,如H5则是
MWEB
签名生成
1. 参与生成签名的参数必须非空
2. 参数按照
ASCII
码由小到大排序,参数名区分大小写
3. 按照上述规则,将参数拼接成如
k1=v1&
的字符串
4. 将上一步得到的字符串拼接上
key
, 如
k1=v1&k2=v2&key=192006250b4c09247ec02e
5. 再将最后得到的字符串进行
MD5
加密,再转为大写,即为最终的
sign
值。
$signA ="appid=$appid&body=$body&mch_id=$mch_id&nonce_str=$nonce_str¬ify_url=$notify_url&out_trade_no=$out_trade_no&scene_info=$scene_info&spbi
//拼接字符串 注意顺序微信有个测试网址 顺序按照他的来 直接点下面的校正测试 包括下面XML 是否正确
$strSignTmp = $signA."&key=$key";
// MD5 后转换成大写
$sign = strtoupper(MD5($strSignTmp));
封装函数
/**
* 生成签名
* @return 签名
*/
function makeSign( $params ){
//签名步骤一:按字典序排序数组参数
ksort($params);
$string = makeUrlParams($params);
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$this->key;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 将参数拼接为url: key=value&key=value
* @param $params
* @return string
*/
function makeUrlParams( $params ){
$string = '';
if( !empty($params) ){
$array = array();
foreach( $params as $key => $value ){
$array[] = $key.'='.$value;
}
$string = implode("&",$array);
}
return $string;
}
用户IP
H5支付要求商户在统一下单接口中上传用户真实IP地址
spbill_create_ip
,为保证微信端获取的用户
IP
地址与商户端获取的一致,提供了以下获取
用户
IP
的指引。
没有代理的情况
在商户的前端接入层没有做代理的情况下获取IP的方式比较简单,直接获取
REMOTE_ADDR
即可。
有代理的情况
在有代理的情况下,因为要代替客户端去访问服务器,所以,当请求包经过反向代理后,在代理服务器这里这个IP数据包的IP包头做了修改,最终
后端WEB服务器得到的数据包的头部源IP地址是代理服务器的IP地址。这样一来,后端服务器的程序就无法获取用户的真实IP。
在Nginx中配置中加入
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
函数封装
function getClientIp($type = 0) {
$type = $type ? 1 : 0;
$ip = 'unknown';
if ($ip !== 'unknown') return $ip[$type];
if(isset($_SERVER["HTTP_X_REAL_IP"]) && !empty($_SERVER['HTTP_X_REAL_IP'])){//nginx 代理模式下,获取客户端真实 IP
$ip=$_SERVER['HTTP_X_REAL_IP'];
}elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的 ip
$ip = $_SERVER['HTTP_CLIENT_IP'];
}elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown',$arr);
if(false !== $pos) unset($arr[$pos]);
$ip = trim($arr[0]);
}elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的 ip 地址
}else{
$ip=$_SERVER['REMOTE_ADDR'];
}
// IP 地址合法验证
$long = sprintf("%u",ip2long($ip));
$ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
return $ip[$type];
}
接口返回
return_code 为 SUCCESS 代表支付请求成功 /** * 错误代码 * @param $code 服务器输出的错误代码 * return string */ function getErrorMessage( $code ){ $errList = array( 'NOAUTH' => '商户未开通此接口权限', 'NOTENOUGH' => '用户帐号余额不足', 'ORDERNOTEXIST' => '订单号不存在', 'ORDERPAID' => '商户订单已支付,无需重复操作', 'ORDERCLOSED' => '当前订单已关闭,无法支付', 'SYSTEMERROR' => '系统错误!系统超时', 'APPID_NOT_EXIST' => '参数中缺少APPID', 'MCHID_NOT_EXIST' => '参数中缺少MCHID', 'APPID_MCHID_NOT_MATCH' => 'appid和mch_id不匹配', 'LACK_PARAMS' => '缺少必要的请求参数', 'OUT_TRADE_NO_USED' => '同一笔交易不能多次提交', 'SIGNERROR' => '参数签名结果不正确', 'XML_FORMAT_ERROR' => 'XML格式错误', 'REQUIRE_POST_METHOD' => '未使用post传递参数 ', 'POST_DATA_EMPTY' => 'post数据不能为空', 'NOT_UTF8' => '未使用指定编码格式', ); if( array_key_exists( $code , $errList ) ){ return $errList[$code]; } } mweb_url 为支付跳转页,此时客户端通过 mweb_url 已经可以调起微信支付 mweb_url 是为拉起微信支付收银台的中间页面,可通过访问该URL来拉起微信客户端,完成支付, mweb_url 的有效期为 5 分钟。 /cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx28f231619da2&package=1037687096 正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在 mweb_url 后拼接上 redirect_url 参数(需对 redirect_url 进 行 urlencode 处理),来指定回调页面。 在设置 redirect_url 时需要注意的是设置的回跳地址的域名与申请H5支付时提交的授权域名是否一致,否则会出现错误,若想通过 redirect_url 跳转 到本域名之外的地址,可以先跳回域名内地址之后再跳出到域名外的地址。 /cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx28f231619da2&package=1037687096&redirect_url=https%3A% 微信的移动端 WAP 支付,在非微信浏览器中,需要通过微信的 H5 支付方式来唤起微信支付客户端,实现是通过拼装一个 DeepLink 链接,并访问 该链接达到唤起微信支付客户端,客户端也可以通过此 DeepLink 值直接调起支付。通过测试发现,存在浏览器兼容问题。 weixin://wap/pay?prepayid%3Dwx222ac74712189&package=2656135616&noncestr=1542888966&sign=e31dbc2d1231708ff8a982b15a 在得到微信返回的 mweb_url 参数后,可在服务端进一步获得 DeepLink 。 /** * 获取微信支付中间页deepLink参数 * @param string $url 微信返回的mweb_url * @param string $ip 用户端IP */ function getDeeplink(string $url, string $ip) { $headers = array("X-FORWARDED-FOR:$ip", "CLIENT-IP:$ip"); ob_start(); $ch = curl_init(); curl_setopt ($ch, CURLOPT_URL, $url); curl_setopt ($ch, CURLOPT_HTTPHEADER , $headers ); curl_setopt ($ch, CURLOPT_REFERER, ""); curl_setopt( $ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; Android 6.0.1; OPPO R11s Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chro curl_exec($ch); curl_close ($ch); $out = ob_get_contents(); ob_clean(); $a = preg_match('/weixin:wap.*/',$out, $str); if ($a) { return substr($str[0], 0, strlen($str[0])-1); } else { return ''; } } 支付中间页 支付中间页 支付中间页 同步回调 正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在 MWEB_URL 后拼接上 redirect_url 参数,来指定回调页 面。需对 redirect_url 进行 urlencode 处理。 $redirect_url = ""; $redirect_url = urlencode($redirect_url); // 返回至指定页面 $mweb_url = $mweb_url."&redirect_url=".$redirect_url; 由于设置 redirect_url 后,回跳指定页面的操作可能发生在: 1. 微信支付中间页调起微信收银台后超过5秒 2. 用户点击“取消支付“或支付完成后点“完成”按钮。 因此无法保证页面回跳时,支付流程已结束,所以商户设置的 redirect_url 地址不能自动执行查单操作,应让用户去点击按钮触发查单操作。 异步回调 // 接收text/xml数据 $str_Post = file_get_contents("php://input"); // 禁止引用外部XML实体 libxml_disable_entity_loader(true); $postObj = simplexml_load_string($str_Post, 'SimpleXMLElement', LIBXML_NOCDATA); $postObj = json_encode($postObj); $postObj = json_decode($postObj, true); $result_code = trim($postObj["result_code"]); $return_code = trim($postObj["return_code"]); $sign = trim($postObj["sign"]); $out_trade_no = trim($postObj["out_trade_no"]); if ($result_code == 'SUCCESS' && $return_code == 'SUCCESS') { } 类库 /** * 微信支付服务器端下单 * 微信APP支付文档地址: /wiki/doc/api/?chapter=8_6 * 使用示例 * 构造方法参数 * 'appid' => //填写微信分配的公众账号ID * 'mch_id' => //填写微信支付分配的商户号 * 'notify_url'=> //填写微信支付结果回调地址 * 'key' => //填写微信商户支付密钥 * ); * 统一下单方法 * $obj = new WxPay($options); * $params['body'] = '商品描述'; //商品描述 * $params['out_trade_no'] = '01407'; //自定义的订单号,不能重复 * $params['total_fee'] = '100'; //订单金额 只能为整数 单位为分 * $params['trade_type'] = 'APP'; //交易类型 JSAPI | NATIVE |APP | WAP * $obj->unifiedOrder( $params ); */ class WxPay { //接口API URL前缀 const API_URL_PREFIX = ''; //下单地址URL const UNIFIEDORDER_URL = "/pay/unifiedorder"; //查询订单URL const ORDERQUERY_URL = "/pay/orderquery"; //关闭订单URL const CLOSEORDER_URL = "/pay/closeorder"; //公众账号ID private $appid; //商户号 private $mch_id; //随机字符串 private $nonce_str; //签名 private $sign; //商品描述 private $body; //商户订单号 private $out_trade_no; //支付总金额 private $total_fee; //终端IP private $spbill_create_ip; //支付结果回调通知地址 private $notify_url; //交易类型 //交易类型 private $trade_type; //支付密钥 private $key; //证书路径 private $SSLCERT_PATH; private $SSLKEY_PATH; //所有参数 private $params = []; public function __construct($appid, $mch_id, $notify_url, $key) { $this->appid = $appid; $this->mch_id = $mch_id; $this->notify_url = $notify_url; $this->key = $key; } /** 场景信息 必要参数*/ public function buildSceneInfo($wap_url, $wap_name="支付") { return '{"h5_info":{"type":"Wap","wap_url":"'.$wap_url.'","wap_name":"'.$wap_name.'"}}'; } /** * 下单方法 * @param $params 下单参数 */ public function unifiedOrder( $params ){ $this->body = $params['body']; $this->out_trade_no = $params['out_trade_no']; $this->total_fee = $params['total_fee']; $this->trade_type = $params['trade_type']; $this->scene_info = $params['scene_info']; $this->nonce_str = $this->genRandomString(); $this->spbill_create_ip = $_SERVER['REMOTE_ADDR']; $this->params['appid'] = $this->appid; $this->params['mch_id'] = $this->mch_id; $this->params['nonce_str'] = $this->nonce_str; $this->params['body'] = $this->body; $this->params['out_trade_no'] = $this->out_trade_no; $this->params['total_fee'] = $this->total_fee; $this->params['spbill_create_ip'] = $this->spbill_create_ip; $this->params['notify_url'] = $this->notify_url; $this->params['trade_type'] = $this->trade_type; $this->params['scene_info'] = $this->scene_info; //获取签名数据 $this->sign = $this->makeSign( $this->params ); $this->params['sign'] = $this->sign; $xml = $this->makeArrayToXml($this->params); $response = $this->postXmlCurl($xml, self::API_URL_::UNIFIEDORDER_URL); if( !$response ){ return false; } $result = $this->parseXmlToArray( $response ); if( !empty($result['result_code']) && !empty($result['err_code']) ){ $result['err_msg'] = $this->getErrorMessage( $result['err_code'] ); } return $result; } /** * 获取微信支付中间页deepLink参数 * 获取deepLink客户端通过次链接可直接调起支付 * @param string $url 微信返回的mweb_url * @param string $ip 用户端IP */ public function getDeepLink($url, $ip) { $headers = ["X-FORWARDED-FOR:$ip", "CLIENT-IP:$ip"]; ob_start(); $ch = curl_init(); curl_setopt ($ch, CURLOPT_URL, $url); curl_setopt ($ch, CURLOPT_HTTPHEADER , $headers ); curl_setopt ($ch, CURLOPT_REFERER, ""); curl_setopt( $ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; Android 6.0.1; OPPO R11s Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Ch curl_exec($ch); curl_close ($ch); $out = ob_get_contents(); ob_clean(); $a = preg_match('/weixin:wap.*/',$out, $str); if ($a) { return substr($str[0], 0, strlen($str[0])-1); } else { return ''; } } /** * 查询订单信息 * @param $out_trade_no 订单号 * @return array */ public function orderQuery( $out_trade_no ){ $this->params['appid'] = $this->appid; $this->params['mch_id'] = $this->mch_id; $this->params['nonce_str'] = $this->genRandomString(); $this->params['out_trade_no'] = $out_trade_no; //获取签名数据 $this->sign = $this->makeSign( $this->params ); $this->params['sign'] = $this->sign; $xml = $this->makeArrayToXml($this->params); $response = $this->postXmlCurl($xml, self::API_URL_::ORDERQUERY_URL); if( !$response ){ return false; } $result = $this->parseXmlToArray( $response ); if( !empty($result['result_code']) && !empty($result['err_code']) ){ $result['err_msg'] = $this->getErrorMessage( $result['err_code'] ); } return $result; } /** * 关闭订单 * @param $out_trade_no 订单号 * @return array */ public function closeOrder( $out_trade_no ){ $this->params['appid'] = $this->appid; $this->params['mch_id'] = $this->mch_id; $this->params['nonce_str'] = $this->genRandomString(); $this->params['out_trade_no'] = $out_trade_no; //获取签名数据 $this->sign = $this->makeSign( $this->params ); $this->params['sign'] = $this->sign; $xml = $this->makeArrayToXml($this->params); $response = $this->postXmlCurl($xml, self::API_URL_::CLOSEORDER_URL); if( !$response ){ return false; } $result = $this->parseXmlToArray( $response ); return $result; } /** * * 获取支付结果通知数据 * return array */ public function getNotifyData(){ //获取通知的数据 //获取通知的数据 $xml = $GLOBALS['HTTP_RAW_POST_DATA']; $data = array(); if( empty($xml) ){ return false; } $data = $this->parseXmlToArray( $xml ); if( !empty($data['return_code']) ){ if( $data['return_code'] == 'FAIL' ){ return false; } } return $data; } /** * 接收通知成功后应答输出XML数据 * @param string $xml */ public function replyNotify(){ $data['return_code'] = 'SUCCESS'; $data['return_msg'] = 'OK'; $xml = $this->makeArrayToXml( $data ); echo $xml; die(); } /** * 生成APP端支付参数 * @param $prepayid 预支付id */ public function getAppPayParams( $prepayid ){ $data = []; $data['appid'] = $this->appid; $data['partnerid'] = $this->mch_id; $data['prepayid'] = $prepayid; $data['package'] = 'Sign=WXPay'; $data['noncestr'] = $this->genRandomString(); $data['timestamp'] = time(); $data['sign'] = $this->makeSign( $data ); return $data; } /** * 生成签名 * @return 签名 */ public function makeSign( $params ){ //签名步骤一:按字典序排序数组参数 ksort($params); $string = $this->ToUrlParams($params); //签名步骤二:在string后加入KEY $string = $string . "&key=".$this->key; //签名步骤三:MD5加密 $string = md5($string); //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } /** * 将参数拼接为url: key=value&key=value * @param $params * @return string */ public function ToUrlParams( $params ){ $string = ''; if( !empty($params) ){ $array = array(); foreach( $params as $key => $value ){ $array[] = $key.'='.$value; } $string = implode("&",$array); } return $string; } /** * 输出xml字符 * 输出xml字符 * @param $params 参数名称 * return string 返回组装的xml **/ public function makeArrayToXml( $params ){ if(!is_array($params)|| count($params) <= 0) { return false; } $xml = " foreach ($params as $key=>$val) { if (is_numeric($val)){ $xml.="<".$key.">".$val."".$key.">"; }else{ $xml.="<".$key.">".$key.">"; } } $xml.=""; return $xml; } /** * 将xml转为array * @param string $xml * return array */ public function parseXmlToArray($xml){ if(!$xml){ return false; } //将XML转为array //禁止引用外部xml实体 libxml_disable_entity_loader(true); $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $data; } /** * 获取毫秒级别的时间戳 */ private static function getMillisecond(){ //获取毫秒的时间戳 $time = explode ( " ", microtime () ); $time = $time[1] . ($time[0] * 1000); $time2 = explode( ".", $time ); $time = $time2[0]; return $time; } /** * 产生一个指定长度的随机字符串,并返回给用户 * @param type $len 产生字符串的长度 * @return string 随机字符串 */ private function genRandomString($len = 32) { $chars = array( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ); $charsLen = count($chars) - 1; // 将数组打乱 shuffle($chars); $output = ""; for ($i = 0; $i < $len; $i++) { $output .= $chars[mt_rand(0, $charsLen)]; } return $output; } /** * 以post方式提交xml到对应的接口url * * * @param string $xml 需要post的xml数据 * @param string $url url * @param bool $useCert 是否需要证书,默认不需要 * @param int $second url执行超时时间,默认30s * @throws WxPayException */ private function postXmlCurl($xml, $url, $useCert = false, $second = 30){ $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if($useCert == true){ //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); //curl_setopt($ch,CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); //curl_setopt($ch,CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH); } //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); //返回结果 if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); curl_close($ch); return false; } } /** * 错误代码 * @param $code 服务器输出的错误代码 * return string */ public function getErrorMessage( $code ){ $errList = array( 'NOAUTH' => '商户未开通此接口权限', 'NOTENOUGH' => '用户帐号余额不足', 'ORDERNOTEXIST' => '订单号不存在', 'ORDERPAID' => '商户订单已支付,无需重复操作', 'ORDERCLOSED' => '当前订单已关闭,无法支付', 'SYSTEMERROR' => '系统错误!系统超时', 'APPID_NOT_EXIST' => '参数中缺少APPID', 'MCHID_NOT_EXIST' => '参数中缺少MCHID', 'APPID_MCHID_NOT_MATCH' => 'appid和mch_id不匹配', 'LACK_PARAMS' => '缺少必要的请求参数', 'OUT_TRADE_NO_USED' => '同一笔交易不能多次提交', 'SIGNERROR' => '参数签名结果不正确', 'XML_FORMAT_ERROR' => 'XML格式错误', 'REQUIRE_POST_METHOD' => '未使用post传递参数 ', 'POST_DATA_EMPTY' => 'post数据不能为空', 'NOT_UTF8' => '未使用指定编码格式', ); if( array_key_exists( $code , $errList ) ){ return $errList[$code]; } } }
发布者:admin,转转请注明出处:http://www.yc00.com/num/1702297432a1198796.html
评论列表(0条)