C#开发微信门户及应用(40)--使用微信JSAPI实现微信支付功能

在我前面的几篇博客,有介绍了微信支付、微信红包、企业付款等各种和支付相关的操作,不过上面都是基于微信普通API的封装,本篇随笔继续微信支付这一主题,继续介绍基于微信网页JSAPI的方式发起的微信支付功能实现,微信的JSAPI相对于普通的API操作,调试没有那么方便,而且有时候有些错误需要反复核实。本篇随笔基于实际的微信网页支付案例,对微信JSAPI的支付实现进行介绍。

1、微信JS-SDK的知识

在我前面《C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能》介绍的内容里面,有介绍了很多JS-SDK的基础知识,我们基于网页发起的微信支付,我们也是基于JS-SDK的基础上进行发起的,因此需要了解这些JS-SDK的使用步骤。

一般来说,我们网页JSAPI发起的微信支付,需要使用wx.chooseWXPay的操作方法,而这个方法也是需要在完成wx.config初始化的时候,由界面元素进行触发处理的。

例如我们可以这样实现整个微信支付的处理过程:

1)先使用JS对API进行初始化配置

    wx.config({
        debug: false,
        appId: appid, // 必填,公众号的唯一标识
        timestamp: timestamp, // 必填,生成签名的时间戳
        nonceStr: noncestr, // 必填,生成签名的随机串
        signature: signature, // 必填,签名,见附录1
        jsApiList: [
           'checkJsApi',
           'chooseWXPay',
           'hideOptionMenu'
        ]
    });

2)使用wx.chooseWXPay发起微信支付

wx.chooseWXPay({
    timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
    nonceStr: '', // 支付签名随机串,不长于 32 位
    package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
    signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
    paySign: '', // 支付签名
    success: function (res) {
        // 支付成功后的回调函数
    }
});

备注:prepay_id 通过微信支付统一下单接口拿到,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。

3)获取用户的openid

在一些JSAPI操作里面,有时候需要传入当前用户的openid,由于这个值,一般是不能直接获得的,但可以通过用户授权代码获取,因此我们可以在菜单中配置好重定向的URL,根据URL获取对应的code,然后解析code为对应的openid即可。

通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

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

2、微信支付JSAPI初始化的参数处理

要获取相关的JS-SDK的相关接口参数,我们需要先生成JSAPI-Ticket凭证,生成这个凭证代码接口实现如下所示。一般来说,这个接口的数据需要缓存起来的,具体可以自己实现处理。

        /// <summary>
        /// 获取JSAPI_TICKET接口
        /// </summary>
        /// <param name="accessToken">调用接口凭证</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;
        }

我们要实现JSSDK签名的处理,必须先根据几个变量,构建好URL字符串,具体的处理过程,我们可以把它们逐一放在一个Hashtable里面,如下代码所示。

        /// <summary>
        /// 获取JSSDK所需要的参数信息,返回Hashtable结合
        /// </summary>
        /// <param name="appId">微信AppID</param>
        /// <param name="jsTicket">根据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();

            // 这里参数的顺序要按照 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, // 必填,公众号的唯一标识
        timestamp: timestamp, // 必填,生成签名的时间戳
        nonceStr: noncestr, // 必填,生成签名的随机串
        signature: signature, // 必填,签名,见附录1
        jsApiList: [
           'checkJsApi',
           'chooseWXPay',
           'hideOptionMenu'
        ]
    });

为了在MVC视图页面里面,设置我们计算出来的值,一般我们需要在后台进行计算好,并把它们放在ViewBag变量中就可以在页面前端使用了,如下所示是MVC视图页面的后台代码。

        /// <summary>
        /// 刷新JS-SDK的票据
        /// </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的视图页面里面,我们的代码可以这样实现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, // 必填,公众号的唯一标识
        timestamp: timestamp, // 必填,生成签名的时间戳
        nonceStr: noncestr, // 必填,生成签名的随机串
        signature: signature, // 必填,签名,见附录1
        jsApiList: [
           'checkJsApi',
           'chooseWXPay',
           'hideOptionMenu'
        ]
    });

