當(dāng)前位置:首頁 > IT技術(shù) > 微信平臺(tái) > 正文

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能
2021-07-25 20:32:33

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能

在前面幾篇博客,有介紹了微信支付、微信紅包、企業(yè)付款等各種和支付相關(guān)的操作,不過上面都是基于微信API的封裝,本篇隨筆繼續(xù)微信支付這一主題,繼續(xù)介紹基于微信網(wǎng)頁JSAPI的方式發(fā)起的微信支付功能實(shí)現(xiàn),微信的JSAPI相對于普通的API操作,調(diào)試沒有那么方便,而且有時(shí)候有些錯(cuò)誤需要反復(fù)核實(shí)。本篇隨筆基于實(shí)際的微信網(wǎng)頁支付案例,對微信JSAPI的支付實(shí)現(xiàn)進(jìn)行介紹。

在我前面的幾篇博客,有介紹了微信支付、微信紅包、企業(yè)付款等各種和支付相關(guān)的操作,不過上面都是基于微信普通API的封裝,本篇隨筆繼續(xù)微信支付這一主題,繼續(xù)介紹基于微信網(wǎng)頁JSAPI的方式發(fā)起的微信支付功能實(shí)現(xiàn),微信的JSAPI相對于普通的API操作,調(diào)試沒有那么方便,而且有時(shí)候有些錯(cuò)誤需要反復(fù)核實(shí)。本篇隨筆基于實(shí)際的微信網(wǎng)頁支付案例,對微信JSAPI的支付實(shí)現(xiàn)進(jìn)行介紹。

1、微信JS-SDK的知識

在我前面《C#開發(fā)微信門戶及應(yīng)用(39)--使用微信JSSDK實(shí)現(xiàn)簽到的功能》介紹的內(nèi)容里面,有介紹了很多JS-SDK的基礎(chǔ)知識,我們基于網(wǎng)頁發(fā)起的微信支付,我們也是基于JS-SDK的基礎(chǔ)上進(jìn)行發(fā)起的,因此需要了解這些JS-SDK的使用步驟。

一般來說,我們網(wǎng)頁JSAPI發(fā)起的微信支付,需要使用wx.chooseWXPay的操作方法,而這個(gè)方法也是需要在完成wx.config初始化的時(shí)候,由界面元素進(jìn)行觸發(fā)處理的。

例如我們可以這樣實(shí)現(xiàn)整個(gè)微信支付的處理過程:

1)先使用JS對API進(jìn)行初始化配置

    wx.config({
        debug: false,
        appId: appid, // 必填,公眾號的唯一標(biāo)識
        timestamp: timestamp, // 必填,生成簽名的時(shí)間戳
        nonceStr: noncestr, // 必填,生成簽名的隨機(jī)串
        signature: signature, // 必填,簽名,見附錄1
        jsApiList: [
           'checkJsApi',
           'chooseWXPay',
           'hideOptionMenu'
        ]
    });

?

2)使用wx.chooseWXPay發(fā)起微信支付

wx.chooseWXPay({
    timestamp: 0, // 支付簽名時(shí)間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后臺(tái)生成簽名使用的timeStamp字段名需大寫其中的S字符
    nonceStr: '', // 支付簽名隨機(jī)串,不長于 32 位
    package: '', // 統(tǒng)一支付接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=***)
    signType: '', // 簽名方式,默認(rèn)為'SHA1',使用新版支付需傳入'MD5'
    paySign: '', // 支付簽名
    success: function (res) {
        // 支付成功后的回調(diào)函數(shù)
    }
});

備注:prepay_id 通過微信支付統(tǒng)一下單接口拿到,paySign 采用統(tǒng)一的微信支付 Sign 簽名生成方法,注意這里 appId 也要參與簽名,appId 與 config 中傳入的 appId 一致,即最后參與簽名的參數(shù)有appId, timeStamp, nonceStr, package, signType。

?

3)獲取用戶的openid

在一些JSAPI操作里面,有時(shí)候需要傳入當(dāng)前用戶的openid,由于這個(gè)值,一般是不能直接獲得的,但可以通過用戶授權(quán)代碼獲取,因此我們可以在菜單中配置好重定向的URL,根據(jù)URL獲取對應(yīng)的code,然后解析code為對應(yīng)的openid即可。

