C#微信支付完成前端回调通知notify_url完整版源码|CSframework.com原创文章
作者:作者不详  发布日期:2020/03/03 18:07:40
  C#微信支付完成前端回调通知notify_url完整版源码|CSframework.com原创文章

C#微信支付完成前端回调通知notify_url完整版源码|CSframework.com原创文章

微信回调接口POST过来的XML数据格式:

XML Code:

<xml>
<appid><![CDATA[wx44495463a43b7c]]></appid>
<bank_type><![CDATA[CMB_CREDIT]]></bank_type>
<cash_fee><![CDATA[20]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1527472222]]></mch_id>
<nonce_str><![CDATA[6OQ37L7i6JN1PRLD]]></nonce_str>
<openid><![CDATA[oCohp1HF7xLcx-o5QdA-hLc2ios8]]></openid>
<out_trade_no><![CDATA[15557331433589231953]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[6C6A7CB3F3C742D0AE56687A10F4EA31]]></sign>
<time_end><![CDATA[20190415203040]]></time_end>
<total_fee>20</total_fee><trade_type>
<![CDATA[APP]]></trade_type>
<transaction_id><![CDATA[4200000290201904150837155038]]></transaction_id>
</xml>

//来源:C/S框架网(www.csframework.com) QQ:23404761


微信回调接口设计要求:



1,notify_url:回调通知接口HTTP地址,POST请求,不能是IP地址(必须是域名)不能带端口,预设80端口。

参考: http://www.XXXX.com/notify_url.aspx

2,必须是外网能访问HTTP地址,POST方式,微信服务器POST XML数据过来,请使用PostMan工具调试。

3,微信回调的接口中完成更改商户数据库的订单状态,操作成功给微信返回xml数据,这样微信后台就认为这笔订单交易成功,不会再次回调接口。


详情参考微信API接口说明:





后端完整版源码:



贴图图片-CSharp微信支付完成前端回调通知notify_url




C# Code:

protected void Page_Load(object sender, EventArgs e)
{
  
try
  {
    
string ip = GetWebClientIp();//获取客户端IP
    
String xmlData = GetPostStr();//获取请求数据
    

    DBHelper.WriteLog(
"WX-Callback", "xmlData:" + xmlData, ip);
    
    
if (String.IsNullOrWhiteSpace(xmlData))
    {
      
this.Response.Write("请求数据不能为空!");//返回微信服务器
      
return;
    }
    
    
//把数据重新返回给客户端
    
DataSet ds = new DataSet();
    StringReader stram
= new StringReader(xmlData);
    XmlTextReader datareader
= new XmlTextReader(stram);
    ds.ReadXml(datareader);
    
if (ds.Tables[0].Rows[0]["return_code"].ToString() == "SUCCESS")
    {
      
string wx_appid = "";//微信开放平台审核通过的应用APPID
      
string wx_mch_id = "";//微信支付分配的商户号
      
string wx_nonce_str = "";//随机字符串,不长于32位
      
string wx_sign = "";//签名,详见签名算法
      
string wx_result_code = "";//SUCCESS/FAIL
      
string wx_return_code = "";
      
string wx_openid = "";//用户在商户appid下的唯一标识
      
string wx_is_subscribe = "";//用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
      
string wx_trade_type = "";// APP
      
string wx_bank_type = "";// 银行类型,采用字符串类型的银行标识,银行类型见银行列表
      
string wx_fee_type = "";// 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
      
string wx_transaction_id = "";//微信支付订单号
      
string wx_out_trade_no = "";//商户系统的订单号,与请求一致。
      
string wx_time_end = "";// 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
      
int wx_total_fee = -1;// 订单总金额,单位为分
      
int wx_cash_fee = -1;//现金支付金额订单现金支付金额,详见支付金额
      

      
#region 数据解析,注意signstr组合排序,从小到大排列,最后添加key密钥
      
      
//列 是否存在
      
string signstr = "";//需要前面的字符串
      
      
//wx_appid
      
if (ds.Tables[0].Columns.Contains("appid"))
      {
        wx_appid
= ds.Tables[0].Rows[0]["appid"].ToString();
        
if (!string.IsNullOrEmpty(wx_appid))
        {
          signstr +
= "appid=" + wx_appid;
        }
      }
      
      
//wx_bank_type
      
if (ds.Tables[0].Columns.Contains("bank_type"))
      {
        wx_bank_type
= ds.Tables[0].Rows[0]["bank_type"].ToString();
        
if (!string.IsNullOrEmpty(wx_bank_type))
        {
          signstr +
= "&bank_type=" + wx_bank_type;
        }
      }
      
//wx_cash_fee
      
if (ds.Tables[0].Columns.Contains("cash_fee"))
      {
        wx_cash_fee
= Convert.ToInt32(ds.Tables[0].Rows[0]["cash_fee"].ToString());
        
        signstr +
= "&cash_fee=" + wx_cash_fee;
      }
      
      
//wx_fee_type
      
if (ds.Tables[0].Columns.Contains("fee_type"))
      {
        wx_fee_type
= ds.Tables[0].Rows[0]["fee_type"].ToString();
        
if (!string.IsNullOrEmpty(wx_fee_type))
        {
          signstr +
= "&fee_type=" + wx_fee_type;
        }
      }
      
      
//wx_is_subscribe
      
if (ds.Tables[0].Columns.Contains("is_subscribe"))
      {
        wx_is_subscribe
= ds.Tables[0].Rows[0]["is_subscribe"].ToString();
        
if (!string.IsNullOrEmpty(wx_is_subscribe))
        {
          signstr +
= "&is_subscribe=" + wx_is_subscribe;
        }
      }
      
      
//wx_mch_id
      
if (ds.Tables[0].Columns.Contains("mch_id"))
      {
        wx_mch_id
= ds.Tables[0].Rows[0]["mch_id"].ToString();
        
if (!string.IsNullOrEmpty(wx_mch_id))
        {
          signstr +
= "&mch_id=" + wx_mch_id;
        }
      }
      
      
//wx_nonce_str
      
if (ds.Tables[0].Columns.Contains("nonce_str"))
      {
        wx_nonce_str
= ds.Tables[0].Rows[0]["nonce_str"].ToString();
        
if (!string.IsNullOrEmpty(wx_nonce_str))
        {
          signstr +
= "&nonce_str=" + wx_nonce_str;
        }
      }
      
      
//wx_openid
      
if (ds.Tables[0].Columns.Contains("openid"))
      {
        wx_openid
= ds.Tables[0].Rows[0]["openid"].ToString();
        
if (!string.IsNullOrEmpty(wx_openid))
        {
          signstr +
= "&openid=" + wx_openid;
        }
      }
      
      
//wx_out_trade_no
      
if (ds.Tables[0].Columns.Contains("out_trade_no"))
      {
        wx_out_trade_no
= ds.Tables[0].Rows[0]["out_trade_no"].ToString();
        
if (!string.IsNullOrEmpty(wx_out_trade_no))
        {
          signstr +
= "&out_trade_no=" + wx_out_trade_no;
        }
      }
      
      
//wx_result_code
      
if (ds.Tables[0].Columns.Contains("result_code"))
      {
        wx_result_code
= ds.Tables[0].Rows[0]["result_code"].ToString();
        
if (!string.IsNullOrEmpty(wx_result_code))
        {
          signstr +
= "&result_code=" + wx_result_code;
        }
      }
      
      
//wx_return_code
      
if (ds.Tables[0].Columns.Contains("return_code"))
      {
        wx_return_code
= ds.Tables[0].Rows[0]["return_code"].ToString();
        
if (!string.IsNullOrEmpty(wx_return_code))
        {
          signstr +
= "&return_code=" + wx_return_code;
        }
      }
      
      
//wx_sign
      
if (ds.Tables[0].Columns.Contains("sign"))
      {
        wx_sign
= ds.Tables[0].Rows[0]["sign"].ToString();
      }
      
      
//wx_time_end
      
if (ds.Tables[0].Columns.Contains("time_end"))
      {
        wx_time_end
= ds.Tables[0].Rows[0]["time_end"].ToString();
        
if (!string.IsNullOrEmpty(wx_time_end))
        {
          signstr +
= "&time_end=" + wx_time_end;
        }
      }
      
      
//wx_total_fee
      
if (ds.Tables[0].Columns.Contains("total_fee"))
      {
        wx_total_fee
= Convert.ToInt32(ds.Tables[0].Rows[0]["total_fee"].ToString());
        
        signstr +
= "&total_fee=" + wx_total_fee;
      }
      
      
//wx_trade_type
      
if (ds.Tables[0].Columns.Contains("trade_type"))
      {
        wx_trade_type
= ds.Tables[0].Rows[0]["trade_type"].ToString();
        
if (!string.IsNullOrEmpty(wx_trade_type))
        {
          signstr +
= "&trade_type=" + wx_trade_type;
        }
      }
      
      
//wx_transaction_id
      
if (ds.Tables[0].Columns.Contains("transaction_id"))
      {
        wx_transaction_id
= ds.Tables[0].Rows[0]["transaction_id"].ToString();
        
if (!string.IsNullOrEmpty(wx_transaction_id))
        {
          signstr +
= "&transaction_id=" + wx_transaction_id;
        }
      }
      
      
#endregion
      
      
//追加key 密钥
      
signstr += "&key=" + System.Web.Configuration.WebConfigurationManager.AppSettings["wx_key"].ToString();
      
      
string md5 = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(signstr, "MD5").ToUpper();
      
      
//签名正确
      
if (wx_sign == md5)
      {
        
//签名正确,更新本地数据库订单状态
        
bool success = DBHelper.UpdateOrderState(wx_out_trade_no, wx_transaction_id, wx_total_fee);
        
        
if (success)
        {
          DBHelper.WriteLog(
"WX-Callback", "回调更改订单状态成功!", ip);
          
this.Response.Write(this.CreateResult(true, ""));//返回微信服务器
          
}
          
else
          {
            
string refData = "out_trade_no=" + wx_out_trade_no + ",total_fee=" + wx_total_fee.ToString();
            DBHelper.WriteLog(
"WX-Callback", "回调更改订单状态失败!" + refData, ip);
            
this.Response.Write(this.CreateResult(false, "更改订单状态失败"));//返回微信服务器
            
}
          }
          
else
          {
            DBHelper.WriteLog(
"WX-Callback", "回调接口发现签名错误!", ip);
            
this.Response.Write(this.CreateResult(false, "回调接口发现签名错误!"));//返回微信服务器
            
}
          }
          
else
          {
            
this.Response.Write(this.CreateResult(false, "回调函数发现微信接口返回FAIL"));//返回微信服务器
            
}
          }
          
catch (Exception ex)
          {
            
this.Response.Write(ex.Message);
          }
        }
        
        