3、微信支付JSAPI发起微信支付的参数处理

在第一小节里面,我提到了,初始化JS-API后,还需要使用wx.chooseWXPay发起微信支付,这个接口也有几个相关的参数。

wx.chooseWXPay({
    timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
    nonceStr: '', // 支付签名随机串,不长于 32 位
    package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
    signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
    paySign: '', // 支付签名
    success: function (res) {
        // 支付成功后的回调函数
    }
});

其中这里的timestamp和nonceStr的规则和前面初始化操作的参数规则一样,但是注意不能和初始化接口的timestamp和nonceStr保持一样,否则发起支付会出现【 支付验证签名失败】的错误。

package的变量就是我们调用统一下单接口的获得的预下单id,格式如下所示:

prepay_id=wx2016051517463160322779de0375788970

而为了获得这个预下单的ID,我们先需要根据统一下单接口的需要,构建一个数据对象,如下所示。

                PayOrderData data = new PayOrderData()
                {
                    product_id = id,
                    body = "测试支付" + id,
                    attach = "爱奇迪技术支持",
                    detail = "测试JSAPI支付" + id,
                    total_fee = 1,
                    goods_tag = "test" + id,
                    trade_type = "JSAPI",
                    openid = openid
                };

然后调用前面封装过的统一下单接口API获取对应的统一下单ID

                TenPayApi api = new TenPayApi(accountInfo);
                var orderResult = api.UnifiedOrder(data);
                LogHelper.Debug(string.Format("统一下单结果:{0}", (orderResult != null) ? orderResult.ToJson() : "为空值"));

                if (string.IsNullOrEmpty(orderResult.prepay_id) || string.IsNullOrEmpty(orderResult.appid))
                {
                    throw new WeixinException("统一下单结果返回失败!");
                }

signType固定为MD5,

最后剩下paySign这个比较复杂的参数了,这个参数就是需要根据前面这些参数进行签名的值。微信支付的签名还是和普通API的做法(在前面介绍微信支付的时候,有介绍过相关的规则,具体可以看看《C#开发微信门户及应用(32)--微信支付接入和API封装使用》),引入实体类 WxPayData 来存储一些业务参数,以及实现参数的签名处理。

值得注意的是,使用普通API的签名为Sign,而使用JSAPI的签名变量名称为paySign,两者处理逻辑一样,只是名称不同。

这样我们在后台处理相关的变量的代码如下所示。

        /// <summary>
        /// 获取JSAPI方式的微信字符串参数对象
        /// </summary>
        /// <param name="accountInfo">当前账号</param>
        /// <param name="prepay_id">统一下单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;
        }

然后,再定义一个控制器接口,返回相关的参数数据,部分逻辑代码如下所示。这样方便前端通过JSON的格式获取对应的变量值。

                //支付需要的参数
                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方式获得发起微信支付的相关参数,代码如下所示。

    //发起一个微信支付
    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,  // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
                    nonceStr: json.nonceStr,    // 支付签名随机串,不长于 32 位
                    package: json.package,      // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
                    signType: json.signType,    // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
                    paySign: json.paySign,      // 支付签名
                    success: function (res) {   // 支付成功后的回调函数
                        if (res.errMsg == 'chooseWXPay:ok') {
                            $.toast('支付成功');
                            //setTimeout(function () {
                            //    window.location.href = "/";//这里默认跳转到主页
                            //}, 2000);
                            //window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val();
                        } else if (res.errMsg == 'chooseWXPay:cancel' || res.errMsg == 'chooseWXPay:fail') {
                            $.toast("支付失败");
                            //window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val();
                        }
                    },
                    cancel: function () {
                        $.toast("用户取消了支付");
                        //window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val();
                    }
                });
                wx.error(function (res) {
                    $.toast("调用支付出现异常");
                    //window.location.href = "/Pay/order_details?orderId=" + $("#orderId").val();
                })
            },
            error: function (xhr, status, error) {
                $.toast("操作失败" + xhr.responseText); //xhr.responseText
            }
        });
    };

