mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-12-29 18:04:42 +08:00
feat(tenpayv3): 兼容平台证书与公钥灰度切换
This commit is contained in:
@@ -69,7 +69,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global
|
|||||||
PlatformAuthScheme = _ROOT_.Settings.PlatformAuthScheme.Certificate,
|
PlatformAuthScheme = _ROOT_.Settings.PlatformAuthScheme.Certificate,
|
||||||
PlatformCertificateManager = options.PlatformCertificateManager,
|
PlatformCertificateManager = options.PlatformCertificateManager,
|
||||||
AutoEncryptRequestSensitiveProperty = options.AutoEncryptRequestSensitiveProperty,
|
AutoEncryptRequestSensitiveProperty = options.AutoEncryptRequestSensitiveProperty,
|
||||||
AutoDecryptResponseSensitiveProperty = options.AutoDecryptResponseSensitiveProperty
|
AutoDecryptResponseSensitiveProperty = options.AutoDecryptResponseSensitiveProperty,
|
||||||
}, httpClient, disposeClient);
|
}, httpClient, disposeClient);
|
||||||
|
|
||||||
Credentials = new Settings.Credentials(options);
|
Credentials = new Settings.Credentials(options);
|
||||||
@@ -100,7 +100,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IFlurlRequest CreateFlurlRequest(WechatTenpayGlobalRequest request, HttpMethod httpMethod, params object[] urlSegments)
|
public IFlurlRequest CreateFlurlRequest(WechatTenpayGlobalRequest request, HttpMethod httpMethod, params object[] urlSegments)
|
||||||
{
|
{
|
||||||
return base.CreateFlurlRequest(request, httpMethod, urlSegments);
|
return ProxyClient.CreateFlurlRequest(request, httpMethod, urlSegments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
|
||||||
return WechatTenpayClientSigningExtensions.VerifySignature(
|
return WechatTenpayClientSigningExtensions._VerifySignature(
|
||||||
client,
|
client,
|
||||||
strTimestamp: webhookTimestamp,
|
strTimestamp: webhookTimestamp,
|
||||||
strNonce: webhookNonce,
|
strNonce: webhookNonce,
|
||||||
@@ -127,7 +127,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
|
||||||
return WechatTenpayClientSigningExtensions.VerifySignatureAsync(
|
return WechatTenpayClientSigningExtensions._VerifySignatureAsync(
|
||||||
client,
|
client,
|
||||||
strTimestamp: webhookTimestamp,
|
strTimestamp: webhookTimestamp,
|
||||||
strNonce: webhookNonce,
|
strNonce: webhookNonce,
|
||||||
|
|||||||
@@ -25,16 +25,37 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
if (request is null) throw new ArgumentNullException(nameof(request));
|
if (request is null) throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
switch (client.PlatformAuthScheme)
|
bool required = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute));
|
||||||
|
if (!required)
|
||||||
|
return request;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
case PlatformAuthScheme.Certificate:
|
switch (client.PlatformAuthScheme)
|
||||||
return EncryptRequestSensitivePropertyByCertificate<TRequest>(client, request);
|
{
|
||||||
|
case PlatformAuthScheme.Certificate:
|
||||||
|
{
|
||||||
|
WechatTenpayClientRequestSerialNumberExtensions._EnsureRequestWechatpaySerialNumberIsSet(client, request);
|
||||||
|
return EncryptRequestSensitivePropertyUseCertificateManager<TRequest>(client.PlatformCertificateManager, client.Credentials.SignScheme, request);
|
||||||
|
}
|
||||||
|
|
||||||
case PlatformAuthScheme.PublicKey:
|
case PlatformAuthScheme.PublicKey:
|
||||||
return EncryptRequestSensitivePropertyByPublicKey<TRequest>(client, request);
|
{
|
||||||
|
WechatTenpayClientRequestSerialNumberExtensions._EnsureRequestWechatpaySerialNumberIsSet(client, request);
|
||||||
|
return EncryptRequestSensitivePropertyUsePublicKeyManager<TRequest>(client.PlatformPublicKeyManager, client.Credentials.SignScheme, request);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new WechatTenpayException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\".");
|
throw new NotSupportedException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WechatTenpayException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new WechatTenpayException("Failed to encrypt request. Please see the inner exception for more details.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,84 +66,47 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task<TRequest> EncryptRequestSensitivePropertyAsync<TRequest>(this WechatTenpayClient client, TRequest request, CancellationToken cancellationToken = default)
|
public static async Task<TRequest> EncryptRequestSensitivePropertyAsync<TRequest>(this WechatTenpayClient client, TRequest request, CancellationToken cancellationToken = default)
|
||||||
where TRequest : WechatTenpayRequest
|
where TRequest : WechatTenpayRequest
|
||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
if (request is null) throw new ArgumentNullException(nameof(request));
|
if (request is null) throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
switch (client.PlatformAuthScheme)
|
bool required = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute));
|
||||||
{
|
if (!required)
|
||||||
case PlatformAuthScheme.Certificate:
|
return request;
|
||||||
return EncryptRequestSensitivePropertyByCertificateAsync<TRequest>(client, request);
|
|
||||||
|
|
||||||
case PlatformAuthScheme.PublicKey:
|
|
||||||
return EncryptRequestSensitivePropertyByPublicKeyAsync<TRequest>(client, request);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new WechatTenpayException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TRequest EncryptRequestSensitivePropertyByCertificate<TRequest>(this WechatTenpayClient client, TRequest request)
|
|
||||||
where TRequest : WechatTenpayRequest
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool requireEncrypt = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute));
|
switch (client.PlatformAuthScheme)
|
||||||
if (!requireEncrypt)
|
|
||||||
return request;
|
|
||||||
|
|
||||||
string signScheme = client.Credentials.SignScheme;
|
|
||||||
string algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密
|
|
||||||
SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_RSA :
|
|
||||||
SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_SM2 :
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request. Unsupported signing scheme: \"{signScheme}\".");
|
|
||||||
|
|
||||||
string certificate;
|
|
||||||
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
|
||||||
{
|
{
|
||||||
// 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的
|
case PlatformAuthScheme.Certificate:
|
||||||
IEnumerable<CertificateEntry> entries = client.PlatformCertificateManager.AllEntries()
|
{
|
||||||
.Where(e => e.AlgorithmType == algorithmType)
|
if (client.PlatformCertificateManager is not ICertificateManagerAsync)
|
||||||
.OrderByDescending(e => e.ExpireTime);
|
{
|
||||||
if (!entries.Any())
|
// 降级为同步调用
|
||||||
{
|
return EncryptRequestSensitivePropertyUseCertificateManager(client.PlatformCertificateManager, client.Credentials.SignScheme, request);
|
||||||
throw new WechatTenpayException("Failed to encrypt request, because the platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CertificateEntry entry = entries.First();
|
await WechatTenpayClientRequestSerialNumberExtensions._EnsureRequestWechatpaySerialNumberIsSetAsync(client, request, cancellationToken).ConfigureAwait(false);
|
||||||
certificate = entry.Certificate;
|
return await EncryptRequestSensitivePropertyUseCertificateManagerAsync<TRequest>((ICertificateManagerAsync)client.PlatformCertificateManager, client.Credentials.SignScheme, request, cancellationToken).ConfigureAwait(false);
|
||||||
request.WechatpaySerialNumber = entry.SerialNumber;
|
}
|
||||||
|
|
||||||
|
case PlatformAuthScheme.PublicKey:
|
||||||
|
{
|
||||||
|
if (client.PlatformPublicKeyManager is not IPublicKeyManagerAsync)
|
||||||
|
{
|
||||||
|
// 降级为同步调用
|
||||||
|
return EncryptRequestSensitivePropertyUsePublicKeyManager(client.PlatformPublicKeyManager, client.Credentials.SignScheme, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
await WechatTenpayClientRequestSerialNumberExtensions._EnsureRequestWechatpaySerialNumberIsSetAsync(client, request, cancellationToken).ConfigureAwait(false);
|
||||||
|
return await EncryptRequestSensitivePropertyUsePublicKeyManagerAsync<TRequest>((IPublicKeyManagerAsync)client.PlatformPublicKeyManager, client.Credentials.SignScheme, request, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\".");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值
|
|
||||||
CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(request.WechatpaySerialNumber!);
|
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \"{request.WechatpaySerialNumber}\". Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
certificate = entry.Value.Certificate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReflectionHelper.ReplaceObjectStringProperties(request, (_, currentProp, oldValue) =>
|
|
||||||
{
|
|
||||||
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
WechatTenpaySensitivePropertyAttribute? attribute = currentProp
|
|
||||||
.GetCustomAttributes<WechatTenpaySensitivePropertyAttribute>()
|
|
||||||
.FirstOrDefault(attr => attr.Scheme == signScheme);
|
|
||||||
if (attribute is null)
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
string newValue = GenerateEncryptedValueByCertificate(attribute.Algorithm, certificate, oldValue);
|
|
||||||
return (true, newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
}
|
||||||
catch (WechatTenpayException)
|
catch (WechatTenpayException)
|
||||||
{
|
{
|
||||||
@@ -134,281 +118,128 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TRequest> EncryptRequestSensitivePropertyByCertificateAsync<TRequest>(this WechatTenpayClient client, TRequest request, CancellationToken cancellationToken = default)
|
private static TRequest EncryptRequestSensitivePropertyUseCertificateManager<TRequest>(ICertificateManager manager, string signScheme, TRequest request)
|
||||||
where TRequest : WechatTenpayRequest
|
where TRequest : WechatTenpayRequest
|
||||||
{
|
{
|
||||||
if (client.PlatformCertificateManager is not ICertificateManagerAsync)
|
if (manager is null)
|
||||||
{
|
throw new NullReferenceException("The platform certificate manager is not configured.");
|
||||||
// 降级为同步调用
|
|
||||||
return EncryptRequestSensitivePropertyByCertificate(client, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
CertificateEntry? entry = manager.GetEntry(request.WechatpaySerialNumber!);
|
||||||
{
|
if (!entry.HasValue)
|
||||||
bool requireEncrypt = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute));
|
throw new WechatTenpayException($"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \"{request.WechatpaySerialNumber}\". Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
||||||
if (!requireEncrypt)
|
|
||||||
return request;
|
|
||||||
|
|
||||||
string signScheme = client.Credentials.SignScheme;
|
return PopulateRequestEncryptedFieldsByCertificate(signScheme, entry.Value.Certificate, ref request);
|
||||||
string algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密
|
|
||||||
SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_RSA :
|
|
||||||
SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_SM2 :
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request. Unsupported signing scheme: \"{signScheme}\".");
|
|
||||||
|
|
||||||
string certificate;
|
|
||||||
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
|
||||||
{
|
|
||||||
if (client.PlatformCertificateManager is null)
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request, because the platform certificate manager is not initialized.");
|
|
||||||
|
|
||||||
// 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的
|
|
||||||
IEnumerable<CertificateEntry> entries = await ((ICertificateManagerAsync)client.PlatformCertificateManager).AllEntriesAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
entries = entries.Where(e => e.AlgorithmType == algorithmType).OrderByDescending(e => e.ExpireTime);
|
|
||||||
if (!entries.Any())
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request, because the platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
CertificateEntry entry = entries.First();
|
|
||||||
certificate = entry.Certificate;
|
|
||||||
request.WechatpaySerialNumber = entry.SerialNumber;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值
|
|
||||||
CertificateEntry? entry = await ((ICertificateManagerAsync)client.PlatformCertificateManager).GetEntryAsync(request.WechatpaySerialNumber!, cancellationToken).ConfigureAwait(false);
|
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \"{request.WechatpaySerialNumber}\". Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
certificate = entry.Value.Certificate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReflectionHelper.ReplaceObjectStringProperties(request, (_, currentProp, oldValue) =>
|
|
||||||
{
|
|
||||||
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
WechatTenpaySensitivePropertyAttribute? attribute = currentProp
|
|
||||||
.GetCustomAttributes<WechatTenpaySensitivePropertyAttribute>()
|
|
||||||
.FirstOrDefault(attr => attr.Scheme == signScheme);
|
|
||||||
if (attribute is null)
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
string newValue = GenerateEncryptedValueByCertificate(attribute.Algorithm, certificate, oldValue);
|
|
||||||
return (true, newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
catch (WechatTenpayException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request. Please see the inner exception for more details.", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TRequest EncryptRequestSensitivePropertyByPublicKey<TRequest>(this WechatTenpayClient client, TRequest request)
|
private static async Task<TRequest> EncryptRequestSensitivePropertyUseCertificateManagerAsync<TRequest>(ICertificateManagerAsync manager, string signScheme, TRequest request, CancellationToken cancellationToken = default)
|
||||||
where TRequest : WechatTenpayRequest
|
where TRequest : WechatTenpayRequest
|
||||||
{
|
{
|
||||||
try
|
if (manager is null)
|
||||||
{
|
throw new NullReferenceException("The platform certificate manager is not configured.");
|
||||||
bool requireEncrypt = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute));
|
|
||||||
if (!requireEncrypt)
|
|
||||||
return request;
|
|
||||||
|
|
||||||
string signScheme = client.Credentials.SignScheme;
|
CertificateEntry? entry = await manager.GetEntryAsync(request.WechatpaySerialNumber!, cancellationToken).ConfigureAwait(false);
|
||||||
string algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密
|
if (!entry.HasValue)
|
||||||
SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? PublicKeyEntry.ALGORITHM_TYPE_RSA :
|
throw new WechatTenpayException($"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \"{request.WechatpaySerialNumber}\". Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
||||||
SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? PublicKeyEntry.ALGORITHM_TYPE_SM2 :
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request. Unsupported signing scheme: \"{signScheme}\".");
|
|
||||||
|
|
||||||
string publicKey;
|
return PopulateRequestEncryptedFieldsByCertificate(signScheme, entry.Value.Certificate, ref request);
|
||||||
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
|
||||||
{
|
|
||||||
// 如果未在请求中指定特定的平台公钥 ID,从管理器中取第一个
|
|
||||||
IEnumerable<PublicKeyEntry> entries = client.PlatformPublicKeyManager.AllEntries()
|
|
||||||
.Where(e => e.AlgorithmType == algorithmType);
|
|
||||||
if (!entries.Any())
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request, because the platform public key manager is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PublicKeyEntry entry = entries.First();
|
|
||||||
publicKey = entry.PublicKey;
|
|
||||||
request.WechatpaySerialNumber = entry.SerialNumber;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果已在请求中指定特定的平台公钥 ID,直接从管理器中取值
|
|
||||||
PublicKeyEntry? entry = client.PlatformPublicKeyManager.GetEntry(request.WechatpaySerialNumber!);
|
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request, because the platform public key manager does not contain a key matched the serial number \"{request.WechatpaySerialNumber}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey = entry.Value.PublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReflectionHelper.ReplaceObjectStringProperties(request, (_, currentProp, oldValue) =>
|
|
||||||
{
|
|
||||||
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
WechatTenpaySensitivePropertyAttribute? attribute = currentProp
|
|
||||||
.GetCustomAttributes<WechatTenpaySensitivePropertyAttribute>()
|
|
||||||
.FirstOrDefault(attr => attr.Scheme == signScheme);
|
|
||||||
if (attribute is null)
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
string newValue = GenerateEncryptedValueByPublicKey(attribute.Algorithm, publicKey, oldValue);
|
|
||||||
return (true, newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
catch (WechatTenpayException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request. Please see the inner exception for more details.", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TRequest> EncryptRequestSensitivePropertyByPublicKeyAsync<TRequest>(this WechatTenpayClient client, TRequest request, CancellationToken cancellationToken = default)
|
private static TRequest EncryptRequestSensitivePropertyUsePublicKeyManager<TRequest>(IPublicKeyManager manager, string signScheme, TRequest request)
|
||||||
where TRequest : WechatTenpayRequest
|
where TRequest : WechatTenpayRequest
|
||||||
{
|
{
|
||||||
if (client.PlatformPublicKeyManager is not IPublicKeyManagerAsync)
|
if (manager is null)
|
||||||
{
|
throw new NullReferenceException("The platform public key manager is not configured.");
|
||||||
// 降级为同步调用
|
|
||||||
return EncryptRequestSensitivePropertyByPublicKey(client, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
PublicKeyEntry? entry = manager.GetEntry(request.WechatpaySerialNumber!);
|
||||||
{
|
if (!entry.HasValue)
|
||||||
bool requireEncrypt = request.GetType().IsDefined(typeof(WechatTenpaySensitiveAttribute));
|
throw new WechatTenpayException($"Failed to encrypt request, because the platform public key manager does not contain a key matched the serial number \"{request.WechatpaySerialNumber}\".");
|
||||||
if (!requireEncrypt)
|
|
||||||
return request;
|
|
||||||
|
|
||||||
string signScheme = client.Credentials.SignScheme;
|
return PopulateRequestEncryptedFieldsByPublicKey(signScheme, entry.Value.PublicKey, ref request);
|
||||||
string algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密
|
|
||||||
SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? PublicKeyEntry.ALGORITHM_TYPE_RSA :
|
|
||||||
SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? PublicKeyEntry.ALGORITHM_TYPE_SM2 :
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request. Unsupported signing scheme: \"{signScheme}\".");
|
|
||||||
|
|
||||||
string publicKey;
|
|
||||||
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
|
||||||
{
|
|
||||||
if (client.PlatformPublicKeyManager is null)
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request, because the platform public key manager is not initialized.");
|
|
||||||
|
|
||||||
// 如果未在请求中指定特定的平台公钥 ID,从管理器中第一个
|
|
||||||
IEnumerable<PublicKeyEntry> entries = await ((IPublicKeyManagerAsync)client.PlatformPublicKeyManager).AllEntriesAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
entries = entries.Where(e => e.AlgorithmType == algorithmType);
|
|
||||||
if (!entries.Any())
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request, because the platform public key manager is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PublicKeyEntry entry = entries.First();
|
|
||||||
publicKey = entry.PublicKey;
|
|
||||||
request.WechatpaySerialNumber = entry.SerialNumber;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果已在请求中指定特定的平台公钥 ID,直接从管理器中取值
|
|
||||||
PublicKeyEntry? entry = await ((IPublicKeyManagerAsync)client.PlatformPublicKeyManager).GetEntryAsync(request.WechatpaySerialNumber!, cancellationToken).ConfigureAwait(false);
|
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException($"Failed to encrypt request, because the platform public key manager does not contain a key matched the serial number \"{request.WechatpaySerialNumber}\".");
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey = entry.Value.PublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReflectionHelper.ReplaceObjectStringProperties(request, (_, currentProp, oldValue) =>
|
|
||||||
{
|
|
||||||
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
WechatTenpaySensitivePropertyAttribute? attribute = currentProp
|
|
||||||
.GetCustomAttributes<WechatTenpaySensitivePropertyAttribute>()
|
|
||||||
.FirstOrDefault(attr => attr.Scheme == signScheme);
|
|
||||||
if (attribute is null)
|
|
||||||
return (false, oldValue);
|
|
||||||
|
|
||||||
string newValue = GenerateEncryptedValueByPublicKey(attribute.Algorithm, publicKey, oldValue);
|
|
||||||
return (true, newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
catch (WechatTenpayException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new WechatTenpayException("Failed to encrypt request. Please see the inner exception for more details.", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GenerateEncryptedValueByCertificate(string algorithm, string certificate, string value)
|
private static async Task<TRequest> EncryptRequestSensitivePropertyUsePublicKeyManagerAsync<TRequest>(IPublicKeyManagerAsync manager, string signScheme, TRequest request, CancellationToken cancellationToken = default)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
{
|
{
|
||||||
switch (algorithm)
|
if (manager is null)
|
||||||
|
throw new NullReferenceException("The platform public key manager is not configured.");
|
||||||
|
|
||||||
|
PublicKeyEntry? entry = await manager.GetEntryAsync(request.WechatpaySerialNumber!, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (!entry.HasValue)
|
||||||
|
throw new WechatTenpayException($"Failed to encrypt request, because the platform public key manager does not contain a key matched the serial number \"{request.WechatpaySerialNumber}\".");
|
||||||
|
|
||||||
|
return PopulateRequestEncryptedFieldsByPublicKey(signScheme, entry.Value.PublicKey, ref request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TRequest PopulateRequestEncryptedFieldsByCertificate<TRequest>(string scheme, string certificate, ref TRequest request)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
switch (scheme)
|
||||||
{
|
{
|
||||||
case EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1:
|
case SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256:
|
||||||
case EncryptionAlgorithms.RSA_2048_ECB_PKCS1:
|
|
||||||
{
|
{
|
||||||
string publicKey = RSAUtility.ExportPublicKeyFromCertificate(certificate);
|
string publicKey = RSAUtility.ExportPublicKeyFromCertificate(certificate);
|
||||||
return GenerateEncryptedValueByPublicKey(algorithm, publicKey, value);
|
return PopulateRequestEncryptedFieldsByPublicKey(scheme, publicKey, ref request);
|
||||||
}
|
}
|
||||||
|
|
||||||
case EncryptionAlgorithms.SM2_C1C3C2_ASN1:
|
case SignSchemes.WECHATPAY2_SM2_WITH_SM3:
|
||||||
{
|
{
|
||||||
string publicKey = SM2Utility.ExportPublicKeyFromCertificate(certificate);
|
string publicKey = SM2Utility.ExportPublicKeyFromCertificate(certificate);
|
||||||
return GenerateEncryptedValueByPublicKey(algorithm, publicKey, value);
|
return PopulateRequestEncryptedFieldsByPublicKey(scheme, publicKey, ref request);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new WechatTenpayException($"Failed to encrypt request. Unsupported encryption algorithm: \"{algorithm}\".");
|
throw new NotSupportedException($"Unsupported signing scheme: \"{scheme}\".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GenerateEncryptedValueByPublicKey(string algorithm, string publicKey, string value)
|
private static TRequest PopulateRequestEncryptedFieldsByPublicKey<TRequest>(string scheme, string publicKey, ref TRequest request)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
{
|
{
|
||||||
switch (algorithm)
|
ReflectionHelper.ReplaceObjectStringProperties(ref request, (_, currentProp, oldValue) =>
|
||||||
{
|
{
|
||||||
case EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1:
|
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
||||||
return RSAUtility.EncryptWithECB(
|
return (false, oldValue);
|
||||||
publicKeyPem: publicKey,
|
|
||||||
plainData: value,
|
|
||||||
paddingMode: RSAUtility.PADDING_MODE_OAEPWITHSHA1ANDMGF1
|
|
||||||
)!;
|
|
||||||
|
|
||||||
case EncryptionAlgorithms.RSA_2048_ECB_PKCS1:
|
WechatTenpaySensitivePropertyAttribute? attribute = currentProp
|
||||||
return RSAUtility.EncryptWithECB(
|
.GetCustomAttributes<WechatTenpaySensitivePropertyAttribute>()
|
||||||
publicKeyPem: publicKey,
|
.FirstOrDefault(attr => attr.Scheme == scheme);
|
||||||
plainData: value,
|
if (attribute is null)
|
||||||
paddingMode: RSAUtility.PADDING_MODE_PKCS1
|
return (false, oldValue);
|
||||||
)!;
|
|
||||||
|
|
||||||
case EncryptionAlgorithms.SM2_C1C3C2_ASN1:
|
string newValue;
|
||||||
return SM2Utility.Encrypt(
|
switch (attribute.Algorithm)
|
||||||
publicKeyPem: publicKey,
|
{
|
||||||
plainData: value,
|
case EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1:
|
||||||
asn1Encoding: true
|
newValue = RSAUtility.EncryptWithECB(
|
||||||
)!;
|
publicKeyPem: publicKey,
|
||||||
|
plainData: oldValue,
|
||||||
|
paddingMode: RSAUtility.PADDING_MODE_OAEPWITHSHA1ANDMGF1
|
||||||
|
)!;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
case EncryptionAlgorithms.RSA_2048_ECB_PKCS1:
|
||||||
throw new WechatTenpayException($"Failed to encrypt request. Unsupported encryption algorithm: \"{algorithm}\".");
|
newValue = RSAUtility.EncryptWithECB(
|
||||||
}
|
publicKeyPem: publicKey,
|
||||||
|
plainData: oldValue,
|
||||||
|
paddingMode: RSAUtility.PADDING_MODE_PKCS1
|
||||||
|
)!;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EncryptionAlgorithms.SM2_C1C3C2_ASN1:
|
||||||
|
newValue = SM2Utility.Encrypt(
|
||||||
|
publicKeyPem: publicKey,
|
||||||
|
plainData: oldValue,
|
||||||
|
asn1Encoding: true
|
||||||
|
)!;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Unsupported encryption algorithm: \"{attribute.Algorithm}\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true, newValue);
|
||||||
|
});
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
|
|
||||||
string signScheme = client.Credentials.SignScheme;
|
string signScheme = client.Credentials.SignScheme;
|
||||||
|
|
||||||
ReflectionHelper.ReplaceObjectStringProperties(response, (_, currentProp, oldValue) =>
|
ReflectionHelper.ReplaceObjectStringProperties(ref response, (_, currentProp, oldValue) =>
|
||||||
{
|
{
|
||||||
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
if (currentProp is null || !currentProp.IsDefined(typeof(WechatTenpaySensitivePropertyAttribute)))
|
||||||
return (false, oldValue);
|
return (false, oldValue);
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
|
||||||
return WechatTenpayClientSigningExtensions.VerifySignature(
|
return WechatTenpayClientSigningExtensions._VerifySignature(
|
||||||
client,
|
client,
|
||||||
strTimestamp: responseTimestamp,
|
strTimestamp: responseTimestamp,
|
||||||
strNonce: responseNonce,
|
strNonce: responseNonce,
|
||||||
@@ -192,7 +192,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
|
||||||
return WechatTenpayClientSigningExtensions.VerifySignatureAsync(
|
return WechatTenpayClientSigningExtensions._VerifySignatureAsync(
|
||||||
client,
|
client,
|
||||||
strTimestamp: responseTimestamp,
|
strTimestamp: responseTimestamp,
|
||||||
strNonce: responseNonce,
|
strNonce: responseNonce,
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||||
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants;
|
||||||
|
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
|
||||||
|
|
||||||
|
internal static class WechatTenpayClientRequestSerialNumberExtensions
|
||||||
|
{
|
||||||
|
public static TRequest _EnsureRequestWechatpaySerialNumberIsSet<TRequest>(this WechatTenpayClient client, TRequest request)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
if (request is null) throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (client.PlatformAuthScheme)
|
||||||
|
{
|
||||||
|
case PlatformAuthScheme.Certificate:
|
||||||
|
return EnsureRequestWechatpaySerialNumberIsSetUseCertificateManager<TRequest>(client.PlatformCertificateManager, client.Credentials.SignScheme, request);
|
||||||
|
|
||||||
|
case PlatformAuthScheme.PublicKey:
|
||||||
|
return EnsureRequestWechatpaySerialNumberIsSetUsePublicKeyManager<TRequest>(client.PlatformPublicKeyManager, client.Credentials.SignScheme, request);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WechatTenpayException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new WechatTenpayException("Failed to set the wechatpay serial number of request. Please see the inner exception for more details.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<TRequest> _EnsureRequestWechatpaySerialNumberIsSetAsync<TRequest>(this WechatTenpayClient client, TRequest request, CancellationToken cancellationToken = default)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
if (request is null) throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (client.PlatformAuthScheme)
|
||||||
|
{
|
||||||
|
case PlatformAuthScheme.Certificate:
|
||||||
|
{
|
||||||
|
if (client.PlatformCertificateManager is not ICertificateManagerAsync)
|
||||||
|
{
|
||||||
|
// 降级为同步调用
|
||||||
|
return EnsureRequestWechatpaySerialNumberIsSetUseCertificateManager(client.PlatformCertificateManager, client.Credentials.SignScheme, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await EnsureRequestWechatpaySerialNumberIsSetUseCertificateManagerAsync<TRequest>((ICertificateManagerAsync)client.PlatformCertificateManager, client.Credentials.SignScheme, request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PlatformAuthScheme.PublicKey:
|
||||||
|
{
|
||||||
|
if (client.PlatformPublicKeyManager is not IPublicKeyManagerAsync)
|
||||||
|
{
|
||||||
|
// 降级为同步调用
|
||||||
|
return EnsureRequestWechatpaySerialNumberIsSetUsePublicKeyManager(client.PlatformPublicKeyManager, client.Credentials.SignScheme, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await EnsureRequestWechatpaySerialNumberIsSetUsePublicKeyManagerAsync<TRequest>((IPublicKeyManagerAsync)client.PlatformPublicKeyManager, client.Credentials.SignScheme, request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WechatTenpayException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new WechatTenpayException("Failed to set the wechatpay serial number of request. Please see the inner exception for more details.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetAlgorithmType(string signScheme, out string algorithmType)
|
||||||
|
{
|
||||||
|
algorithmType = // 签名方式与加密算法保持一致:RSA_SHA256 签名需 RSA 加密,SM3 签名需 SM2 加密
|
||||||
|
SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_RSA :
|
||||||
|
SignSchemes.WECHATPAY2_SM2_WITH_SM3.Equals(signScheme) ? CertificateEntry.ALGORITHM_TYPE_SM2 :
|
||||||
|
default!;
|
||||||
|
return !string.IsNullOrEmpty(algorithmType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TRequest EnsureRequestWechatpaySerialNumberIsSetUseCertificateManager<TRequest>(ICertificateManager manager, string signScheme, TRequest request)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
||||||
|
{
|
||||||
|
if (!TryGetAlgorithmType(signScheme, out string algorithmType))
|
||||||
|
throw new NotSupportedException($"Unsupported signing scheme: \"{signScheme}\".");
|
||||||
|
|
||||||
|
IEnumerable<CertificateEntry> entries = manager.AllEntries();
|
||||||
|
entries = entries.Where(e => e.AlgorithmType == algorithmType).OrderByDescending(e => e.ExpireTime);
|
||||||
|
if (!entries.Any())
|
||||||
|
throw new Exception("The platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
||||||
|
|
||||||
|
CertificateEntry entry = entries.First();
|
||||||
|
request.WechatpaySerialNumber = entry.SerialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<TRequest> EnsureRequestWechatpaySerialNumberIsSetUseCertificateManagerAsync<TRequest>(ICertificateManagerAsync manager, string signScheme, TRequest request, CancellationToken cancellationToken = default)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
||||||
|
{
|
||||||
|
if (!TryGetAlgorithmType(signScheme, out string algorithmType))
|
||||||
|
throw new NotSupportedException($"Unsupported signing scheme: \"{signScheme}\".");
|
||||||
|
|
||||||
|
IEnumerable<CertificateEntry> entries = await manager.AllEntriesAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
entries = entries.Where(e => e.AlgorithmType == algorithmType).OrderByDescending(e => e.ExpireTime);
|
||||||
|
if (!entries.Any())
|
||||||
|
throw new Exception("The platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first.");
|
||||||
|
|
||||||
|
CertificateEntry entry = entries.First();
|
||||||
|
request.WechatpaySerialNumber = entry.SerialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TRequest EnsureRequestWechatpaySerialNumberIsSetUsePublicKeyManager<TRequest>(IPublicKeyManager manager, string signScheme, TRequest request)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
||||||
|
{
|
||||||
|
if (!TryGetAlgorithmType(signScheme, out string algorithmType))
|
||||||
|
throw new NotSupportedException($"Unsupported signing scheme: \"{signScheme}\".");
|
||||||
|
|
||||||
|
IEnumerable<PublicKeyEntry> entries = manager.AllEntries();
|
||||||
|
entries = entries.Where(e => e.AlgorithmType == algorithmType);
|
||||||
|
if (!entries.Any())
|
||||||
|
throw new Exception("The platform public key manager is empty. Perhaps you forget to add one?");
|
||||||
|
|
||||||
|
PublicKeyEntry entry = entries.First();
|
||||||
|
request.WechatpaySerialNumber = entry.SerialNumber;
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<TRequest> EnsureRequestWechatpaySerialNumberIsSetUsePublicKeyManagerAsync<TRequest>(IPublicKeyManagerAsync manager, string signScheme, TRequest request, CancellationToken cancellationToken = default)
|
||||||
|
where TRequest : WechatTenpayRequest
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(request.WechatpaySerialNumber))
|
||||||
|
{
|
||||||
|
if (!TryGetAlgorithmType(signScheme, out string algorithmType))
|
||||||
|
throw new NotSupportedException($"Unsupported signing scheme: \"{signScheme}\".");
|
||||||
|
|
||||||
|
IEnumerable<PublicKeyEntry> entries = await manager.AllEntriesAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
entries = entries.Where(e => e.AlgorithmType == algorithmType);
|
||||||
|
if (!entries.Any())
|
||||||
|
throw new Exception("The platform public key manager is empty. Perhaps you forget to add one?");
|
||||||
|
|
||||||
|
PublicKeyEntry entry = entries.First();
|
||||||
|
request.WechatpaySerialNumber = entry.SerialNumber;
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -10,75 +11,54 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
|
|
||||||
internal static class WechatTenpayClientSigningExtensions
|
internal static class WechatTenpayClientSigningExtensions
|
||||||
{
|
{
|
||||||
public static ErroredResult VerifySignature(this WechatTenpayClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber)
|
public static ErroredResult _VerifySignature(this WechatTenpayClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber)
|
||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
|
||||||
switch (client.PlatformAuthScheme)
|
PlatformAuthScheme authSchema;
|
||||||
|
if (!TryGetAuthScheme(client, strSerialNumber, out authSchema))
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new Exception("Could not detect the platform auth schema because the serial number is missing or invalid."));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (authSchema)
|
||||||
{
|
{
|
||||||
case PlatformAuthScheme.Certificate:
|
case PlatformAuthScheme.Certificate:
|
||||||
{
|
{
|
||||||
CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(strSerialNumber);
|
return VerifySignatureUseCertificateManager(client.PlatformCertificateManager, strTimestamp: strTimestamp, strNonce: strNonce, strContent: strContent, strSignature: strSignature, strSignScheme: strSignScheme, strSerialNumber: strSerialNumber);
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
return ErroredResult.Fail(new Exception($"The platform certificate manager does not contain a certificate matched the serial number \"{strSerialNumber}\"."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateSignatureResultByCertificate(
|
|
||||||
scheme: strSignScheme,
|
|
||||||
certificate: entry.Value.Certificate,
|
|
||||||
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
|
||||||
signature: strSignature
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case PlatformAuthScheme.PublicKey:
|
case PlatformAuthScheme.PublicKey:
|
||||||
{
|
{
|
||||||
PublicKeyEntry? entry = client.PlatformPublicKeyManager.GetEntry(strSerialNumber);
|
return VerifySignatureUsePublicKeyManager(client.PlatformPublicKeyManager, strTimestamp: strTimestamp, strNonce: strNonce, strContent: strContent, strSignature: strSignature, strSignScheme: strSignScheme, strSerialNumber: strSerialNumber);
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
return ErroredResult.Fail(new Exception($"The platform public key manager does not contain a key matched the serial number \"{strSerialNumber}\"."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateSignatureResultByPublicKey(
|
|
||||||
scheme: strSignScheme,
|
|
||||||
publicKey: entry.Value.PublicKey,
|
|
||||||
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
|
||||||
signature: strSignature
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ErroredResult.Fail(new Exception($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\"."));
|
return ErroredResult.Fail(new NotSupportedException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\"."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ErroredResult> VerifySignatureAsync(this WechatTenpayClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber, CancellationToken cancellationToken = default)
|
public static async Task<ErroredResult> _VerifySignatureAsync(this WechatTenpayClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (client is null) throw new ArgumentNullException(nameof(client));
|
if (client is null) throw new ArgumentNullException(nameof(client));
|
||||||
|
|
||||||
switch (client.PlatformAuthScheme)
|
PlatformAuthScheme authSchema;
|
||||||
|
if (!TryGetAuthScheme(client, strSerialNumber, out authSchema))
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new Exception("Could not detect the platform auth schema because the serial number is missing or invalid."));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (authSchema)
|
||||||
{
|
{
|
||||||
case PlatformAuthScheme.Certificate:
|
case PlatformAuthScheme.Certificate:
|
||||||
{
|
{
|
||||||
if (client.PlatformCertificateManager is not ICertificateManagerAsync)
|
if (client.PlatformCertificateManager is not ICertificateManagerAsync)
|
||||||
{
|
{
|
||||||
// 降级为同步调用
|
// 降级为同步调用
|
||||||
return VerifySignature(client, strTimestamp, strNonce, strContent, strSignature, strSignScheme, strSerialNumber);
|
return _VerifySignature(client, strTimestamp, strNonce, strContent, strSignature, strSignScheme, strSerialNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
CertificateEntry? entry = await ((ICertificateManagerAsync)client.PlatformCertificateManager).GetEntryAsync(strSerialNumber, cancellationToken).ConfigureAwait(false);
|
return await VerifySignatureUseCertificateManagerAsync((ICertificateManagerAsync)client.PlatformCertificateManager, strTimestamp: strTimestamp, strNonce: strNonce, strContent: strContent, strSignature: strSignature, strSignScheme: strSignScheme, strSerialNumber: strSerialNumber, cancellationToken).ConfigureAwait(false);
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
return ErroredResult.Fail(new Exception($"The platform certificate manager does not contain a certificate matched the serial number \"{strSerialNumber}\"."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateSignatureResultByCertificate(
|
|
||||||
scheme: strSignScheme,
|
|
||||||
certificate: entry.Value.Certificate,
|
|
||||||
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
|
||||||
signature: strSignature
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case PlatformAuthScheme.PublicKey:
|
case PlatformAuthScheme.PublicKey:
|
||||||
@@ -86,34 +66,130 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
if (client.PlatformCertificateManager is not IPublicKeyManagerAsync)
|
if (client.PlatformCertificateManager is not IPublicKeyManagerAsync)
|
||||||
{
|
{
|
||||||
// 降级为同步调用
|
// 降级为同步调用
|
||||||
return VerifySignature(client, strTimestamp, strNonce, strContent, strSignature, strSignScheme, strSerialNumber);
|
return _VerifySignature(client, strTimestamp, strNonce, strContent, strSignature, strSignScheme, strSerialNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
PublicKeyEntry? entry = await ((IPublicKeyManagerAsync)client.PlatformPublicKeyManager).GetEntryAsync(strSerialNumber, cancellationToken).ConfigureAwait(false);
|
return await VerifySignatureUseCertificateManagerAsync((IPublicKeyManagerAsync)client.PlatformPublicKeyManager, strTimestamp: strTimestamp, strNonce: strNonce, strContent: strContent, strSignature: strSignature, strSignScheme: strSignScheme, strSerialNumber: strSerialNumber, cancellationToken).ConfigureAwait(false);
|
||||||
if (!entry.HasValue)
|
|
||||||
{
|
|
||||||
return ErroredResult.Fail(new Exception($"The platform public key manager does not contain a key matched the serial number \"{strSerialNumber}\"."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateSignatureResultByPublicKey(
|
|
||||||
scheme: strSignScheme,
|
|
||||||
publicKey: entry.Value.PublicKey,
|
|
||||||
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
|
||||||
signature: strSignature
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ErroredResult.Fail(new Exception($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\"."));
|
return ErroredResult.Fail(new NotSupportedException($"Unsupported platform auth scheme: \"{client.PlatformAuthScheme}\"."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ErroredResult VerifySignatureUseCertificateManager(ICertificateManager manager, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber)
|
||||||
|
{
|
||||||
|
if (manager is null)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new NullReferenceException("The platform certificate manager is not configured."));
|
||||||
|
}
|
||||||
|
|
||||||
|
CertificateEntry? entry = manager.GetEntry(strSerialNumber);
|
||||||
|
if (!entry.HasValue)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new Exception($"The platform certificate manager does not contain a certificate matched the serial number \"{strSerialNumber}\"."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateVerifyResultByCertificate(
|
||||||
|
scheme: strSignScheme,
|
||||||
|
certificate: entry.Value.Certificate,
|
||||||
|
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
||||||
|
signature: strSignature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ErroredResult> VerifySignatureUseCertificateManagerAsync(ICertificateManagerAsync manager, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (manager is null)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new NullReferenceException("The platform certificate manager is not configured."));
|
||||||
|
}
|
||||||
|
|
||||||
|
CertificateEntry? entry = await manager.GetEntryAsync(strSerialNumber, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (!entry.HasValue)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new Exception($"The platform certificate manager does not contain a certificate matched the serial number \"{strSerialNumber}\"."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateVerifyResultByCertificate(
|
||||||
|
scheme: strSignScheme,
|
||||||
|
certificate: entry.Value.Certificate,
|
||||||
|
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
||||||
|
signature: strSignature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ErroredResult VerifySignatureUsePublicKeyManager(IPublicKeyManager manager, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber)
|
||||||
|
{
|
||||||
|
if (manager is null)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new NullReferenceException("The platform public key manager is not configured."));
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicKeyEntry? entry = manager.GetEntry(strSerialNumber);
|
||||||
|
if (!entry.HasValue)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new Exception($"The platform public key manager does not contain a key matched the serial number \"{strSerialNumber}\"."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateVerifyResultByPublicKey(
|
||||||
|
scheme: strSignScheme,
|
||||||
|
publicKey: entry.Value.PublicKey,
|
||||||
|
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
||||||
|
signature: strSignature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ErroredResult> VerifySignatureUseCertificateManagerAsync(IPublicKeyManagerAsync manager, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (manager is null)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new NullReferenceException("The platform public key manager is not configured."));
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicKeyEntry? entry = await manager.GetEntryAsync(strSerialNumber, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (!entry.HasValue)
|
||||||
|
{
|
||||||
|
return ErroredResult.Fail(new Exception($"The platform public key manager does not contain a key matched the serial number \"{strSerialNumber}\"."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateVerifyResultByPublicKey(
|
||||||
|
scheme: strSignScheme,
|
||||||
|
publicKey: entry.Value.PublicKey,
|
||||||
|
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
|
||||||
|
signature: strSignature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetAuthScheme(WechatTenpayClient client, string strSerialNumber, out PlatformAuthScheme authScheme)
|
||||||
|
{
|
||||||
|
authScheme = client.PlatformAuthScheme;
|
||||||
|
if (client.PlatformAuthFallbackSwitch)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(strSerialNumber))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Regex.IsMatch(strSerialNumber, "^PUB_KEY_ID_", RegexOptions.IgnoreCase))
|
||||||
|
{
|
||||||
|
authScheme = PlatformAuthScheme.PublicKey;
|
||||||
|
}
|
||||||
|
else if (Regex.IsMatch(strSerialNumber, "^[A-Za-z0-9]+$", RegexOptions.IgnoreCase))
|
||||||
|
{
|
||||||
|
authScheme = PlatformAuthScheme.Certificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static string GenerateMessageForSignature(string timestamp, string nonce, string body)
|
private static string GenerateMessageForSignature(string timestamp, string nonce, string body)
|
||||||
{
|
{
|
||||||
return $"{timestamp}\n{nonce}\n{body}\n";
|
return $"{timestamp}\n{nonce}\n{body}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ErroredResult GenerateSignatureResultByCertificate(string scheme, string certificate, string message, string signature)
|
private static ErroredResult GenerateVerifyResultByCertificate(string scheme, string certificate, string message, string signature)
|
||||||
{
|
{
|
||||||
string publicKey = string.Empty;
|
string publicKey = string.Empty;
|
||||||
|
|
||||||
@@ -146,10 +222,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GenerateSignatureResultByPublicKey(scheme, publicKey, message, signature);
|
return GenerateVerifyResultByPublicKey(scheme, publicKey, message, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ErroredResult GenerateSignatureResultByPublicKey(string scheme, string publicKey, string message, string signature)
|
private static ErroredResult GenerateVerifyResultByPublicKey(string scheme, string publicKey, string message, string signature)
|
||||||
{
|
{
|
||||||
ErroredResult result;
|
ErroredResult result;
|
||||||
|
|
||||||
@@ -199,7 +275,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
result = ErroredResult.Fail(new Exception($"Unsupported signing scheme: \"{scheme}\"."));
|
result = ErroredResult.Fail(new NotSupportedException($"Unsupported signing scheme: \"{scheme}\"."));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
|||||||
{
|
{
|
||||||
internal static partial class ReflectionHelper
|
internal static partial class ReflectionHelper
|
||||||
{
|
{
|
||||||
public static void ReplaceObjectStringProperties(object targetObj, ReplaceObjectStringPropertiesReplacementDelegate replacement)
|
public static void ReplaceObjectStringProperties<T>(ref T targetObj, ReplaceObjectStringPropertiesReplacementDelegate replacement)
|
||||||
{
|
{
|
||||||
if (targetObj is null) throw new ArgumentNullException(nameof(targetObj));
|
if (targetObj is null) throw new ArgumentNullException(nameof(targetObj));
|
||||||
if (replacement is null) throw new ArgumentNullException(nameof(replacement));
|
if (replacement is null) throw new ArgumentNullException(nameof(replacement));
|
||||||
@@ -24,7 +24,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
|||||||
{
|
{
|
||||||
public delegate (bool IsModified, string NewValue) ReplaceObjectStringPropertiesReplacementDelegate(object currentObj, PropertyInfo? currentProp, string oldValue);
|
public delegate (bool IsModified, string NewValue) ReplaceObjectStringPropertiesReplacementDelegate(object currentObj, PropertyInfo? currentProp, string oldValue);
|
||||||
|
|
||||||
private static void InnerReplaceObjectStringProperties(ref object currentObj, PropertyInfo? currentProp, ReplaceObjectStringPropertiesReplacementDelegate replacement, ISet<object> visited)
|
private static void InnerReplaceObjectStringProperties<T>(ref T currentObj, PropertyInfo? currentProp, ReplaceObjectStringPropertiesReplacementDelegate replacement, ISet<object> visited)
|
||||||
{
|
{
|
||||||
if (currentObj is null) throw new ArgumentNullException(nameof(currentObj));
|
if (currentObj is null) throw new ArgumentNullException(nameof(currentObj));
|
||||||
if (replacement is null) throw new ArgumentNullException(nameof(replacement));
|
if (replacement is null) throw new ArgumentNullException(nameof(replacement));
|
||||||
@@ -53,7 +53,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
|||||||
// 处理数组类型
|
// 处理数组类型
|
||||||
if (type.IsArray)
|
if (type.IsArray)
|
||||||
{
|
{
|
||||||
Array currentObjAsArray = (Array)currentObj;
|
Array currentObjAsArray = (currentObj as Array)!;
|
||||||
for (int i = 0; i < currentObjAsArray.Length; i++)
|
for (int i = 0; i < currentObjAsArray.Length; i++)
|
||||||
{
|
{
|
||||||
object? element = currentObjAsArray.GetValue(i);
|
object? element = currentObjAsArray.GetValue(i);
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
public Settings.Credentials Credentials { get; }
|
public Settings.Credentials Credentials { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前客户端使用的微信支付平台认证方案。
|
/// 获取当前客户端是否开启微信支付平台 API 认证方案回退开关。
|
||||||
|
/// </summary>
|
||||||
|
public bool PlatformAuthFallbackSwitch { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前客户端使用的微信支付平台 API 认证方案。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Settings.PlatformAuthScheme PlatformAuthScheme { get; }
|
public Settings.PlatformAuthScheme PlatformAuthScheme { get; }
|
||||||
|
|
||||||
@@ -65,6 +70,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
if (options is null) throw new ArgumentNullException(nameof(options));
|
if (options is null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
Credentials = new Settings.Credentials(options);
|
Credentials = new Settings.Credentials(options);
|
||||||
|
PlatformAuthFallbackSwitch = options.PlatformAuthFallbackSwitch;
|
||||||
PlatformAuthScheme = options.PlatformAuthScheme;
|
PlatformAuthScheme = options.PlatformAuthScheme;
|
||||||
PlatformCertificateManager = options.PlatformCertificateManager;
|
PlatformCertificateManager = options.PlatformCertificateManager;
|
||||||
PlatformPublicKeyManager = options.PlatformPublicKeyManager;
|
PlatformPublicKeyManager = options.PlatformPublicKeyManager;
|
||||||
@@ -96,6 +102,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
{
|
{
|
||||||
IFlurlRequest flurlRequest = base.CreateFlurlRequest(request, httpMethod, urlSegments);
|
IFlurlRequest flurlRequest = base.CreateFlurlRequest(request, httpMethod, urlSegments);
|
||||||
|
|
||||||
|
if (PlatformAuthFallbackSwitch)
|
||||||
|
{
|
||||||
|
this._EnsureRequestWechatpaySerialNumberIsSet(request);
|
||||||
|
}
|
||||||
|
|
||||||
if (AutoEncryptRequestSensitiveProperty)
|
if (AutoEncryptRequestSensitiveProperty)
|
||||||
{
|
{
|
||||||
this.EncryptRequestSensitiveProperty(request);
|
this.EncryptRequestSensitiveProperty(request);
|
||||||
|
|||||||
@@ -74,7 +74,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
public bool AutoDecryptResponseSensitiveProperty { get; set; }
|
public bool AutoDecryptResponseSensitiveProperty { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置微信支付平台认证方案。
|
/// 获取或设置微信支付平台 API 认证方案回退开关。<br/>
|
||||||
|
/// 开启后会在验证签名时根据响应或回调中接收到的序列号自动推测使用哪种认证方案。<br/>
|
||||||
|
/// 开启后会有一定的性能损耗,建议仅在灰度切换期间时开启,待切换进度为百分之百后请关闭。
|
||||||
|
/// <para>
|
||||||
|
/// 默认值:false
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public bool PlatformAuthFallbackSwitch { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置微信支付平台 API 认证方案。
|
||||||
/// <para>默认值:<see cref="Settings.PlatformAuthScheme.Certificate"/></para>
|
/// <para>默认值:<see cref="Settings.PlatformAuthScheme.Certificate"/></para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Settings.PlatformAuthScheme PlatformAuthScheme { get; set; } = Settings.PlatformAuthScheme.Certificate;
|
public Settings.PlatformAuthScheme PlatformAuthScheme { get; set; } = Settings.PlatformAuthScheme.Certificate;
|
||||||
@@ -82,7 +92,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置微信支付平台证书管理器。
|
/// 获取或设置微信支付平台证书管理器。
|
||||||
/// <para>
|
/// <para>
|
||||||
/// 仅当 <see cref="PlatformAuthScheme"/> 的值为 <see cref="Settings.PlatformAuthScheme.Certificate"/> 时有效。
|
/// 仅当 <see cref="PlatformAuthScheme"/> 的值为 <see cref="Settings.PlatformAuthScheme.Certificate"/>,或开启 <see cref="PlatformAuthSchemaFallback"/> 开关时有效。
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// 默认值:<see cref="Settings.InMemoryCertificateManager"/>
|
/// 默认值:<see cref="Settings.InMemoryCertificateManager"/>
|
||||||
@@ -93,7 +103,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置微信支付平台公钥管理器。
|
/// 获取或设置微信支付平台公钥管理器。
|
||||||
/// <para>
|
/// <para>
|
||||||
/// 仅当 <see cref="PlatformAuthScheme"/> 的值为 <see cref="Settings.PlatformAuthScheme.PublicKey"/> 时有效。
|
/// 仅当 <see cref="PlatformAuthScheme"/> 的值为 <see cref="Settings.PlatformAuthScheme.PublicKey"/>,或开启 <see cref="PlatformAuthSchemaFallback"/> 开关时有效。
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// 默认值:<see cref="Settings.InMemoryPublicKeyManager"/>
|
/// 默认值:<see cref="Settings.InMemoryPublicKeyManager"/>
|
||||||
|
|||||||
@@ -2561,7 +2561,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
|||||||
var manager = new Settings.InMemoryCertificateManager();
|
var manager = new Settings.InMemoryCertificateManager();
|
||||||
manager.AddEntry(new Settings.CertificateEntry(
|
manager.AddEntry(new Settings.CertificateEntry(
|
||||||
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_RSA,
|
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_RSA,
|
||||||
serialNumber: "OBSOLETED1",
|
serialNumber: "MOCK1",
|
||||||
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
||||||
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
||||||
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
||||||
@@ -2575,7 +2575,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
|||||||
));
|
));
|
||||||
manager.AddEntry(new Settings.CertificateEntry(
|
manager.AddEntry(new Settings.CertificateEntry(
|
||||||
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_RSA,
|
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_RSA,
|
||||||
serialNumber: "OBSOLETED2",
|
serialNumber: "MOCK2",
|
||||||
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
||||||
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
||||||
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
||||||
@@ -2598,7 +2598,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
|||||||
var manager = new Settings.InMemoryCertificateManager();
|
var manager = new Settings.InMemoryCertificateManager();
|
||||||
manager.AddEntry(new Settings.CertificateEntry(
|
manager.AddEntry(new Settings.CertificateEntry(
|
||||||
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_SM2,
|
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_SM2,
|
||||||
serialNumber: "OBSOLETED1",
|
serialNumber: "MOCK1",
|
||||||
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
||||||
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
||||||
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
||||||
@@ -2612,7 +2612,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
|||||||
));
|
));
|
||||||
manager.AddEntry(new Settings.CertificateEntry(
|
manager.AddEntry(new Settings.CertificateEntry(
|
||||||
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_SM2,
|
algorithmType: Settings.CertificateEntry.ALGORITHM_TYPE_SM2,
|
||||||
serialNumber: "OBSOLETED2",
|
serialNumber: "MOCK2",
|
||||||
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
certificate: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
|
||||||
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
effectiveTime: DateTimeOffset.Now.AddSeconds(-1),
|
||||||
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
expireTime: DateTimeOffset.Now.AddSeconds(-1)
|
||||||
|
|||||||
Reference in New Issue
Block a user