简单介绍了微信公众号支付的申请、接入、使用、确认支付结果等相关流程javascript
系列一 微信App支付全解析
系列二 支付宝App支付全解析
系列三 微信公众号支付全解析
系列四 微信扫码支付全解析
系列五 支付宝即时到帐支付全解析
系列六 微信退款全解析
系列七 支付宝退款全解析
系列八 支付宝开放平台支付更新升级全解析php
申请步骤直接参考官方文档html
主要2个大块:java
所有申请经过后,获取支付必须的参数以下:json
公众平台建立的应用惟一标识。
登陆微信公众平台,进入应用详情可查看AppID和AppSecret。api
微信支付申请完成以后,微信商户平台会给你的邮箱发通知邮件,里面包含开通支付的商户信息数组
即商户支付秘钥,主要负责处理通讯相关参数加密。登录微信商户平台(帐号密码在微信商户平台发来的邮件里)
点击左侧的「帐户设置 - API 安全」(第一次登录会让你安装操做证书,请先安装操做证书)。点击设置密钥,设置本身的密钥。安全
用于退款等一些须要证书验证的接口使用。在微信商户平台点击「帐户中心 - API 安全」,点击「下载证书」微信
证书下载后,打开压缩包会看到「apiclient_cert.pem」和「apiclient_key.pem」和rootca.pem证书。app
参考接入文档
主要几个步骤:
$appid = ""; //你的appid
$mch_id = ""; //商户id
$wx_api_key = ""; //商户api秘钥
$out_trade_no = ""; //本身业务系统生成的交易no,能够惟一标识
$client_ip = ""; //客户端ip
$notify_url = ""; //接收支付结果通知url
$openid = ""; //微信受权得到的openid
$UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //统一下单地址
$data = array();
$data['appid'] = $appid;
$data['mch_id'] =$mch_id;
$data['nonce_str'] = randomStr(20); //随机20位字符串
$data['body'] = "微信公众号支付测试";
$data['detail'] = "微信公众号支付测试detail";
$data['out_trade_no'] = $out_trade_no;
$data['total_fee'] = 1; //注意 单位是分
$data['spbill_create_ip'] = $client_ip;
$data['openid'] = $openid;
$data['notify_url'] = $notify_url;
$data['trade_type'] = "JSAPI"; //交易类型
$data['sign'] =sign($data, $wx_api_key); //签名
//转为xml格式
$xml_str = arrayToXmlStr($data);
//发送请求 使用封装好的curl_post
$result = curl_post($UNIFIED_ORDER_URL, $xml_str);
//解析获得的值
$get_data = simplexml_load_string($raw_data, 'SimpleXMLElement', LIBXML_NOCDATA);
$get_para = array();
$get_sign = "";
foreach ($get_data->children() as $child)
{
if($child->getName() == 'sign') {
$get_sign = strval($child);
} else {
$get_para[strval($child->getName())] = strval($child);
}
}
if($get_para['return_code'] !== "SUCCESS") {
//return code fail
}
//验证签名
if(!verifySign($get_sign, $get_para, $wx_api_key)) {
//验证签名非法
}
//能够自行处理解析得到的参数
//todo...复制代码
一些函数:
/** * array转成xml str * @param $arr */
public static function arrayToXmlStr($arr) {
$xml_data = new \SimpleXMLElement("<xml></xml>");
Func::arrayToXml($arr, $xml_data);
return $xml_data->asXML();
}
/** * 生成指定长度的随机字符串(包含大写英文字母, 小写英文字母, 数字) * @param $length int 须要生成的字符串的长度 * @return string 包含 大小写英文字母 和 数字 的随机字符串 */
public static function randomStr($length){
//生成一个包含 大写英文字母, 小写英文字母, 数字 的数组
$arr = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
$str = '';
$arr_len = count($arr);
for ($i = 0; $i < $length; $i++) {
$rand = mt_rand(0, $arr_len-1);
$str.=$arr[$rand];
}
return $str;
}
/** * 微信签名 * @param $para mixed 带签名参数数组 * @param $wx_key string wxkey */
public static function sign($para, $wx_key) {
$unsign_str = Func::createLinkString(Func::argSort($para)) . "&key=" . $wx_key;
$sign = strtoupper(md5($unsign_str));
return $sign;
}
/** * 微信签名验证 * @param $sign * @param $para * @param $wx_key * @return false-验证失败 true-验证成功 */
public static function verifySign($sign, $para, $wx_key) {
$unsign_str = Func::createLinkString(Func::argSort($para)) . "&key=" . $wx_key;
$sign_str = strtoupper(md5($unsign_str));
if($sign === $sign_str) {
return true;
}
return false;
}复制代码
客户端须要的支付参数是带签名的,因此最好支付参数也在服务端生成后,jsondecode后传入客户端便可直接调用
//生成支付参数
$data = array();
$data['appId'] = $appid;
$data['timeStamp'] = time();
$data['nonceStr'] = randomStr(20);
$data['package'] = "prepay_id=$prepay_id";
$data['signType'] = "MD5";
$data['timestamp'] = time();
$data['sign'] =sign($data, $wx_api_key);
$pay_param = json_encode($data);复制代码
注:微信公众号发起支付的路径必须是在公众号支付-开发配置-公众号支付-支付受权目录中设置中填入的支付路径,并且不能是多级目录
客户端js调用,封装了一个简单的js,能够直接调用:
//调用微信JS api 支付function
jsApiCall(pay_param, callback){
var pay_param_arr = eval("(" + pay_param + ")");
WeixinJSBridge.invoke(
'getBrandWCPayRequest',{
'appId':pay_param_arr.appId,
'timeStamp':pay_param_arr.timeStamp+"",
'nonceStr':pay_param_arr.nonceStr,
'package':pay_param_arr.package,
'signType':pay_param_arr.signType,
'paySign':pay_param_arr.paySign
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok") {
//成功
callback("ok");
} else if(res.err_msg == "get_brand_wcpay_request:cancel") {
//取消
callback("cancel");
} else {
//失败
callback("fail", res.err_desc);
}
} );
}
/** * 微信公众号支付 * @param pay_param 服务端生成的pay_param */
function callWxJsPay(pay_param, callback){
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall(pay_param, callback);
}
}复制代码
调用示例:
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>微信支付样例-支付</title>
<script type="text/javascript" src="../pay_h5_sdk/js/wx_jspay.js"></script>
<script type="text/javascript"> function docallpay() { var pay_param = document.getElementById('pay_param').value; callWxJsPay(pay_param, callback); } function callback(state, msg) { alert(state + " " + msg); } </script>
</head>
<body>
<br/>
<div align="center">
输入支付参数:<br /><br />
<textarea id="pay_param"></textarea> <br/><br />
<button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="docallpay()" >当即支付</button>
</div>
</body>
</html>复制代码
注:尤为要注意通知结果验证成功后要能正确处理重复通知,放置屡次发货形成资金损失
$raw_data = $GLOBALS["HTTP_RAW_POST_DATA"];
$get_data = simplexml_load_string($raw_data, 'SimpleXMLElement', LIBXML_NOCDATA);
$get_para = array();
$get_sign = "";
foreach ($get_data->children() as $child)
{
if($child->getName() == 'sign') {
$get_sign = strval($child);
} else {
$get_para[strval($child->getName())] = strval($child);
}
}
if($get_para['return_code'] !== "SUCCESS") {
//return code fail
die("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
}
//验证签名
if(!verifySign($get_sign, $get_para, $wx_api_key)) {
//验证签名非法
//todo
die("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
}
//在这其实通知已经接受成功 能够返回成功告诉微信不用再次通知了
echo("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
//业务状态码判断
if ($get_para['result_code'] !== 'SUCCESS') { //状态码错误
//支付错误 更改订单状态 记录log等
//...
}
//支付成功 更改订单状态 记录log等
//todo复制代码
更多文章关注个人公众号