2015-09-04 15:44:28 WXbluethink 阅读数 2215
对于微信企业号接口文档,可参考:http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8

一、所用到的知识点

     1、post

         HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 这几种。其中 POST 一般用来向服务端提交数据。


        application/json

        application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于   JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。

     2、Json

     可参考 :http://baike.baidu.com/link?url=xrUgOk9ZoTZ_X-KwLoM6zuA2UWJAmwGnrCsKUMKCZWEEaU-0WaIxkrj2JhEsI2zA3x-mQ48svXorw5SK4j9hIK
   

  对于本事例,我们用了第三方的 json.net,可见下图

  


二、主要代码

   对于开发语言,我们使用C#语言,目标Framework为 .NET Framework 4

     获取accesstoken的方法,可见:http://blog.csdn.net/wxbluethink/article/details/48153529

    在新建项目,引入json.net

    post的相关代码:

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        StreamWriter writer = new StreamWriter(request.GetRequestStream());
        writer.Write(postDataStr);
        writer.Flush();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        string encoding = response.ContentEncoding;
        if (encoding == null || encoding.Length < 1)
        {
            encoding = "UTF-8"; //默认编码
        }
        StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(encoding));
        string retString = reader.ReadToEnd();
        return retString;

 创建成功后返回的Json

 

    /// <summary>
    /// 返回的错误消息
    /// </summary>
    public string errcode { get; set; }

    /// <summary>
    /// 对返回码的文本描述内容
    /// </summary>
    public string errmsg { get; set; }

    /// <summary>
    /// 创建的部门id。
    /// </summary>
    public int id { get; set; }

创建部门代码:

        DeptCreateResult depCjson = CreateDept(LabelAT.Text, TextBoxDept.Text, "1");  

        if (depCjson != null)
        {
            if (depCjson.errcode == "0")
            {
                Response.Write("部门新建成功!");
            }
            else
            {
                Response.Write("部门新建失败!");
                return;
            }
        }



  代码写完成后,我们进行测试,见下图:

再看看微信企业号:


ok,成功了。


源码下载:http://pan.baidu.com/s/1i39231z


但是对于源码中web.config 下图中的,你要修改成自己的。



更多C# 微信企业号见下目录链接...

C# 微信企业号系列目录


C#微信企业号回调模式,请访问 回调模式


2015-07-21 13:35:08 AnotherWant 阅读数 3291

C# 微信服务号开发基础 完整代码

作为一个新手,开发微信公众号各种不知道、各种无从下手。很希望能找到一个连引用命名空间都有的完整代码的demo拿来就用。经过两天辛苦的查阅,总结如下代码,直接可用,没有什么自定义基类,也没有需要隐藏的部分。代码中有注释,请童鞋边看边操作。
新建一个一般处理程序,粘到里面就好。只有简单的类型判断自动回复。

代码块:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Web.Security;
using System.IO;
using System.Text;

namespace BigDataMedia.Web.WeiXin
{
    /// <summary>
    /// WeixinApi 测试所用,简单判断类型自动回复。
    /// </summary>

    //post请求
    public class WeixinApi : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            weixin wx = new weixin();
            String postStr = String.Empty;

            if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
            {
                Stream s = HttpContext.Current.Request.InputStream;
                Byte[] b = new Byte[s.Length];
                s.Read(b, 0, (Int32)s.Length);
                postStr = Encoding.UTF8.GetString(b);

                if (!String.IsNullOrEmpty(postStr))
                {
                    wx.Handle(postStr);
                }
            }
            else
            {
                wx.Auth();
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
    //验证token
    public class weixin
    {
        private String Token = "你的token";

        public void Auth()
        {
            String echoStr = HttpContext.Current.Request.QueryString["echoStr"];

            if (CheckSignature())
            {
                if (!String.IsNullOrEmpty(echoStr))
                {
                    HttpContext.Current.Response.Write(echoStr);
                    HttpContext.Current.Response.End();
                }
            }
        }

        //验证微信签名
        private bool CheckSignature()
        {
            String signature = HttpContext.Current.Request.QueryString["signature"];
            String timestamp = HttpContext.Current.Request.QueryString["timestamp"];
            String nonce = HttpContext.Current.Request.QueryString["nonce"];
            String[] ArrTmp = { Token, timestamp, nonce };

            Array.Sort(ArrTmp);
            String tmpStr = String.Join("", ArrTmp);

            tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
            tmpStr = tmpStr.ToLower();

            if (tmpStr == signature)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        //判断回复类型
        public void Handle(String postStr)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(postStr);

            XmlElement rootElement = doc.DocumentElement;
            XmlNode MsgType = rootElement.SelectSingleNode("MsgType");

            RequestXML requestXML = new RequestXML();
            requestXML.ToUserName = rootElement.SelectSingleNode("ToUserName").InnerText;
            requestXML.FromUserName = rootElement.SelectSingleNode("FromUserName").InnerText;
            requestXML.CreateTime = rootElement.SelectSingleNode("CreateTime").InnerText;
            requestXML.MsgType = MsgType.InnerText;

            if (requestXML.MsgType == "text")
            {
                requestXML.Content = rootElement.SelectSingleNode("Content").InnerText;
            }

            else if (requestXML.MsgType == "location")
            {
                requestXML.Location_X = rootElement.SelectSingleNode("Location_X").InnerText;
                requestXML.Location_Y = rootElement.SelectSingleNode("Location_Y").InnerText;
                requestXML.Scale = rootElement.SelectSingleNode("Scale").InnerText;
                requestXML.MapInfo = rootElement.SelectSingleNode("Label").InnerText;
            }

            else if (requestXML.MsgType == "image")
            {
                requestXML.PicUrl = rootElement.SelectSingleNode("PicUrl").InnerText;
            }

            ResponseMsg(requestXML);
        }



        /// <summary>
        /// 回复消息
        /// </summary>
        /// <param name="weixinXML"></param>
        private void ResponseMsg(RequestXML requestXML)
        {
            String responseContent = String.Empty;

            if (requestXML.MsgType == "text")
            {
                responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, "已收到消息!");
            }
            else if (requestXML.MsgType == "location")
            {
                responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, "已知道您的位置!");
            }
            else if (requestXML.MsgType == "image") 
            {
                responseContent = FormatTextXML(requestXML.FromUserName,requestXML.ToUserName,"图片已接收!");
            }

            HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;
            HttpContext.Current.Response.Write(responseContent);
        }

