微信H5支付

微信H5支付


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."";

} else {

$xml.="<".$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="$appid$body$mch_id$nonce_str$notify_url<

";

";

$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

$appid

$body

$mch_id

$nonce_str

$notify_url

$out_trade_no

$scene_info

$spbill_create_ip

$total_fee

$trade_type

$sign

签名生成

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."";

}else{

$xml.="<".$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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信