需求:用户购买视频调起微信支付,支付成功后观看视频,所以不需要像商品一样的支付状态和订单状态,点击购买时直接调起微信支付传递视频ID号和用户ID号,支付成功后异步通知里处理程序添加一条订单即可,相对简单。不需要点击购买时先下单在调起支付时传递订单号,异步通知时通过订单号修改订单支付状态。

小程序里点击购买时,请求后台将会员ID和视频ID传递到后台:

//请求     
                wx.request({
                  url: app.globalData.url + '/api.asmx/XiaDan', / /调用自己的后台方法
                   data: {
                    MemID: userid,// 三个参数openid_key    userid    videoid    后台通过openid_key(sessionkey)获取openid 
                    VideoID: vedioid,
                    openid_key: sessionSync,
                  },
                  method: 'POST',
                  header: {
                    'content-type': 'application/x-www-form-urlencoded;charset=utf-8' // 默认值  x-www-form-urlencoded;charset=utf-8    json
                  },
                  success: function (res) {
                    console.log(res.data);
                    //请求成功后的参数
                    var timeStamp = res.data[0].timeStamp;
                    var nonceStr = res.data[0].nonceStr;
                    var signType = res.data[0].signType;//不必要
                    var paySign = res.data[0].paySign;
                    var outTradeNo = res.data[0].outTradeNo;
                    var packagenew = res.data[0].package;
                    console.log("------------" + packagenew);
                    //小程序接收到后台传回的参数后发起支付请求:
                    wx.requestPayment({
                      'timeStamp': timeStamp,//时间戳
                      'nonceStr': nonceStr,//随机数
                      'package': packagenew,//prepay_id=xxx
                      'signType': signType,//签名类型,一般为MD5
                      'paySign': paySign,//已签名的参数串
                      'success': function (res) {
                        app.sysShowToast('支付成功!', 'success', 2000);
                        //成功后跳转页面
                        wx.navigateTo({
                          url: '/pages/exam/vedio?vedioid=' + vedioid + '&VideoSrc=' + VideoSrc,
                        })
                      },
                      'fail': function (res) {
                        app.sysShowToast('充值失败!', 'fail', 2000);
                      }
                    })
                  }
                })

后台:

/// <summary>
    /// 调起支付
    /// </summary>
    /// <param name="MemID">会员ID</param>
    /// <param name="VideoID">视频ID</param>
    /// <param name="openid_key">openid键</param>
    [WebMethod]
    public void XiaDan(int MemID, int VideoID, string openid_key)
    {
        //System.IO.File.AppendAllText(Server.MapPath("/log1.txt"), MemID+"_"+VideoID+"_"+openid_key + "==" + DateTime.Now, Encoding.Unicode);
        string key= Maticsoft.DBUtility.DESEncrypt.Decrypt(openid_key);
        var jObject = JObject.Parse(key);
        string openid = jObject["openid"].ToString(); //得到openid
        //System.IO.File.AppendAllText(Server.MapPath("/log2.txt"), openid.ToString() + "==" + DateTime.Now, Encoding.Unicode);
        Maticsoft.BLL.Video videobll = new Maticsoft.BLL.Video();
        Maticsoft.Model.Video video = videobll.GetModel(VideoID);//购买的视频
        decimal total_fee = Convert.ToDecimal(video.Univalence);//金额
        string body = MemID + "_" + VideoID;
        string attach= MemID + "_" + VideoID;
        string str = WxPayService.GetPayParams(openid, total_fee, body, attach);
        Context.Response.Write(str);
    }

支付WxPayService类:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Data;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Xml;