//来源:C/S框架网(www.csframework.com) QQ:23404761



C# Code:

/// <summary>
/// 创建返回的XML数据
/// </summary>
/// <param name="success"></param>
/// <param name="failMessage"></param>
/// <returns></returns>
private string CreateResult(bool success, string failMessage)
{
  
if (success) failMessage = "";
  
  
//SUCCESS/FAIL
  
string result = "" +
  
"<xml>" +
  
" <return_code><![CDATA[" + (success ? "SUCCESS" : "FAIL") + "]]></return_code>" +
  
" <return_msg><![CDATA[" + (success ? "OK" : failMessage) + "]]></return_msg>" +
  
"</xml>";
  
return result;
}

//来源:C/S框架网(www.csframework.com) QQ:23404761



C# Code:

/// <summary>
/// 获得Post过来的XML数据
/// </summary>
/// <returns></returns>
private string GetPostStr()
{
  Int32 intLen
= Convert.ToInt32(Request.InputStream.Length);
  
byte[] b = new byte[intLen];
  Request.InputStream.Read(b,
0, intLen);
  
return System.Text.Encoding.UTF8.GetString(b);
}

//来源:C/S框架网(www.csframework.com) QQ:23404761



C# Code:

/// <summary>
/// 获取客户端IP
/// </summary>
/// <returns></returns>
public static string GetWebClientIp()
{
  
string userIP = "IP";
  
  
try
  {
    
if (System.Web.HttpContext.Current == null || System.Web.HttpContext.Current.Request == null || System.Web.HttpContext.Current.Request.ServerVariables == null) return "";
    
    
string CustomerIP = "";
    
    
//CDN加速后取到的IP
    
CustomerIP = System.Web.HttpContext.Current.Request.Headers["Cdn-Src-Ip"];
    
if (!string.IsNullOrEmpty(CustomerIP)) return CustomerIP;
    
    CustomerIP
= System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    
    
if (!String.IsNullOrEmpty(CustomerIP)) return CustomerIP;
    
    
if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null)
    {
      CustomerIP
= System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
      
if (CustomerIP == null)
      CustomerIP
= System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
    }
    
else
    {
      CustomerIP
= System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
    }
    
    
if (string.Compare(CustomerIP, "unknown", true) == 0)
    
return System.Web.HttpContext.Current.Request.UserHostAddress;
    
    
return CustomerIP;
  }
  
