目錄:
場景介紹
商戶已有H5商城網站,用戶通過消息或掃描二維碼在微信內打開網頁時,可以調用微信支付完成下單購買的流程。
步驟(1):
商戶下發圖文消息或者通過自定義菜單吸引用戶點擊進入商戶網頁(商戶網頁下單)。
步驟(2):
進入商戶網頁,用戶選擇購買,完成選購流程(請求微信支付)。
步驟(3):
調起微信支付控件,用戶開始輸入支付密碼。
步驟(4):
密碼驗證通過,支付成功。商戶後臺得到支付成功的通知。
步驟(5):
返回商戶頁面,顯示購買成功。該頁面由商戶自定義。
步驟(6):
微信支付公衆號下發支付憑證。
步驟(7):
商戶公衆號下發消息,提示發貨成功。該步驟可選。
注意:商戶也可以把商品網頁的鏈接生成二維碼,用戶掃一掃打開後即可完成購買支付。
交互細節:
以下是支付場景的交互細節,請認真閱讀,設計商戶頁面的邏輯:
(1)用戶打開商戶網頁選購商品,發起支付,在網頁通過JavaScript調用getBrandWCPayRequest接口,發起微信支付請求,用戶進入支付流程。
(2)用戶成功支付點擊完成按鈕後,商戶的前端會收到JavaScript的返回值。商戶可直接跳轉到支付成功的靜態頁面進行展示。
(3)商戶後臺收到來自微信開放平臺的支付成功回調通知,標誌該筆訂單支付成功。
注:(2)和(3)的觸發不保證遵循嚴格的時序。JS API返回值作爲觸發商戶網頁跳轉的標誌,但商戶後臺應該只在收到微信後臺的支付成功回調通知後,才做真正的支付成功的處理。
官方流程圖
簡單流程圖:
代碼:
商品選擇頁面 Product.aspx
function checkForm() {
var d = document, n, c, validn, validc, obj = {}, mobile = /^1[3|4|5|8|7][0-9]\d{4,8}$/;
n = d.getElementById('tbcarnum').value;
c = d.getElementById('tbcontact').value;
validn = d.getElementById('validnum');
validc = d.getElementById('validcon');
if (n === '') { validn.innerHTML = '必填'; return false; }
else { validn.innerHTML = ''; }
if (c === '') { validc.innerHTML = '必填'; return false; }
else { validc.innerHTML = ''; }
if (c.length != 11 || !mobile.test(c)) {
validc.innerHTML = '聯繫方式格式不正確'; return false;
}
else { validc.innerHTML = ''; }
//go
obj.num = n;
obj.con = c;
obj.type = document.getElementById('hid').value;
//創建訂單
$.post("ashx/CreateOrder.ashx", obj, function (data) {
var json = JSON.parse(data);
if (json.code === 200) {//成功則轉到支付頁面
location.href = "paypage.aspx";
} else {
alert('提交失敗');
}
});
}
支付頁面 PayPage
網頁授權獲取過程
以下代碼DEMO 內是有的,在此我只是做了略微修改
實例化 JsApiPay 類,需要一個 web 窗體作爲參數。
PayPage.cs
public string jsapiparms = "";
public string wxorder = "";
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
JsApiPay payObj = new JsApiPay(this);
// 網頁授權獲取
payObj.GetOpenidAndAccessToken();
//統一下單
WxPayData order = payObj.GetUnifiedOrderResult();
//微信商戶訂單號
wxorder = payObj.wx_ordernum;
//jsapi支付所需的參數
jsapiparms = payObj.GetJsApiParameters();
}
}
PayPage.aspx
<script type="text/javascript">
function onBridgeReady() {
WeixinJSBridge.invoke('getBrandWCPayRequest', <%=jsapiparms %>, function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
<!--調起查詢訂單接口,查詢訂單是否真正交易成功-->
$.post('ashx/CheckOrderStatus.ashx',{onum:'<%=wxorder %>'},function(data){
var json=JSON.parse(data);
if(json.code===200){
document.getElementById('status').innerHTML=json.res;
}else{
alert(json.res);
}
// 轉到自定義成功頁面
window.location.href=json.url;
});
} // 使用以上方式判斷前端返回,微信團隊鄭重提示: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();
}
</script>
- 第一步:利用url跳轉獲取code
/**
*
* 網頁授權獲取用戶基本信息的全部過程
* 詳情請參看網頁授權獲取用戶基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
* 第一步:利用url跳轉獲取code
* 第二步:利用code去獲取openid和access_token
*
*/
public void GetOpenidAndAccessToken()
{
if (!string.IsNullOrEmpty(page.Request.QueryString["code"]))
{
//獲取code碼,以獲取openid和access_token
string code = page.Request.QueryString["code"];
Log.Debug(this.GetType().ToString(), "Get code : " + code);
// 利用code去獲取openid和access_token
GetOpenidAndAccessTokenFromCode(code);
}
else
{
//構造網頁授權獲取code的URL
string host = page.Request.Url.Host;
string path = page.Request.Path;
string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);
WxPayData data = new WxPayData();
data.SetValue("appid", WxPayConfig.APPID);
Log.Info("redirect_url:", redirect_uri);
data.SetValue("redirect_uri", redirect_uri);
data.SetValue("response_type", "code");
data.SetValue("scope", "snsapi_base");
data.SetValue("state", "STATE" + "#wechat_redirect");
string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();
Log.Debug(this.GetType().ToString(), "Will Redirect to URL : " + url);
try
{
//觸發微信返回code碼
page.Response.Redirect(url);//Redirect函數會拋出ThreadAbortException異常,不用處理這個異常
}
catch (System.Threading.ThreadAbortException ex)
{
}
}
}
- 第二步:利用code去獲取openid和access_token
/**
*
* 通過code換取網頁授權access_token和openid的返回數據,正確時返回的JSON數據包如下:
* {
* "access_token":"ACCESS_TOKEN",
* "expires_in":7200,
* "refresh_token":"REFRESH_TOKEN",
* "openid":"OPENID",
* "scope":"SCOPE",
* "unionid": "xxxxxxxxxxxxxxxxxxxxxxxxx"
* }
* 其中access_token可用於獲取共享收貨地址
* openid是微信支付jsapi支付接口統一下單時必須的參數
* 更詳細的說明請參考網頁授權獲取用戶基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
* @失敗時拋異常WxPayException
*/
public void GetOpenidAndAccessTokenFromCode(string code)
{
try
{
//構造獲取openid及access_token的url
WxPayData data = new WxPayData();
data.SetValue("appid", WxPayConfig.APPID);
data.SetValue("secret", WxPayConfig.APPSECRET);
data.SetValue("code", code);
data.SetValue("grant_type", "authorization_code");
string url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + data.ToUrl();
//請求url以獲取數據
string result = HttpService.Get(url);
Log.Debug(this.GetType().ToString(), "GetOpenidAndAccessTokenFromCode response : " + result);
//保存access_token,用於收貨地址獲取
JsonData jd = JsonMapper.ToObject(result);
access_token = (string)jd["access_token"];
//獲取用戶openid
openid = (string)jd["openid"];
Log.Debug(this.GetType().ToString(), "Get openid : " + openid);
Log.Debug(this.GetType().ToString(), "Get access_token : " + access_token);
}
catch (Exception ex)
{
Log.Error(this.GetType().ToString(), ex.ToString());
throw new WxPayException(ex.ToString());
}
}
- 第三步:調用統一下單接口
/**
* 調用統一下單,獲得下單結果
* @return 統一下單結果
* @失敗時拋異常WxPayException
*/
public WxPayData GetUnifiedOrderResult(string proname)
{
//統一下單
WxPayData data = new WxPayData();
data.SetValue("body", proname);
data.SetValue("attach", proname);
string order = WxPayApi.GenerateOutTradeNo();
data.SetValue("out_trade_no", order);
//自定義屬性,保存微信訂單號
wx_ordernum = order;
Log.Info("order", order);
data.SetValue("total_fee", total_fee);
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
data.SetValue("goods_tag", proname);
data.SetValue("trade_type", "JSAPI");
data.SetValue("openid", openid);
WxPayData result = WxPayApi.UnifiedOrder(data);
if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
{
Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");
throw new WxPayException("UnifiedOrder response error!");
}
unifiedOrderResult = result;
return result;
}
- 第四步:從統一下單成功返回的數據中獲取微信瀏覽器調起jsapi支付所需的參數
/**
*
* 從統一下單成功返回的數據中獲取微信瀏覽器調起jsapi支付所需的參數,
* 微信瀏覽器調起JSAPI時的輸入參數格式如下:
* {
* "appId" : "wxxxxxxxxxxxxxxxxxx", //公衆號名稱,由商戶傳入
* "timeStamp":"1395712654", //時間戳,自1970年以來的秒數
* "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //隨機串
* "package" : "prepay_id=xxxxxxxxxxxxxxxxxxxx",
* "signType" : "MD5", //微信簽名方式:
* "paySign" : "xxxxxxxxxxxxxxxxxxxxx" //微信簽名
* }
* @return string 微信瀏覽器調起JSAPI時的輸入參數,json格式可以直接做參數用
* 更詳細的說明請參考網頁端調起支付API:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7
*
*/
public string GetJsApiParameters()
{
Log.Debug(this.GetType().ToString(), "JsApiPay::GetJsApiParam is processing...");
WxPayData jsApiParam = new WxPayData();
jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));
jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
jsApiParam.SetValue("signType", "MD5");
jsApiParam.SetValue("paySign", jsApiParam.MakeSign());
string parameters = jsApiParam.ToJson();
Log.Debug(this.GetType().ToString(), "Get jsApiParam : " + parameters);
return parameters;
}