/// <summary>
/// WxPayService 的摘要说明
/// </summary>
public class WxPayService:BaseService
{
    #region 一、微信用户主动支付
    /// <summary>
    /// 微信用户主动发起的支付(返回必要的参数:package、paySign)
    /// </summary>
    /// <param name="openid"></param>
    /// <param name="payFee"></param>
    /// <param name="clientId"></param>
    /// <param name="isSign"></param>
    /// <returns></returns>
    public static string GetPayParams(string openid, decimal merFee, string merBody, string merAttach/*, string clientId = "139.196.212.68"*/)
    {
        PaySign sign = GetPaySign(openid, merFee, merBody, merAttach/*, clientId*/);
        string signs = ModelToUriParam(sign) + "&key=" + _mchApiKey;
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log3.txt"), signs + "==" + DateTime.Now, Encoding.Unicode);
        string md5Signs = MD5(signs).ToUpper();//MD5签名
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log4.txt"), md5Signs + "==" + DateTime.Now, Encoding.Unicode);
        string body = GenerateBodyXml(sign, md5Signs);
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log5.txt"), body + "==" + DateTime.Now, Encoding.Unicode);
        string res =PostHttp(_payUrl, body);
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log6.txt"), res + "==" + DateTime.Now, Encoding.Unicode);
        #region 得到prepay_id
        //获取xml数据
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(res);
        //xml格式转json
        string json = Newtonsoft.Json.JsonConvert.SerializeXmlNode(doc);
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log9.txt"), json + "==" + DateTime.Now, Encoding.Unicode);
        JObject jo = (JObject)JsonConvert.DeserializeObject(json);
        string prepay_id = jo["xml"]["prepay_id"]["#cdata-section"].ToString();
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log10.txt"), prepay_id + "==" + DateTime.Now, Encoding.Unicode);
        #endregion

        var resSign = new ResSign
        {
            appId = _appId,
            nonceStr = sign.nonce_str,
            package = "prepay_id=" + prepay_id,
            signType = "MD5",
            timeStamp = GetTimeStamp().ToString(),
            key = _mchApiKey
        };
        var pars = ModelToUriParam(resSign);


        //var JSON = new {
        //    timeStamp=resSign.timeStamp,
        //    nonceStr=resSign.nonceStr,
        //    package=resSign.package ,
        //    paySign=MD5(pars).ToUpper(),
        //    signType=resSign.signType,
        //    outTradeNo= sign.out_trade_no
        //};

        DataTable dt = new DataTable();
        dt.Columns.Add("timeStamp");
        dt.Columns.Add("nonceStr");
        dt.Columns.Add("package");
        dt.Columns.Add("paySign");
        dt.Columns.Add("signType");
        dt.Columns.Add("outTradeNo");

        DataRow dr = dt.NewRow();
        dr["timeStamp"] = resSign.timeStamp;
        dr["nonceStr"] = resSign.nonceStr;
        dr["package"] = resSign.package;
        dr["paySign"] = MD5(pars).ToUpper();
        dr["signType"] = resSign.signType;
        dr["outTradeNo"] = sign.out_trade_no;
        dt.Rows.Add(dr);
        string json11 = HelpClass.Dtb2Json(dt);
        //mingdao5 admin   app 11999    sign  ebe216651b277e74ae27ac53bad091a8
        //return JsonConvert.SerializeObject(JSON);
        return json11;

        //return JSON.DecodeToStr(new
        //{
        //    timeStamp = resSign.timeStamp,
        //    nonceStr = resSign.nonceStr,
        //    package = resSign.package,
        //    paySign = MD5(pars).ToUpper(),
        //    signType = resSign.signType,
        //    outTradeNo = sign.out_trade_no//商户订单号
        //});
    }

    /// <summary>
    /// 组装签名对象
    /// </summary>
    /// <param name="openid"></param>
    /// <param name="clientId"></param>
    /// <param name="payFee"></param>
    /// <returns></returns>
    private static PaySign GetPaySign(string openid, decimal merFee, string merBody, string merAttach/*, string clientId*/)
    {
        PaySign paySign = new PaySign
        {
            appid = _appId,
            attach = merAttach,
            body = merBody,
            mch_id = _mch_id,
            nonce_str = GetRandomString(30),
            notify_url = _notify_url,
            openid = openid,
            out_trade_no = GetRandomTime(),
            //spbill_create_ip = clientId,
            total_fee = (Math.Round(merFee * 100, 0)).ToString(),//转化为单位:分,且只能为整型
            trade_type = _trade_type
        };
        return paySign;
    }
    /// <summary>
    /// 生成交易的xml
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    private static string GenerateBodyXml(object obj, string md5Signs)
    {
        PropertyInfo[] propertis = obj.GetType().GetProperties();
        StringBuilder sb = new StringBuilder();
        sb.Append("<xml>");
        foreach (var p in propertis)
        {
            var v = p.GetValue(obj, null);
            if (v == null)
                continue;
            sb.AppendFormat("<{0}>{1}</{0}>", p.Name, v.ToString());
        }
        sb.AppendFormat("<sign>{0}</sign>", md5Signs);
        sb.Append("</xml>");

        return sb.ToString();
    }

    #region Model类