        //返回格式化文本XML内容
        private String FormatTextXML(String fromUserName, String toUserName, String content)
        {
            return "<xml><ToUserName><![CDATA[" + fromUserName + "]]></ToUserName><FromUserName><![CDATA[" + toUserName + "]]></FromUserName><CreateTime>" + ConvertDateTimeInt(DateTime.Now) + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" + content + "]]></Content><FuncFlag>1</FuncFlag></xml>";
        }

        private int ConvertDateTimeInt(System.DateTime time)
        {
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalSeconds;
        }
    }


    //微信请求类
    public class RequestXML
    {
        private String toUserName = String.Empty;

        /// <summary>
        /// 公众号
        /// </summary>
        public String ToUserName
        {
            set;get;
        }

        private String fromUserName = "";

        /// <summary>
        /// 发送方微信号
        /// </summary>
        public String FromUserName
        {
            set;get;
        }

        private String createTime = String.Empty;

        /// <summary>
        /// 创建时间
        /// </summary>
        public String CreateTime
        {
            set;get;
        }

        private String msgType = String.Empty;

        /// <summary>
        /// 信息类型 地理位置:location,文本消息:text,消息类型:image
        /// </summary>
        public String MsgType
        {
            set;get;
        }

        private String content = String.Empty;

        /// <summary>
        /// 信息内容
        /// </summary>
        public String Content
        {
            set;get;
        }

        private String location_X = String.Empty;

        /// <summary>
        /// 地理位置纬度
        /// </summary>
        public String Location_X
        {
            set;get;
        }

        private String location_Y = String.Empty;

        /// <summary>
        /// 地理位置经度
        /// </summary>
        public String Location_Y
        {
            set;get;
        }

        private String scale = String.Empty;

        /// <summary>
        /// 地图缩放大小
        /// </summary>
        public String Scale
        {
            set;get;
        }

        private String mapInfo = String.Empty;

        /// <summary>
        /// 地理位置信息
        /// </summary>
        public String MapInfo
        {
            set;get;
        }

        private String picUrl = String.Empty;

        /// <summary>
        /// 图片链接
        /// </summary>
        public String PicUrl
        {
            set;get;
        }
    }

}

由于本人也是一个新手,不喜勿喷。欢迎一些有经验的前辈来指教。

2018-04-02 09:07:55 QLX119 阅读数 1713

 微信公众号支付详细步骤  

  真实的微信公众号支付开发经历,在这里说句实话,微信的公众号支付开发文档写的真的羞涩的很,你的一个字一个字的看,一遍又一遍的看,一下是我的开发步骤:希望能对其他的人起到帮助作用:

第一步基础的微信公众号配置:

 ·网页授权域名是为获取openid配置的:本文不过多说明openid获取方法


·支付授权目录配置,这个配置的目的为支付准备的因为微信要求支付的页面必须的在配置目录下


第二步编写微信支付需要提交的参数:(以实体类的形式写)
  public class UnifiedOrder
    {
        /// <summary>
        /// 公共号ID(微信分配的公众账号 ID)
        /// </summary>
        public string _appid = "";
        public string appid
        {
            get { return _appid; }
            set { _appid = value; }
        }
        /// <summary>
        /// 商户号(微信支付分配的商户号)
        /// </summary>
        public string _mch_id = "";
        public string mch_id
        {
            get { return _mch_id; }
            set { _mch_id = value; }
        }
        /// <summary>
        /// 微信支付分配的终端设备号
        /// </summary>
       public string _device_info = "";
        public string device_info
        {
            get { return _device_info; }
            set { _device_info = value; }
        }

        /// <summary>
        /// 随机字符串,不长于 32 位
        /// </summary>
        public string _nonce_str = "";
        public string nonce_str
        {
            get { return _nonce_str; }
            set { _nonce_str = value; }
        }
        /// <summary>
        /// 签名
        /// </summary>
        public string _sign = "";
        public string sign
        {
            get { return _sign; }
            set { _sign = value; }
        }
        /// <summary>
        /// 商品描述
        /// </summary>
        public string _body = "";
        public string body
        {
            get { return _body; }
            set { _body = value; }
        }
        /// <summary>
        /// 附加数据,原样返回
        /// </summary>
        public string _attach = "";
        public string attach
        {
            get { return _attach; }
            set { _attach = value; }
        }
        /// <summary>
        /// 商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一,详细说明
        /// </summary>
        public string _out_trade_no = "";
        public string out_trade_no
        {
            get { return _out_trade_no; }
            set { _out_trade_no = value; }
        }
        /// <summary>
        /// 订单总金额,单位为分,不能带小数点
        /// </summary>
        public int _total_fee = 0;
        public int total_fee
        {
            get { return _total_fee; }
            set { _total_fee = value; }
        }
        /// <summary>
        /// 终端IP
        /// </summary>
        public string _spbill_create_ip = "";
        public string spbill_create_ip
        {
            get { return _spbill_create_ip; }
            set { _spbill_create_ip = value; }
        }
        /// <summary>
        /// 订 单 生 成 时 间 , 格 式 为yyyyMMddHHmmss,如 2009 年12 月 25 日 9 点 10 分 10 秒表示为 20091225091010。时区为 GMT+8 beijing。该时间取自商户服务器
        /// </summary>
        public string _time_start = "";
        public string time_start
        {
            get { return _time_start; }
            set { _time_start = value; }
        }
        /// <summary>
        /// 交易结束时间
        /// </summary>
        public string _time_expire = "";
        public string time_expire
        {
            get { return _time_expire; }
            set { _time_expire = value; }
        }
        /// <summary>
        /// 商品标记 商品标记,该字段不能随便填,不使用请填空,使用说明详见第 5 节
        /// </summary>
        public string _goods_tag = "";
        public string goods_tag
        {
            get { return _goods_tag; }
            set { _goods_tag = value; }
        }
        /// <summary>
        /// 接收微信支付成功通知
        /// </summary>
        public string _notify_url = "";
        public string notify_url
        {
            get { return _notify_url; }
            set { _notify_url = value; }
        }
        /// <summary>
        /// JSAPI、NATIVE、APP
        /// </summary>
        public string _trade_type = "";
        public string trade_type
        {
            get { return _trade_type; }
            set { _trade_type = value; }
        }
        /// <summary>
        /// 用户标识 trade_type 为 JSAPI时,此参数必传
        /// </summary>
        public string _openid = "";
        public string openid
        {
            get { return _openid; }
            set { _openid = value; }
        }
        /// <summary>
        /// 只在 trade_type 为 NATIVE时需要填写。
        /// </summary>
        public string _product_id = "";
        public string product_id
        {
            get { return _product_id; }
            set { _product_id = value; }
        }

        public string _sign_type = "";
        public string sign_type
        {
            get { return _sign_type; }
            set { _sign_type = value; }
        }



    }