通過code換取的是一個(gè)特殊的網(wǎng)頁授權(quán)access_token,與基礎(chǔ)支持中的access_token(該access_token用于調(diào)用其他接口)不同。公眾號可通過下述接口來獲取網(wǎng)頁授權(quán)access_token。如果網(wǎng)頁授權(quán)的作用域?yàn)閟nsapi_base,則本步驟中獲取到網(wǎng)頁授權(quán)access_token的同時(shí),也獲取到了openid,snsapi_base式的網(wǎng)頁授權(quán)流程即到此為止。

獲取code后,請求以下鏈接獲取access_token: 
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

?

2、微信支付JSAPI初始化的參數(shù)處理

要獲取相關(guān)的JS-SDK的相關(guān)接口參數(shù),我們需要先生成JSAPI-Ticket憑證,生成這個(gè)憑證代碼接口實(shí)現(xiàn)如下所示。一般來說,這個(gè)接口的數(shù)據(jù)需要緩存起來的,具體可以自己實(shí)現(xiàn)處理。

        /// <summary>
        /// 獲取JSAPI_TICKET接口
        /// </summary>
        /// <param name="accessToken">調(diào)用接口憑證</param>
        /// <returns></returns>
        public string GetJSAPI_Ticket(string accessToken)
        {
            var url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", accessToken);
            var result = JsonHelper<GetTicketResult>.ConvertJson(url);
            return result != null ? result.ticket : null;
        }

我們要實(shí)現(xiàn)JSSDK簽名的處理,必須先根據(jù)幾個(gè)變量,構(gòu)建好URL字符串,具體的處理過程,我們可以把它們逐一放在一個(gè)Hashtable里面,如下代碼所示。

        /// <summary>
        /// 獲取JSSDK所需要的參數(shù)信息,返回Hashtable結(jié)合
        /// </summary>
        /// <param name="appId">微信AppID</param>
        /// <param name="jsTicket">根據(jù)Token獲取到的JSSDK ticket</param>
        /// <param name="url">頁面URL</param>
        /// <returns></returns>
        public static Hashtable GetParameters(string appId, string jsTicket, string url)
        {
            string timestamp = GetTimeStamp();
            string nonceStr = GetNonceStr();

            // 這里參數(shù)的順序要按照 key 值 ASCII 碼升序排序  
            string rawstring = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url + "";

            string signature = GetSignature(rawstring);
            Hashtable signPackage = new Hashtable();
            signPackage.Add("appid", appId);
            signPackage.Add("noncestr", nonceStr);
            signPackage.Add("timestamp", timestamp);
            signPackage.Add("url", url);
            signPackage.Add("signature", signature);
            signPackage.Add("jsapi_ticket", jsTicket);
            signPackage.Add("rawstring", rawstring);

            return signPackage;
        }

這樣把它們放在哈希表里面,方便我們提取出來使用。

    wx.config({
        debug: false,
        appId: appid, // 必填,公眾號的唯一標(biāo)識
        timestamp: timestamp, // 必填,生成簽名的時(shí)間戳
        nonceStr: noncestr, // 必填,生成簽名的隨機(jī)串
        signature: signature, // 必填,簽名,見附錄1
        jsApiList: [
           'checkJsApi',
           'chooseWXPay',
           'hideOptionMenu'
        ]
    });

為了在MVC視圖頁面里面,設(shè)置我們計(jì)算出來的值,一般我們需要在后臺(tái)進(jìn)行計(jì)算好,并把它們放在ViewBag變量中就可以在頁面前端使用了,如下所示是MVC視圖頁面的后臺(tái)代碼。

        /// <summary>
        /// 刷新JS-SDK的票據(jù)
        /// </summary>
        protected virtual void RefreshTicket(AccountInfo accountInfo)
        {
            Hashtable ht = baseApi.GetJSAPI_Parameters(accountInfo.AppID, accountInfo.AppSecret, Request.Url.AbsoluteUri);
            ViewBag.appid = ht["appid"].ToString();
            ViewBag.nonceStr = ht["noncestr"].ToString();
            ViewBag.timestamp = ht["timestamp"].ToString();
            ViewBag.signature = ht["signature"].ToString();
        }

這樣,在MVC的視圖頁面里面,我們的代碼可以這樣實(shí)現(xiàn)JSAPI變量的初始化。

    <script language="javascript">
    var openid = '@ViewBag.openid';
    var appid = '@ViewBag.appid';
    var noncestr = '@ViewBag.noncestr';
    var signature = '@ViewBag.signature';
    var timestamp = '@ViewBag.timestamp';

    wx.config({
        debug: false,
        appId: appid, // 必填,公眾號的唯一標(biāo)識
        timestamp: timestamp, // 必填,生成簽名的時(shí)間戳
        nonceStr: noncestr, // 必填,生成簽名的隨機(jī)串
        signature: signature, // 必填,簽名,見附錄1
        jsApiList: [
           'checkJsApi',
           'chooseWXPay',
           'hideOptionMenu'
        ]
    });

