c# .net 微信支付v3,支付结果通知处理;含AEAD_AES_256_GCM解码

 c# .net 微信支付v3,支付结果通知处理;含AEAD_AES_256_GCM解码

作者的程序框架:.NET Framework 4.6.1

微信官方说明地址:

 注意重点:

微信支付-开发者文档

https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml

注意:
• 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。• 如果在所有通知频率后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。特别提醒:商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。

参数解密

下面详细描述对通知数据进行解密的流程:1、用商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,记为key;
2、针对resource.algorithm中描述的算法(目前为AEAD_AES_256_GCM),取得对应的参数nonce和associated_data;
3、使用key、nonce和associated_data,对数据密文resource.ciphertext进行解密,得到JSON形式的资源对象;注: AEAD_AES_256_GCM算法的接口细节,请参考rfc5116。微信支付使用的密钥key长度为32个字节,随机串nonce长度12个字节,associated_data长度小于16个字节并可能为空字符串。

证书和回调报文解密

微信支付-开发者文档

https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml

位置:文档中心>开发指南>证书和回调报文解密

官方提供的Net代码

public class AesGcm
{private static string ALGORITHM = "AES/GCM/NoPadding";private static int TAG_LENGTH_BIT = 128;private static int NONCE_LENGTH_BYTE = 12;private static string AES_KEY = "yourkeyhere";public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext){GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());AeadParameters aeadParameters = new AeadParameters(new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)), 128, Encoding.UTF8.GetBytes(nonce), Encoding.UTF8.GetBytes(associatedData));gcmBlockCipher.Init(false, aeadParameters);byte[] data = Convert.FromBase64String(ciphertext);byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);gcmBlockCipher.DoFinal(plaintext, length);return Encoding.UTF8.GetString(plaintext);}
}

这里有个坑,如果你直接用是会报错的

所以需要引用一个包

给项目添加引用包

项目》引用》右键“NuGet”》搜索“Portable.BouncyCastle”》找到如图的包安装即可

 

我安装版本数据

描述:BouncyCastle portable version with support for .NET 4, .NET Standard 2.0

版本:1.9.0

作者:Claire Novotny

发布日期:2021年10月19日 (2021/10/19)

项目URL:https://www.bouncycastle.org/csharp/

完整代码

AEAD_AES_256_GCM 解码工具类

using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace test1.WeiXinPay.Tools
{/// /// 解密微信通知结果帮助类/// 参考资料:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml/// .NET5环境使用该代码,需要安装Portable.BouncyCastle组件/// public class AesGcmHelper{private static string ALGORITHM = "AES/GCM/NoPadding";private static int TAG_LENGTH_BIT = 128;private static int NONCE_LENGTH_BYTE = 12;private static string AES_KEY = string.Empty;/// ///  解密微信通知结果帮助类/// /// 通知数据resource下的associated_data/// 通知数据resource.nonce/// 通知数据resource.ciphertext/// APIV3的密钥key/// public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext, string APIV3Key){GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());AeadParameters aeadParameters = new AeadParameters(new KeyParameter(Encoding.UTF8.GetBytes(APIV3Key)),128,Encoding.UTF8.GetBytes(nonce),Encoding.UTF8.GetBytes(associatedData));gcmBlockCipher.Init(false, aeadParameters);byte[] data = Convert.FromBase64String(ciphertext);byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);gcmBlockCipher.DoFinal(plaintext, length);return Encoding.UTF8.GetString(plaintext);}}
}

所需模型代码

/// /// 微信支付通过后的回调 模型/// public class NotifyUrlModel{/// /// 通知的唯一ID/// public string id { get; set; }/// /// 通知创建的时间,遵循rfc3339标准格式,/// public string create_time { get; set; }/// /// 通知的资源数据类型,支付成功通知为encrypt-resource/// public string resource_type { get; set; }/// /// 通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS/// public string event_type { get; set; }/// /// 回调摘要:如:支付成功/// public string summary { get; set; }/// /// 通知资源数据 json格式,/// public ResourceModel resource { get; set; }}/// /// 通知资源数据 json格式,/// public class ResourceModel{/// /// 原始回调类型,为transaction/// public string original_type { get; set; }/// /// 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM/// public string algorithm { get; set; }/// /// Base64编码后的开启/停用结果数据密文/// public string ciphertext { get; set; }/// /// 附加数据/// public string associated_data { get; set; }/// /// 加密使用的随机串/// public string nonce { get; set; }}

回调页面-业务代码

 /// /// 订单回调地址/// /// [HttpPost]public JsonResult NotifyUrl(){var result = new { code= "SUCCESS" , message = "成功" };var httpContext= HttpContext;var request = Request;try{#region 获取字符串流 System.IO.Stream s = 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();var str = Encoding.UTF8.GetString(buffer);#endregion#region 获取请求头信息StringBuilder RHeadersStr = new StringBuilder();RHeadersStr.Append("{");foreach (var item in request.Headers){RHeadersStr.Append($"\"{item.ToString()}\":\"{request.Headers[item.ToString()]}\",");}RHeadersStr.Remove((RHeadersStr.Length - 1), 1);RHeadersStr.Append("}");  #endregion#region 把字符流和请求头的信息写入日志log.Info($@"获取字符串流:{str.Trim('\0')}\r\n获取请求头信息:{RHeadersStr.ToString().Trim()}\r\n==================================================\r\n");#endregion#region 获取字符串流string contentStr = str.Trim('\0');#endregion#region 获取请求头信息string headersStr = RHeadersStr.ToString().Trim();#endregionNotifyUrlModel notifyUrlModel = JsonConvert.DeserializeObject(contentStr);JObject headers = JsonConvert.DeserializeObject(contentStr);string APIV3Key = "XXXXXadsadsadasddddAPIV3Key";//获取解码数据var decryptStr = AesGcmHelper.AesGcmDecrypt(notifyUrlModel.resource.associated_data, notifyUrlModel.resource.nonce, notifyUrlModel.resource.ciphertext, APIV3Key);}catch (Exception e){log.Error(e.LogErrorTxt());throw;}return Json(result);}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部