第二步实现统一下单:

实现的方法:

    //创建随机字符串32位
    public static string CreateNonce()
    {
        char[] constant = new char[]
        {   
            '0','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',
            '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'
        };
        StringBuilder newRandom = new StringBuilder(constant.Length);
        Random rd = new Random(Guid.NewGuid().GetHashCode());
        for (int i = 0; i < 32; i++)
        {
            newRandom.Append(constant[rd.Next(constant.Length)]);
        }
        return newRandom.ToString();
    }
   //创建签名
    public static string CreateSign(Dictionary<string, string> data, string key)
    {

        SortedDictionary<string, string> data1 = new SortedDictionary<string, string>();

        foreach (KeyValuePair<string, string> item in data)
        {
            data1.Add(item.Key, item.Value);
        }
        string s = string.Empty;
        foreach (KeyValuePair<string, string> item in data1)
        {
            s += item.Key + "=" + item.Value + "&";        //HttpUtility.UrlEncode(item.Value, Encoding.UTF8)
        }
        s += "key=" + key;
        return MD5(s).ToUpper();
    }
  //md5加密
    public static string MD5(string data)
    {
        MD5 md5 = new MD5CryptoServiceProvider();
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
        bytes = md5.ComputeHash(bytes);
        md5.Clear();
        string s = string.Empty;
        for (int i = 0; i < bytes.Length; i++)
        {
            s += Convert.ToString(bytes[i], 16).PadLeft(2, '0');
        }
        return s.PadLeft(32, '0');
    }
//统一下单prepay_id
    public static string getPrepay_id( Model.UnifiedOrder order, string key)
    {
        string return_string = string.Empty;

        Dictionary<string, string> sParams = new Dictionary<string, string>();
        sParams.Add("appid", order.appid);
        sParams.Add("attach", order.attach);
        sParams.Add("body", order.body);
        sParams.Add("device_info", order.device_info);
        sParams.Add("mch_id", order.mch_id);
        sParams.Add("nonce_str", order.nonce_str);
        sParams.Add("notify_url", order.notify_url);
        sParams.Add("openid", order.openid);
        sParams.Add("out_trade_no", order.out_trade_no);
        sParams.Add("spbill_create_ip", order.spbill_create_ip);
        sParams.Add("total_fee", order.total_fee.ToString());
        sParams.Add("trade_type", order.trade_type);
        sParams.Add("sign_type", order.sign_type);
       // order.sign = getsign(sParams, key);

        order.sign = CreateSign(sParams, key);

        sParams.Add("sign", order.sign);

        //拼接成XML请求数据
        StringBuilder sbPay = new StringBuilder();
        foreach (KeyValuePair<string, string> k in sParams)
        {
            if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
            {
                sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
            }
            else
            {
                sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
            }
        }
        return_string = string.Format("<xml>{0}</xml>", sbPay.ToString());
        byte[] byteArray = Encoding.UTF8.GetBytes(return_string);
        return_string = Encoding.GetEncoding("UTF-8").GetString(byteArray);
       
        return return_string;

    }
第三步ajax与后台的数据交换:

   js通过调用此方法获得prepay_id

        private string h5zhifu(HttpContext context)          
        {
            Model.UnifiedOrder ufo = new Model.UnifiedOrder();

            ufo.openid = "oEfd2wTE4TLaYYDGHzJH7jy1GM5w";
            ufo.appid = WeiXinUtil.AppID;
            ufo.attach = "qin";              //附加数据
            ufo.body = "test";              //商品描述
           ufo.device_info = "qqq";
            ufo.mch_id = "1460089802";          //商户号
            ufo.nonce_str = WeiXinUtil.CreateNonce();    //随机字符串
            ufo.notify_url = "http://www.yuyue58.cn";      //通知地址
            ufo.out_trade_no = WeiXinUtil.GetTimeStamp() + "001";   //商户内部订单号
          // ufo.spbill_create_ip = WeiXinUtil.getIP();

            ufo.spbill_create_ip = "180.173.29.203";  
   
            ufo.total_fee = 1;                    //支付金额
            ufo.trade_type = "JSAPI";             //交易类型
            ufo.sign_type = "MD5";               //签名类型
          
            String preid = WeiXinUtil.getPrepay_id(ufo, WeiXinUtil.Key);


            string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";      //微信支付接口


            String getid =WeiXinUtil.PostXmlToUrl(url,preid);


            return getid;
        }

js通过此方法获得签名paySign 

特别注意这里的paySign是有appId,timeStamp,nonceStr,package,signType这5个请求参数生成的 

   //获取微信签名
        private string getPaysign(HttpContext context) {

            string timestamp = context.Request.Params["timestamp"];
            string suiNum = context.Request.Params["suiNum"];
            string prepay_ids = context.Request.Params["prepay_id"];
            
            Dictionary<string, string> sParams = new Dictionary<string, string>();

            sParams.Add("appId","wxf96f8d284bc577f7");
            sParams.Add("timeStamp",timestamp);
            sParams.Add("nonceStr",suiNum);
            sParams.Add("package", "prepay_id=" + prepay_ids);
            sParams.Add("signType", "MD5");

            string paysign = WeiXinUtil.CreateSign(sParams, WeiXinUtil.Key);

            return paysign;
        }
