这是重头戏了,修改订单状态一般就是两个地方,一个是前台轮训后发现微信订单完成后修改状态,另一个就是通知回调那了。
通知回调类,修改了下微信Demo代码,兼容了支付和退款两种情况,觉得放一起不好的,想分成两个接口就拆开就好了
/// <summary>
/// 支付结果通知回调处理类
/// 负责接收微信支付后台发送的支付结果并对订单有效性进行验证,将验证结果反馈给微信支付后台
/// </summary>
public class ResultNotify : Notify
{
public override WxPayData ProcessNotify(HttpContext context)
{
WxPayData notifyData = GetNotifyData(context);
//支付回调
if (notifyData.IsSet("result_code"))
{
#region 验证签名
try
{
notifyData.CheckSign();//验证签名,不通过会抛异常
}
catch (Exception ex)
{
//若签名错误,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", ex.Message);
//Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
context.Response.Write(res.ToXml());
return null;
}
#endregion
//检查支付结果中transaction_id是否存在
if (!notifyData.IsSet("transaction_id"))
{
//若transaction_id不存在,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "支付结果中微信订单号不存在");
//Log.Error(this.GetType().ToString(), "The Pay result is error : " + res.ToXml());
context.Response.Write(res.ToXml());
}
else
{
string transaction_id = notifyData.GetValue("transaction_id").ToString();
//查询订单,判断订单真实性
if (!QueryPayOrder(transaction_id))
{
//若订单查询失败,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "订单查询失败");
//Log.Error(this.GetType().ToString(), "Order query failure : " + res.ToXml());
context.Response.Write(res.ToXml());
}
//查询订单成功
else
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "SUCCESS");
res.SetValue("return_msg", "OK");
//Log.Info(this.GetType().ToString(), "order query success : " + res.ToXml());
context.Response.Write(res.ToXml());
return notifyData;
}
}
}
//退款回调
else if (notifyData.IsSet("req_info"))
{
if (!notifyData.IsSet("return_code") || notifyData.GetValue("return_code").ToString() != "SUCCESS" || !notifyData.IsSet("req_info"))
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "未返回正确微信支付退款解密信息");
context.Response.Write(res.ToXml());
}
//信息解密
string req_info = DecodeReqInfo(notifyData.GetValue("req_info").ToString());
if (req_info == null)
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "解密信息出错");
context.Response.Write(res.ToXml());
}
else
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "SUCCESS");
res.SetValue("return_msg", "OK");
//Log.Info(this.GetType().ToString(), "order query success : " + res.ToXml());
context.Response.Write(res.ToXml());
//FormXML会校验签名
WxPayData riData = new WxPayData();
riData.FromXmlNoCheck(req_info);
notifyData.SetValue("req_info", riData);
return notifyData;
}
}
else
{
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", "未知的通知回调");
context.Response.Write(res.ToXml());
}
return null;
}
//查询订单
private bool QueryPayOrder(string transaction_id)
{
WxPayData req = new WxPayData();
req.SetValue("transaction_id", transaction_id);
WxPayData res = WxPayApi.OrderQuery(req);
if (res.GetValue("return_code").ToString() == "SUCCESS" &&
res.GetValue("result_code").ToString() == "SUCCESS")
{
return true;
}
else
{
return false;
}
}
#region 退款解密
/// <summary>
/// AES-256-ECB字符解密
/// </summary>
/// <param name="s">要解密字符串</param>
/// <param name="key">密钥</param>
/// <returns></returns>
public static string DecodeAES256ECB(string s, string key)
{
string r = null;
try
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] toEncryptArray = Convert.FromBase64String(s);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.ECB;
rDel.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
r = UTF8Encoding.UTF8.GetString(resultArray);
}
catch { }
return r;
}
/// <summary>
/// 解密微信支付退款结果通知
/// </summary>
/// <param name="s">要解密字符串</param>
/// <returns></returns>
public static string DecodeReqInfo(string s)
{
string r = null;
string key = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(Config.Key, "md5").ToLower();
r = DecodeAES256ECB(s, key);
return r;
}
#endregion
}
回调基类,基础帮助类 里面也有写到
/// <summary>
/// 回调处理基类
/// 主要负责接收微信支付后台发送过来的数据,对数据进行签名验证
/// 子类在此类基础上进行派生并重写自己的回调处理过程
/// </summary>
public class Notify
{
/// <summary>
/// 接收从微信支付后台发送过来的数据并验证签名
/// </summary>
/// <returns>微信支付后台返回的数据</returns>
public WxPayData GetNotifyData(HttpContext context)
{
//接收从微信后台POST过来的数据
System.IO.Stream s = context.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();
//Log.Info(this.GetType().ToString(), "Receive data from WeChat : " + builder.ToString());
//转换数据格式, 取消签名校验,签名调用方自己调用,兼容支付和退款
WxPayData data = new WxPayData();
try
{
//FormXML会校验签名
data.FromXmlNoCheck(builder.ToString());
}
catch (Exception ex)
{
//若签名错误,则立即返回结果给微信支付后台
WxPayData res = new WxPayData();
res.SetValue("return_code", "FAIL");
res.SetValue("return_msg", ex.Message);
//Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
context.Response.Write(res.ToXml());
context.Response.End();
}
//Log.Info(this.GetType().ToString(), "Check sign success");
return data;
}
//派生类需要重写这个方法,进行不同的回调处理
public virtual WxPayData ProcessNotify(HttpContext context)
{
return null;
}
}
具体接口调用
public void ProcessRequest(HttpContext context)
{
ResultNotify resultNotify = new ResultNotify();
WxPayData notifyData = resultNotify.ProcessNotify(context);
if (notifyData != null)
{
//支付回调
if (notifyData.IsSet("result_code"))
{
//TODO:核对订单金额与微信订单是否一致
//TODO:核对订单金额与系统订单是否一致
//TODO:核对订单号,系统订单号,商户订单号三者是否一致
//TODO:修改订单状态
//TODO:记录日志
}
//退款回调
else if (notifyData.IsSet("req_info"))
{
//TODO:核对退款金额与系统退款是否一致
//TODO:核对订单号,系统订单号,商户订单号三者是否一致
//TODO:修改订单状态
//TODO:记录日志
}
}
}
一定要核对下订单金额和订单号,并且返回结果给微信,不然前者会有假冒数据,后者会让微信几分钟内一直发请求给你。。。。。
解密那有点坑,解密的流程有点繁琐,遇到好几次错误才通过了。其他的没啥想到的,感觉出错都是自己代码问题,微信那好像没什么坑,另外这个回调并不是即时发送的,有延迟的,微信那边人多的时候,有一次30多秒才收到回调。。。所以实际开发的时候,最好轮训和回调都要有。