C#⽀付(六)——通知回调
这是重头戏了,修改订单状态⼀般就是两个地⽅,⼀个是前台轮训后发现订单完成后修改状态,另⼀个就是通知回调那了。通知回调类,修改了下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多秒才收到回调。。。所以实际开发的时候,最好轮训和回调都要有。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。