    /// <summary>
    /// 签名类A(请注意,此类的属性字段顺序不可调整)
    /// 微信预支付前面规则,是按参数ASCII码依次排列的,以下属性已人为排列
    /// </summary>
    public class PaySign
    {
        public string appid { get; set; }
        /// <summary>
        /// 附加数据(描述)
        /// </summary>
        public string attach { get; set; }
        /// <summary>
        /// 商品描述
        /// </summary>
        public string body { get; set; }
        /// <summary>
        /// 商户号
        /// </summary>
        public string mch_id { get; set; }
        /// <summary>
        /// 小于32位的随机数
        /// </summary>
        public string nonce_str { get; set; }
        /// <summary>
        /// 通知地址
        /// </summary>
        public string notify_url { get; set; }
        /// <summary>
        /// 微信用户openid
        /// </summary>
        public string openid { get; set; }
        /// <summary>
        /// 商户订单号
        /// </summary>
        public string out_trade_no { get; set; }
        /// <summary>
        /// 客户端ip
        /// </summary>
        //public string spbill_create_ip { get; set; }
        /// <summary>
        /// 订单金额
        /// </summary>
        public object total_fee { get; set; }
        /// <summary>
        /// 支付类型
        /// </summary>
        public string trade_type { get; set; }
    }

    /// <summary>
    /// 返回前端的签名类B(请注意,此类的属性字段顺序不可调整)
    /// </summary>
    public class ResSign
    {
        public string appId { get; set; }
        /// <summary>
        /// 小于32位的随机数
        /// </summary>
        public string nonceStr { get; set; }
        /// <summary>
        /// package
        /// </summary>
        public string package { get; set; }
        /// <summary>
        /// signType
        /// </summary>
        public string signType { get; set; }
        /// <summary>
        /// timeStamp
        /// </summary>
        public string timeStamp { get; set; }
        /// <summary>
        /// key
        /// </summary>
        public string key { get; set; }
    }
    #endregion
    #endregion

    /// <summary>
    /// post请求
    /// </summary>
    /// <param name="url">请求url(不含参数)</param>
    /// <param name="body">请求body. 如果是soap"text/xml; charset=utf-8"则为xml字符串;post的cotentType为"application/x-www-form-urlencoded"则格式为"roleId=1&uid=2"</param>
    /// <param name="timeout">等待时长(毫秒)</param>
    /// <param name="contentType">Content-type http标头的值. post默认为"text/xml;charset=UTF-8"</param>
    /// <returns></returns>
    public static string PostHttp(string url, string body, string contentType = "text/xml;charset=utf-8")
    {
        HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);

        httpWebRequest.ContentType = contentType;
        httpWebRequest.Method = "POST";
        //httpWebRequest.Timeout = timeout;//设置超时
        if (contentType.Contains("text/xml"))
        {
            httpWebRequest.Headers.Add("SOAPAction", "http://tempuri.org/mediate");
        }

        byte[] btBodys = Encoding.UTF8.GetBytes(body);
        httpWebRequest.ContentLength = btBodys.Length;
        httpWebRequest.GetRequestStream().Write(btBodys, 0, btBodys.Length);

        HttpWebResponse httpWebResponse;
        try
        {
            httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
        }
        catch (WebException ex)
        {
            //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log7.txt"), ex.Response.ToString() + "==" + DateTime.Now, Encoding.Unicode);
            httpWebResponse = (HttpWebResponse)ex.Response;
        }

        //HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
        StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.UTF8);
        string responseContent = streamReader.ReadToEnd();
        //System.IO.File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("/log8.txt"), responseContent + "==" + DateTime.Now, Encoding.Unicode);
        httpWebResponse.Close();
        streamReader.Close();
        httpWebRequest.Abort();
        httpWebResponse.Close();

        return responseContent;
    }
}

支付BaseService类:

using System;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
/// <summary>
/// BaseService 的摘要说明
/// </summary>
public class BaseService
{
    public static Maticsoft.Model.PayException GetPayexcept()
    {
        Maticsoft.BLL.PayException payexceptionbll = new Maticsoft.BLL.PayException();
        Maticsoft.Model.PayException payexcept = payexceptionbll.GetModelByCache(1);//获取支付参数
        return payexcept;
    }
   
    /// <summary>
    /// AppID
    /// </summary>
    protected static string _appId = GetPayexcept().WxAppID;
    /// <summary>
    /// AppSecret
    /// </summary>
    protected static string _appSecret = GetPayexcept().WxAppSecret;
    /// <summary>
    /// 微信服务器提供的获得登录后的用户信息的接口
    /// </summary>
    protected static string _wxLoginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";