第五步前端js调用:
      $(function (){
             $(".btn").click(function () {                            
                 //预支付交易会话标识
                 prepay_id = $.ajax({
                     url: '../Ajax.ashx',
                     type: 'POST',
                     data: { action: "h5zhifu", },
                     dataType: 'XML',
                     async: false
                 }).responseText;              
                 var a = $.parseXML(prepay_id);  
                 prepay_ids = $(a).find('prepay_id').text();    //获取prepay_id
                 //获取时间戳和随机字符串
                 str = $.ajax({
                     url: '../Ajax.ashx',
                     type: 'POST',
                     data: { action: "getTimeStampAndSuiNum", },
                     dataType: 'html',
                     async: false
                 }).responseText;

                 arr2 = String(str).split("|");

                 timestamp = arr2[0];

                 suiNum = arr2[1];

                 //获取微信签名
                 paysign = $.ajax({
                     url: '../Ajax.ashx',
                     type: 'POST',
                     data: { action: "getPaysign", timestamp: timestamp, suiNum: suiNum, prepay_id: prepay_ids },
                     dataType: 'html',
                     async: false
                 }).responseText;
                     
                      WeixinJSBridge.invoke(
                          'getBrandWCPayRequest', {
                              "appId": "wxf96f8d284bc577f7",   //公众号名称,由商户传入     
                              "timeStamp": timestamp,          //时间戳,自1970年以来的秒数     
                              "nonceStr": suiNum,              //随机串     
                              "package":"prepay_id=" + prepay_ids,
                              "signType": "MD5",                //微信签名方式:     
                              "paySign": paysign               //微信签名 
                          },
                          function (res) {
                              
                              if (res.err_msg == "get_brand_wcpay_request:ok") {

                                  window.location = "success.aspx";

                              }
                              // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
                          }
                        )

                   
  
             });
第六步源码下载:

   源码中的代码是公司的项目,所以删出来部分内容,仅仅留下了与支付有关的代码,仅供参考,写的可能不够好希望谅解。

 源码链接

2014-12-23 08:22:00 weixin_34304013 阅读数 48
原文:C#微信公众号开发系列教程五(接收事件推送与消息排重)


微信公众号开发系列教程一(调试环境部署)

微信公众号开发系列教程一(调试环境部署续:vs远程调试)

C#微信公众号开发系列教程二(新手接入指南)

C#微信公众号开发系列教程三(消息体签名及加解密)

C#微信公众号开发系列教程四(接收普通消息)

C#微信公众号开发系列教程五(接收事件推送与消息排重)

在上一篇的博文中讲到,微信的消息可以大体分为两种类型,一种是包括:文本,语音,图片等的普通消息,另一种就是本篇要将的事件类型。包括:关注/取消关注事件,扫描带参数二维码事件,上报地理位置事件,自定义菜单相关事件等。本篇一一进行讲解。

上一篇也提到了,微信服务器在5秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。这样的话,问题就来了。有这样一个场景:当用户关注微信账号时,获取当前用户信息,然后将信息写到数据库中。类似于pc端网站的注册。可能由于这个关注事件中,我们需要处理的业务逻辑比较复杂。如送积分啊,写用户日志啊,分配用户组啊。等等……一系列的逻辑需要执行,或者网络环境比较复杂,无法保证5秒内响应当前用户的操作,那如果当操作尚未完成,微信服务器又给我们的服务器推送了一条相同的关注事件,我们将再次执行我们的那些逻辑,这样就有可能导致数据库中出现重复的数据(有的童鞋就会说了,我在插入数据之前先判断当前是否已经存在了,如果存在了就不执行插入的操作。我想说的是,我当初也是这样想的,但真实的运行环境和我们的调试环境还是有差距的,直到发现数据库中有不少重复的用户信息时,我才发现消息去重的重要性。)。

消息的去重普通消息和事件消息是有区别的。普通消息使用msgid,而事件消息使用FromUserName + CreateTime。我的思路是:

  1. 新建类BaseMsg,有三个属性分别是FromUser,MsgFlag,CreateTime。代码如下:
    public class BaseMsg
        {
            /// <summary>
            /// 发送者标识
            /// </summary>
            public string FromUser { get; set; }
            /// <summary>
            /// 消息表示。普通消息时,为msgid,事件消息时,为事件的创建时间
            /// </summary>
            public string MsgFlag { get; set; }
            /// <summary>
            /// 添加到队列的时间
            /// </summary>
            public DateTime CreateTime { get; set; }
        }
  2. 创建个静态列表_queue,用来存储消息列表,列表的类型是List<BaseMsg>.
  3. 在处理微信消息体前,首先判断列表是否实例化,如果没有实例化则实例化,否则判断列表的长度是否大于或等于50(这个可以自定义,用处就是微信并发的消息量),如果大于或等于50,则保留20秒内未响应的消息(5秒重试一次,总共重试3次,就是15秒,保险起见这里写20秒)。
  4. 获取当前消息体的消息类型,并根据_queue判断当前消息是否已经请求了。如果是事件则保存FromUser和创建时间。如果是普通消息则保存MsgFlag。下面是代码:
if (_queue == null)
            {
                _queue = new List<BaseMsg>();
            }
            else if(_queue.Count>=50)
            {
                _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息
            }
            XElement xdoc = XElement.Parse(xml);
            var msgtype = xdoc.Element("MsgType").Value.ToUpper();
            var FromUserName = xdoc.Element("FromUserName").Value;
            var MsgId = xdoc.Element("MsgId").Value;
            var CreateTime = xdoc.Element("CreateTime").Value;
            MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype);
            if (type!=MsgType.EVENT)
            {
                if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = MsgId
                    });
                }
                else
                {
                    return null;
                }
               
            }
            else
            {
                if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = CreateTime
                    });
                }
                else
                {
                    return null;
                }
            }

当消息已经存在队列中时,则不转换当前的消息为实体了,直接返回null,调用的时候,当返回null时就不做任何处理。

下面开始讲解事件消息。接上篇讲。所有的消息都继承BaseMessage,而所有的事件类型都包含一个Event的属性。这里为了方便调用,将消息类型定义为枚举,代码如下:

