時間: 2016年8月11日
說明: 文檔主要描述微信公眾號支付開發(fā)過程中處理流程和一些遇到的問題。
1 準備工作

- 微信公眾號申請,類型選擇服務(wù)號或企業(yè)號,訂閱號不支持微信支付功能
- 微信商戶帳號申請(需要審核)
- 選擇對應(yīng)版本的 SDK 文件準備開發(fā)
2 支付原理

3 通知(同步、異步)
- 3.1 同步通知
商戶根據(jù)微信支付后返回的結(jié)果來處理自己的業(yè)務(wù)。
- 3.2 異步通知
微信支付完成后,會通過在下單時候設(shè)置的 notify_url 通知商戶支付結(jié)果,商戶通過返回的結(jié)果來處理內(nèi)部訂單然后給微信返回處理結(jié)果,如果返回成功則說明商戶校驗信息正常通知微信不需要再次通知,否則微信會在一定時間內(nèi)重復(fù)發(fā)通知給商戶。通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒。
4 開發(fā)流程
官方文檔: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
4.1 統(tǒng)一下單 unifiedorder
4.1.1 方法中需要參數(shù)都填寫,注意參數(shù)名大小寫和長度
4.1.2 統(tǒng)一下單時簽名問題
4.1.3 微信用戶 openid 獲取
4.1.4 加強下單時參數(shù)驗證
public static function unifiedOrder($wxpay_config = array(), $ssl_config = array(), $proxy_config = array())
{
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//檢測必填參數(shù)
if (!array_key_exists('out_trade_no', $wxpay_config)) {
throw new WxPayException("缺少統(tǒng)一支付接口必填參數(shù) out_trade_no!");
}
if (!array_key_exists('body', $wxpay_config)) {
throw new WxPayException("缺少統(tǒng)一支付接口必填參數(shù) body!");
}
if (!array_key_exists('total_fee', $wxpay_config)) {
throw new WxPayException("缺少統(tǒng)一支付接口必填參數(shù) total_fee!");
}
if (!array_key_exists('trade_type', $wxpay_config)) {
throw new WxPayException("缺少統(tǒng)一支付接口必填參數(shù) trade_type!");
}
//關(guān)聯(lián)參數(shù)
if ('JSAPI' == $wxpay_config['trade_type']) {
if (!array_key_exists('openid', $wxpay_config)) {
throw new WxPayException("統(tǒng)一支付接口中,缺少必填參數(shù)openid!trade_type為JSAPI時,openid為必填參數(shù)!");
}
}
if ('NATIVE' == $wxpay_config['trade_type']) {
if (!array_key_exists('product_id', $wxpay_config)) {
throw new WxPayException("統(tǒng)一支付接口中,缺少必填參數(shù)product_id!trade_type為JSAPI時,product_id為必填參數(shù)!");
}
}
if (!array_key_exists('notify_url', $wxpay_config)) {
throw new WxPayException("缺少統(tǒng)一支付接口必填參數(shù)!notify_url");
}
$xml = WxPayCore::toXml($wxpay_config);
$startTimeStamp = WxPayCore::getMillisecond();//請求開始時間
$response = WxPayCore::postXmlCurl($xml, $url, $proxy_config['curl_proxy_host'], $proxy_config['curl_proxy_port'], $ssl_config['sslcert_path'], $ssl_config['sslkey_path'], true);
// 響應(yīng)結(jié)果
$responseArr = WxPayCore::fromXml($response);
return $responseArr;
}
4.2 微信支付JSAPI
4.2.1 統(tǒng)一下單成功后返回數(shù)據(jù)
4.2.2 簽名驗證
public function getJSApiParameters($UnifiedOrderResult, $key)
{
if (!array_key_exists("appid", $UnifiedOrderResult)
|| !array_key_exists("prepay_id", $UnifiedOrderResult)
|| $UnifiedOrderResult['prepay_id'] == "") {
throw new WxPayException("參數(shù)錯誤");
}
$timemap = time();
// 生成隨機數(shù)
$o_ranchar = new Core\Utility\RandChar();
$s_ranchar = $o_ranchar->randChar('mix', 32);
// 生成簽名
$sign_config = array(
"appId" => $UnifiedOrderResult['appid'],
"timeStamp" => "$timemap",
"nonceStr" => $s_ranchar,
"package" => "prepay_id=" . $UnifiedOrderResult['prepay_id'],
"signType" => "MD5",
);
$o_sign = new Core\Utility\WxPay\WxPaySign($sign_config);
$s_sign = $o_sign->makeSign($key);
$params = array(
"appId" => $UnifiedOrderResult['appid'],
"timeStamp" => "$timemap",
"nonceStr" => $s_ranchar,
"package" => "prepay_id=" . $UnifiedOrderResult['prepay_id'],
"signType" => "MD5",
"paySign" => $s_sign
);
return $params;
}
4.3 微信公眾號內(nèi)置支付功能
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : "wx2421b1c4370ec43b", //公眾號名稱,由商戶傳入
"timeStamp":" 1395712654", //時間戳,自1970年以來的秒數(shù)
"nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //隨機串
"package" : "prepay_id=u802345jgfjsdfgsdg888",
"signType" : "MD5", //微信簽名方式:
"paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {}
// 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但并不保證它絕對可靠。
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
將 微信支付 JSAPI 生成的數(shù)據(jù)作為微信內(nèi)置函數(shù) WeixinJSBridge.invoke() 發(fā)起 getBrandWCPayRequest 請求時的參數(shù),如果參數(shù)正確當點擊支付時候可以在微信公眾號中調(diào)用微信客戶端支付功能。
4.4 同步通知流程處理
商戶根據(jù)微信內(nèi)置函數(shù)調(diào)用支付結(jié)果來處理業(yè)務(wù),例如:當支付返回 get_brand_wcpay_request:ok 說明支付成功,get_brand_wcpay_request:fail 或 get_brand_wcpay_request:cancle 說明支付失敗,但是 返回數(shù)據(jù)不一定可靠。
4.5 異步通知處理
微信會通過統(tǒng)一下單時設(shè)置的 notify_url 向商戶發(fā)送支付結(jié)果通知,商戶應(yīng)該根據(jù)返回的數(shù)據(jù)對訂單信息和商戶信息等重要信息驗證
避免訂單錯誤導(dǎo)致資金錯誤,然后更新內(nèi)部訂單狀態(tài),最后將處理后的結(jié)果返回給微信,如果微信收到商戶的應(yīng)答不是成功或超時,微信認為通知失敗,微信會通過一定的策略定期重新發(fā)起通知,盡可能提高通知的成功率,但微信不保證通知最終能成功。 (通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒)
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
------------------------------------------------------------
<xml>
<return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[商戶返回失敗的原因]]></return_msg>
</xml>
5 常見問題
5.1 統(tǒng)一下單時簽名問題
5.2 支付時簽名驗證失敗
5.3 異步通知未響應(yīng)
Q&A: 簽名錯誤
簽名問題大部分是因為生成簽名時參數(shù)不一致,例如:在統(tǒng)一下單時候需要傳遞簽名參數(shù)和其他參數(shù),個人感覺微信在調(diào)用生成預(yù)支付訂單時候內(nèi)部也產(chǎn)生簽名并且
和傳遞過來的簽名進行對比驗證,當不一致時就會提示簽名錯誤,原因就是在下單時商戶主動生成的簽名參數(shù)和微信內(nèi)部生成參數(shù)不一致,當簽名不一致時建議將方
法所需的參數(shù)作為生成簽名的參數(shù),然后再將生成簽名傳遞。
Q&A: 異步通知未響應(yīng)
1. 統(tǒng)一下單時 notify_url 是否設(shè)置
2. notify_url 是否符合規(guī)范:1.不能帶有參數(shù),例如:https://pay.weixin.qq.com/notify.php?wx=20160811 2.能夠訪問
3. 檢查接口是否有訪問權(quán)限設(shè)置
4. 通過 error_log 檢查是否有日志文件產(chǎn)生
6 總結(jié)
在微信公眾號支付開發(fā)過程中,遇到很多問題有些官方文檔沒怎么描述清楚,導(dǎo)致后面花較長時間去測試原因。然后是理解到同步和異步通知的區(qū)別并且學(xué)習到
error_log 日志功能的好處,通過生成的日志文件方便去解決問題和優(yōu)化,遠遠超過自己預(yù)測情況時沒有數(shù)據(jù)來對比分析。