当前位置:   article > 正文

​C# 微信支付接口V2版本回调开发实践

​C# 微信支付接口V2版本回调开发实践

目录

关于微信支付接口V2版本

如何配置APIv2密钥

配置Native支付回调链接

微信返回的数据

回调业务实现

获取微信返回的数据

反馈微信服务器

业务程序处理

小结


关于微信支付接口V2版本

使用微信支付接口V2版本开发微信支付,这里我们以JSAPI为例,其将使用APIv2密钥,该密钥是指调用微信支付API时,要按照指定规则对请求数据进行签名。服务器收到调用请求后会进行签名验证,需用APIv2密钥生成签名,从而界定商户的身份并防止他人恶意篡改数据。签名的计算规则中,使用到的key就是APIv2密钥。

如何配置APIv2密钥

 登录微信支付商家平台:微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式

进入平台后,如下图选择帐户中心、API安全,如果没有申请API证书请申请(退款时需要使用证书),下面就是配置APIv2密钥,我们可以选择创建或修改,配置过程需要提供操作员的密码和手机认证短信。

配置Native支付回调链接

如下图进入产品中心、开发配置。

滚动界面至支付配置、Native支付回调链接,如下图所示:

 点击添加或修改链接,强烈建议选择 HTTPS://,后面则填写我们处理回调的程序 URL 地址,配置过程需要提供操作员的密码和手机认证短信。

微信返回的数据

V2版本在微信支付成功后,POST返回给回调地址的数据格式为XML,其样例如下:

  1. <xml><appid><![CDATA[wx64]]></appid>
  2. <attach><![CDATA[0.01]]></attach>
  3. <bank_type><![CDATA[OTHERS]]></bank_type>
  4. <cash_fee><![CDATA[1]]></cash_fee>
  5. <fee_type><![CDATA[CNY]]></fee_type>
  6. <is_subscribe><![CDATA[N]]></is_subscribe>
  7. <mch_id><![CDATA[163]]></mch_id>
  8. <nonce_str><![CDATA[34173]]></nonce_str>
  9. <openid><![CDATA[ou4Rz6qfCP]]></openid>
  10. <out_trade_no><![CDATA[5f26]]></out_trade_no>
  11. <result_code><![CDATA[SUCCESS]]></result_code>
  12. <return_code><![CDATA[SUCCESS]]></return_code>
  13. <sign><![CDATA[5C4A6D72C485]]></sign>
  14. <time_end><![CDATA[20240408111541]]></time_end>
  15. <total_fee>1</total_fee>
  16. <trade_type><![CDATA[JSAPI]]></trade_type>
  17. <transaction_id><![CDATA[4200002165]]></transaction_id>
  18. </xml>

因此我们需要根据返回的数据进行后续业务处理,包括签名验证。

回调业务实现

获取微信返回的数据

GetPostContent方法用于微信支付成功后,接收微信POST过来的回调数据,示例代码如下:

  1. private string GetPostContent()
  2. {
  3. Int32 intLength = Convert.ToInt32(Request.InputStream.Length);
  4. byte[] con = new byte[intLength];
  5. Request.InputStream.Read(con, 0, intLength);
  6. return System.Text.Encoding.UTF8.GetString(con);
  7. }

反馈微信服务器

处理程序的最终结果需要反馈给微信服务器,反馈以下XML数据则表示通知微信服务器处理成功:

  1. <xml>
  2. <return_code><![CDATA[SUCCESS]]></return_code>
  3. <return_msg><![CDATA[OK]]></return_msg>
  4. </xml>

除以上信息外的任何反馈,微信服务器则会定时轮询处理。以下代码为反馈程序,该程序指定了PostResult方法:

  1. private string PostResult(bool success, string failMessage)
  2. {
  3. if (success) failMessage = "";
  4. //SUCCESS/FAIL
  5. string result = "" +
  6. "<xml>" +
  7. " <return_code><![CDATA[" + (success ? "SUCCESS" : "FAIL") + "]]></return_code>" +
  8. " <return_msg><![CDATA[" + (success ? "OK" : failMessage) + "]]></return_msg>" +
  9. "</xml>";
  10. return result;
  11. }

