.NET微信统一下单后台服务编程示例(2022年最新,亲测可用)

 

.NET微信统一下单后台服务编程示例(2022最新,亲测可用)

 

一、简述微信统一下单支付流程

     1.APP前端调用后台服务的登录接口,获取到用户的OpenID(我们这一步骤让前端去获取)

     2.服务端代码这边生成订单

     3.服务端调用支付统一下单的Api

     4.服务端将再次签名,返回5个参数(前端得到数据后可以调起支付)

     5.微信后台会回调我们服务端,我们通过回调更新订单状态

     6.前端也会调用服务端订单查询接口,服务端查询订单状态(防止微信回调这边的一个时间差),如果成功了,在这个接口里会向用户发送一个返回消息。

     重点是步骤3和4,特别是签名那块的格式要求务必按照微信的要求来(具体可以参考微信统一下单官网)。

 

二、提前准备工作

       2.1 微信开放平台已认证,【管理中心】获取“管理中心 / 应用详情”页面的AppID,AppSecret。

       2.2进入微信支付下关联的商户号mch_id,以及商户平台中获取商户秘钥wx_key。

       2.3阿里云或者华为云开通ECS服务,配置IIS7.0,asp.net微信支付后台建议安装.Net4.0或更高版本。

三、代码案例

 本项目代码开发IDE采用Vs2019,新建项目WxAPI,创建两个aspx页面,名称分别为:AppServer.aspx(统一下单访问页面)和notify_url.aspx(返回结果页面)。

    AppServer.aspx页面代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Net.Http;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