4、微信支付JSAPI发起微信支付的界面效果

通过上面的代码,我们可以顺利发起微信支付,并可以看到具体的测试结果了,读者可以关注我们的公众号【广州爱奇迪】对其中微信支付进行测试了解。

    

   

微信支付成功后,我们同样可以在微信支付的对话里面看到响应的结果了。

如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(40)--使用微信JSAPI实现微信支付功能

C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能

C#开发微信门户及应用(38)--微信摇一摇红包功能

C#开发微信门户及应用(37)--微信公众号标签管理功能

C#开发微信门户及应用(36)--微信卡劵管理的封装操作

C#开发微信门户及应用(35)--微信支付之企业付款封装操作

C#开发微信门户及应用(34)--微信裂变红包

C#开发微信门户及应用(33)--微信现金红包的封装及使用

C#开发微信门户及应用(32)--微信支付接入和API封装使用

C#开发微信门户及应用(31)--微信语义理解接口的实现和处理

C#开发微信门户及应用(30)--消息的群发处理和预览功能

C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

C#开发微信门户及应用(27)-公众号模板消息管理 

C#开发微信门户及应用(26)-公众号微信素材管理

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密 

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答

C#开发微信门户及应用(2)--微信消息的处理和应答

C#开发微信门户及应用(1)--开始使用微信接口

本文转自博客园伍华聪的博客,原文链接:C#开发微信门户及应用(40)--使用微信JSAPI实现微信支付功能,如需转载请自行联系原博主。

时间: 2024-09-10 12:14:43

C#开发微信门户及应用(40)--使用微信JSAPI实现微信支付功能的相关文章

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

原文:C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息 在前面几篇文章中,逐步从原有微信的API封装的基础上过渡到微信应用平台管理系统里面,逐步介绍管理系统中的微信数据的界面设计,以及相关的处理操作过程的逻辑和代码,希望从更高一个层次,向大家介绍微信的应用开发过程.本篇主要介绍在管理系统中,如何实现微信用户分组信息的同步操作. 其实微信能够风风火火的原因,主要就是因为有用户信息,所以同步并管理好微信账号的关注用户数据是非常重要的.有了微信用户的数据,你可以和你任何应用系统对接,

C#开发微信门户及应用(47) - 整合Web API、微信后台管理及前端微信小程序的应用方案

在微信开发中,我一直强调需要建立一个比较统一的Web API接口体系,以便实现数据的集中化,这样我们在常规的Web业务系统,Winform业务系统.微信应用.微信小程序.APP等方面,都可以直接调用基于JSON数据格式的Web API接口,在我之前的几篇随笔中,对这方面都有一定的介绍,本篇继续这个主题,细致深入的阐述如何在接口和源码的基础上整合Web API.微信后台管理及前端微信小程序的应用方案. 1.基于Web API的微信开发框架 首先我们各个业务模块,都应该围绕着Web API进行展开,

微信公众平台将对帐号分类 新版增支付功能

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 新浪科技讯 7月3日下午消息,腾讯合作伙伴大会今日在北京国家会议中心召开,期间微信产品部助理总经理曾鸣透露,新版微信公众账号将分为订阅号和服务号,而即将推出的微信5.0版本中也将确认增加微信支付的功能. 此前腾讯已经透露将修改微信公众平台政策的信息,此次曾鸣进一步解释了微信公众平台的调整细节.据介绍,新版公众账号将实现分类,分成订阅号和服务号