?

3、微信支付JSAPI發(fā)起微信支付的參數(shù)處理

在第一小節(jié)里面,我提到了,初始化JS-API后,還需要使用wx.chooseWXPay發(fā)起微信支付,這個(gè)接口也有幾個(gè)相關(guān)的參數(shù)。

wx.chooseWXPay({
    timestamp: 0, // 支付簽名時(shí)間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后臺(tái)生成簽名使用的timeStamp字段名需大寫其中的S字符
    nonceStr: '', // 支付簽名隨機(jī)串,不長于 32 位
    package: '', // 統(tǒng)一支付接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=***)
    signType: '', // 簽名方式,默認(rèn)為'SHA1',使用新版支付需傳入'MD5'
    paySign: '', // 支付簽名
    success: function (res) {
        // 支付成功后的回調(diào)函數(shù)
    }
});

其中這里的timestamp和nonceStr的規(guī)則和前面初始化操作的參數(shù)規(guī)則一樣,但是注意不能和初始化接口的timestamp和nonceStr保持一樣,否則發(fā)起支付會(huì)出現(xiàn)【?支付驗(yàn)證簽名失敗】的錯(cuò)誤。

package的變量就是我們調(diào)用統(tǒng)一下單接口的獲得的預(yù)下單id,格式如下所示:

prepay_id=wx2016051517463160322779de0375788970

而為了獲得這個(gè)預(yù)下單的ID,我們先需要根據(jù)統(tǒng)一下單接口的需要,構(gòu)建一個(gè)數(shù)據(jù)對象,如下所示。

                PayOrderData data = new PayOrderData()
                {
                    product_id = id,
                    body = "測試支付" + id,
                    attach = "愛奇迪技術(shù)支持",
                    detail = "測試JSAPI支付" + id,
                    total_fee = 1,
                    goods_tag = "test" + id,
                    trade_type = "JSAPI",
                    openid = openid
                };

然后調(diào)用前面封裝過的統(tǒng)一下單接口API獲取對應(yīng)的統(tǒng)一下單ID

                TenPayApi api = new TenPayApi(accountInfo);
                var orderResult = api.UnifiedOrder(data);
                LogHelper.Debug(string.Format("統(tǒng)一下單結(jié)果:{0}", (orderResult != null) ? orderResult.ToJson() : "為空值"));
                
                if (string.IsNullOrEmpty(orderResult.prepay_id) || string.IsNullOrEmpty(orderResult.appid))
                {
                    throw new WeixinException("統(tǒng)一下單結(jié)果返回失敗!");
                }

signType固定為MD5,

