需求:用户购买视频调起微信支付,支付成功后观看视频,所以不需要像商品一样的支付状态和订单状态,点击购买时直接调起微信支付传递视频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();
}
}
}