You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
4.9 KiB

2 years ago
#region Apache License Version 2.0
/*----------------------------------------------------------------
Copyright 2022 Jeffrey Su & Suzhou Senparc Network Technology Co.,Ltd.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.
Detail: https://github.com/JeffreySu/WeiXinMPSDK/blob/master/license.md
----------------------------------------------------------------*/
#endregion Apache License Version 2.0
/*----------------------------------------------------------------
Copyright (C) 2022 Senparc
TenPayNotifyHandler.cs
V3 handler
Senparc - 20210811
Senparc - 20210819
使TenPaySignHelper
----------------------------------------------------------------*/
using Microsoft.AspNetCore.Http;
using Senparc.CO2NET.Helpers;
using Senparc.Weixin.Entities;
using Senparc.Weixin.TenPayV3.Apis.Entities;
using Senparc.Weixin.TenPayV3.Helpers;
using System;
using System.IO;
using System.Threading.Tasks;
namespace Senparc.Weixin.TenPayV3
{
/// <summary>
/// 微信支付通知消息处理器
/// </summary>
public class TenPayNotifyHandler
{
readonly private NotifyRequest NotifyRequest;
readonly private string Body;
private ISenparcWeixinSettingForTenpayV3 _tenpayV3Setting { get; }
private HttpContext _httpContext;
/// <summary>
/// 构造函数
/// 注意:.NetCore环境必须传入HttpContext实例不能传Null这个接口调试特别困难千万别出错
/// </summary>
/// <param name="httpContext"></param>
/// <param name="senparcWeixinSettingForTenpayV3"></param>
public TenPayNotifyHandler(HttpContext httpContext, ISenparcWeixinSettingForTenpayV3 senparcWeixinSettingForTenpayV3 = null)
{
_ = httpContext ?? throw new ArgumentNullException(nameof(httpContext));
_httpContext = httpContext;
_tenpayV3Setting = senparcWeixinSettingForTenpayV3 ?? Senparc.Weixin.Config.SenparcWeixinSetting.TenpayV3Setting;
// 获得body
if (_httpContext.Request.Method == "POST"
|| _httpContext.Request.Method == "PUT"
|| _httpContext.Request.Method == "PATCH")
{
using (var reader = new StreamReader(_httpContext.Request.Body))
{
Body = reader.ReadToEndAsync().GetAwaiter().GetResult();
NotifyRequest = Body.GetObject<NotifyRequest>();
}
}
}
/// <summary>
/// 将返回的结果中的ciphertext进行AEAD_AES_256_GCM解反序列化为实体
/// 签名规则见微信官方文档 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
/// </summary>
/// <param name="aes_key">这里需要传入apiv3秘钥进行AEAD_AES_256_GCM解密 可空</param>
/// <param name="nonce">加密的随机串 可空</param>
/// <param name="associated_data">附加数据包 可空</param>
/// <returns></returns>
// TODO: 本方法持续测试
public async Task<T> AesGcmDecryptGetObjectAsync<T>(string aes_key = null, string nonce = null, string associated_data = null) where T : ReturnJsonBase, new()
{
aes_key ??= _tenpayV3Setting.TenPayV3_APIv3Key;
nonce ??= NotifyRequest.resource.nonce;
associated_data ??= NotifyRequest.resource.associated_data;
var decrypted_string = SecurityHelper.AesGcmDecryptCiphertext(aes_key, nonce, associated_data, NotifyRequest.resource.ciphertext);
T result = decrypted_string.GetObject<T>();
//验证请求签名
var wechatpayTimestamp = _httpContext.Request.Headers?["Wechatpay-Timestamp"];
var wechatpayNonce = _httpContext.Request.Headers?["Wechatpay-Nonce"];
var wechatpaySignature = _httpContext.Request.Headers?["Wechatpay-Signature"];
var wechatpaySerial = _httpContext.Request.Headers?["Wechatpay-Serial"];
result.VerifySignSuccess = await TenPaySignHelper.VerifyTenpaySign(wechatpayTimestamp, wechatpayNonce, wechatpaySignature, Body, wechatpaySerial, this._tenpayV3Setting);
result.ResultCode = new TenPayApiResultCode($"{_httpContext.Response.StatusCode} / {_httpContext.Request.Method}", "", "", "", result.VerifySignSuccess == true);
return result;
}
}
}