catch { }
  
  
return userIP;
}

//来源:C/S框架网(www.csframework.com) QQ:23404761



交易历史记录(tb_PayTransList)表结构:


SQL Code:

CREATE TABLE [dbo].[tb_PayTransList]
(
  
[isid] [int] NOT NULL IDENTITY(1, 1),
  
[TransID] [varchar] (32) COLLATE Chinese_PRC_CI_AS NOT NULL,
  
[TransType] [nvarchar] (10) COLLATE Chinese_PRC_CI_AS NULL,
  
[MerID] [varchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL,
  
[MerDate] [datetime] NULL,
  
[Account] [varchar] (32) COLLATE Chinese_PRC_CI_AS NULL,
  
[ItemID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL,
  
[ItemName] [nvarchar] (200) COLLATE Chinese_PRC_CI_AS NULL,
  
[RefID] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL,
  
[RefBeginDate] [datetime] NULL,
  
[RefEndDate] [datetime] NULL,
  
[Amount] [float] NULL,
  
[PayType] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL,
  
[Remark] [nvarchar] (200) COLLATE Chinese_PRC_CI_AS NULL,
  
[FlagTrans] [int] NULL,
  
[CreateTime] [datetime] NULL,
  
[WxPreOrderID] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL,
  
[WxPreOrderTime] [datetime] NULL,
  
[WxCallbackTime] [datetime] NULL,
  
[WxNoncestr] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL,
  
[WxTS] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL,
  
[WxSign] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL,
  
[WxTransactionID] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL
  )
ON [PRIMARY]
  
GO
  
ALTER TABLE [dbo].[tb_PayTransList] ADD CONSTRAINT [PK_tb_PayTransList] PRIMARY KEY CLUSTERED ([TransID]) ON [PRIMARY]
  
GO
  
CREATE NONCLUSTERED INDEX [IX_tb_PayTransList] ON [dbo].[tb_PayTransList] ([isid]) ON [PRIMARY]
  
GO
  
CREATE UNIQUE NONCLUSTERED INDEX [IX_tb_PayTransList_1] ON [dbo].[tb_PayTransList] ([MerID]) ON [PRIMARY]
  
GO
  
CREATE NONCLUSTERED INDEX [IX_tb_PayTransList_2] ON [dbo].[tb_PayTransList] ([Account]) ON [PRIMARY]
  
GO
  
  
  
//来源:C/S框架网(www.csframework.com) QQ:23404761



usp_Wallet_WXCallbackUpdate 存储过程:


SQL Code:

--转换为分进行对比,更新交易状态、回调时间
UPDATE dbo.tb_PayTransList SET FlagTrans=2,WxCallbackTime=GETDATE(),WxTransactionID=@WxTransactionID
WHERE MerID=@OrderID AND Amount*100=@Amount AND WxCallbackTime IS NULL


//来源:C/S框架网(www.csframework.com) QQ:23404761






DBHelper类,引用多数据库底层接口IDatabase。


参考:

C#多数据库组件包支持MSSQL+Oracle+MySQL+用户操作手册



C# Code:

using CSFramework.DB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// DBHelper数据层
/// </summary>
public static class DBHelper
{
  
//日志数据库
  
private static IDatabase CreateLogDB()
  {
    
string conn_log = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["Simidata_LogDB"].ToString();
    
return DatabaseFactory.CreateDatabase(DatabaseType.SqlServer, conn_log);
  }
  
  
//业务数据库
  
private static IDatabase CreateSimidataDB()
  {
    
string conn_log = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["Simidata"].ToString();
    
return DatabaseFactory.CreateDatabase(DatabaseType.SqlServer, conn_log);
  }
  
  
/// <summary>
  
/// 写日志
  
/// </summary>
  
/// <param name="logType">日志类型</param>
  
/// <param name="content">内容</param>
  
/// <param name="ip">IP</param>
  
public static void WriteLog(string logType, string content, string ip)
  {
    
string sql = "INSERT INTO dbo.sys_Log(TS,IP,LogType,LogContent) SELECT GETDATE(),@IP,@LogType,@LogContent";
    IDatabase db
= CreateLogDB();
    CommandHelper cmd
= db.CreateCommand(sql);
    cmd.AddParam(
"@IP", ip);
    cmd.AddParam(
"@LogType", logType);
    cmd.AddParam(
"@LogContent", content);
    db.ExecuteCommand(cmd.Command);
  }
  
  
/// <summary>
  
/// 更新本地数据库订单状态
  
/// </summary>
  
/// <param name="orderID">订单编号</param>
  
/// <param name="success">true/false</param>
  
/// <param name="WxTransactionID">微信交易号</param>
  
/// <param name="amount">金额(分)</param>
  
/// <returns></returns>
  
public static bool UpdateOrderState(string orderID, string WxTransactionID, int amount)
  {
    IDatabase db
= CreateSimidataDB();
    CommandHelper cmd
= db.CreateSqlProc("usp_Wallet_WXCallbackUpdate");
    cmd.AddParam(
"@OrderID", orderID);
    cmd.AddParam(
"@WxTransactionID", WxTransactionID);
    cmd.AddParam(
"@Amount", amount);//--单位(分)
    
object o = db.ExecuteScalar(cmd.Command);
    
return o == null ? false : o.ToString().ToUpper() == "OK";
  }
}

//来源:C/S框架网(www.csframework.com) QQ:23404761



使用PostMan测试notify_url地址:



贴图图片-微信截图_20190416092332



必须返回XML格式的数据给微信服务器:


C# Code:

<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>




商户本地数据库的日志信息:


贴图图片-微信截图_20190416092452



<本文完>




CSFramework.微信后端服务器WebApi开发框架-标准版V1.0


适用开发 适用开发:快速构建支持多种客户端的服务端程序,支持APP、B/S、C/S跨平台移动终端等。
运行平台 运行平台:Windows + .NET Framework 4.5
开发工具 开发工具:Visual Studio 2015+,C#语言
数据库 数据库:Microsoft SQLServer 2008R2+(支持多数据库:Oracle/MySql)

WebApi服务端开发框架



 一、产品介绍


    CSFramework.WebApi是服务端快速开发框架(后端框架),借助ASP.NET WebAPI底层架构的强大编程能力,封装成为可复用的以及可定制开发的服务端软件模板,CSFramework.WebApi提供可复用的软件架构和开发包,为用户快速轻松搭建基于HTTP协议、HTTPS协议以及支持多种客户端(如:APP、B/S、C/S、微信公众号、微信小程序等)各种跨平台移动终端的服务端应用程序。 

    服务端应用开发、后端接口开发是软件项目重要工作环节,服务端注重业务逻辑、数据处理和数据分析、算法等方面的设计和服务,前端主要体现在用户体验、界面操作和数据采集方面。前端软件系统和后端服务架构共同搭建跨平台大型数据管理应用系统。 





扫一扫加微信:
 

上一篇 下一篇