欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

微信小程序支付功能 C# .NET开发

程序员文章站 2023-10-16 19:48:57
微信小程序支付功能的开发的时候坑比较多,不过对于钱的事谨慎也是好事。网上关于小程序支付的实例很多,但是大多多少有些问题,C#开发的更少。此篇文档的目的是讲开发过程中遇到的问题做一个备注,也方便其他开发的同学作为参考! 1、首先建议把官方文档支付部分看上三遍,每个细节都不要放过,因为任何一个点和微信要 ......

微信小程序支付功能的开发的时候坑比较多,不过对于钱的事谨慎也是好事。网上关于小程序支付的实例很多,但是大多多少有些问题,c#开发的更少。此篇文档的目的是讲开发过程中遇到的问题做一个备注,也方便其他开发的同学作为参考!

 

       1、首先建议把官方文档支付部分看上三遍,每个细节都不要放过,因为任何一个点和微信要求不符都会导致支付不成功。https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=3_1

      2、经过验证的微信支付功能,会需要一些商户号、支付秘钥等,不要搞混。

     3、经常遇到的是“签名错误”,请仔细看需要传送的xml参数及取值规则是否符合微信规则。微信有个验证工具可以验证发送的xml字段是否合法。

 

下面上代码:

 

web.config

 

    <add key="connectionstring" value="server=127.0.0.1;database=;uid=sa;pwd="/>
    <add key="connectionstring2" value="server=127.0.0.1;database=codematic2;uid=sa;pwd=1"/>
    <add key="appid" value=""/>//appid
    <add key="secret" value=""/>//小程序秘钥
    <add key="mch_id" value=""/>//商户号
    <add key="key" value=""/>//支付秘钥
    <add key="ip" value=""/>//服务器ip
    <add key="payresulturl" value=""/>//微信返回接收信息的url地址
  </appsettings>

支付后台xiadan.ashx

 

 

<%@ webhandler language="c#" class="xiadan" %>

using system;
using system.web;
using system.net;
using system.io;
using system.configuration;
using maticsoft.model;
using maticsoft.bll;
using system.security.cryptography;
using system.text;
using system.xml.serialization;
using system.xml;
using system.collections.generic;
using system.data;
using system.net.security;
using system.security.cryptography.x509certificates;
using system.linq;
using newtonsoft.json;

public class xiadan : ihttphandler
{

    public void processrequest(httpcontext context)
    {
        context.response.contenttype = "text/plain";
        string openid = context.request.params["openid"];
        string ordertime = context.request.params["ordertime"];

        string appid = configurationmanager.appsettings["appid"];
        string secret = configurationmanager.appsettings["secret"];
        string key = configurationmanager.appsettings["key"];
        string mch_id = configurationmanager.appsettings["mch_id"];
        string ip = configurationmanager.appsettings["ip"];
        string payresulturl = configurationmanager.appsettings["payresulturl"];
        string roomid = context.request.params["roomid"];
        string aa = "-押金";////商品描述交易字段格式根据不同的应用场景按照以下格式:app——需传入应用市场上的app名字-实际商品名称,天天爱消除-游戏充值。

        string strcode = aa;
        byte[] buffer = encoding.utf8.getbytes(strcode);
        string body = encoding.utf8.getstring(buffer, 0, buffer.length);
        string totalfee = context.request.params["totalfee"];
        string output = "";
        if ((context.request.params["openid"] != null) && (context.request.params["openid"] != ""))
        {
            //orderinfo order = new orderinfo();

            //order.appid = appid;

            system.random random = new system.random();



            var dic = new dictionary<string, string>
{
    {"appid", appid},
    {"mch_id", mch_id},
    {"nonce_str", getrandomstring(20)/*random.next().tostring()*/},
    {"body",body},
    {"out_trade_no",roomid + datetime.now.tostring("yyyymmddhhmmssfff") + random.next(999).tostring()},//商户自己的订单号码
    {"total_fee",totalfee},
    {"spbill_create_ip",ip},//服务器的ip地址
    {"notify_url",payresulturl},//异步通知的地址,不能带参数
    {"trade_type","jsapi" },
    {"openid",openid}
};
      //加入签名
            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", appid},
    {"timestamp", gettimestamp()},
    {"noncestr", dic["nonce_str"]},
    {"package",  "prepay_id="+ds.tables[0].rows[0]["prepay_id"].tostring()},
    {"signtype", "md5"}
};

                    //在服务器上签名
                    res.add("paysign", getsignstring(res));
                    // string signapp = res.tostring();
                    string signapp = jsonconvert.serializeobject(res);
                    if ((context.request.params["openid"] != null) && (context.request.params["openid"] != ""))
                    { 
                    //存储订单信息
                    maticsoft.model.order_history oh = new maticsoft.model.order_history();
                    //oh.shop_id =
                    oh.room_id = convert.toint32(roomid);
                    oh.pay_price = convert.todecimal(totalfee);
                    oh.out_trade_no = dic["out_trade_no"];
                    oh.order_timestart = convert.todatetime(ordertime);
                    oh.openid = openid;
                    oh.creating_date = datetime.now;

                    maticsoft.bll.order_history bll = new maticsoft.bll.order_history();
                    bll.add(oh);

                }
                context.response.write(signapp);
            }
        }




    }
    context.response.write(output);
    }

public bool isreusable
{
    get
    {
        return false;
    }
}

public string getmd5hash(string input)
{
    if (input == null)
    {
        return null;
    }

    md5 md5hash = md5.create();

    // 将输入字符串转换为字节数组并计算哈希数据  
    byte[] data = md5hash.computehash(encoding.utf8.getbytes(input));

    // 创建一个 stringbuilder 来收集字节并创建字符串  
    stringbuilder sbuilder = new stringbuilder();

    // 循环遍历哈希数据的每一个字节并格式化为十六进制字符串  
    for (int i = 0; i < data.length; i++)
    {
        sbuilder.append(data[i].tostring());
    }

    // 返回十六进制字符串  
    return sbuilder.tostring();
}
/// <summary>  
/// 对象序列化成 xml string  
/// </summary>  
public static string xmlserialize<t>(t obj)
{
    string xmlstring = string.empty;
    xmlserializer xmlserializer = new xmlserializer(typeof(t));
    using (memorystream ms = new memorystream())
    {
        xmlserializer.serialize(ms, obj);
        xmlstring = encoding.utf8.getstring(ms.toarray());
    }
    return xmlstring;
}
/// <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["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
    // sign = system.web.security.formsauthentication.hashpasswordforstoringinconfigfile(sign, "md5").toupper();
    system.security.cryptography.md5 md5 = system.security.cryptography.md5.create();
    sign = bitconverter.tostring(md5.computehash(encoding.utf8.getbytes(sign))).replace("-", null);
    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.ashx

 

<%@ webhandler language="c#" class="notify_url" %>

using system;
using system.web;
using system.collections.generic;
using system.data;
using system.io;
using system.text;
using system.xml;
using system.net;
public class notify_url : ihttphandler
{
    public string return_result = "";
    public void processrequest(httpcontext context)
    {
        context.response.contenttype = "text/plain";
        context.response.write("hello world");


        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,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
                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["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();
        }
    }


    public bool isreusable
    {
        get
        {
            return false;
        }
    }

    //获得post过来的数据
    public string getpoststr()
    {
        int32 intlen = convert.toint32(system.web.httpcontext.current.request.inputstream.length);
        byte[] b = new byte[intlen];
        system.web.httpcontext.current.request.inputstream.read(b, 0, intlen);
        return system.text.encoding.utf8.getstring(b);
    }
}