    /// <summary>
    /// 微信商户号
    /// </summary>
    protected static string _mch_id = GetPayexcept().WxMchID;
    /// <summary>
    /// 微信商户号api密匙
    /// </summary>
    protected static string _mchApiKey = GetPayexcept().WxAppKey;
    /// <summary>
    /// 通知地址
    /// </summary>
    protected static string _notify_url = StateClass.Basic().MobSite + "/wxjsapi/paynotice.aspx";
    /// <summary>
    /// 支付类型
    /// </summary>
    protected static string _trade_type = "JSAPI";
    /// <summary>
    /// 微信下单统一接口
    /// </summary>
    protected static string _payUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /// <summary>
    /// 支付中签约接口
    /// </summary>
    protected static string _signUrl = "https://api.mch.weixin.qq.com/pay/contractorder";
    protected static string _planId = "";//协议模板id,在‘商户平台->高级业务->委托代扣签约管理’中去申请


    /// <summary>
    /// 生成订单号
    /// </summary>
    /// <returns></returns>
    protected static string GetRandomTime()
    {
        Random rd = new Random();//用于生成随机数
        string DateStr = DateTime.Now.ToString("yyyyMMddHHmmssMM");//日期
        string str = DateStr + rd.Next(10000).ToString().PadLeft(4, '0');//带日期的随机数
        return str;
    }

    /// <summary>
    /// 生成随机串    
    /// </summary>
    /// <param name="length">字符串长度</param>
    /// <returns></returns>
    protected static string GetRandomString(int length)
    {
        const string key = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
        if (length < 1)
            return string.Empty;

        Random rnd = new Random();
        byte[] buffer = new byte[8];

        ulong bit = 31;
        ulong result = 0;
        int index = 0;
        StringBuilder sb = new StringBuilder((length / 5 + 1) * 5);

        while (sb.Length < length)
        {
            rnd.NextBytes(buffer);

            buffer[5] = buffer[6] = buffer[7] = 0x00;
            result = BitConverter.ToUInt64(buffer, 0);

            while (result > 0 && sb.Length < length)
            {
                index = (int)(bit & result);
                sb.Append(key[index]);
                result = result >> 5;
            }
        }
        return sb.ToString();
    }

    /// <summary>
    /// 获取时间戳 GetTimeStamp
    /// </summary>
    /// <returns></returns>
    protected static long GetTimeStamp()
    {
        TimeSpan cha = (DateTime.Now - TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)));
        long t = (long)cha.TotalSeconds;
        return t;
    }

    /// <summary>
    /// MD5签名方法  
    /// </summary>  
    /// <param name="inputText">加密参数</param>  
    /// <returns></returns>  
    protected static string MD5(string inputText)
    {
        System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider();
        byte[] fromData = System.Text.Encoding.UTF8.GetBytes(inputText);
        byte[] targetData = md5.ComputeHash(fromData);
        string byte2String = null;

        for (int i = 0; i < targetData.Length; i++)
        {
            byte2String += targetData[i].ToString("x2");
        }

        return byte2String;
    }
    /// <summary>
    /// HMAC-SHA256签名方式
    /// </summary>
    /// <param name="message"></param>
    /// <param name="secret"></param>
    /// <returns></returns>
    protected static string HmacSHA256(string message, string secret)
    {
        secret = secret ?? "";
        var encoding = new System.Text.UTF8Encoding();
        byte[] keyByte = encoding.GetBytes(secret);
        byte[] messageBytes = encoding.GetBytes(message);
        using (var hmacsha256 = new HMACSHA256(keyByte))
        {
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashmessage);
        }
    }

    /// <summary>
    /// 将Model对象转化为url参数形式
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="url"></param>
    /// <returns></returns>
    protected static string ModelToUriParam(object obj)
    {
        PropertyInfo[] propertis = obj.GetType().GetProperties();
        StringBuilder sb = new StringBuilder();
        foreach (var p in propertis)
        {
            var v = p.GetValue(obj, null);
            if (v == null)
                continue;

            sb.Append(p.Name);
            sb.Append("=");
            sb.Append(v.ToString());
            //sb.Append(HttpUtility.UrlEncode(v.ToString()));
            sb.Append("&");
        }
        sb.Remove(sb.Length - 1, 1);

        return sb.ToString();
    }
}

异步通知处理程序:

using System;
using System.Text;
using System.Web;
using System.Xml;
using WxPayAPI;