using System.Data;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace WxAppServer
{
    public partial class AppServer : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

            string Body = string.Empty;  //订单描述
            string Subject = string.Empty;//订单简介
            string TotalAmount = string.Empty;//金额
            string OutTradeNo = string.Empty;//订单号
            string PayDdnom = string.Empty;//商户订单号,这里暂时由app前端传值

            Body = "支付";
            Subject = "微信支付";
            //TotalAmount = 201;
            OutTradeNo = "100000" + DateTime.Now.ToString("yyyyMMddhhmm") + "887766";


            if (!string.IsNullOrEmpty(Request.Params["TotalAmount"]))
            {
                TotalAmount = Request.Params["TotalAmount"];
                //TotalAmount = (int.Parse(TotalAmount) * 100).ToString();
            }
            else
            {
                TotalAmount = "0";
            }

            if (!string.IsNullOrEmpty(Request.Params["PayDdnom"]))
            {
                PayDdnom = Request.Params["PayDdnom"];
            }
            else
            {
                PayDdnom = "";
            }



            //微信支付 基础配置信息
            string wx_appid = System.Web.Configuration.WebConfigurationManager.AppSettings["wx_appid"].ToString();//微信开放平台审核通过的应用
            string wx_mch_id = System.Web.Configuration.WebConfigurationManager.AppSettings["wx_mch_id"].ToString(); //微信支付分配的商户号
            string wx_nonce_str = GetRandomString(16);//随机字符串,不长于32位
            string aa = "***服务系统-" + Subject;////商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天昱凤凰城-物业充值。

            string strcode = aa;
            byte[] buffer = Encoding.UTF8.GetBytes(strcode);
            string wx_body = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
            //string wx_out_trade_no = DateTime.Now.ToString("yyyyMMddHHmmss") + GetRandomString(10);//商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号

            string wx_out_trade_no = PayDdnom;//商户系统内部的订单号,32个字符内、可包含字母,这里暂时由APP前端传值
            string wx_total_fee = Convert.ToString(TotalAmount);//;//订单总金额,单位为分,详见支付金额
            string wx_spbill_create_ip = GetWebClientIp();////     用户端实际ip
            string wx_notify_url = System.Web.Configuration.WebConfigurationManager.AppSettings["wxpay_notifyurl"].ToString();////接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
            string wx_trade_type = "APP";////支付类型
            string wx_sign = "";// 签名
            string wx_key = System.Web.Configuration.WebConfigurationManager.AppSettings["wx_key"].ToString(); //密钥

            var dic = new Dictionary<string, string>
{
    {"appid", wx_appid},
    {"mch_id", wx_mch_id},
    {"nonce_str", wx_nonce_str},
    {"body", wx_body},
    {"out_trade_no", wx_out_trade_no},//商户自己的订单号码
    {"total_fee", wx_total_fee},
    {"spbill_create_ip",wx_spbill_create_ip},//服务器的IP地址
    {"notify_url", wx_notify_url},//异步通知的地址,不能带参数
    {"trade_type", wx_trade_type}
};


            //加入签名
            dic.Add("sign", GetSignString(dic));

            var sb = new StringBuilder();
            sb.Append("<xml>");


            foreach (var d in dic)
            {
                sb.Append("<" + d.Key + ">" + d.Value + "</" + d.Key + ">");
            }
            sb.Append("</xml>"); var xml = new XmlDocument();
            //  xml.LoadXml(GetPostString("https://api.mch.weixin.qq.com/pay/unifiedorder", sb.ToString()));
            CookieCollection coo = new CookieCollection();
            Encoding en = Encoding.GetEncoding("UTF-8");

            HttpWebResponse response = CreatePostHttpResponse("https://api.mch.weixin.qq.com/pay/unifiedorder", sb.ToString(), en);
            //打印返回值
            Stream stream = response.GetResponseStream();   //获取响应的字符串流
            StreamReader sr = new StreamReader(stream); //创建一个stream读取流
            string html = sr.ReadToEnd();   //从头读到尾,放到字符串html

            Console.WriteLine(html);
            xml.LoadXml(html);
            //对请求返回值 进行处理

            var root = xml.DocumentElement;

            DataSet ds = new DataSet();
            StringReader stram = new StringReader(html);
            XmlTextReader reader = new XmlTextReader(stram);
            ds.ReadXml(reader);
            string return_code = ds.Tables[0].Rows[0]["return_code"].ToString();
            if (return_code.ToUpper() == "SUCCESS")
            {
                //通信成功
                string result_code = ds.Tables[0].Rows[0]["result_code"].ToString();//业务结果
                if (result_code.ToUpper() == "SUCCESS")
                {
                    var res = new Dictionary<string, string>
{
    {"appid", wx_appid},
    {"partnerid", wx_mch_id},
    {"prepayid", root.SelectSingleNode("/xml/prepay_id").InnerText},
    {"noncestr", dic["nonce_str"]},
    {"timestamp", GetTimeStamp()},
    {"package", "Sign=WXPay"}
};




                    //在服务器上签名
                    //res.Add("sign", GetSignString(res));
                    //string signapp = res.ToString();

                    //GetSignString1(res);
                    //Response.End();
                    string json_str = "{'appid':'" + wx_appid + "','noncestr':'" + dic["nonce_str"] + "','package':'Sign=WXPay','partnerid':'" + wx_mch_id + "','prepayid':'" + root.SelectSingleNode("/xml/prepay_id").InnerText + "','timestamp':" + GetTimeStamp() + ",'sign':'" + GetSignString(res) + "'}";
                    json_str = json_str.Replace("'", "\"");
                    Response.Write(json_str);
                    // Response.Write("{\"appid\":\"wx0411fa6a39d61297\",\"noncestr\":\"T5Z0CQhdsErFb6TJ\",    \"package\":\"Sign=WXPay\",\"partnerid\":\"1230636401\",\"prepayid\":\"wx20170918224227155b06a0c60785392660\",\"timestamp\":1505745747,\"sign\":\"F505772C941F240EECB25A943B079607\"}");  //官方的返回数据
                    // Response.Write("{\"appid\":\"wx92fe0ecdb6d0c5db\",\"noncestr\":\"M4OTEMYT43UBGFJ4\",\"package\":\"Sign=WXPay\",\"partnerid\":\"1487416022\",\"prepayid\":\"wx2017092313454242bf6e8d210030506192\",\"timestamp\":1506146531,\"sign\":\"3477EB571C3C65A17ED78621E5C7C63B\"}");

                }
            }





        }


        /// <summary>
        /// 从字符串里随机得到,规定个数的字符串.
        /// </summary>
        /// <param name="allChar"></param>
        /// <param name="CodeCount"></param>
        /// <returns></returns>
        public static string GetRandomString(int CodeCount)
        {
            string allChar = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,i,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
            string[] allCharArray = allChar.Split(',');
            string RandomCode = "";
            int temp = -1;
            Random rand = new Random();
            for (int i = 0; i < CodeCount; i++)
            {
                if (temp != -1)
                {
                    rand = new Random(temp * i * ((int)DateTime.Now.Ticks));
                }
                int t = rand.Next(allCharArray.Length - 1);
                while (temp == t)
                {
                    t = rand.Next(allCharArray.Length - 1);
                }
                temp = t;
                RandomCode += allCharArray[t];
            }

            return RandomCode;
        }


        public static string GetWebClientIp()
        {
            string userIP = "IP";

            try
            {
                if (System.Web.HttpContext.Current == null
            || System.Web.HttpContext.Current.Request == null
            || System.Web.HttpContext.Current.Request.ServerVariables == null)
                    return "";

                string CustomerIP = "";

                //CDN加速后取到的IP   
                CustomerIP = System.Web.HttpContext.Current.Request.Headers["Cdn-Src-Ip"];
                if (!string.IsNullOrEmpty(CustomerIP))
                {
                    return CustomerIP;
                }

                CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];


                if (!String.IsNullOrEmpty(CustomerIP))
                    return CustomerIP;

                if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null)
                {
                    CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                    if (CustomerIP == null)
                        CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
                }
                else
                {
                    CustomerIP = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

                }

                if (string.Compare(CustomerIP, "unknown", true) == 0)
                    return System.Web.HttpContext.Current.Request.UserHostAddress;
                return CustomerIP;
            }
            catch { }

            return userIP;
        }




        private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true; //总是接受   
        }

        public static HttpWebResponse CreatePostHttpResponse(string url, string datas, Encoding charset)
        {
            HttpWebRequest request = null;
            //HTTPSQ请求
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
            request = WebRequest.Create(url) as HttpWebRequest;
            request.ProtocolVersion = HttpVersion.Version10;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            //如果需要POST数据   
            //if (!(parameters == null || parameters.Count == 0))
            //{
            StringBuilder buffer = new StringBuilder();
            //int i = 0;
            //foreach (string key in parameters.Keys)
            //{
            //    if (i > 0)
            //    {
            //        buffer.AppendFormat("&{0}={1}", key, parameters[key]);
            //    }
            //    else
            //    {
            //        buffer.AppendFormat("{0}={1}", key, parameters[key]);
            //    }
            //    i++;
            //}
            buffer.AppendFormat(datas);
            byte[] data = charset.GetBytes(buffer.ToString());
            using (Stream stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }
            //}
            return request.GetResponse() as HttpWebResponse;
        }


        public string GetSignString(Dictionary<string, string> dic)
        {
            string key = System.Web.Configuration.WebConfigurationManager.AppSettings["wx_key"].ToString(); ;//商户平台 API安全里面设置的KEY  32位长度
            //排序
            dic = dic.OrderBy(d => d.Key).ToDictionary(d => d.Key, d => d.Value);
            //连接字段
            var sign = dic.Aggregate("", (current, d) => current + (d.Key + "=" + d.Value + "&"));
            sign += "key=" + key;
            //MD5
            //Response.Write(sign+"<br/>");
            sign = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sign, "MD5").ToUpper();

            return sign;
        }

        public string GetSignString1(Dictionary<string, string> dic)
        {
            string key = System.Web.Configuration.WebConfigurationManager.AppSettings["wx_key"].ToString(); ;//商户平台 API安全里面设置的KEY  32位长度
            //排序
            dic = dic.OrderBy(d => d.Key).ToDictionary(d => d.Key, d => d.Value);
            //连接字段
            var sign = dic.Aggregate("", (current, d) => current + (d.Key + "=" + d.Value + "&"));
            sign += "key=" + key;
            //MD5
            Response.Write(sign + "<br/>" + System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sign, "MD5").ToUpper());
            sign = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sign, "MD5").ToUpper();
            return sign;
        }



        /// <summary>  
        /// 获取时间戳  
        /// </summary>  
        /// <returns></returns>  
        public static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }
    }
}