微信O2O拼图浮现:内测CRM接口 拟增支付功能

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 11月22日消息,微信布局O2O领域已经一年有余,除了二维码之外,最近关于微信的传闻频频曝光. 在今年9月举行的互联网大会上,腾讯CEO马化腾为微信"定了调".他表示,"二维码将是腾讯整合线上和线下业务的关键入口."这一席话也让外界认定O2O将成为微信未来发展的主要方向. 目前,微信上与O2O相关的功能

C#开发微信门户及应用(20)-微信企业号的菜单管理

原文:C#开发微信门户及应用(20)-微信企业号的菜单管理 前面几篇陆续介绍了很多微信企业号的相关操作,企业号和公众号一样都可以自定义菜单,因此他们也可以通过API进行菜单的创建.获取列表.删除的操作,因此本篇继续探讨这个主体,介绍企业号的菜单管理操作. 菜单在很多情况下,能够给我们提供一个快速入口,也可以用来获取用户信息的主要入口,通过OAuth2验证接口,以及自定义的重定向菜单,我们就可以获取对应的用户ID,然后进一步获取到用户的相关数据,可以显示给客户. 1.菜单的总体介绍 菜单的事件处理

C#开发微信门户及应用(6)--微信门户菜单的管理操作

原文:C#开发微信门户及应用(6)--微信门户菜单的管理操作 前面几篇继续了我自己对于C#开发微信门户及应用的技术探索和相关的经验总结,继续探索微信API并分享相关的技术,一方面是为了和大家对这方面进行互动沟通,另一方面也是专心做好微信应用的底层技术开发,把基础模块夯实,在未来的应用中派上用途.本随笔继续介绍微信门户菜单的管理操作. 1.菜单的基础信息 微信门户的菜单,一般服务号和订阅号都可以拥有这个模块的开发,但是订阅号好像需要认证后才能拥有,而服务号则不需要认证就可以拥有了.这个菜单可以有编

C#开发微信门户及应用(29)--微信个性化菜单的实现

有一段时间没有接着微信的主题继续介绍里面的功能模块了,这段时间来,微信也做了不少的变化改动,针对这些特性我全面核对了一下相关的微信公众号和企业号的接口,对原有的微信API和系统管理做了全面的更新,本随笔以及后面的随笔就是基于这个工作上的总结,以期把微信涉及的功能模块,都使用C#实现的方式来介绍.本随笔主要介绍微信公众号的个性化菜单的实现,虽然目前微信公众号和企业号已经在功能上接近一致,不过在企业号上还没有个性化菜单的相关接口. 1.个性化菜单介绍 我们先了解一下个性化菜单的介绍,根据官方的资料,

C#开发微信门户及应用(12)-使用语音处理

原文:C#开发微信门户及应用(12)-使用语音处理 我们知道,微信最开始就是做语音聊天而使得其更加流行的,因此语音的识别处理自然也就成为微信交流的一个重要途径,微信的开发接口,也提供了对语音的消息请求处理.本文主要介绍如何利用语音的识别,对C#开发的微信门户应用的整个事件链的处理操作,使得在我们的微信账号里面,更加方便和多元化对用户的输入进行处理. 1.微信语音接口的定义0 微信的API这么定义语音的识别的:开通语音识别功能,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增加

C#开发微信门户及应用(1)--开始使用微信接口

原文:C#开发微信门户及应用(1)--开始使用微信接口 微信应用如火如荼,很多公司都希望搭上信息快车,这个是一个商机,也是一个技术的方向,因此,有空研究下.学习下微信的相关开发,也就成为日常计划的重要事情之一了.本系列文章希望从一个循序渐进的角度上,全面介绍微信的相关开发过程和相关经验总结,希望给大家了解一下相关的开发历程.本随笔主要针对微信开发过程的前期准备和一些初始的工作的介绍. 在写下本文的之前一周时间里,我主要就是参考一些介绍文章以及微信公众平台的相关接口说明,并结合C#的代码开发,整理