public partial class wxjsapi_paynotice : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //接收从微信后台POST过来的数据
        System.IO.Stream s = HttpContext.Current.Request.InputStream;
        int count = 0;
        byte[] buffer = new byte[1024];
        StringBuilder builder = new StringBuilder();
        while ((count = s.Read(buffer, 0, 1024)) > 0)
        {
            builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
        }
        s.Flush();
        s.Close();
        s.Dispose();

        ProcessNotify(builder.ToString());
    }
    public string getReqval(string xmlStr, string qname)
    {
        try
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.XmlResolver = null;
            xmlDoc.LoadXml(xmlStr);
            XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
            XmlNodeList nodes = xmlNode.ChildNodes;
            foreach (XmlNode xn in nodes)
            {
                XmlElement xe = (XmlElement)xn;
                if (xe.Name == qname)
                {
                    return xe.InnerText;
                }
            }
            return "--";
        }
        catch (Exception ee)
        {
            return "----";
        }
    }
    public void ProcessNotify(string xmlStr)
    {
        string attach = getReqval(xmlStr, "attach");
        Maticsoft.BLL.PayException payexceptionbll = new Maticsoft.BLL.PayException();
        var payexce = payexceptionbll.GetModelByCache(1);//查询支付参数

        //根据订单号或attach获取支付API
        string APPID = payexce.WxAppID;//应用ID(appid)
        string MCHID = payexce.WxMchID;//商户号(ID)
        string KEY = payexce.WxAppKey;//商户支付密钥(KEY)
        string APPSECRET = payexce.WxAppSecret;//公众帐号(secert)

        Notify wxNotify = new Notify(APPID, MCHID, KEY, APPSECRET);
        WxPayData notifyData = new WxPayData(APPID, MCHID, KEY, APPSECRET);
        notifyData = wxNotify.GetNotifyData(xmlStr);
        //检查支付结果中transaction_id是否存在
        if (!notifyData.IsSet("transaction_id"))
        {
            //https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7
            //transaction_id=微信支付订单号
            //attach商家数据包,原样返回
            //若transaction_id不存在,则立即返回结果给微信支付后台
            WxPayData res = new WxPayData(APPID, MCHID, KEY, APPSECRET);
            res.SetValue("return_code", "FAIL");
            res.SetValue("return_msg", "支付结果中微信订单号不存在");
            Log.Error(this.GetType().ToString(), "The Pay result is error : " + res.ToXml());
            Response.Write(res.ToXml());
            Response.End();
        }

        string transaction_id = notifyData.GetValue("transaction_id").ToString();
        ResultNotify resultNotify = new ResultNotify(APPID, MCHID, KEY, APPSECRET);
        //查询订单,判断订单真实性
        if (!resultNotify.QueryOrder(transaction_id))
        {
            //若订单查询失败,则立即返回结果给微信支付后台
            WxPayData res = new WxPayData(APPID, MCHID, KEY, APPSECRET);
            res.SetValue("return_code", "FAIL");
            res.SetValue("return_msg", "订单查询失败");
            Log.Error(this.GetType().ToString(), "Order query failure : " + res.ToXml());
            Response.Write(res.ToXml());
            Response.End();
        }
        //查询订单成功
        else
        {
            WxPayData res = new WxPayData(APPID, MCHID, KEY, APPSECRET);
            //程序-业务处理
            // string out_trade_no = notifyData.GetValue("out_trade_no").ToString();
            decimal totalfee = Convert.ToDecimal(int.Parse(notifyData.GetValue("total_fee").ToString())) / 100;

            Maticsoft.BLL.Member memberbll = new Maticsoft.BLL.Member();
            var member = memberbll.GetModel(int.Parse(attach.Split('_')[0]));//查询当前学员
            Maticsoft.BLL.Video videobll = new Maticsoft.BLL.Video();
            var video = videobll.GetModel(int.Parse(attach.Split('_')[1]));//查询当前要购买的视频
            Maticsoft.BLL.ExAmi examibll = new Maticsoft.BLL.ExAmi();
            var exami = examibll.GetModel(video.ExaID);//查询视频对应的考场

            Maticsoft.BLL.Indent indentbll = new Maticsoft.BLL.Indent();
            Maticsoft.Model.Indent indent = new Maticsoft.Model.Indent();
            indent.ExaID = exami.ID;
            indent.ExaName = exami.ExamiName;
            indent.ExaUser = exami.UserName;
            indent.MemID = member.ID;
            indent.MemName = member.MemName;
            indent.MemUser = member.MemUser;
            indent.VideoID = video.ID;
            indent.VideoTitle = video.VideoTitle;
            indent.Sum = video.Univalence;
            indent.PayTime = DateTime.Now;
            indent.PayCode = transaction_id;
            indent.Deleter = 0;
            string sql = "";
            sql = "update Video set PayCount=" + video.PayCount + 1 + " where ID=" + video.ID;//修改视频的购买量

            if (indentbll.Add(indent) <= 0 && Maticsoft.DBUtility.DbHelperSQL.ExecuteSql(sql) < 1)
            {
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "业务处理失败");
            }
            else
            {
                res.SetValue("return_code", "SUCCESS");
                res.SetValue("return_msg", "OK");
            }
            Response.Write(res.ToXml());
            Response.End();
        }
    }
}