/// <summary>
    /// 事件类型枚举
    /// </summary>
    public enum Event
    {
        /// <summary>
        /// 非事件类型
        /// </summary>
        NOEVENT,
        /// <summary>
        /// 订阅
        /// </summary>
        SUBSCRIBE,
        /// <summary>
        /// 取消订阅
        /// </summary>
        UNSUBSCRIBE,
        /// <summary>
        /// 扫描带参数的二维码
        /// </summary>
        SCAN,
        /// <summary>
        /// 地理位置
        /// </summary>
        LOCATION,
        /// <summary>
        /// 单击按钮
        /// </summary>
        CLICK,
        /// <summary>
        /// 链接按钮
        /// </summary>
        VIEW,
        /// <summary>
        /// 扫码推事件
        /// </summary>
        SCANCODE_PUSH,
        /// <summary>
        /// 扫码推事件且弹出“消息接收中”提示框
        /// </summary>
        SCANCODE_WAITMSG,
        /// <summary>
        /// 弹出系统拍照发图
        /// </summary>
        PIC_SYSPHOTO,
        /// <summary>
        /// 弹出拍照或者相册发图
        /// </summary>
        PIC_PHOTO_OR_ALBUM,
        /// <summary>
        /// 弹出微信相册发图器
        /// </summary>
        PIC_WEIXIN,
        /// <summary>
        /// 弹出地理位置选择器
        /// </summary>
        LOCATION_SELECT,
        /// <summary>
        /// 模板消息推送
        /// </summary>
        TEMPLATESENDJOBFINISH
    }

定义好枚举后,就是定义消息实体了。

关注/取消关注事件

xml数据包如下:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

对应的实体:

/// <summary>
    /// 订阅/取消订阅事件
    /// </summary>
    public class SubEventMessage : EventMessage
    {
        private string _eventkey;
        /// <summary>
        /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值(已去掉前缀,可以直接使用)
        /// </summary>
        public string EventKey
        {
            get { return _eventkey; }
            set { _eventkey = value.Replace("qrscene_", ""); }
        }
        /// <summary>
        /// 二维码的ticket,可用来换取二维码图片
        /// </summary>
        public string Ticket { get; set; }

    }

这里需要注意的是,当用户扫描带参数的二维码时,如果用户没有关注当前公众号,用户关注时,会在消息体中带上qrscene_参数,和Ticket,所以这里定义了两个属性:EventKey,Ticket。当给EventKey赋值时,替换掉qrscene_,因为我们真正需要的就是后面的参数。

扫描带参数二维码事件

用户扫描带场景值二维码时,可能推送一下两种事件:

  1. 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
  2. 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。、

第一种上面已经讲了,这里就只说明下第二种。

用户已关注时的事件推送

xml包如下:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>

对应的实体如下:

/// <summary>
    /// 扫描带参数的二维码实体
    /// </summary>
    public class ScanEventMessage : EventMessage
    {

        /// <summary>
        /// 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
        /// </summary>
        public string EventKey { get; set; }
        /// <summary>
        /// 二维码的ticket,可用来换取二维码图片
        /// </summary>
        public string Ticket { get; set; }

    }

上报地理位置事件

当公众号开启上报地理位置功能后,每次进入公众号会话时,用户同意上报地理位置后,都会在进入时上报地理位置,或在进入回话后每5秒上报一次地理位置,公众号可以再公众平台的后台中修改设置。上报地理位置时,微信会将上报地理位置事件推送到开发者填写的url。

xml数据包如下:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[LOCATION]]></Event>
<Latitude>23.137466</Latitude>
<Longitude>113.352425</Longitude>
<Precision>119.385040</Precision>
</xml>

对应的实体如下:

/// <summary>
    /// 上报地理位置实体
    /// </summary>
    public class LocationEventMessage : EventMessage
    {

        /// <summary>
        /// 地理位置纬度
        /// </summary>
        public string Latitude { get; set; }
        /// <summary>
        /// 地理位置经度
        /// </summary>
        public string Longitude { get; set; }
       /// <summary>
        /// 地理位置精度
       /// </summary>
        public string Precision { get; set; }

    }

自定义菜单事件常用的事件有:click,view,scancode_puth,scancode_waitmsg,location_select。另外还有三种发图的事件,由于并不常用,笔者也没想到使用场景,再次就不一一讲述了,有兴趣的可以自己研究下,或者和我进行交流。

click事件推送的xml数据包:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[CLICK]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>

view事件推送的xml数据包和click的格式是一样的,所以定义一个类就可以了,如下:

/// <summary>
    /// 普通菜单事件,包括click和view
    /// </summary>
    public class NormalMenuEventMessage : EventMessage
    {

        /// <summary>
        /// 事件KEY值,设置的跳转URL
        /// </summary>
        public string EventKey { get; set; }
  

    }

scancode事件的xml数据包如下:

<xml><ToUserName><![CDATA[ToUserName]]></ToUserName>
<FromUserName><![CDATA[FromUserName]]></FromUserName>
<CreateTime>1419265698</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[scancode_push]]></Event>
<EventKey><![CDATA[EventKey]]></EventKey>
<ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType>
<ScanResult><![CDATA[http://weixin.qq.com/r/JEy5oRLE0U_urVbC9xk2]]></ScanResult>
</ScanCodeInfo>
</xml>

对应的实体如下:

/// <summary>
    /// 菜单扫描事件
    /// </summary>
    public class ScanMenuEventMessage : EventMessage
    {

        /// <summary>
        /// 事件KEY值
        /// </summary>
        public string EventKey { get; set; }
        /// <summary>
        /// 扫码类型。qrcode是二维码,其他的是条码
        /// </summary>
        public string ScanType { get; set; }
        /// <summary>
        /// 扫描结果
        /// </summary>
        public string ScanResult { get; set; }
    }

至此,当前常用的事件类型消息都已定义完毕,结合上一篇所讲的,将xml数据包转换成对象的完整代码如下:

public class MessageFactory
    {
        private static List<BaseMsg> _queue; 
        public static BaseMessage CreateMessage(string xml)
        {
            if (_queue == null)
            {
                _queue = new List<BaseMsg>();
            }
            else if(_queue.Count>=50)
            {
                _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息
            }
            XElement xdoc = XElement.Parse(xml);
            var msgtype = xdoc.Element("MsgType").Value.ToUpper();
            var FromUserName = xdoc.Element("FromUserName").Value;
            var MsgId = xdoc.Element("MsgId").Value;
            var CreateTime = xdoc.Element("CreateTime").Value;
            MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype);
            if (type!=MsgType.EVENT)
            {
                if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = MsgId
                    });
                }
                else
                {
                    return null;
                }
               
            }
            else
            {
                if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null)
                {
                    _queue.Add(new BaseMsg
                    {
                        CreateTime = DateTime.Now,
                        FromUser = FromUserName,
                        MsgFlag = CreateTime
                    });
                }
                else
                {
                    return null;
                }
            }
            switch (type)
            {
                case MsgType.TEXT: return Utils.ConvertObj<TextMessage>(xml);
                case MsgType.IMAGE: return Utils.ConvertObj<ImgMessage>(xml);
                case MsgType.VIDEO: return Utils.ConvertObj<VideoMessage>(xml);
                case MsgType.VOICE: return Utils.ConvertObj<VoiceMessage>(xml);
                case MsgType.LINK:
                    return Utils.ConvertObj<LinkMessage>(xml);
                case MsgType.LOCATION:
                    return Utils.ConvertObj<LocationMessage>(xml);
                case MsgType.EVENT://事件类型
                {
                    var eventtype = (Event)Enum.Parse(typeof(Event), xdoc.Element("Event").Value.ToUpper());
                    switch (eventtype)
                    {
                        case Event.CLICK:
                            return Utils.ConvertObj<NormalMenuEventMessage>(xml);
                        case Event.VIEW: return Utils.ConvertObj<NormalMenuEventMessage>(xml);
                        case Event.LOCATION: return Utils.ConvertObj<LocationEventMessage>(xml);
                        case Event.LOCATION_SELECT: return Utils.ConvertObj<LocationMenuEventMessage>(xml);
                        case Event.SCAN: return Utils.ConvertObj<ScanEventMessage>(xml);
                        case Event.SUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml);
                        case Event.UNSUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml);
                        case Event.SCANCODE_WAITMSG: return Utils.ConvertObj<ScanMenuEventMessage>(xml);
                        default:
                            return Utils.ConvertObj<EventMessage>(xml);
                    }
                } break;
                default:
                    return Utils.ConvertObj<BaseMessage>(xml);
            }
        }
    }