notify_url.aspx页面代码如下:

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;


namespace WxAppServer
{
    public partial class notify_url : System.Web.UI.Page
    {
        public string return_result = "";
        protected void Page_Load(object sender, EventArgs e)
        {

            String xmlData = getPostStr();//获取请求数据
            if (xmlData == "")
            {

            }
            else
            {
                var dic = new Dictionary<string, string>
{
    {"return_code", "SUCCESS"},
    {"return_msg","OK"}

};
                var sb = new StringBuilder();
                sb.Append("<xml>");


                foreach (var d in dic)
                {
                    sb.Append("<" + d.Key + ">" + d.Value + "</" + d.Key + ">");
                }
                sb.Append("</xml>");





                //把数据重新返回给客户端
                DataSet ds = new DataSet();
                StringReader stram = new StringReader(xmlData);
                XmlTextReader datareader = new XmlTextReader(stram);
                ds.ReadXml(datareader);
                if (ds.Tables[0].Rows[0]["return_code"].ToString() == "SUCCESS")
                {


                    string wx_appid = "";//微信开放平台审核通过的应用APPID
                    string wx_mch_id = "";//微信支付分配的商户号

                    string wx_nonce_str = "";//     随机字符串,不长于32位
                    string wx_sign = "";//签名,详见签名算法
                    string wx_result_code = "";//SUCCESS/FAIL

                    string wx_return_code = "";
                    string wx_openid = "";//用户在商户appid下的唯一标识
                    string wx_is_subscribe = "";//用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
                    string wx_trade_type = "";//     APP
                    string wx_bank_type = "";//     银行类型,采用字符串类型的银行标识,银行类型见银行列表
                    string wx_fee_type = "";//     货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型


                    string wx_transaction_id = "";//微信支付订单号
                    string wx_out_trade_no = "";//商户系统的订单号,与请求一致。
                    string wx_time_end = "";//     支付完成时间,格式为yyyyMMddHHmmss,如2019年12月25日9点10分10秒表示为20191225091010。其他详见时间规则
                    int wx_total_fee = -1;//     订单总金额,单位为分
                    int wx_cash_fee = -1;//现金支付金额订单现金支付金额,详见支付金额


                    #region  数据解析
                    //列 是否存在
                    string signstr = "";//需要前面的字符串
                    //wx_appid
                    if (ds.Tables[0].Columns.Contains("appid"))
                    {
                        wx_appid = ds.Tables[0].Rows[0]["appid"].ToString();
                        if (!string.IsNullOrEmpty(wx_appid))
                        {
                            signstr += "appid=" + wx_appid;
                        }
                    }

                    //wx_bank_type
                    if (ds.Tables[0].Columns.Contains("bank_type"))
                    {
                        wx_bank_type = ds.Tables[0].Rows[0]["bank_type"].ToString();
                        if (!string.IsNullOrEmpty(wx_bank_type))
                        {
                            signstr += "&bank_type=" + wx_bank_type;
                        }
                    }
                    //wx_cash_fee
                    if (ds.Tables[0].Columns.Contains("cash_fee"))
                    {
                        wx_cash_fee = Convert.ToInt32(ds.Tables[0].Rows[0]["cash_fee"].ToString());

                        signstr += "&cash_fee=" + wx_cash_fee;
                    }






                    //wx_fee_type
                    if (ds.Tables[0].Columns.Contains("fee_type"))
                    {
                        wx_fee_type = ds.Tables[0].Rows[0]["fee_type"].ToString();
                        if (!string.IsNullOrEmpty(wx_fee_type))
                        {
                            signstr += "&fee_type=" + wx_fee_type;
                        }
                    }

                    //wx_is_subscribe
                    if (ds.Tables[0].Columns.Contains("is_subscribe"))
                    {
                        wx_is_subscribe = ds.Tables[0].Rows[0]["is_subscribe"].ToString();
                        if (!string.IsNullOrEmpty(wx_is_subscribe))
                        {
                            signstr += "&is_subscribe=" + wx_is_subscribe;
                        }
                    }

                    //wx_mch_id
                    if (ds.Tables[0].Columns.Contains("mch_id"))
                    {
                        wx_mch_id = ds.Tables[0].Rows[0]["mch_id"].ToString();
                        if (!string.IsNullOrEmpty(wx_mch_id))
                        {
                            signstr += "&mch_id=" + wx_mch_id;
                        }
                    }

                    //wx_nonce_str
                    if (ds.Tables[0].Columns.Contains("nonce_str"))
                    {
                        wx_nonce_str = ds.Tables[0].Rows[0]["nonce_str"].ToString();
                        if (!string.IsNullOrEmpty(wx_nonce_str))
                        {
                            signstr += "&nonce_str=" + wx_nonce_str;
                        }
                    }

                    //wx_openid
                    if (ds.Tables[0].Columns.Contains("openid"))
                    {
                        wx_openid = ds.Tables[0].Rows[0]["openid"].ToString();
                        if (!string.IsNullOrEmpty(wx_openid))
                        {
                            signstr += "&openid=" + wx_openid;
                        }
                    }

                    //wx_out_trade_no
                    if (ds.Tables[0].Columns.Contains("out_trade_no"))
                    {
                        wx_out_trade_no = ds.Tables[0].Rows[0]["out_trade_no"].ToString();
                        if (!string.IsNullOrEmpty(wx_out_trade_no))
                        {
                            signstr += "&out_trade_no=" + wx_out_trade_no;
                        }
                    }

                    //wx_result_code 
                    if (ds.Tables[0].Columns.Contains("result_code"))
                    {
                        wx_result_code = ds.Tables[0].Rows[0]["result_code"].ToString();
                        if (!string.IsNullOrEmpty(wx_result_code))
                        {
                            signstr += "&result_code=" + wx_result_code;
                        }
                    }

                    //wx_result_code 
                    if (ds.Tables[0].Columns.Contains("return_code"))
                    {
                        wx_return_code = ds.Tables[0].Rows[0]["return_code"].ToString();
                        if (!string.IsNullOrEmpty(wx_return_code))
                        {
                            signstr += "&return_code=" + wx_return_code;
                        }
                    }

                    //wx_sign 
                    if (ds.Tables[0].Columns.Contains("sign"))
                    {
                        wx_sign = ds.Tables[0].Rows[0]["sign"].ToString();
                        //if (!string.IsNullOrEmpty(wx_sign))
                        //{
                        //    signstr += "&sign=" + wx_sign;
                        //}
                    }

                    //wx_time_end
                    if (ds.Tables[0].Columns.Contains("time_end"))
                    {
                        wx_time_end = ds.Tables[0].Rows[0]["time_end"].ToString();
                        if (!string.IsNullOrEmpty(wx_time_end))
                        {
                            signstr += "&time_end=" + wx_time_end;
                        }
                    }

                    //wx_total_fee
                    if (ds.Tables[0].Columns.Contains("total_fee"))
                    {
                        wx_total_fee = Convert.ToInt32(ds.Tables[0].Rows[0]["total_fee"].ToString());

                        signstr += "&total_fee=" + wx_total_fee;
                    }

                    //wx_trade_type
                    if (ds.Tables[0].Columns.Contains("trade_type"))
                    {
                        wx_trade_type = ds.Tables[0].Rows[0]["trade_type"].ToString();
                        if (!string.IsNullOrEmpty(wx_trade_type))
                        {
                            signstr += "&trade_type=" + wx_trade_type;
                        }
                    }

                    //wx_transaction_id
                    if (ds.Tables[0].Columns.Contains("transaction_id"))
                    {
                        wx_transaction_id = ds.Tables[0].Rows[0]["transaction_id"].ToString();
                        if (!string.IsNullOrEmpty(wx_transaction_id))
                        {
                            signstr += "&transaction_id=" + wx_transaction_id;
                        }
                    }

                    #endregion

                    //追加key 密钥
                    signstr += "&key=" + System.Web.Configuration.WebConfigurationManager.AppSettings["wx_key"].ToString();
                    //签名正确
                    string orderStrwhere = "ordernumber='" + wx_out_trade_no + "'";



                    if (wx_sign == System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(signstr, "MD5").ToUpper())
                    {
                        //签名正确   处理订单操作逻辑


                    }
                    else
                    {
                        //追加备注信息

                    }

                }
                else
                {
                    // 返回信息,如非空,为错误原因  签名失败 参数格式校验错误
                    string return_msg = ds.Tables[0].Rows[0]["return_msg"].ToString();

                }


                return_result = sb.ToString();
            }
        }

