簡單介紹了微信公眾號支付的申請母剥、接入、使用形导、確認支付結果等相關流程
0 系列文章
系列一 微信App支付全解析
系列二 支付寶App支付全解析
系列三 微信公眾號支付全解析
系列四 微信掃碼支付全解析
系列五 支付寶即時到賬支付全解析
系列六 微信退款全解析
系列七 支付寶退款全解析
系列八 支付寶開放平臺支付更新升級全解析
1 申請
申請步驟直接參考官方文檔
主要2個大塊:
- 申請開通公眾平臺环疼,創(chuàng)建應用
- 申請支付開通商戶平臺
全部申請通過后,獲取支付必須的參數(shù)如下:
1.1 AppID和AppSecret
公眾平臺創(chuàng)建的應用唯一標識朵耕。
登錄微信公眾平臺炫隶,進入應用詳情可查看AppID和AppSecret。
1.2 mch_id
微信支付申請完成之后阎曹,微信商戶平臺會給你的郵箱發(fā)通知郵件伪阶,里面包含開通支付的商戶信息
1.3 API秘鑰
即商戶支付秘鑰,主要負責處理通信相關參數(shù)加密处嫌。登陸微信商戶平臺(賬號密碼在微信商戶平臺發(fā)來的郵件里)
點擊左側的「賬戶設置 - API 安全」(第一次登陸會讓你安裝操作證書栅贴,請先安裝操作證書)。點擊設置密鑰锰霜,設置自己的密鑰筹误。
1.4 商戶證書
用于退款等一些需要證書驗證的接口使用。在微信商戶平臺點擊「賬戶中心 - API 安全」癣缅,點擊「下載證書」
證書下載后厨剪,打開壓縮包會看到「apiclient_cert.pem」和「apiclient_key.pem」和rootca.pem證書。
2 接入流程
參考接入文檔
主要幾個步驟:
- 統(tǒng)一下單(放在服務端友存,需要加密參數(shù))
- 生成支付參數(shù)(放在服務端,需要生成簽名)
- 調用客戶端SDK發(fā)起支付
- 服務端異步接收支付結果
2.1 統(tǒng)一下單
$appid = ""; //你的appid
$mch_id = ""; //商戶id
$wx_api_key = ""; //商戶api秘鑰
$out_trade_no = ""; //自己業(yè)務系統(tǒng)生成的交易no屡立,可以唯一標識
$client_ip = ""; //客戶端ip
$notify_url = ""; //接收支付結果通知url
$openid = ""; //微信授權獲得的openid
$UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //統(tǒng)一下單地址
$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);
//發(fā)送請求 使用封裝好的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)) {
//驗證簽名非法
}
//可以自行處理解析獲得的參數(shù)
//todo...
一些函數(shù):
/**
* 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();
}
/**
* 生成指定長度的隨機字符串(包含大寫英文字母, 小寫英文字母, 數(shù)字)
* @param $length int 需要生成的字符串的長度
* @return string 包含 大小寫英文字母 和 數(shù)字 的隨機字符串
*/
public static function randomStr($length){
//生成一個包含 大寫英文字母, 小寫英文字母, 數(shù)字 的數(shù)組
$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 帶簽名參數(shù)數(shù)組
* @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;
}
2.2 生成支付參數(shù)
客戶端需要的支付參數(shù)是帶簽名的直晨,所以最好支付參數(shù)也在服務端生成后,jsondecode后傳入客戶端即可直接調用
//生成支付參數(shù)
$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);
3. 調用支付
注:微信公眾號發(fā)起支付的路徑必須是在公眾號支付-開發(fā)配置-公眾號支付-支付授權目錄中設置中填入的支付路徑,而且不能是多級目錄
客戶端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">
輸入支付參數(shù):<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>
4 異步結果通知
注:尤其要注意通知結果驗證成功后要能正確處理重復通知罩句,放置多次發(fā)貨造成資金損失
$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>");
//業(yè)務狀態(tài)碼判斷
if ($get_para['result_code'] !== 'SUCCESS') { //狀態(tài)碼錯誤
//支付錯誤 更改訂單狀態(tài) 記錄log等
//...
}
//支付成功 更改訂單狀態(tài) 記錄log等
//todo
5 其他
- 客戶端收到同步支付結果后建議一段時間內輪詢檢查服務端,獲取服務端的結果敛摘,支付最終狀態(tài)以服務端為準
結尾
更多文章關注我的公眾號