时间仓促,如有不明白的,请留言,如果你觉得本篇博文对你有帮助,请点击一下推荐,推荐给更多的朋友的。

各位有建议或者意见可留言给我哦,或者加如QQ群一起进行交流。C#微信开发交流

如果你是土豪,可以扫描下面的二维码悬赏一下,你的支持是笔者继续更新下去的动力。

2019-10-02 21:05:53 qq_38856773 阅读数 170

最近在做微信支付项目,记录一下:
1、需要现在微信公众平台上设置,具体在 菜单:设置-公众号设置-功能设置,把开发程序所在的域名增加,具体在业务域名、JS接口安全域名
、网页授权域名(这个主要是用于授权用的) 都要增加相应的域名目录。
2、在微信支付平台,产品中心-开发配置-支付设置,公众号支付-JSAPI授权目录增加域名目录,否则不能成功调用。 以上两步必须同时设置好。
3、然后阅读开发微信支付开发文档,根据文档开发即可。虽然大家都说有很多坑,但是慢慢解决,能够很好的实现调用。自己感觉微信支付比支付宝支付要简单。如果还是无法成功,或者提示签名错误,有的时候需要重置API密钥试试(调试了几天 找了好多办法都没有解决,结果最后真的重置了API密钥就好了)。
4、还有一个问题,就是微信调起支付后,没有支付,然后再支付的时候提示重复下单或这缺少total_fee参数(这个实际上有的时候并不是真少这个参数),这个时候应该是body内容不一样但是订单号一样。
我的解决办法是这样的:每次调起支付的时候,将生成的支付信息保存到数据库,如果当时调起支付未支付,那么再次调起的时候会先搜索一下数据库,如果有这条记录,那么将当时的timestamp、NonceStr、paysign、Prepay_id等信息提取出来,生成支付数据。如果数据库没有数据,则启用新的支付参数。这样就能保证统一支付下单的是一致的,不至于重复下单。在这里插入代码片

几个关键代码参考:


	 public class UnifiedOrder
    {
        /// <summary>

        /// <summary>
        /// 微信支付分配的终端设备号
        /// </summary>
        public string _device_info = ""; /// <summary>
        /// 公共号ID(微信分配的公众账号 ID)
        /// </summary>
        public string appid = "";
        /// <summary>
        /// 商户号(微信支付分配的商户号)
        /// </summary>
        public string mch_id = "";
        /// <summary>
        /// 微信支付分配的终端设备号
        /// </summary>
        public string device_info = "";
        /// <summary>
        /// 随机字符串,不长于 32 位
        /// </summary>
        public string nonce_str = "";
        /// <summary>
        /// 签名
        /// </summary>
        public string sign = "";
        /// <summary>
        /// 商品描述
        /// </summary>
        public string body = "";
        /// <summary>
        /// 附加数据,原样返回
        /// </summary>
        public string attach = "";
        /// <summary>
        /// 商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一,详细说明
        /// </summary>
        public string out_trade_no = "";
        /// <summary>
        /// 订单总金额,单位为分,不能带小数点
        /// </summary>
        public int total_fee = 0;
        /// <summary>
        /// 终端IP
        /// </summary>
        public string spbill_create_ip = "";
        /// <summary>
        /// 订 单 生 成 时 间 , 格 式 为yyyyMMddHHmmss,如 2009 年12 月 25 日 9 点 10 分 10 秒表示为 20091225091010。时区为 GMT+8 beijing。该时间取自商户服务器
        /// </summary>
        public string time_start = "";
        /// <summary>
        /// 交易结束时间
        /// </summary>
        public string time_expire = "";
        /// <summary>
        /// 商品标记 商品标记,该字段不能随便填,不使用请填空,使用说明详见第 5 节
        /// </summary>
        public string goods_tag = "";
        /// <summary>
        /// 接收微信支付成功通知
        /// </summary>
        public string notify_url = "";
        /// <summary>
        /// JSAPI、NATIVE、APP
        /// </summary>
        public string trade_type = "";
        /// <summary>
        /// 用户标识 trade_type 为 JSAPI时,此参数必传
        /// </summary>
        public string openid = "";
        /// <summary>
        /// 只在 trade_type 为 NATIVE时需要填写。
        /// </summary>
        public string product_id = "";
 
     }

这个是主要的代码

// <summary>
///TenpayUtil 的摘要说明
/// </summary>
public class TenpayUtil
{
    public TenpayUtil()
    {
        //
      
    }


    //TODO: 在此处添加构造函数逻辑
    //
    /// <summary>
    /// 统一支付接口
    /// </summary>
  const  string UnifiedPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /// <summary>
    /// 网页授权接口
    /// </summary>
  const string access_tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";