        //获得Post过来的数据
        public string getPostStr()
        {
            Int32 intLen = Convert.ToInt32(Request.InputStream.Length);
            byte[] b = new byte[intLen];
            Request.InputStream.Read(b, 0, intLen);
            return System.Text.Encoding.UTF8.GetString(b);
        }
    }
}

Web.config配置代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!--
  有关如何配置 ASP.NET 应用程序的详细信息,请访问
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation targetFramework="4.0" />
  </system.web>
  <appSettings>
    <!--微信开放平台审核通过的应用APPID-->
    <add key="wx_appid" value="wx100000000000000000000" />
    <!--微信支付分配的商户号APPID-->
    <add key="wx_mch_id" value="10000000000" />
    <!--wx_key 微信key 密钥-->
    <add key="wx_key" value="000000000000000000000000000000" />
    <!--wxpay_notifyurl 请求回调服务器网址-->
    <add key="wxpay_notifyurl" value="http://127.0.0.1:999/notify_url.aspx" />
  </appSettings>
</configuration>
<!--ProjectGuid: {7F7F944F-4BEA-4542-A558-1ADCF0224F9C}-->

    提示:前端访问Url格式为http://127.0.0.1:9999/AppServer.aspx?TotalAmount=100&PayDdnom=202222222222222222222222

posted @ 2022-03-25 20:27  高山流水学编程  阅读(233)  评论(0编辑  收藏  举报