最后剩下paySign這個(gè)比較復(fù)雜的參數(shù)了,這個(gè)參數(shù)就是需要根據(jù)前面這些參數(shù)進(jìn)行簽名的值。微信支付的簽名還是和普通API的做法(在前面介紹微信支付的時(shí)候,有介紹過相關(guān)的規(guī)則,具體可以看看《C#開發(fā)微信門戶及應(yīng)用(32)--微信支付接入和API封裝使用》),引入實(shí)體類?WxPayData?來存儲(chǔ)一些業(yè)務(wù)參數(shù),以及實(shí)現(xiàn)參數(shù)的簽名處理。

值得注意的是,使用普通API的簽名為Sign,而使用JSAPI的簽名變量名稱為paySign,兩者處理邏輯一樣,只是名稱不同。

這樣我們在后臺(tái)處理相關(guān)的變量的代碼如下所示。

        /// <summary>
        /// 獲取JSAPI方式的微信字符串參數(shù)對象
        /// </summary>
        /// <param name="accountInfo">當(dāng)前賬號</param>
        /// <param name="prepay_id">統(tǒng)一下單ID</param>
        /// <returns></returns>
        private WxPayData GetJSPayParam(AccountInfo accountInfo, string prepay_id)
        {
            WxPayData data = new WxPayData();
            data.SetValue("appId", ViewBag.appId);
            data.SetValue("timeStamp", data.GenerateTimeStamp());
            data.SetValue("nonceStr", data.GenerateNonceStr());
            data.SetValue("signType", "MD5");
            data.SetValue("package", string.Format("prepay_id={0}", prepay_id));

            data.SetValue("paySign", data.MakeSign(accountInfo.PayAPIKey));//簽名
            return data;
        }

然后,再定義一個(gè)控制器接口,返回相關(guān)的參數(shù)數(shù)據(jù),部分邏輯代碼如下所示。這樣方便前端通過JSON的格式獲取對應(yīng)的變量值。

                //支付需要的參數(shù)
                WxPayData param = GetJSPayParam(accountInfo, orderResult.prepay_id);
                LogHelper.Debug("GetJSPayParam:" + param.ToJson());

                var obj = new
                {
                    timeStamp = param.GetString("timeStamp"),
                    nonceStr = param.GetString("nonceStr"),
                    signType = param.GetString("signType"),
                    package = param.GetString("package"),
                    paySign = param.GetString("paySign")
                };
                return Content(obj.ToJson());

在前面頁面,通過ajax方式獲得發(fā)起微信支付的相關(guān)參數(shù),代碼如下所示。

    //發(fā)起一個(gè)微信支付
    function chooseWXPay(id) {
        //alert(window.location.href);
        var uid = getUrlVars()["uid"];
        $.ajax({
            type: 'POST',
            url: '/JSSDKTest/GetWXPayData',
            //async: false, //同步
            dataType: 'json',
            data : {
                id: id,
                uid: uid,
                openid : openid
            },
            success: function (json) {
                wx.chooseWXPay({
                    appId: appid,
                    timestamp: json.timeStamp,  // 支付簽名時(shí)間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后臺(tái)生成簽名使用的timeStamp字段名需大寫其中的S字符
                    nonceStr: json.nonceStr,    // 支付簽名隨機(jī)串,不長于 32 位
                    package: json.package,      // 統(tǒng)一支付接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=***)
                    signType: json.signType,    // 簽名方式,默認(rèn)為'SHA1',使用新版支付需傳入'MD5'
                    paySign: json.paySign,      // 支付簽名
                    success: function (res) {   // 支付成功后的回調(diào)函數(shù)
                        if (res.errMsg == 'chooseWXPay:ok') {
                            $.toast('支付成功');
                            //setTimeout(function () {
                            //    window.location.href = "/";//這里默認(rèn)跳轉(zhuǎn)到主頁
                            //}, 2000);
                            //window.location.href = "/Pay/order_details?order#orderId").val();
                        } else if (res.errMsg == 'chooseWXPay:cancel' || res.errMsg == 'chooseWXPay:fail') {
                            $.toast("支付失敗");
                            //window.location.href = "/Pay/order_details?order#orderId").val();
                        }                        
                    },
                    cancel: function () {
                        $.toast("用戶取消了支付");
                        //window.location.href = "/Pay/order_details?order#orderId").val();
                    }
                });
                wx.error(function (res) {
                    $.toast("調(diào)用支付出現(xiàn)異常");
                    //window.location.href = "/Pay/order_details?order#orderId").val();
                })
            },
            error: function (xhr, status, error) {
                $.toast("操作失敗" + xhr.responseText); //xhr.responseText
            }
        });
    };

?

4、微信支付JSAPI發(fā)起微信支付的界面效果

通過上面的代碼,我們可以順利發(fā)起微信支付,并可以看到具體的測試結(jié)果了,讀者可以關(guān)注我們的公眾號【廣州愛奇迪】對其中微信支付進(jìn)行測試了解。

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用??C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_02??C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_03

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_04?C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_05??C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_06

微信支付成功后,我們同樣可以在微信支付的對話里面看到響應(yīng)的結(jié)果了。

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_07

?

如果對這個(gè)《C#開發(fā)微信門戶及應(yīng)用》系列感興趣,可以關(guān)注我的其他文章,系列隨筆如下所示:

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能

C#開發(fā)微信門戶及應(yīng)用(39)--使用微信JSSDK實(shí)現(xiàn)簽到的功能

C#開發(fā)微信門戶及應(yīng)用(38)--微信搖一搖紅包功能

C#開發(fā)微信門戶及應(yīng)用(37)--微信公眾號標(biāo)簽管理功能

C#開發(fā)微信門戶及應(yīng)用(36)--微信卡劵管理的封裝操作

C#開發(fā)微信門戶及應(yīng)用(35)--微信支付之企業(yè)付款封裝操作

C#開發(fā)微信門戶及應(yīng)用(34)--微信裂變紅包

C#開發(fā)微信門戶及應(yīng)用(33)--微信現(xiàn)金紅包的封裝及使用

C#開發(fā)微信門戶及應(yīng)用(32)--微信支付接入和API封裝使用

C#開發(fā)微信門戶及應(yīng)用(31)--微信語義理解接口的實(shí)現(xiàn)和處理

C#開發(fā)微信門戶及應(yīng)用(30)--消息的群發(fā)處理和預(yù)覽功能

C#開發(fā)微信門戶及應(yīng)用(28)--微信“搖一搖·周邊”功能的使用和接口的實(shí)現(xiàn)

C#開發(fā)微信門戶及應(yīng)用(27)-公眾號模板消息管理?

C#開發(fā)微信門戶及應(yīng)用(26)-公眾號微信素材管理

C#開發(fā)微信門戶及應(yīng)用(25)-微信企業(yè)號的客戶端管理功能

C#開發(fā)微信門戶及應(yīng)用(24)-微信小店貨架信息管理

C#開發(fā)微信門戶及應(yīng)用(23)-微信小店商品管理接口的封裝和測試

C#開發(fā)微信門戶及應(yīng)用(22)-微信小店的開發(fā)和使用

C#開發(fā)微信門戶及應(yīng)用(21)-微信企業(yè)號的消息和事件的接收處理及解密?

C#開發(fā)微信門戶及應(yīng)用(20)-微信企業(yè)號的菜單管理

C#開發(fā)微信門戶及應(yīng)用(19)-微信企業(yè)號的消息發(fā)送(文本、圖片、文件、語音、視頻、圖文消息等)

C#開發(fā)微信門戶及應(yīng)用(18)-微信企業(yè)號的通訊錄管理開發(fā)之成員管理

C#開發(fā)微信門戶及應(yīng)用(17)-微信企業(yè)號的通訊錄管理開發(fā)之部門管理

C#開發(fā)微信門戶及應(yīng)用(16)-微信企業(yè)號的配置和使用

C#開發(fā)微信門戶及應(yīng)用(15)-微信菜單增加掃一掃、發(fā)圖片、發(fā)地理位置功能

C#開發(fā)微信門戶及應(yīng)用(14)-在微信菜單中采用重定向獲取用戶數(shù)據(jù)

C#開發(fā)微信門戶及應(yīng)用(13)-使用地理位置擴(kuò)展相關(guān)應(yīng)用

C#開發(fā)微信門戶及應(yīng)用(12)-使用語音處理

C#開發(fā)微信門戶及應(yīng)用(11)--微信菜單的多種表現(xiàn)方式介紹

C#開發(fā)微信門戶及應(yīng)用(10)--在管理系統(tǒng)中同步微信用戶分組信息

C#開發(fā)微信門戶及應(yīng)用(9)-微信門戶菜單管理及提交到微信服務(wù)器

C#開發(fā)微信門戶及應(yīng)用(8)-微信門戶應(yīng)用管理系統(tǒng)功能介紹

C#開發(fā)微信門戶及應(yīng)用(7)-微信多客服功能及開發(fā)集成

C#開發(fā)微信門戶及應(yīng)用(6)--微信門戶菜單的管理操作

C#開發(fā)微信門戶及應(yīng)用(5)--用戶分組信息管理

C#開發(fā)微信門戶及應(yīng)用(4)--關(guān)注用戶列表及詳細(xì)信息管理

C#開發(fā)微信門戶及應(yīng)用(3)--文本消息和圖文消息的應(yīng)答

C#開發(fā)微信門戶及應(yīng)用(2)--微信消息的處理和應(yīng)答

C#開發(fā)微信門戶及應(yīng)用(1)--開始使用微信接口

C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_08主要研究技術(shù):代碼生成工具、會(huì)員管理系統(tǒng)、客戶關(guān)系管理軟件、病人資料管理軟件、Visio二次開發(fā)、酒店管理系統(tǒng)、倉庫管理系統(tǒng)等共享軟件開發(fā)
專注于Winform開發(fā)框架/混合式開發(fā)框架、Web開發(fā)框架、Bootstrap開發(fā)框架、微信門戶開發(fā)框架的研究及應(yīng)用。
??轉(zhuǎn)載請注明出處:
C#開發(fā)微信門戶及應(yīng)用(40)--使用微信JSAPI實(shí)現(xiàn)微信支付功能_微信公眾平臺(tái)及門戶應(yīng)用_08撰寫人:伍華聰?

本文摘自 :https://blog.51cto.com/w

開通會(huì)員,享受整站包年服務(wù)立即開通 >