    /// <summary>
    /// 微信订单查询接口
    /// </summary>
  const string OrderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";

        /// <summary>
        /// 随机串
        /// </summary>
        public static string getNoncestr()
        {
            Random random = new Random();
            return MD5Util.GetMD5(random.Next(1000).ToString(), "GBK").ToLower().Replace("s", "S");
        }

        /// <summary>
        /// 时间截,自1970年以来的秒数
        /// </summary>
        public static string getTimestamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        /// <summary>
        /// 网页授权接口
        /// </summary>
        public static string getAccess_tokenUrl()
        {
            return access_tokenUrl;
        }

        /// <summary>
        /// 获取微信签名
        /// </summary>
        /// <param name="sParams"></param>
        /// <returns></returns>
        public string getsign(SortedDictionary<string, string> sParams, string key)
        {
            int i = 0;
            string sign = string.Empty;
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string, string> temp in sParams)
            {
                if (temp.Value == "" || temp.Value == null || temp.Key.ToLower() == "sign" )
                {
                    continue;
                }
                i++;
                sb.Append(temp.Key.Trim() + "=" + temp.Value.Trim() + "&");
            }
            sb.Append("key=" + key.Trim() + "");
            string signkey = sb.ToString();
            sign = MD5Util.GetMD5(signkey, "utf-8");


            return sign;
        }

        /// <summary>
        /// post数据到指定接口并返回数据
        /// </summary>
        public string PostXmlToUrl(string url, string postData)
        {
            //string returnmsg = "";
            //using (System.Net.WebClient wc = new System.Net.WebClient())
            //{
            //    returnmsg = wc.UploadString(url, "POST", postData);
            //}
            //return returnmsg;


            HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(url);
            hwr.Method = "POST";

            Stream stream = hwr.GetRequestStream();

            StreamWriter sw = new StreamWriter(stream, System.Text.Encoding.UTF8);
            sw.Write(postData);
            sw.Close();

            stream = hwr.GetResponse().GetResponseStream();

            StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8);
            string ret = sr.ReadToEnd();
            sr.Close();

            return ret;   
        }

        /// <summary>
        /// 获取prepay_id
        /// </summary>
        public string getPrepay_id(UnifiedOrder order, string key)
        {
            string prepay_id = "";
            string post_data = getUnifiedOrderXml(order, key);
            string request_data = PostXmlToUrl(UnifiedPayUrl, post_data);
            SortedDictionary<string, string> requestXML = GetInfoFromXml(request_data);
            foreach (KeyValuePair<string, string> k in requestXML)
            {
                if (k.Key == "prepay_id")
                {
                    prepay_id = k.Value;
                    break;
                }
            }
            return prepay_id;
        }

        /// <summary>
        /// 获取微信订单明细
        /// </summary>
        public OrderDetail getOrderDetail(QueryOrder queryorder, string key)
        {
            string post_data = getQueryOrderXml(queryorder, key);
            string request_data = PostXmlToUrl(OrderQueryUrl, post_data);
            OrderDetail orderdetail = new OrderDetail();
            SortedDictionary<string, string> requestXML = GetInfoFromXml(request_data);
            foreach (KeyValuePair<string, string> k in requestXML)
            {
                switch (k.Key)
                {
                    case "retuen_code":
                        orderdetail.result_code = k.Value;
                        break;
                    case "return_msg":
                        orderdetail.return_msg = k.Value;
                        break;
                    case "appid":
                        orderdetail.appid = k.Value;
                        break;
                    case "mch_id":
                        orderdetail.mch_id = k.Value;
                        break;
                    case "nonce_str":
                        orderdetail.nonce_str = k.Value;
                        break;
                    case "sign":
                        orderdetail.sign = k.Value;
                        break;
                    case "result_code":
                        orderdetail.result_code = k.Value;
                        break;
                    case "err_code":
                        orderdetail.err_code = k.Value;
                        break;
                    case "err_code_des":
                        orderdetail.err_code_des = k.Value;
                        break;
                    case "trade_state":
                        orderdetail.trade_state = k.Value;
                        break;
                    case "device_info":
                        orderdetail.device_info = k.Value;
                        break;
                    case "openid":
                        orderdetail.openid = k.Value;
                        break;
                    case "is_subscribe":
                        orderdetail.is_subscribe = k.Value;
                        break;
                    case "trade_type":
                        orderdetail.trade_type = k.Value;
                        break;
                    case "bank_type":
                        orderdetail.bank_type = k.Value;
                        break;
                    case "total_fee":
                        orderdetail.total_fee = k.Value;
                        break;
                    case "coupon_fee":
                        orderdetail.coupon_fee = k.Value;
                        break;
                    case "fee_type":
                        orderdetail.fee_type = k.Value;
                        break;
                    case "transaction_id":
                        orderdetail.transaction_id = k.Value;
                        break;
                    case "out_trade_no":
                        orderdetail.out_trade_no = k.Value;
                        break;
                    case "attach":
                        orderdetail.attach = k.Value;
                        break;
                    case "time_end":
                        orderdetail.time_end = k.Value;
                        break;
                    default:
                        break;
                }
            }
            return orderdetail;
        }

        /// <summary>
        /// 把XML数据转换为SortedDictionary<string, string>集合
        /// </summary>
        /// <param name="strxml"></param>
        /// <returns></returns>
        protected SortedDictionary<string, string> GetInfoFromXml(string xmlstring)
        {
            SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlstring);
                XmlElement root = doc.DocumentElement;
                int len = root.ChildNodes.Count;
                for (int i = 0; i < len; i++)
                {
                    string name = root.ChildNodes[i].Name;
                    if (!sParams.ContainsKey(name))
                    {
                        sParams.Add(name.Trim(), root.ChildNodes[i].InnerText.Trim());
                    }
                }
            }
            catch { }
            return sParams;
        }

        /// <summary>
        /// 微信统一下单接口xml参数整理
        /// </summary>
        /// <param name="order">微信支付参数实例</param>
        /// <param name="key">密钥</param>
        /// <returns></returns>
        protected string getUnifiedOrderXml(UnifiedOrder order, string key)
        {
            string return_string = string.Empty;
            SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
            sParams.Add("appid", order.appid);
            sParams.Add("attach", order.attach);
            sParams.Add("body", order.body);
            sParams.Add("device_info", order.device_info);
            sParams.Add("mch_id", order.mch_id);
            sParams.Add("nonce_str", order.nonce_str);
            sParams.Add("notify_url", order.notify_url);
            sParams.Add("openid", order.openid);
            sParams.Add("out_trade_no", order.out_trade_no);
            sParams.Add("spbill_create_ip", order.spbill_create_ip);
            sParams.Add("total_fee", order.total_fee.ToString());
            sParams.Add("trade_type", order.trade_type);
            order.sign = getsign(sParams, key);
            sParams.Add("sign", order.sign);

            //拼接成XML请求数据
            StringBuilder sbPay = new StringBuilder();
            foreach (KeyValuePair<string, string> k in sParams)
            {
                //if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
                //{
                //    sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
                //}
                //else
                //{
                    sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
               // }
            }
            return_string = string.Format("<xml>{0}</xml>", sbPay.ToString());
            byte[] byteArray = Encoding.UTF8.GetBytes(return_string);
            return_string = Encoding.GetEncoding("UTF-8").GetString(byteArray);
            return return_string;
            
        }

        /// <summary>
        /// 微信订单查询接口XML参数整理
        /// </summary>
        /// <param name="queryorder">微信订单查询参数实例</param>
        /// <param name="key">密钥</param>
        /// <returns></returns>
        protected string getQueryOrderXml(QueryOrder queryorder, string key)
        {
            string return_string = string.Empty;
            SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
            sParams.Add("appid", queryorder.appid);
            sParams.Add("mch_id", queryorder.mch_id);
            sParams.Add("transaction_id", queryorder.transaction_id);
            sParams.Add("out_trade_no", queryorder.out_trade_no);
            sParams.Add("nonce_str", queryorder.nonce_str);
            queryorder.sign = getsign(sParams, key);
            sParams.Add("sign", queryorder.sign);

            //拼接成XML请求数据
            StringBuilder sbPay = new StringBuilder();
            foreach (KeyValuePair<string, string> k in sParams)
            {
                if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
                {
                    sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
                }
                else
                {
                    sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
                }
            }
            return_string = string.Format("<xml>{0}</xml>", sbPay.ToString().TrimEnd(','));
            return return_string;
        }
    
	}


