.NET 微信支付--扫码支付

开发前,我们先去官网下载一个Demo(根据自己的开发语言选择不同的版本):

https://pay.weixin.qq.com/wiki/doc/api/native_sl.php?chapter=11_1

第一步,打开这个Demo,首先我们看这个获取支付二维码地址的方法:

/**
        * 生成直接支付url,支付url有效期为2小时,模式二
        * @param productId 商品ID
        * @return 模式二URL
        */
        public string GetPayUrl(string productId)
        {
            WxPayData data = new WxPayData();
            data.SetValue("body", "扫码支付--模式二");//商品描述
            data.SetValue("attach", "可以添加需要传递的数据");//附加数据
            data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
            data.SetValue("total_fee", 1);//总金额
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
            data.SetValue("goods_tag", "jjj");//商品标记
            data.SetValue("trade_type", "NATIVE");//交易类型
            data.SetValue("product_id", productId);//商品ID

            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接

            return url;
        }

这里面会设置一些参数,需要注意的地方,我觉得有三个点:

1. 设置“attach”。

    这个东西注释解释的是附加数据,其实你可以理解为一个扩张字段,用于存放你下单后需要取的数据。举一个具体的例子:我有十个售货机,编号1-10,假如我从第一台售货机买了一个东西,然后在售货机生成支付二维码的时候,我在attach中存放一个数据:5,然后等待支付完成后,我在后台获得这笔支付的订单的时候,根据订单中attach的值,我就能知道是哪台机器的订单了。(这只是个举例,可以这么用,但是他的用处肯定不会这么小啦,各位理解就好。。)

2.设置“total_fee”。

    我们看到这个东西设置的是1,但是其实支付的时候显示的是0.01元,这是因为微信的最小单位是1元。我的意思就是,,如果你要设置扫码支付的价格是1元的话,那么这里你就要写100。

3.设置“trade_type”。

    这个是代表的支付类型,如果你用的是扫码支付,那么这里就是写“NATIVE”。当然还有其他的支付方式,比如:调用微信的支付,那么这里就需要写“JSAPI”啦。


第二步,设置完这些就准备下单了(UnifiedOrder)

/**
        * 
        * 统一下单
        * @param WxPaydata inputObj 提交给统一下单API的参数
        * @param int timeOut 超时时间
        * @throws WxPayException
        * @return 成功时返回,其他抛异常
        */
        public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 6)
        {
            string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            //检测必填参数
            if (!inputObj.IsSet("out_trade_no"))
            {
                throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
            }
            else if (!inputObj.IsSet("body"))
            {
                throw new WxPayException("缺少统一支付接口必填参数body!");
            }
            else if (!inputObj.IsSet("total_fee"))
            {
                throw new WxPayException("缺少统一支付接口必填参数total_fee!");
            }
            else if (!inputObj.IsSet("trade_type"))
            {
                throw new WxPayException("缺少统一支付接口必填参数trade_type!");
            }

            //关联参数
            if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
            {
                throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
            }
            if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
            {
                throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
            }

            //异步通知url未设置,则使用配置文件中的url
            if (!inputObj.IsSet("notify_url"))
            {
                inputObj.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//异步通知url
            }

            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
            inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip	  	    
            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串

            //签名
            inputObj.SetValue("sign", inputObj.MakeSign());
            string xml = inputObj.ToXml();

            var start = DateTime.Now;
            string response = HttpService.Post(xml, url, false, timeOut);
            var end = DateTime.Now;
            int timeCost = (int)((end - start).TotalMilliseconds);

            WxPayData result = new WxPayData();
            result.FromXml(response);

            ReportCostTime(url, timeCost, result);//测速上报

            return result;
        }

在下单里面需要注意的几个方面:

1.异步通知url未设置。

    这个URL的用处就是,你支付完成之后的回调函数,作用是用来告诉你支付完成啦(同时,微信会把本次支付的订单的详情也返回到这个URL地址的界面)。

2.特别特别要注意的问题来了。

    首先看下这段代码:

 inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
 inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
 inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip	  	    
 inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串

这里面就牵扯到另一个概念了:微信的服务商和商户。

一般的情况是这样子,一个微信公众号对应一个微信服务商,一个微信服务商下面可以有多个商户。这句话什么意思呢?举个例子(感觉有不准的地方,大神们请斧正下):一个微信公众号相当于一个上市公司,这个微信服务商相当于这个上市公司的财务部门,管收钱的,就是你们下来(微信支付,扫码支付等)的钱和出去(结算,转账)的钱,我都可以打到这个账户上。

那上面的appid就相当于公司的代码一样,mch_id就相当于财务的银行账号(我不习惯管这个叫商户号,因为我觉得更像是服务商号),我需要打钱,直接填写上面的信息就可以了。

但是有很多公司是有独立子公司的(也就是独立营收的,账务独立),意思就是,我子公司的钱我是不会直接打到总公司账上去的,,这个时候我需要他打钱到我子公司的账户上,所以我还需要把我子公司的账号给他,就变成如下代码了:

inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip	  	    
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
inputObj.SetValue("sub_mch_id", WxPayConfig.SUB_MCH_ID);

发现多了一个字段:sub_mch_id。这个就是我子公司的账号,对应微信里面这个就是真正的商户的商户号。


第三步,设置完这些,我们直接调用GetPayUrl方法后就会获得微信返回的一个二维码的地址,我们拿到这个地址,只需要把他转为图片,显示到页面上,等待扫码支付然后就完成了。


第四步,异步通知url。

第二步的时候说到一个异步通知URL的问题,其实就是一个接受微信支付结果的地址。

首先我们找到微信官方Demo这个文件下面的代码看下:

namespace WxPayAPI
{
    public partial class ResultNotifyPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            ResultNotify resultNotify = new ResultNotify(this);
            resultNotify.ProcessNotify();
        }       
    }
}
很简单的几行代码,关键点在于resultNotify.ProcessNotify()这一行,返回的数据解析如下(以下数据已做修改,不是真实数据,原因你懂的。。。)
 
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
        
大体的返回信息如上,你只要看到result_code=SUCCES,那么恭喜你,

你的整个流程已经成功99%啦。等等,为什么说成功99%,而不是100%,这就要说另一个坑啦,都怪自己当时没仔细阅读每一行文档。

这么说吧,我们做程序的一般都是等待支付完成后还有其他的操作,,也就是真实逻辑并没有走完。比如用户支付成功了,我们会给他推送消息,APP消息,微信模板消息等等的消息或者其他操作,比如积分变动啊,这时候仅仅依靠result_code=SUCCES去判断会有一个问题,当时楼主后续操作就是会发送微信模板消息,然后靠这个判断,造成的结果就是支付一次成功后会不断的给我发消息,最后还是看了文档中提到,微信的支付回调函数是会多次回调的,也就是多次访问你设置的那个异步通知URL,然后每访问一次就会发一次。

所以还需要我们人为的判断,比如,我会在订单建立之初创建一条记录,记录的支付状态字段为:未支付。下单的时候把这个记录的ID放到订单的attach字段中,等到在回调函数中收到已完成的订单,,取出attach内容(记录ID),然后根据这个ID去查我数据库中这条记录的支付状态,如果是未支付,那么改为已支付,同时发送微信模板消息。如果是其他状态,比如:已支付或者取消支付等,我都不做操作,直接返回false。


好啦,到这里基本整个流程就走完啦。


你可能感兴趣的:(WeChat)