using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Edu.Common.WeChatPayAPIv3
{
internal class WxPayRequestHandler : DelegatingHandler
{
///
/// 直连商户的商户号,由微信支付生成并下发。
///
private string _merchantId = string.Empty;
///
/// 证书序列号,查看证书序列号:https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan#ru-he-cha-kan-zheng-shu-xu-lie-hao
///
private string _serialNo = string.Empty;
///
/// 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- 亦不包括结尾的-----END PRIVATE KEY-----
///
private string _privateKey = string.Empty;
///
/// 构造方法
///
/// 直连商户的商户号,由微信支付生成并下发。
/// 证书序列号,查看证书序列号:https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan#ru-he-cha-kan-zheng-shu-xu-lie-hao
/// 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- 亦不包括结尾的-----END PRIVATE KEY-----
public WxPayRequestHandler(string mchid, string serialNo, string privateKey)
{
InnerHandler = new HttpClientHandler();
this._merchantId = mchid;
this._serialNo = serialNo;
this._privateKey = privateKey;
}
protected async override Task SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var auth = await BuildAuthAsync(request);
string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
request.Headers.Add("Authorization", value);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
return await base.SendAsync(request, cancellationToken);
}
private async Task BuildAuthAsync(HttpRequestMessage request)
{
var method = request.Method.ToString();
var body = "";
if (method == "POST" || method == "PUT" || method == "PATCH")
{
var c = request.Content;
body = await c.ReadAsStringAsync();//这里读取的数据一定要跟传入的参数一致,debug时看到的数据与传入的参数不一致是不可以的
}
string uri = request.RequestUri.PathAndQuery;
var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
string nonce = Path.GetRandomFileName();
var message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
var signature = Sign(message);
return $"mchid=\"{_merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{_serialNo}\",signature=\"{signature}\"";
}
private string Sign(string message)
{
byte[] keyData = Convert.FromBase64String(_privateKey);
using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSACng rsa = new RSACng(cngKey))
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}
}
}
}