MD5加密:

 public class MD5Util
    {
        public MD5Util()
        {
            //
            // TODO: 在此处添加构造函数逻辑
            //
        }

        /** 获取大写的MD5签名结果 */
        public static string GetMD5(string encypStr, string charset)
        {
            string retStr;
            MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();

            //创建md5对象
            byte[] inputBye;
            byte[] outputBye;

            //使用GB2312编码方式把字符串转化为字节数组.
            try
            {
                inputBye = Encoding.GetEncoding(charset).GetBytes(encypStr);
            }
            catch (Exception ex)
            {
                inputBye = Encoding.GetEncoding("GB2312").GetBytes(encypStr);
            }
            outputBye = m5.ComputeHash(inputBye);

            retStr = System.BitConverter.ToString(outputBye);
            retStr = retStr.Replace("-", "").ToUpper();
            return retStr;
        }
    }

Pay.aspx主要内容
   //支付信息
        function Pay() {
            var appId = "<%=appId %>";
            var timeStamp = "<%=timeStamp %>";
            var nonceStr = "<%=nonceStr %>";
            var prepay_id = "<%=prepay_id %>";
            var paySign = "<%=paySign %>";
            var OrderID = "<%=OrderID %>";
            //alert("appId:" + appId + ",timeStamp:" + timeStamp + ",nonceStr:" + nonceStr + ",prepay_id:" + prepay_id + ",paySign:" + paySign);
            //return;
            function onBridgeReady() {
                WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
               "appId": appId,     //公众号名称,由商户传入     
               "timeStamp": timeStamp,         //时间戳,自1970年以来的秒数     
               "nonceStr": nonceStr, //随机串     
               "package": "prepay_id=" + prepay_id,
               "signType": "MD5",         //微信签名方式:     
               "paySign": paySign //微信签名 
       },
       function (res) {
           if (res.err_msg == "get_brand_wcpay_request:ok") {

               $.showLoading("正在校验支付");
               //检查是否支付成功
               $.get("Tools/CheckPay.aspx?OrderId=" + OrderID, function (data) {

                   $.hideLoading();
                   if (data == "success") {
                       alert("支付成功");
                       window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random();
                   }
                   else {
                       alert("支付待验证,返回结果:" + data);
                       window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random();
                   }
               }); //get结束
           }     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
           else {
               alert("交易取消");
               window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random();
           }


       }
   );
            }
            if (typeof WeixinJSBridge == "undefined") {
                if (document.addEventListener) {
                    document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                } else if (document.attachEvent) {
                    document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                    document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                }
            } else {
                onBridgeReady();
            }
        }



Pay.aspx.cs 主要内容


  private void PayInfo(string OpenId, string OrderId, string OrderAmount, string paySignKey)
    {
        try
        {
            TenpayUtil tenpay = new TenpayUtil();
            UnifiedOrder order = new UnifiedOrder();
            order.appid = "这里是APPID";
            order.attach = "随便写";
            order.body = "这里是订单内容";
            order.device_info = "";
            order.mch_id = "这里是微信支付的商户号";//商户号
            order.nonce_str = TenpayUtil.getNoncestr();
            order.notify_url = "回调通知地址";
            order.openid = OpenId;
            order.out_trade_no = OrderId;
            order.trade_type = "JSAPI";
            order.spbill_create_ip = Page.Request.UserHostAddress;
            order.total_fee = int.Parse(OrderAmount);
            //order.total_fee = int.Parse("12") * 100;

            prepay_id = tenpay.getPrepay_id(order, paySignKey);

            timeStamp = TenpayUtil.getTimestamp();
            nonceStr = TenpayUtil.getNoncestr();

            SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
            sParams.Add("appId", appId);
            sParams.Add("timeStamp", timeStamp);
            sParams.Add("nonceStr", nonceStr);
            sParams.Add("package", "prepay_id=" + prepay_id);
            sParams.Add("signType", "MD5");
            paySign = tenpay.getsign(sParams, paySignKey);


          

        }
        catch (Exception ex)
        {
            Response.Write(ex.Message);
        }
    }

C#微信开发

阅读数 5015

c# 微信jsapi支付

阅读数 734

C#微信网页授权

阅读数 131

没有更多推荐了,返回首页