业务程序处理

设计流程基本思路如下:

1、获取微信POST过来的XML,判断return_code是否为SUCCESS

2、解析XML,根据字段进行签名字串的拼接

3、使用APIv2密钥对签名字串继续拼接并生成大写的MD5字符串,与POST的XML中的sign字段值进行比较以验证

4、验证成功则处理自己的业务程序,并反馈给微信服务器数据

核心代码如下:

  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3. try
  4.    {
  5.     String xmlData = GetPostContent();//获取请求数据
  6.     
  7.      if (String.IsNullOrWhiteSpace(xmlData))
  8.      {
  9.        this.Response.Write("未接受到微信回调数据。");//返回微信服务器
  10.       return;
  11.      }
  12.     
  13.      //把数据重新返回给客户端
  14.     DataSet ds = new DataSet();
  15.      StringReader stram = new StringReader(xmlData);
  16.      XmlTextReader datareader = new XmlTextReader(stram);
  17.      ds.ReadXml(datareader);
  18.      if (ds.Tables[0].Rows[0]["return_code"].ToString() == "SUCCESS")
  19.      {
  20.        string wx_appid = "";
  21. string wx_attach = "";
  22.        string wx_mch_id = "";
  23.        string wx_nonce_str = "";
  24.        string wx_sign = "";
  25.        string wx_result_code = "";//SUCCESS/FAIL
  26.        string wx_return_code = "";
  27.        string wx_openid = "";
  28.        string wx_is_subscribe = "";
  29.        string wx_trade_type = "";
  30.        string wx_bank_type = "";
  31.        string wx_fee_type = "";
  32.        string wx_transaction_id = "";
  33.        string wx_out_trade_no = "";
  34.        string wx_time_end = "";
  35.        int wx_total_fee = -1;
  36.        int wx_cash_fee = -1;
  37.       
  38.        string signstr = "";//需要拼接的字符串
  39.       
  40.        //wx_appid
  41.        if (ds.Tables[0].Columns.Contains("appid"))
  42.        {
  43.          wx_appid = ds.Tables[0].Rows[0]["appid"].ToString();
  44.          if (!string.IsNullOrEmpty(wx_appid))
  45.          {
  46.            signstr += "appid=" + wx_appid;
  47.          }
  48.        }
  49. //wx_attach
  50. if (ds.Tables[0].Columns.Contains("attach"))
  51.        {
  52. wx_attach = ds.Tables[0].Rows[0]["attach"].ToString();
  53. if (!string.IsNullOrEmpty(wx_attach))
  54.          {
  55. signstr += "&attach=" + wx_attach;
  56.          }
  57.        }
  58.       
  59.        //wx_bank_type
  60.        if (ds.Tables[0].Columns.Contains("bank_type"))
  61.        {
  62.          wx_bank_type = ds.Tables[0].Rows[0]["bank_type"].ToString();
  63.          if (!string.IsNullOrEmpty(wx_bank_type))
  64.          {
  65.            signstr += "&bank_type=" + wx_bank_type;
  66.          }
  67.        }
  68.        //wx_cash_fee
  69.        if (ds.Tables[0].Columns.Contains("cash_fee"))
  70.        {
  71.          wx_cash_fee = Convert.ToInt32(ds.Tables[0].Rows[0]["cash_fee"].ToString());
  72.         
  73.          signstr += "&cash_fee=" + wx_cash_fee;
  74.        }
  75.       
  76.        //wx_fee_type
  77.        if (ds.Tables[0].Columns.Contains("fee_type"))
  78.        {
  79.          wx_fee_type = ds.Tables[0].Rows[0]["fee_type"].ToString();
  80.          if (!string.IsNullOrEmpty(wx_fee_type))
  81.          {
  82.            signstr += "&fee_type=" + wx_fee_type;
  83.          }
  84.        }
  85.       
  86.        //wx_is_subscribe
  87.        if (ds.Tables[0].Columns.Contains("is_subscribe"))
  88.        {
  89.          wx_is_subscribe = ds.Tables[0].Rows[0]["is_subscribe"].ToString();
  90.          if (!string.IsNullOrEmpty(wx_is_subscribe))
  91.          {
  92.            signstr += "&is_subscribe=" + wx_is_subscribe;
  93.          }
  94.        }
  95.       
  96.        //wx_mch_id
  97.        if (ds.Tables[0].Columns.Contains("mch_id"))
  98.        {
  99.          wx_mch_id = ds.Tables[0].Rows[0]["mch_id"].ToString();
  100.          if (!string.IsNullOrEmpty(wx_mch_id))
  101.          {
  102.            signstr += "&mch_id=" + wx_mch_id;
  103.          }
  104.        }
  105.       
  106.        //wx_nonce_str
  107.        if (ds.Tables[0].Columns.Contains("nonce_str"))
  108.        {
  109.          wx_nonce_str = ds.Tables[0].Rows[0]["nonce_str"].ToString();
  110.          if (!string.IsNullOrEmpty(wx_nonce_str))
  111.          {
  112.            signstr += "&nonce_str=" + wx_nonce_str;
  113.          }
  114.        }
  115.       
  116.        //wx_openid
  117.        if (ds.Tables[0].Columns.Contains("openid"))
  118.        {
  119.          wx_openid = ds.Tables[0].Rows[0]["openid"].ToString();
  120.          if (!string.IsNullOrEmpty(wx_openid))
  121.          {
  122.            signstr += "&openid=" + wx_openid;
  123.          }
  124.        }
  125.       
  126.        //wx_out_trade_no
  127.        if (ds.Tables[0].Columns.Contains("out_trade_no"))
  128.        {
  129.          wx_out_trade_no = ds.Tables[0].Rows[0]["out_trade_no"].ToString();
  130.          if (!string.IsNullOrEmpty(wx_out_trade_no))
  131.          {
  132.            signstr += "&out_trade_no=" + wx_out_trade_no;
  133.          }
  134.        }
  135.       
  136.        //wx_result_code
  137.        if (ds.Tables[0].Columns.Contains("result_code"))
  138.        {
  139.          wx_result_code = ds.Tables[0].Rows[0]["result_code"].ToString();
  140.          if (!string.IsNullOrEmpty(wx_result_code))
  141.          {
  142.            signstr += "&result_code=" + wx_result_code;
  143.          }
  144.        }
  145.       
  146.        //wx_return_code
  147.        if (ds.Tables[0].Columns.Contains("return_code"))
  148.        {
  149.          wx_return_code = ds.Tables[0].Rows[0]["return_code"].ToString();
  150.          if (!string.IsNullOrEmpty(wx_return_code))
  151.          {
  152.            signstr += "&return_code=" + wx_return_code;
  153.          }
  154.        }
  155.       
  156.        //wx_sign
  157.        if (ds.Tables[0].Columns.Contains("sign"))
  158.        {
  159.          wx_sign = ds.Tables[0].Rows[0]["sign"].ToString();
  160.        }
  161.       
  162.        //wx_time_end
  163.        if (ds.Tables[0].Columns.Contains("time_end"))
  164.        {
  165.          wx_time_end = ds.Tables[0].Rows[0]["time_end"].ToString();
  166.          if (!string.IsNullOrEmpty(wx_time_end))
  167.          {
  168.            signstr += "&time_end=" + wx_time_end;
  169.          }
  170.        }
  171.       
  172.        //wx_total_fee
  173.        if (ds.Tables[0].Columns.Contains("total_fee"))
  174.        {
  175.          wx_total_fee = Convert.ToInt32(ds.Tables[0].Rows[0]["total_fee"].ToString());
  176.         
  177.          signstr += "&total_fee=" + wx_total_fee;
  178.        }
  179.       
  180.        //wx_trade_type
  181.        if (ds.Tables[0].Columns.Contains("trade_type"))
  182.        {
  183.          wx_trade_type = ds.Tables[0].Rows[0]["trade_type"].ToString();
  184.          if (!string.IsNullOrEmpty(wx_trade_type))
  185.          {
  186.            signstr += "&trade_type=" + wx_trade_type;
  187.          }
  188.        }
  189.       
  190.        //wx_transaction_id
  191.        if (ds.Tables[0].Columns.Contains("transaction_id"))
  192.        {
  193.          wx_transaction_id = ds.Tables[0].Rows[0]["transaction_id"].ToString();
  194.          if (!string.IsNullOrEmpty(wx_transaction_id))
  195.          {
  196.            signstr += "&transaction_id=" + wx_transaction_id;
  197.          }
  198.        }
  199.       
  200.       
  201.        //追加key APIv2密钥
  202.        signstr += "&key=" + System.Web.Configuration.WebConfigurationManager.AppSettings["APIv2"].ToString();
  203. dal.ErrorMessage = "";
  204. dal.RowsCount = 0;
  205. dal.ConnKeyString = "JaneConnection_enroll";
  206. ArrayList paras = new ArrayList();
  207. paras.Clear();
  208. paras.Add(new SqlParameter("orderid", wx_out_trade_no));
  209. object rv = dal.GetDataSet("select cid from dlzp_ypz where wxPreOrdId=@orderid", paras);
  210. if (dal.ErrorMessage != "" || dal.RowsCount != 1)
  211. {
  212. this.Response.Write(this.PostResult(false, "定位用户ID错误!" + wx_out_trade_no));//返回微信服务器
  213. return;
  214. }
  215. DataSet dsper = rv as DataSet;
  216. string ypz_cid = dsper.Tables[0].Rows[0]["cid"].ToString();
  217. string err_msg = "";
  218. if (wx_result_code == "SUCCESS")
  219. {
  220. err_msg = "get_brand_wcpay_request:ok";
  221. }
  222.        string md5 = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(signstr, "MD5").ToUpper();
  223.       
  224.        //签名验证成功
  225.        if (wx_sign == md5)
  226.        {
  227.          //签名正确,更新数据库订单状态
  228. paras.Clear();
  229. paras.Add(new SqlParameter("ypz_cid", ypz_cid));
  230. paras.Add(new SqlParameter("mch_id", wx_mch_id));
  231. paras.Add(new SqlParameter("openid", wx_openid));
  232. paras.Add(new SqlParameter("orderid", wx_out_trade_no));
  233. paras.Add(new SqlParameter("err_msg", err_msg));
  234. dal.ExecDbScripts("ar_wx_updUserPayInfo", paras, CommandType.StoredProcedure);
  235. if (dal.ErrorMessage != "" || dal.RowsCount == 0)
  236. {
  237. this.Response.Write(this.PostResult(false, "更新支付状态失败!"));//返回微信服务器
  238. return;
  239. }
  240.            this.Response.Write(this.PostResult(true, ""));//返回微信服务器成功信息
  241.        }
  242.        else
  243.        {
  244. this.Response.Write(this.PostResult(false, "签名错误!"));//返回微信服务器
  245.        }
  246.       }
  247.       else
  248.       {
  249.              this.Response.Write(this.PostResult(false, "微信接口返回FAIL"));//返回微信服务器
  250.        }
  251.       }//SUCCESS
  252.     catch (Exception ex)
  253.     {
  254.       this.Response.Write(ex.Message);
  255.     }
  256. }//pageload

小结

 1、拼接的字符串格式如下示例:

appid=wx64&attach=0.01&bank_type=OTHERS&cash_fee=1&fee_type=CNY&is_subscribe=N&mch_id=163&nonce_str=34173&openid=ou4Rz&out_trade_no=5f26&result_code=SUCCESS&return_code=SUCCESS&time_end=2024&total_fee=1&trade_type=JSAPI&transaction_id=42000021&key=APIv2

我们在开发调试中务必遵循关键字及值的拼接顺序

2、示例代码中的 dal类实现了对数据库的操作,是自研发的组件,我们需要根据实际的业务进行自行定义与开发。

3、后台通知反馈微信服务器时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。

4、为完善系统功能,建议进一步开发查询订单对帐功能,并成功更新确认订单状态。

以上是本人的一些体会与实践,再次感谢您的阅读,欢迎讨论、指教!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/443626
推荐阅读
相关标签
  

闽ICP备14008679号