1、统一下单前计算签名
除付款码支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
- 1、将需要发送的必填参数名按照ASCII码从小到大排序
- 2、参数数组使用URL键值对的格式(即key1=value1&key2=value2…)拼接为字符串
- 3、在字符串后边拼接商户平台设置的密钥key
- 4、使用md5加密拼接的字符串
- 5、得到字符串全部转化为大写就是需要的签名
注意:
◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
◆ key设置路径:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>密钥设置
$post['appid'] = $appid; //微信公众号的appid
$post['mch_id'] = $mch_id; //微信支付分配的商户号(微信支付平台查看)
$post['nonce_str'] = $nonce_str; //随机字符串,长度要求在32位以内。
$post['body'] = $body; //商品描述
$post['out_trade_no'] = $out_trade_no; //商户订单号
$post['total_fee'] = intval($total_fee); //总金额 单位为分 最低为一分钱 必须是整数
$post['spbill_create_ip'] = $_SERVER['SERVER_ADDR']; //服务器终端的ip
$post['notify_url'] = $notify_url; //微信回调地址 异步通知
$post['trade_type'] = $trade_type; //交易类型 JSAPI -JSAPI支付
$post['openid'] = $openid; //
//计算签名
$sign = $this->MakeSign($post,$KEY); //签名
//计算签名
function MakeSign( $params,$KEY){
//签名步骤一:按字典序排序数组参数
ksort($params);
$string = $this->ToUrlParams($params); //参数进行拼接key=value&k=v
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$KEY;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
//拼接字符串
function ToUrlParams( $params ){
$string = '';
if( !empty($params) ){
$array = array();
foreach( $params as $key => $value ){
$array[] = $key.'='.$value;
}
$string = implode("&",$array);
}
return $string;
}
2、统一下单并判断是否成功
- 1、将统一下单的必要参数和签名转化为xml格式
- 2、以post方式提交到接口:https://api.mch.weixin.qq.com/pay/unifiedorder
- 3、将返回的xml格式数据转为数组格式
当返回的return_code(状态码)和result_code(业务结果)都为success时,统一下单成功,否则根据err_code(错误代码)和err_code_des(错误代码描述)的值判断错误类型。
错误代码详情参考【微信支付常见错误和统一下单错误码详情】
$post_xml=$this->ToXml(array_merge($post,['sign'=>$sign]));
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$xml = $this->http_request($url,$post_xml); //POST方式请求http
$array = $this->xml2array($xml); //将【统一下单】api返回xml数据转换成数组,全要大写
//数组转xml
function ToXml($array){
$xml = "<xml>";
foreach ($array as $key=>$val){
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
//获取xml里面数据,转换成array
private function xml2array($xml){
$p = xml_parser_create();
xml_parse_into_struct($p, $xml, $vals, $index);
xml_parser_free($p);
$data = "";
foreach ($index as $key=>$value) {
if($key == 'xml' || $key == 'XML') continue;
$tag = $vals[$value[0]]['tag'];
$value = $vals[$value[0]]['value'];
$data[$tag] = $value;
}
return $data;
}
//curl请求
public function http_request($url,$data = null,$headers=array()){
$curl = curl_init();
if( count($headers) >= 1 ){
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)){
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
3、微信内H5调起支付
后台:进行签名的参数
名称 | 变量名 | 描述 |
---|---|---|
公众号id | appId | 商户注册具有支付权限的公众号成功后即可获得 |
时间戳 | timeStamp | 当前的时间 |
随机字符串 | nonceStr | 随机字符串,不长于32位 |
订单详情扩展字符串 | package | 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=*** |
签名方式 | signType | 签名类型,默认为MD5,支持HMAC-SHA256和MD5 |
注意:
- 1、签名规则和第一步的规则相同。
- 2、注意此处需与统一下单的签名类型一致
- 3、时间戳必须为字符串类型
$time = time();
$tmp['appId'] = $appid;
$tmp['timeStamp'] = "$time";
$tmp['nonceStr'] = "$nonce_str";
$tmp['package'] = 'prepay_id='.$array['PREPAY_ID'];
$tmp['signType'] = 'MD5';
$data['paySign'] = $this->MakeSign($tmp,$KEY); //签名,具体签名方案参见微信公众号支付帮助文档;
$data['timeStamp'] = "$time"; //时间戳
$data['nonceStr'] = "$nonce_str"; //随机字符串
$data['package'] = 'prepay_id='.$array['PREPAY_ID']; //统一下单接口返回的 prepay_id 参数值
$data['signType'] = 'MD5'; //签名算法,暂支持 MD5
前端:然后在前端页面调用微信JS-SDK中的【chooseWXPay】微信支付->发起一个微信支付请求,具体详情参考【微信公众号调用微信接口上传图片https://blog.csdn.net/G925010178/article/details/106462391】注入配置信息【wx.comfig】。
<script>
wx.chooseWXPay({
timestamp: data.data['timeStamp'], // 支付签名时间戳,支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: data.data['nonceStr'], // 支付签名随机串,不长于 32 位
package: data.data['package'], // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*
signType: data.data['signType'], // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: data.data['paySign'], // 支付签名
success: function (res) {
//支付成功后的回调函数
},
cancel:function(res){
//微信支付回调
},
fail:function(res){
//接口调用失败时执行的回调函数
}
});
</script>
参考链接
微信支付-统一下单https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
微信支付-接口规则-安全规范https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
微信网页开发-JS-SDK说明文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#58