調用微信支付,咱們首先須要獲取到微信用戶的基本資料,在微信開發——網頁H5受權,獲取微信用戶資料一文中介紹到了如何獲取微信用戶信息,獲取到後,咱們保存在webstorage
中,其餘的頁面也都能使用了,本文記錄一下微信H5支付的配置以及須要注意的問題。php
在公衆號裏面接入支付申請html
注意:公衆號須要關聯商戶號,不然提示 appid和mch_id不匹配 前端
商戶祕鑰須要動態獲取,商戶支付密鑰本身進行配置32位字母web
進入pay.weixin.qq.com配置微信支付的相關開發項ajax
支付目錄的配置,通常到控制器層就行了好比:http://www.testxxx.com/ticketSys/
數據庫
注意:若是使用阿里雲ECS,調用微信【統一下單】支付接口可能存在超時問題,是DNS解析的問題,將服務器的DNS配置爲騰訊公共DNS:119.29.29.29 json
阿里雲dns: 首選:10.202.72.116 備選:10.202.72.118後端
調用微信統一下單接口,須要appId,商戶號,商戶支付祕鑰,可是咱們是在html前臺中進行調用,這些祕鑰信息是不能暴露在客戶端的,須要服務端去調用,前端傳遞公開信息(金額,備註)就行api
支付,前端請求代碼,獲取微信客戶端支付須要的參數(都是服務端生成好的參數返回給前端JsPayApiParams):瀏覽器
$.ajax({
type: "post",
data: {
totalfee: vChargeVal, // 用戶支付金額
openId: app.wxUser.openId // 當前用戶的openId,必須
},
url: "/Home/GetJsPayApi",
dataType: "json",
success: function (data) {
if (data.isSuccess) {
// 獲取到後端返回的微信jsApi支付參數,後面代碼有詳解,調用微信客戶端支付
onBridgeReady(data.data);
}
else {
alert("調用微信支付模塊失敗,可能網絡服務器緣由,請您重試:" + data.msg);
}
},
error: function () {
alert("調用微信支付模塊失敗,可能網絡服務器緣由,請您重試");
}
})
//調用微信支付模塊 WeixinJSBridge 這個是在微信客戶端瀏覽器纔會有的對象
function onBridgeReady(json) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": json.appId, //公衆號名稱,由商戶傳入
"timeStamp": json.timeStamp, //時間戳,自1970年以來的秒數
"nonceStr": json.nonceStr, //隨機串
"package": json.package,
"signType": "MD5", //微信簽名方式:
"paySign": json.paySign //微信簽名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
alert('支付成功,請稍後查詢,正在保存訂票信息,若有疑問,請聯繫管理員..')
// 提交用戶數據到數據庫操做...
location.href = "/Home/MyTicket";
//不要在這裏提交最後的數據到數據庫,這裏可能不正常(微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。)
}
else {
console.log('關閉了微信支付')
}
});
}
複製代碼
再看關鍵的服務端代碼,返回微信H5JSAPI
支付須要的參數:
/// <summary>
/// 預下單,獲取jspay api參數
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<JsonResult> GetJsPayApi(UserOrder buyer)
{
try
{
#region 獲取客戶端參數 & 校驗
string strTotal_fee = Request.Form["totalfee"];
string strFee = (double.Parse(strTotal_fee) * 100).ToString();
//JSAPI支付預處理
//若傳遞了相關參數,則調統一下單接口,得到後續相關接口的入口參數
string openId = string.Empty; //注意這裏須要當前用戶openId
if (Session["openid"] != null)
{
openId = Session["openid"].ToString(); //注意這裏須要當前用戶openId
}
int totalFee = int.Parse(strFee);
var wxPayConfig = new WeiXinPayConfig()
{
AppId = WxPayConfig.APPID,
MchId = WxPayConfig.MCHID,
OpenId = openId,
NotifyUrl = WxPayConfig.NOTIFY_URL,
MchPayKey = WxPayConfig.KEY,
ServerIp = WxPayConfig.IP,
TradeType = "JSAPI",
OutTradeNo = Guid.NewGuid().ToString("N").ToUpper()
};
#endregion
buyer.PayStatus = false;
buyer.PayOrderNo = wxPayConfig.OutTradeNo;
_userOrder.Insert(buyer);
var unifiedOrderData = await WxPayHelper.UnifiedOrder(wxPayConfig, totalFee);
var jsApiPayData = WxPayHelper.GetJsApiParams(wxPayConfig.MchPayKey, unifiedOrderData);
return Success(jsApiPayData);
}
catch (Exception ex)
{
return Fail(ex.Message);
}
}
複製代碼
代碼說明:
await WxPayHelper.UnifiedOrder(wxPayConfig, totalFee)
這個是封裝的調用微信支付的【統一下單接口】,至關於預下單,參考接口說明,比較簡單,須要注意的是這裏的金額是分爲單位,這樣的設計在電商中比較廣泛,避免小數點的不少問題。
WxPayHelper.GetJsApiParams(wxPayConfig.MchPayKey, unifiedOrderData)
這個是獲取H5客戶端JSAPI
支付須要的參數,參考接口文檔,也就是上面調用onBridgeReady
方法喚起支付須要的參數,
微信H5內喚起支付package
參數須要【統一下單】的返回值,因此要預先調用這個接口了
這樣咱們拿到JsApiPayParams
JS支付的參數,就能夠直接調用起支付了
如上就能夠經過微信內H5喚起微信客戶端的支付功能進行付款了,可是這樣就行了嗎?想的太美了
在上線後,陸陸續續的發現部分安卓機支付了,後臺沒有數據,檢查了日誌,服務端也沒有報錯,那麼確定的是,微信客戶端沒有執行我回調成功的代碼
if (res.err_msg == "get_brand_wcpay_request:ok") { // 提交用戶數據到數據庫... }
複製代碼
去查閱了微信支付這塊的文檔,//不要在這裏提交最後的數據到數據庫,這裏可能不正常(微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。)
,表示微信客戶端顯示支付成功,並不必定可靠啊,微信本身都不相信本身,我信你個鬼啊,因此部分機器沒有執行這裏的回調邏輯...
在統一下單的時候,咱們會傳遞一個回調地址給微信,若是真正支付成功了,微信會將支付成功的回調信息傳遞給咱們,雙方確認,這是異構系統雙方識別成功的重要條件吧
根據微信的回調,咱們才能判斷哪些訂單真正支付成功了,而後更新咱們數據庫的數據,參考微信支付結果通知接口文檔
在咱們調用【統一下單】的時候,咱們會自動生成一個訂單號 out_trade_no
傳遞給微信,咱們將客戶下單的信息和這個訂單號關聯起來(以前代碼不是這麼寫的)
微信支付成功結果通知給咱們,會把訂單號帶上,咱們就能夠根據這個訂單號進行判斷,若是有回調通知,就表示支付成功了,那麼改變這個訂單號的狀態爲支付成成功就行了
微信支付通知回調報文:
<xml><appid><![CDATA[wx1393620e886ef***]]></appid>
<attach><![CDATA[附加信息:]]></attach>
<bank_type><![CDATA[ABC_DEBIT]]></bank_type>
<cash_fee><![CDATA[9950]]></cash_fee>
<device_info><![CDATA[WEB]]></device_info>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1546577***]]></mch_id>
<nonce_str><![CDATA[804b9ff2ad7d4f2180a872694c608***]]></nonce_str>
<openid><![CDATA[oysLsw_FjFSgiAx0R6PBniyS1***]]></openid>
<out_trade_no><![CDATA[C8D24B9622B144A4B5AFF5D4DD8F3***]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[3D10FCE425D8D104A551C1800CEC2***]]></sign>
<time_end><![CDATA[20190727204913]]></time_end>
<total_fee>9950</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[4200000384201907273227620***]]></transaction_id>
</xml>
複製代碼
解析通知的xml報文,服務端代碼示例:
/// <summary>
/// 微信支付回調
/// </summary>
/// <returns></returns>
[Route("notify")]
public ActionResult Notify()
{
string requestData = GetPostedString();
_log.Info("收到微信notify支付回調通知:" + requestData);
WeiXinPayData notifyResut = new WeiXinPayData();
notifyResut.FromXml(requestData);
if (!notifyResut.IsSet("transaction_id") || notifyResut.Get("transaction_id").ToString() == "") //若微信支付訂單號不存在,直接響應錯誤給微信
{
return Content(GetNotifyMsg(false, "微信訂單號不存在,響應失敗!"));
};
string outTradeNo = notifyResut.Get("out_trade_no").ToString(); //商戶系統內部訂單號
string payResultCode = notifyResut.Get("result_code").ToString();
if (!string.IsNullOrEmpty(outTradeNo) && payResultCode == "SUCCESS")
{
var userOrder = _userOrder.Find(c => c.PayOrderNo == outTradeNo);
userOrder.PayStatus = true;
_userOrder.SaveChanges();
_log.Info($"微信回調商戶支付單號:【{outTradeNo}】,購票人openId:{userOrder.OpenId},姓名:{userOrder.Name},電話:{userOrder.MobileNo},總金額:【{userOrder.Amount}】,支付確認成功!");
}
_log.Info("回調成功信息響應給微信:" + GetNotifyMsg(true, "OK"));
//訂單驗證成功後,將成功消息應答給微信回調 至關於再次給微信確認信息,表示已經收到了通知,否則微信會覺得你沒有收到通知消息,會再重試發給你幾回
return Content(GetNotifyMsg(true, "OK"));
}
/// <summary>
/// 生成成功/失敗信息應答給微信支付回調
/// </summary>
/// <param name="errorMsg"></param>
/// <returns></returns>
private string GetNotifyMsg(bool isSuccess, string msg)
{
WeiXinPayData replyToWx = new WeiXinPayData();
replyToWx.Set("return_code", isSuccess ? "SUCCESS" : "FAIL");
replyToWx.Set("return_msg", msg);
// _log.Info("生成回推微信消息:" + replyToWx.ToXml());
return replyToWx.ToXml();
}
複製代碼
這樣服務端雙方的通知就真正表示支付成功了。