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


    在微信的应用上,微信支付是一个比较有用的部分,但也是比较复杂的技术要点,在微商大行其道的年代,自己的商店没有增加微信支付好像也说不过去,微信支付旨在为广大微信用户及商户提供更优质的支付服务,微信的支付和安全系统由腾讯财付通提供支持。本文主要介绍如何在微信公众号上实现微信支付的接入、微信支付API的封装,以及API的调用,实现我们一些常见的业务调用。

    1、开通微信支付并配置

    微信支付是需要微信公众号的认证基础,也就是只对认证的公众号开放,微信认证需要签署相关的资料,并且进行对账认证,一般会有电话联系确认相关的信息的。

    在微信支付API开始使用前,我们一般需要在后台进行一定的配置,如我们需要配置公众号支付的授权目录,测试白名单等信息,以及扫码支持的回调处理地址(这个实现在后面再讲),如下所示。

    在使用API之前,我们要知道微信一些关键的操作,如退款、撤销订单等操作是需要证书的,而且常规的支付操作,我们也需要商户号、商户支付秘钥等信息,这些证书和秘钥信息,是我们从微信支付的商户平台上获取的,我们微信支付开通并审核通过后,我们就可以登录商户平台进行相关的操作了。

    首先我们需要在开发的电脑上安装证书。

    然后需要设置API的秘钥

    最后在【API安全】项目上下载证书供我们开发环境使用。

    2、微信支付API的介绍

    微信支付配置相关的参数,并获得证书、API秘钥、商户号等信息后,我们可以开始了解微信支付的API的具体使用了,我们需要先把API封装为C#的类库进行使用,这样才能在各种应用里面方便调用。

    微信支付分为有多种方式,如扫码支付、公众号支付、JSAPI支付、APP支付等方面,不过核心的API都差不多,基本上都覆盖了下面截图的几个API,只是有部分的接口差异。

    我们可以从其中扫码支付开始了解,这个是对二维码进行扫码支付的场景,分为了模式一和模式二两种方式。

    扫码支付可分为两种模式,商户根据支付场景选择相应模式。

    【模式一】:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

    【模式二】:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。

    根据扫码支付的API说明,我们可以分别对这些接口(如统一下单、查询订单、关闭订单、申请退款、查询退款、下载对账单等接口进行逐一封装,以方便我们的开发使用。

    模式一和模式二,都需要使用到了统一下单的接口,然后生成相应的二维码给客户扫码支付使用。

    那么我们先来看看统一下单的接口说明,以了解它的具体使用。

    1)应用场景

    除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。

    2)接口链接

    URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

    3)是否需要证书

    不需要

    4)请求参数

    请求参数看似很多,大概分为两部分,一部分是系统必须的固定参数,一部分是业务所需的参数。

    系统必须的固定参数如下所示。

    一部分是业务参数,业务参数如下所示,主要是记录订单的相关产品ID、说明、费用等等

    微信支付接口的调用和公众号其他接口调用不太一样,这里全部是采用XML进行交换的,感觉没有JSON那么方便灵活,如下所示是统一下单的接口提交数据。

     

    然后返回的数据也是XML的,如下面例子代码所示,而且其中的字段内容还不太确定,因此按官网的建议,使用字典集合来存储返回的数据对象。

    3、微信支付APIC#封装和调用

    根据上面的描述,我们大概了解了微信支付API 的大概说明,根据这些信息,我们可以对它进行C#代码的封装了,对于代码的封装,我们关键点在其中第一个,如果顺利封装好第一个接口,那么后面的根据通用的方式,就很容易继续处理这些接口了。

    例如,我们可以定义好微信支付的API接口定义,如下所示。

        /// <summary>
        /// 微信支付接口
        /// </summary>
        public interface ITenPayApi
        {      
            /// <summary>
            /// 生成扫描支付模式一URL
            /// </summary>
            /// <param name="productId">商品ID</param>
            /// <returns></returns>
            string GetPrePayUrl(string productId);
    
            /// <summary>
            /// 生成直接支付url,支付url有效期为2小时,模式二
            /// </summary>
            /// <param name="info">商品订单数据</param>
            /// <returns></returns>
            string GetPayUrl(WxPayOrderData info);
    
            /// <summary>
            /// 统一下单。(不需要证书,默认不需要)
            /// 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,
            /// 返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
            /// </summary>
            /// <param name="info">商品订单数据</param>
            WxPayData UnifiedOrder(WxPayOrderData info);
    
            .............

    其中的接口方法的输入参数我们定义一个实体类 WxPayOrderData 来存储一些业务参数,这些参数根据第二点的接口说明进行定义,代码如下所示

        /// <summary>
        /// 统一下单的商品订单信息
        /// </summary>
        public class WxPayOrderData
        {
            /// <summary>
            /// 商品ID, trade_type=NATIVE,此参数必传
            /// </summary>
            public string product_id { get; set; }
            /// <summary>
            /// 商品或支付单简要描述
            /// </summary>
            public string body { get; set; }
            /// <summary>
            /// 订单总金额,单位为分
            /// </summary>
            public int total_fee { get; set; }
            /// <summary>
            /// 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
            /// </summary>
            public string goods_tag { get; set; }
    
            /// <summary>
            /// 交易类型,默认为:NATIVE。
            /// JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
            /// </summary>
            public string trade_type { get; set; }
    
            /// <summary>
            /// 商品名称明细列表
            /// </summary>
            public string detail { get; set; }
            /// <summary>
            /// 附加数据
            /// 在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
            /// </summary>
            public string attach { get; set; }
            /// <summary>
            /// 用户标识
            /// trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
            /// </summary>
            public string openid { get; set; }
    
            public WxPayOrderData()
            {
                this.trade_type = "NATIVE";
            }
        }

    然后我们定义一个接口返回的类WxPayData,它用来存储返回的对象信息的,这个类在官网例子里面有说明,其里面内置一个排序过的字典对象进行存储数据,部分代码如下所示,我对它进行了相关的修改,以方便在构造函数里面初始化一些必备的参数(固定参数)。

        public class WxPayData
        {
            //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
            private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();
            
            /// <summary>
            /// 默认构造函数
            /// 如果initDefault为true,则自动填入字段(appid,mch_id,time_stamp,nonce_str,out_trade_no,)
            /// </summary>
            public WxPayData(bool initDefault = false)
            {
                if(initDefault)
                {
                    Init();
                }
            }
    
            /// <summary>
            /// 对象初始化后,自动填入字段(appid,mch_id,time_stamp,nonce_str,out_trade_no,)
            /// </summary>
            public void Init()
            {
                //初始化几个参数
                this.SetValue("appid", WxPayConfig.APPID);//公众帐号id
                this.SetValue("mch_id", WxPayConfig.MCHID);//商户号
                this.SetValue("nonce_str", GenerateNonceStr());//随机字符串
                this.SetValue("out_trade_no", GenerateOutTradeNo(WxPayConfig.MCHID));//随机字符串
    
            }

    然后我们根据上面的数据定义,可以实现统一下单的函数内容,主要是把输入参数转换为我们需要的字典参数集合,如下代码所示。

            /// <summary>
            /// 统一下单。(不需要证书,默认不需要)
            /// 除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,
            /// 返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
            /// </summary>
            /// <param name="info">商品订单数据</param>
            public WxPayData UnifiedOrder(WxPayOrderData info)
            {
                WxPayData data = new WxPayData(true);
                data.SetValue("product_id", info.product_id);//商品ID
                data.SetValue("openid", info.openid);//商品ID
    
                //其他信息
                data.SetValue("body", info.body);//商品描述
                data.SetValue("attach", info.attach);//附加数据
                data.SetValue("total_fee", info.total_fee);//总金额
                data.SetValue("goods_tag", info.goods_tag);//商品标记
                data.SetValue("trade_type", info.trade_type);//交易类型
    
                //默认构建
                data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
                data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
    
              ..............

    最后的数据交换逻辑,我们通过对URL进行POST提交XML数据给它获取返回结果就可以了,如下所示。

                string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
                return GetPostResult(data, url);

    其中上面的函数的代码逻辑如下所示,主要是把返回的结果再还原为XML对象类WxPayData。

            /// <summary>
            /// 通用的获取结果函数
            /// </summary>
            private WxPayData GetPostResult(WxPayData data, string url)
            {
                string xml = data.ToXml();
                string response = helper.GetHtml(url, xml, true);
    
                WxPayData result = new WxPayData();
                result.FromXml(response);
                return result;
            }   

    对于扫码操作的模式二,直接生成一种二维码,不需要后台进行回调的,那么它的实现逻辑只需要对上面代码进行封装就可以了,如先构建二维码的函数代码如下所示。

            /// <summary>
            /// 生成直接支付url,支付url有效期为2小时,模式二
            /// </summary>
            /// <param name="info">商品订单数据</param>
            /// <returns></returns>
            public string GetPayUrl(WxPayOrderData info)
            {
                WxPayData result = UnifiedOrder(info);//调用统一下单接口
                return result.GetString("code_url");//获得统一下单接口返回的二维码链接
            }

    如在Winform界面里面,调用生成二维码的代码如下所示,主要逻辑就是构建好二维码,然后显示在界面上。

            private void btnGetPayUrl_Click(object sender, EventArgs e)
            {
                //测试扫码模式二的生成二维码方式
                WxPayOrderData data = new WxPayOrderData()
                {
                    product_id = "123456789",
                    body = "测试支付-模式二",
                    attach = "爱奇迪技术支持",
                    detail = "测试扫码支付-模式二",
                    total_fee = 1,
                    goods_tag = "test1"
                };
    
                var url = api.GetPayUrl(data);
                var image = api.GenerateQRImage(url);
    
                this.imgGetPayUrl.Image = image;
                this.imgGetPayUrl.SizeMode = PictureBoxSizeMode.StretchImage;
            }

    另外对于模式一,它在前端传入一个简单的产品ID,生成二维码,当用户扫码的时候,微信后台会调用商户平台(我们服务器)的回调处理方法,这个回调方法会调用统一下单的API进行生成支付交易,过程有点复杂,我们来看看,我们的实现代码如下所示。

            /// <summary>
            /// 生成扫描支付模式一URL
            /// </summary>
            /// <param name="productId">商品ID</param>
            /// <returns></returns>
            public string GetPrePayUrl(string productId)
            {
                WxPayData data = new WxPayData(true);
                data.SetValue("product_id", productId);//商品ID     
                data.SetValue("time_stamp", data.GenerateTimeStamp());//随机字符串         
                data.SetValue("sign", data.MakeSign());//签名
    
                string str = data.ToUrlParams();//转换为URL串
                string url = "weixin://wxpay/bizpayurl?" + str;
    
                return url;
            }

    它的调用代码生成二维码操作如下所示。

            private void btnGetPrePayUrl_Click(object sender, EventArgs e)
            {
                var productId = "12345678";
                var url = api.GetPrePayUrl(productId);
                var image = api.GenerateQRImage(url);
    
                this.imgGetPrePayUrl.Image = image;
                this.imgGetPayUrl.SizeMode = PictureBoxSizeMode.StretchImage;
            }

    我们在第一小节里面介绍了,需要在微信后台配置扫码的回调函数,如下所示。

    这样我们还需要添加一个页面aspx、或者一般处理程序ashx的方式来实现扫码的回调过程。具体的逻辑也就是在这个页面里面获取到提交过来的参数,然后调用统一下单处理后,进行数据返回即可,代码逻辑如下所示。

    4、在页面上进行扫码处理

    前面的例子,我介绍了Winfrom的扫码例子,很多时候,我们的应用可能是基于Web的,那么它的实现是如何的呢,下面我继续介绍一下。

    首先我们在自己的业务Web后台系统里面,添加两个页面,主要是用来生成二维码在页面上进行展示的,如下所示。

    最终我们在NativePayPage.aspx页面上展示我们的二维码,方便用户进行扫码支付处理,页面的代码很简单,我们只需要在前端页面放置两个图片控件,图片内容通过MakeQRCode.aspx页面进行生成就可以了。

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <meta http-equiv="content-type" content="text/html;image/gif;charset=utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1" /> 
        <title>微信支付样例-扫码支付</title>
    </head>
    <body>
        <div style="margin-left: 10px;color:#00CD00;font-size:30px;font-weight: bolder;">扫码支付模式一</div><br/>
        <asp:Image ID="Image1" runat="server" style="200px;height:200px;"/>
        <br/><br/><br/>
        <div style="margin-left: 10px;color:#00CD00;font-size:30px;font-weight: bolder;">扫码支付模式二</div><br/>
        <asp:Image ID="Image2" runat="server" style="200px;height:200px;"/>
        
    </body>
    </html>

    页面后台的代码就是绑定二维码的过程,代码如下所示,和Winform的代码类似操作。

            protected void Page_Load(object sender, EventArgs e)
            {
                TenPayApi api = new TenPayApi();
    
                var productId = "123456789";
                //生成扫码支付模式一url
                string url1 = api.GetPrePayUrl(productId);
    
                //生成扫码支付模式二url
                WxPayOrderData info = new WxPayOrderData()
                {
                    product_id = "123456789",
                    body = "测试支付-模式二",
                    attach = "爱奇迪技术支持",
                    detail = "测试扫码支付-模式二",
                    total_fee = 1,
                    goods_tag = "test1"
                };
                string url2 = api.GetPayUrl(info);
    
                //将url生成二维码图片
                Image1.ImageUrl = "MakeQRCode.aspx?data=" + HttpUtility.UrlEncode(url1);
                Image2.ImageUrl = "MakeQRCode.aspx?data=" + HttpUtility.UrlEncode(url2);
            }

    实现后的页面效果如下所示。

    实现并预览效果,确定是我们所需的页面后,我们可以发布在公众号的菜单连接上进行测试使用了。

    打开微信公众号-广州爱奇迪,我们可以看到对应的菜单发生改变,并且看到进入微信支付的菜单可以进行支付了。

        

    以上就是微信支付的扫码过程的一个实现,微信支付还包括很多其他API接口,后面有机会可以继续进行介绍。微信支付的接口实现虽然相对其他微信接口比较复杂一些,但是我们一旦完成几个案例,后面的就相对比较容易的了,因为它的调用方式基本上比较一致,很类似。

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

    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)--开始使用微信接口

  • 相关阅读:
    Codeforces 992C(数学)
    Codeforces 990C (思维)
    Codeforces 989C (构造)
    POJ 1511 Invitation Cards(链式前向星,dij,反向建边)
    Codeforces 1335E2 Three Blocks Palindrome (hard version)(暴力)
    POJ 3273 Monthly Expense(二分)
    POJ 2566 Bound Found(尺取前缀和)
    POJ 1321 棋盘问题(dfs)
    HDU 1506 Largest Rectangle in a Histogram(单调栈)
    POJ 2823 Sliding Window(单调队列)
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/5390468.html
Copyright © 2020-2023  润新知