feat(tenpaybusiness): 升级公共组件

This commit is contained in:
Fu Diwei 2024-01-29 23:12:15 +08:00 committed by RHQYZ
parent fcc5e91510
commit 43eeb6fe6e
52 changed files with 448 additions and 628 deletions

View File

@ -66,9 +66,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Events
/// 获取或设置支付成功时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("pay_succ_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("pay_succ_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? SucceedTime { get; set; }
}
}

View File

@ -71,18 +71,18 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Events
/// 获取或设置退款成功时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("succeeded_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("succeeded_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? SucceedTime { get; set; }
/// <summary>
/// 获取或设置退款创建时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("created_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("created_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset CreateTime { get; set; }
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Exceptions
{
public class WechatTenpayBusinessEventVerificationException : WechatTenpayBusinessException
{
/// <inheritdoc/>
internal WechatTenpayBusinessEventVerificationException()
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessEventVerificationException(string message)
: base(message)
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessEventVerificationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Exceptions
{
public class WechatTenpayBusinessRequestEncryptionException : WechatTenpayBusinessException
{
/// <inheritdoc/>
internal WechatTenpayBusinessRequestEncryptionException()
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessRequestEncryptionException(string message)
: base(message)
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessRequestEncryptionException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Exceptions
{
public class WechatTenpayBusinessRequestSignatureException : WechatTenpayBusinessException
{
/// <inheritdoc/>
internal WechatTenpayBusinessRequestSignatureException()
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessRequestSignatureException(string message)
: base(message)
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessRequestSignatureException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Exceptions
{
public class WechatTenpayBusinessRequestTimeoutException : WechatTenpayBusinessException
{
/// <inheritdoc/>
internal WechatTenpayBusinessRequestTimeoutException()
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessRequestTimeoutException(string message)
: base(message)
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessRequestTimeoutException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Exceptions
{
public class WechatTenpayBusinessResponseDecryptionException : WechatTenpayBusinessException
{
/// <inheritdoc/>
internal WechatTenpayBusinessResponseDecryptionException()
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessResponseDecryptionException(string message)
: base(message)
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessResponseDecryptionException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Exceptions
{
public class WechatTenpayBusinessResponseVerificationException : WechatTenpayBusinessException
{
/// <inheritdoc/>
internal WechatTenpayBusinessResponseVerificationException()
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessResponseVerificationException(string message)
: base(message)
{
}
/// <inheritdoc/>
internal WechatTenpayBusinessResponseVerificationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
@ -8,11 +8,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <para>反序列化得到 <see cref="WechatTenpayBusinessEvent"/> 对象。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackJson"></param>
/// <param name="webhookJson"></param>
/// <returns></returns>
public static WechatTenpayBusinessEvent DeserializeEvent(this WechatTenpayBusinessClient client, string callbackJson)
public static WechatTenpayBusinessEvent DeserializeEvent(this WechatTenpayBusinessClient client, string webhookJson)
{
return DeserializeEvent<WechatTenpayBusinessEvent>(client, callbackJson);
return DeserializeEvent<WechatTenpayBusinessEvent>(client, webhookJson);
}
/// <summary>
@ -20,15 +20,15 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// </summary>
/// <typeparam name="TEvent"></typeparam>
/// <param name="client"></param>
/// <param name="callbackJson"></param>
/// <param name="webhookJson"></param>
/// <returns></returns>
public static TEvent DeserializeEvent<TEvent>(this WechatTenpayBusinessClient client, string callbackJson)
public static TEvent DeserializeEvent<TEvent>(this WechatTenpayBusinessClient client, string webhookJson)
where TEvent : WechatTenpayBusinessEvent
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (string.IsNullOrEmpty(callbackJson)) throw new ArgumentNullException(callbackJson);
if (client is null) throw new ArgumentNullException(nameof(client));
if (webhookJson is null) throw new ArgumentNullException(webhookJson);
return client.JsonSerializer.Deserialize<TEvent>(callbackJson);
return client.JsonSerializer.Deserialize<TEvent>(webhookJson);
}
}
}

View File

@ -8,39 +8,37 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackAuthorization"></param>
/// <param name="callbackBody"></param>
/// <param name="webhookAuthorization"></param>
/// <param name="webhookBody"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string callbackAuthorization, string callbackBody)
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookAuthorization, string webhookBody)
{
return VerifyEventSignature(client, callbackAuthorization, callbackBody, out _);
return VerifyEventSignature(client, webhookAuthorization, webhookBody, out _);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackAuthorization"></param>
/// <param name="callbackBody"></param>
/// <param name="webhookAuthorization"></param>
/// <param name="webhookBody"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string callbackAuthorization, string callbackBody, out Exception? error)
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookAuthorization, string webhookBody, out Exception? error)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (callbackAuthorization == null) throw new ArgumentNullException(nameof(callbackAuthorization));
if (callbackBody == null) throw new ArgumentNullException(nameof(callbackBody));
if (client is null) throw new ArgumentNullException(nameof(client));
if (webhookAuthorization is null) throw new ArgumentNullException(nameof(webhookAuthorization));
if (webhookBody is null) throw new ArgumentNullException(nameof(webhookBody));
bool ret = WechatTenpayBusinessClientSignExtensions.VerifySignature(
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strAuthorization: callbackAuthorization,
strContent: callbackBody,
strAuthorization: webhookAuthorization,
strContent: webhookBody,
out error
);
if (error != null)
{
error = new Exceptions.WechatTenpayBusinessEventVerificationException("Verify signature of event failed. Please see the inner exception for more details.", error);
}
if (!ret)
error ??= new Exception($"Failed to verify webhook event. Maybe the raw signature is invalid.");
return ret;
}
@ -49,22 +47,22 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackTimestamp"></param>
/// <param name="callbackNonce">。</param>
/// <param name="callbackBody"></param>
/// <param name="callbackSignature"></param>
/// <param name="callbackSerialNumber"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSerialNumber"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string callbackTimestamp, string callbackNonce, string callbackBody, string callbackSignature, string callbackSerialNumber)
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber)
{
return VerifyEventSignature(
client,
callbackTimestamp: callbackTimestamp,
callbackNonce: callbackNonce,
callbackBody: callbackBody,
callbackSignature: callbackSignature,
callbackSignatureAlgorithm: Constants.SignAlgorithms.SHA245_WITH_RSA,
callbackSerialNumber: callbackSerialNumber,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureAlgorithm: Constants.SignAlgorithms.SHA245_WITH_RSA,
webhookSerialNumber: webhookSerialNumber,
out _
);
}
@ -73,23 +71,23 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackTimestamp"></param>
/// <param name="callbackNonce">。</param>
/// <param name="callbackBody"></param>
/// <param name="callbackSignature"></param>
/// <param name="callbackSignatureAlgorithm"></param>
/// <param name="callbackSerialNumber"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSignatureAlgorithm"></param>
/// <param name="webhookSerialNumber"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string callbackTimestamp, string callbackNonce, string callbackBody, string callbackSignature, string callbackSignatureAlgorithm, string callbackSerialNumber)
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureAlgorithm, string webhookSerialNumber)
{
return VerifyEventSignature(
client,
callbackTimestamp: callbackTimestamp,
callbackNonce: callbackNonce,
callbackBody: callbackBody,
callbackSignature: callbackSignature,
callbackSignatureAlgorithm: callbackSignatureAlgorithm,
callbackSerialNumber: callbackSerialNumber,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureAlgorithm: webhookSignatureAlgorithm,
webhookSerialNumber: webhookSerialNumber,
out _
);
}
@ -98,24 +96,24 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackTimestamp"></param>
/// <param name="callbackNonce">。</param>
/// <param name="callbackBody"></param>
/// <param name="callbackSignature"></param>
/// <param name="callbackSerialNumber"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string callbackTimestamp, string callbackNonce, string callbackBody, string callbackSignature, string callbackSerialNumber, out Exception? error)
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber, out Exception? error)
{
return VerifyEventSignature(
client,
callbackTimestamp: callbackTimestamp,
callbackNonce: callbackNonce,
callbackBody: callbackBody,
callbackSignature: callbackSignature,
callbackSignatureAlgorithm: Constants.SignAlgorithms.SHA245_WITH_RSA,
callbackSerialNumber: callbackSerialNumber,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureAlgorithm: Constants.SignAlgorithms.SHA245_WITH_RSA,
webhookSerialNumber: webhookSerialNumber,
out error
);
}
@ -124,34 +122,32 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="callbackTimestamp"></param>
/// <param name="callbackNonce">。</param>
/// <param name="callbackBody"></param>
/// <param name="callbackSignature"></param>
/// <param name="callbackSignatureAlgorithm"></param>
/// <param name="callbackSerialNumber"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSignatureAlgorithm"></param>
/// <param name="webhookSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string callbackTimestamp, string callbackNonce, string callbackBody, string callbackSignature, string callbackSignatureAlgorithm, string callbackSerialNumber, out Exception? error)
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureAlgorithm, string webhookSerialNumber, out Exception? error)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayBusinessClientSignExtensions.VerifySignature(
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strTimestamp: callbackTimestamp,
strNonce: callbackNonce,
strContent: callbackBody,
strSignature: callbackSignature,
strSignatureAlgorithm: callbackSignatureAlgorithm,
strSerialNumber: callbackSerialNumber,
strTimestamp: webhookTimestamp,
strNonce: webhookNonce,
strContent: webhookBody,
strSignature: webhookSignature,
strSignatureAlgorithm: webhookSignatureAlgorithm,
strSerialNumber: webhookSerialNumber,
out error
);
if (error != null)
{
error = new Exceptions.WechatTenpayBusinessEventVerificationException("Verify signature of event failed. Please see the inner exception for more details.", error);
}
if (!ret)
error ??= new Exception($"Failed to verify webhook event. Maybe the raw signature \"{webhookSignature}\" is invalid.");
return ret;
}

View File

@ -22,9 +22,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "bill-downloads");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "bill-downloads");
return await client.SendRequestWithJsonAsync<Models.GetBillResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetBillResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -41,9 +41,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "bill-downloads", "trans");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "bill-downloads", "trans");
return await client.SendRequestWithJsonAsync<Models.GetBillTransactionResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetBillTransactionResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -62,10 +62,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, request.DownloadUrl)
.CreateFlurlRequest(request, HttpMethod.Get, request.DownloadUrl)
.WithUrl(request.DownloadUrl);
return await client.SendRequestWithJsonAsync<Models.DownloadBillFileResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.DownloadBillFileResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@ -21,20 +21,20 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.FileName == null)
if (request.FileName is null)
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
if (request.FileHash == null)
if (request.FileHash is null)
request.FileHash = BitConverter.ToString(Utilities.SM3Utility.Hash(request.FileBytes)).Replace("-", string.Empty).ToLower();
if (request.FileContentType == null)
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "file-uploads");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "file-uploads");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendRequestAsync<Models.UploadFileResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsync<Models.UploadFileResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken);
}
}
}

View File

@ -23,9 +23,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "payments", "app-pay");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "payments", "app-pay");
return await client.SendRequestWithJsonAsync<Models.CreatePaymentAppPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePaymentAppPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -42,9 +42,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "payments", "app-registering-pay");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "payments", "app-registering-pay");
return await client.SendRequestWithJsonAsync<Models.CreatePaymentAppRegisteringPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePaymentAppRegisteringPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -61,9 +61,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "payments", "qrcode-pay");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "payments", "qrcode-pay");
return await client.SendRequestWithJsonAsync<Models.CreatePaymentQrcodePayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePaymentQrcodePayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -80,9 +80,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "payments", "h5-pay");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "payments", "h5-pay");
return await client.SendRequestWithJsonAsync<Models.CreatePaymentH5PayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePaymentH5PayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -99,9 +99,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "payments", "mp-pay");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "payments", "mp-pay");
return await client.SendRequestWithJsonAsync<Models.CreatePaymentMiniProgramPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePaymentMiniProgramPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -121,9 +121,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "payments", request.PaymentId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "payments", request.PaymentId);
return await client.SendRequestWithJsonAsync<Models.GetPaymentByPaymentIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetPaymentByPaymentIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -143,9 +143,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "payments", "out-payment-id", request.OutPaymentId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "payments", "out-payment-id", request.OutPaymentId);
return await client.SendRequestWithJsonAsync<Models.GetPaymentByOutPaymentIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetPaymentByOutPaymentIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -165,9 +165,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "payments", request.PaymentId, "close");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "payments", request.PaymentId, "close");
return await client.SendRequestWithJsonAsync<Models.ClosePaymentResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.ClosePaymentResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -187,9 +187,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "redirects");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "redirects");
return await client.SendRequestWithJsonAsync<Models.CreatePaymentRedirectLinkResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePaymentRedirectLinkResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@ -22,9 +22,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "product-applications");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "product-applications");
return await client.SendRequestWithJsonAsync<Models.CreateProductApplicationResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreateProductApplicationResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -41,9 +41,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "product-applications", request.RequestNumber);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "product-applications", request.RequestNumber);
return await client.SendRequestWithJsonAsync<Models.GetProductApplicationByRequestNumberResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProductApplicationByRequestNumberResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -60,9 +60,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "product-applications", "out-request-no", request.OutRequestNumber);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "product-applications", "out-request-no", request.OutRequestNumber);
return await client.SendRequestWithJsonAsync<Models.GetProductApplicationByOutRequestNumberResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProductApplicationByOutRequestNumberResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -79,9 +79,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "product-applications", request.RequestNumber, "links");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "product-applications", request.RequestNumber, "links");
return await client.SendRequestWithJsonAsync<Models.CreateProductApplicationLinkResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreateProductApplicationLinkResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@ -21,13 +21,13 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.EnterpriseId == null)
if (request.EnterpriseId is null)
request.EnterpriseId = client.Credentials.EnterpriseId;
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "profit-allocations");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "profit-allocations");
return await client.SendRequestWithJsonAsync<Models.CreateProfitAllocationResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreateProfitAllocationResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -43,13 +43,13 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.EnterpriseId == null)
if (request.EnterpriseId is null)
request.EnterpriseId = client.Credentials.EnterpriseId;
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "profit-allocations", "finish");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "profit-allocations", "finish");
return await client.SendRequestWithJsonAsync<Models.SetProfitAllocationFinishedResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.SetProfitAllocationFinishedResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -66,9 +66,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", request.AllocationId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", request.AllocationId);
return await client.SendRequestWithJsonAsync<Models.GetProfitAllocationByAllocationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProfitAllocationByAllocationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -85,9 +85,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "out-allocation-id", request.OutAllocationId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "out-allocation-id", request.OutAllocationId);
return await client.SendRequestWithJsonAsync<Models.GetProfitAllocationByOutAllocationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProfitAllocationByOutAllocationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -104,9 +104,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", request.PaymentId, "amounts");
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", request.PaymentId, "amounts");
return await client.SendRequestWithJsonAsync<Models.GetProfitAllocationAmountByPaymentIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProfitAllocationAmountByPaymentIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
#region ReceiverAccount
@ -123,18 +123,18 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.EnterpriseId == null)
if (request.EnterpriseId is null)
request.EnterpriseId = client.Credentials.EnterpriseId;
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "receiver-accounts")
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "receiver-accounts")
.SetQueryParam("ent_id", request.EnterpriseId);
if (request.UnifiedSocialCreditCode != null)
if (request.UnifiedSocialCreditCode is not null)
flurlReq.SetQueryParam("unified_social_credit_code", request.UnifiedSocialCreditCode);
return await client.SendRequestWithJsonAsync<Models.QueryProfitAllocationReceiverAccountsResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.QueryProfitAllocationReceiverAccountsResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -150,13 +150,13 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.EnterpriseId == null)
if (request.EnterpriseId is null)
request.EnterpriseId = client.Credentials.EnterpriseId;
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "profit-allocations", "receiver-accounts-applications");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "profit-allocations", "receiver-accounts-applications");
return await client.SendRequestWithJsonAsync<Models.CreateProfitAllocationReceiverAccountApplicationResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreateProfitAllocationReceiverAccountApplicationResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -173,9 +173,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "receiver-accounts-applications", request.ApplicationId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "receiver-accounts-applications", request.ApplicationId);
return await client.SendRequestWithJsonAsync<Models.GetProfitAllocationReceiverAccountApplicationByApplicationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProfitAllocationReceiverAccountApplicationByApplicationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -192,9 +192,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "receiver-accounts-applications", "out_application_id", request.OutApplicationId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "profit-allocations", "receiver-accounts-applications", "out_application_id", request.OutApplicationId);
return await client.SendRequestWithJsonAsync<Models.GetProfitAllocationReceiverAccountApplicationByOutApplicationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetProfitAllocationReceiverAccountApplicationByOutApplicationIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
#endregion
}

View File

@ -22,9 +22,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "refunds");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "refunds");
return await client.SendRequestWithJsonAsync<Models.CreateRefundResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreateRefundResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -41,9 +41,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "refunds", "refund-id", request.RefundId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "refunds", "refund-id", request.RefundId);
return await client.SendRequestWithJsonAsync<Models.GetRefundByRefundIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetRefundByRefundIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -60,9 +60,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "refunds", "out-refund-id", request.OutRefundId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "refunds", "out-refund-id", request.OutRefundId);
return await client.SendRequestWithJsonAsync<Models.GetRefundByOutRefundIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetRefundByOutRefundIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@ -21,13 +21,13 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.EnterpriseId == null)
if (request.EnterpriseId is null)
request.EnterpriseId = client.Credentials.EnterpriseId;
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "mse-pay", "withdraws");
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "withdraws");
return await client.SendRequestWithJsonAsync<Models.CreateWithdrawResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.CreateWithdrawResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -43,9 +43,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "withdraws", request.WithdrawId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "withdraws", request.WithdrawId);
return await client.SendRequestWithJsonAsync<Models.GetWithdrawByWithdrawIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetWithdrawByWithdrawIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
@ -61,9 +61,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "mse-pay", "withdraws", "out-withdraw-id", request.OutWithdrawId);
.CreateFlurlRequest(request, HttpMethod.Get, "mse-pay", "withdraws", "out-withdraw-id", request.OutWithdrawId);
return await client.SendRequestWithJsonAsync<Models.GetWithdrawByOutWithdrawIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
return await client.SendFlurlRequestAsJsonAsync<Models.GetWithdrawByOutWithdrawIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@ -15,32 +15,32 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
public static TRequest EncryptRequestSensitiveProperty<TRequest>(this WechatTenpayBusinessClient client, TRequest request)
where TRequest : WechatTenpayBusinessRequest
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (request == null) throw new ArgumentNullException(nameof(request));
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
try
{
bool requireEncrypt = request.GetType().GetCustomAttributes<WechatTenpayBusinessSensitiveAttribute>(inherit: true).Any();
if (requireEncrypt)
{
if (request.Encryption is null)
request.Encryption = new WechatTenpayBusinessRequestEncryption() { Algorithm = client.Credentials.SensitivePropertyEncryptionAlgorithm };
if (request.WechatpayEncryption is null)
request.WechatpayEncryption = new WechatTenpayBusinessRequestEncryption() { Algorithm = client.Credentials.SensitivePropertyEncryptionAlgorithm };
if (Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC.Equals(request.Encryption.Algorithm))
if (Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC.Equals(request.WechatpayEncryption.Algorithm))
{
Utilities.ReflectionUtility.ReplacePropertyStringValue(ref request, (target, currentProp, oldValue) =>
{
var attr = currentProp.GetCustomAttribute<WechatTenpayBusinessSensitivePropertyAttribute>();
if (attr == null)
if (attr is null)
return (false, oldValue);
string sm4IV = client.Credentials.SensitivePropertyEncryptionSM4IV!;
string sm4Key = client.Credentials.SensitivePropertyEncryptionSM4Key!;
string sm4EncryptedKey = Utilities.RSAUtility.EncryptWithECB(publicKey: client.Credentials.TBEPCertificatePublicKey, plainText: sm4Key);
request.Encryption.SerialNumber = client.Credentials.TBEPCertificateSerialNumber;
request.Encryption.EncryptedKey = sm4EncryptedKey;
request.Encryption.IV = sm4IV;
request.WechatpayEncryption.SerialNumber = client.Credentials.TBEPCertificateSerialNumber;
request.WechatpayEncryption.EncryptedKey = sm4EncryptedKey;
request.WechatpayEncryption.IV = sm4IV;
string newValue = Utilities.SM4Utility.EncryptWithCBC(key: sm4Key, iv: sm4IV, plainText: oldValue);
return (true, newValue);
@ -48,13 +48,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
}
else
{
throw new NotSupportedException("Unsupported encryption algorithm.");
throw new WechatTenpayBusinessException($"Failed to encrypt request. Unsupported encryption algorithm: \"{request.WechatpayEncryption.Algorithm}\".");
}
}
}
catch (Exception ex) when (!(ex is Exceptions.WechatTenpayBusinessRequestEncryptionException))
catch (WechatTenpayBusinessException)
{
throw new Exceptions.WechatTenpayBusinessRequestEncryptionException("Failed to encrypt request. Please see the inner exception for more details.", ex);
throw;
}
catch (Exception ex)
{
throw new WechatTenpayBusinessException("Failed to encrypt request. Please see the inner exception for more details.", ex);
}
return request;

View File

@ -15,40 +15,40 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
public static TResponse DecryptResponseSensitiveProperty<TResponse>(this WechatTenpayBusinessClient client, TResponse response)
where TResponse : WechatTenpayBusinessResponse
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (response == null) throw new ArgumentNullException(nameof(response));
if (client is null) throw new ArgumentNullException(nameof(client));
if (response is null) throw new ArgumentNullException(nameof(response));
if (!response.IsSuccessful())
throw new Exceptions.WechatTenpayBusinessResponseDecryptionException("Failed to decrypt response, because the response is not successful.");
throw new WechatTenpayBusinessException("Failed to decrypt response, because the response is not successful.");
try
{
bool requireDecrypt = response.GetType().GetCustomAttributes<WechatTenpayBusinessSensitiveAttribute>(inherit: true).Any();
if (requireDecrypt)
{
if (response.Encryption is null)
throw new InvalidOperationException("Could not read value of 'TBEP-Encrypt'.");
if (response.Encryption.PlatformId != null && response.Encryption.SerialNumber != client.Credentials.PlatformCertificateSerialNumber)
throw new Exceptions.WechatTenpayBusinessResponseDecryptionException("Failed to decrypt response, because the platform certificate serial number does not match.");
if (response.Encryption.EnterpriseId != null && response.Encryption.SerialNumber != client.Credentials.EnterpriseCertificateSerialNumber)
throw new Exceptions.WechatTenpayBusinessResponseDecryptionException("Failed to decrypt response, because the enterprise certificate serial number does not match.");
if (response.WechatpayEncryption is null)
throw new InvalidOperationException("Failed to decrypt response, because the value of \"TBEP-Encrypt\" is empty.");
if (response.WechatpayEncryption.PlatformId is not null && response.WechatpayEncryption.SerialNumber != client.Credentials.PlatformCertificateSerialNumber)
throw new WechatTenpayBusinessException($"Failed to decrypt response, because the platform certificate with serial number \"{response.WechatpayEncryption.SerialNumber}\" does not existed.");
if (response.WechatpayEncryption.EnterpriseId is not null && response.WechatpayEncryption.SerialNumber != client.Credentials.EnterpriseCertificateSerialNumber)
throw new WechatTenpayBusinessException($"Failed to decrypt response, because the enterprise certificate serial number \"{response.WechatpayEncryption.SerialNumber}\" does not existed.");
if (Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC.Equals(response.Encryption.Algorithm))
if (Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC.Equals(response.WechatpayEncryption.Algorithm))
{
Utilities.ReflectionUtility.ReplacePropertyStringValue(ref response, (target, currentProp, oldValue) =>
{
var attr = currentProp.GetCustomAttribute<WechatTenpayBusinessSensitivePropertyAttribute>();
if (attr == null)
if (attr is null)
return (false, oldValue);
string sm4EncryptedKey = response.Encryption.EncryptedKey!;
string sm4EncryptedKey = response.WechatpayEncryption.EncryptedKey!;
string sm4Key = Utilities.RSAUtility.DecryptWithECB(
privateKey: response.Encryption.PlatformId != null ? client.Credentials.PlatformCertificatePrivateKey! :
response.Encryption.EnterpriseId != null ? client.Credentials.EnterpriseCertificatePrivateKey! :
privateKey: response.WechatpayEncryption.PlatformId is not null ? client.Credentials.PlatformCertificatePrivateKey! :
response.WechatpayEncryption.EnterpriseId is not null ? client.Credentials.EnterpriseCertificatePrivateKey! :
string.Empty,
cipherText: sm4EncryptedKey
);
string sm4IV = response.Encryption.IV!;
string sm4IV = response.WechatpayEncryption.IV!;
string newValue = Utilities.SM4Utility.DecryptWithCBC(key: sm4Key, iv: sm4IV, cipherText: oldValue);
return (true, newValue);
@ -56,13 +56,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
}
else
{
throw new NotSupportedException("Unsupported decryption algorithm.");
throw new WechatTenpayBusinessException($"Failed to decrypt response. Unsupported encryption algorithm: \"{response.WechatpayEncryption.Algorithm}\".");
}
}
}
catch (Exception ex) when (!(ex is Exceptions.WechatTenpayBusinessResponseDecryptionException))
catch (WechatTenpayBusinessException)
{
throw new Exceptions.WechatTenpayBusinessResponseDecryptionException("Failed to decrypt response. Please see the inner exception for more details.", ex);
throw;
}
catch (Exception ex)
{
throw new WechatTenpayBusinessException("Failed to decrypt response. Please see the inner exception for more details.", ex);
}
return response;

View File

@ -30,18 +30,18 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
public static bool VerifyResponseSignature<TResponse>(this WechatTenpayBusinessClient client, TResponse response, out Exception? error)
where TResponse : WechatTenpayBusinessResponse
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayBusinessClientSignExtensions.VerifySignature(
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strAuthorization: response.RawHeaders.FirstOrDefault(e => string.Equals(e.Key, "TBEP-Authorization", StringComparison.OrdinalIgnoreCase)).Value,
strContent: Encoding.UTF8.GetString(response.RawBytes),
strAuthorization: response.GetRawHeaders().GetFirstValueOrEmpty("TBEP-Authorization"),
strContent: Encoding.UTF8.GetString(response.GetRawBytes()),
out error
);
if (error != null)
if (error is not null)
{
error = new Exceptions.WechatTenpayBusinessResponseVerificationException("Verify signature of response failed. Please see the inner exception for more details.", error);
error = new WechatTenpayBusinessException("Verify signature of response failed. Please see the inner exception for more details.", error);
}
return ret;
@ -135,9 +135,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayBusinessClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureAlgorithm, string responseSerialNumber, out Exception? error)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayBusinessClientSignExtensions.VerifySignature(
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strTimestamp: responseTimestamp,
strNonce: responseNonce,
@ -148,10 +148,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
out error
);
if (error != null)
{
error = new Exceptions.WechatTenpayBusinessResponseVerificationException("Verify signature of response failed. Please see the inner exception for more details.", error);
}
if (!ret)
error ??= new Exception($"Failed to verify response. Maybe the raw signature \"{responseSignature}\" is invalid.");
return ret;
}

View File

@ -4,7 +4,7 @@ using System.Linq;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
internal static class WechatTenpayBusinessClientSignExtensions
internal static class WechatTenpayBusinessClientSigningExtensions
{
public static bool VerifySignature(this WechatTenpayBusinessClient client, string strAuthorization, string strContent, out Exception? error)
{
@ -43,26 +43,26 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
}
}
error = new Exception("Could not read value of 'TBEP-Authorization'.");
error = new Exception("The value of \"TBEP-Authorization\" is empty.");
return false;
}
public static bool VerifySignature(this WechatTenpayBusinessClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignatureAlgorithm, string strSerialNumber, out Exception? error)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (client is null) throw new ArgumentNullException(nameof(client));
switch (strSignatureAlgorithm)
{
case Constants.SignAlgorithms.SHA245_WITH_RSA:
{
if (client.Credentials.TBEPCertificateSerialNumber != null &&
client.Credentials.TBEPCertificatePublicKey != null)
if (client.Credentials.TBEPCertificateSerialNumber is not null &&
client.Credentials.TBEPCertificatePublicKey is not null)
{
try
{
if (!string.Equals(client.Credentials.TBEPCertificateSerialNumber, strSerialNumber))
{
error = new Exception("There is no TBEP public key matched the serial number.");
error = new Exception($"There is no TBEP public key matched the serial number \"{strSerialNumber}\".");
return false;
}
@ -86,7 +86,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
default:
{
error = new Exception("Unsupported sign algorithm.");
error = new Exception($"Unsupported sign algorithm: \"{strSignatureAlgorithm}\".");
return false;
}
}

View File

@ -1,14 +1,13 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Interceptors
{
using SKIT.FlurlHttpClient.Constants;
internal class WechatTenpayBusinessRequestSignatureInterceptor : FlurlHttpCallInterceptor
internal class WechatTenpayBusinessRequestSigningInterceptor : HttpInterceptor
{
private const string HTTP_HEADER_PLATFORM_AUTHORIZATION = HttpHeaders.Authorization;
private const string HTTP_HEADER_ENTERPRISE_AUTHORIZATION = "Enterprise-Authorization";
@ -21,7 +20,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Interceptors
private readonly string? _enterpriseCertSn;
private readonly string? _enterpriseCertPk;
public WechatTenpayBusinessRequestSignatureInterceptor(string signAlg, string platformId, string platformCertSn, string platformCertPk, string? enterpriseId, string? enterpriseCertSn, string? enterpriseCertPk)
public WechatTenpayBusinessRequestSigningInterceptor(string signAlg, string platformId, string platformCertSn, string platformCertPk, string? enterpriseId, string? enterpriseCertSn, string? enterpriseCertPk)
{
_signAlg = signAlg;
_platformId = platformId;
@ -32,35 +31,35 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Interceptors
_enterpriseCertPk = enterpriseCertPk;
}
public override async Task BeforeCallAsync(FlurlCall flurlCall)
public override async Task BeforeCallAsync(HttpInterceptorContext context, CancellationToken cancellationToken = default)
{
if (flurlCall == null) throw new ArgumentNullException(nameof(flurlCall));
if (flurlCall.Completed) throw new Exceptions.WechatTenpayBusinessRequestSignatureException("This interceptor must be called before request completed.");
if (context is null) throw new ArgumentNullException(nameof(context));
if (context.FlurlCall.Completed) throw new WechatTenpayBusinessException("Failed to sign request. This interceptor must be called before request completed.");
string method = flurlCall.HttpRequestMessage.Method.ToString().ToUpper();
string url = flurlCall.HttpRequestMessage.RequestUri?.PathAndQuery ?? string.Empty;
string method = context.FlurlCall.HttpRequestMessage.Method.ToString().ToUpper();
string url = context.FlurlCall.HttpRequestMessage.RequestUri?.PathAndQuery ?? string.Empty;
string timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString();
string nonce = Guid.NewGuid().ToString("N");
string body = string.Empty;
if (flurlCall.HttpRequestMessage.Content is MultipartFormDataContent formdataContent)
if (context.FlurlCall.HttpRequestMessage.Content is MultipartFormDataContent formdataContent)
{
// NOTICE: multipart/form-data 文件上传请求的待签名参数需特殊处理
var httpContent = formdataContent.SingleOrDefault(e => Constants.FormDataFields.FORMDATA_META.Equals(e.Headers.ContentDisposition?.Name?.Trim('\"')));
if (httpContent != null)
if (httpContent is not null)
{
body = await httpContent.ReadAsStringAsync();
}
}
else
{
body = flurlCall.RequestBody ?? string.Empty;
body = context.FlurlCall.RequestBody ?? string.Empty;
}
string plainText = $"{method}\n{url}\n{timestamp}\n{nonce}\n{body}\n";
string signText;
bool softSignRequired = _enterpriseId != null && _enterpriseCertSn != null && _enterpriseCertPk != null;
bool softSignRequired = _enterpriseId is not null && _enterpriseCertSn is not null && _enterpriseCertPk is not null;
string? softSignText = null;
switch (_signAlg)
@ -73,31 +72,27 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Interceptors
if (softSignRequired)
{
byte[] keyBytes = Convert.FromBase64String(_enterpriseCertPk);
byte[] keyBytes = Convert.FromBase64String(_enterpriseCertPk!);
byte[] msgBytes = Convert.FromBase64String(signText);
softSignText = Convert.ToBase64String(Utilities.RSAUtility.SignWithSHA256(keyBytes, msgBytes));
}
}
catch (Exception ex)
{
throw new Exceptions.WechatTenpayBusinessRequestSignatureException("Failed to generate signature of request. Please see the inner exception for more details.", ex);
throw new WechatTenpayBusinessException("Failed to sign request. Please see the inner exception for more details.", ex);
}
}
break;
default:
throw new Exceptions.WechatTenpayBusinessRequestSignatureException("Unsupported authorization sign algorithm.");
throw new WechatTenpayBusinessException($"Failed to sign request. Unsupported signing algorithm: \"{_signAlg}\".");
}
string authString = $"platform_id=\"{_platformId}\",platform_serial_number=\"{_platformCertSn}\",nonce=\"{nonce}\",timestamp=\"{timestamp}\",signature=\"{signText}\",signature_algorithm=\"{_signAlg}\"";
flurlCall.Request.Headers.Remove(HTTP_HEADER_PLATFORM_AUTHORIZATION);
flurlCall.Request.WithHeader(HttpHeaders.Authorization, authString);
context.FlurlCall.Request.WithHeader(HTTP_HEADER_PLATFORM_AUTHORIZATION, $"platform_id=\"{_platformId}\",platform_serial_number=\"{_platformCertSn}\",nonce=\"{nonce}\",timestamp=\"{timestamp}\",signature=\"{signText}\",signature_algorithm=\"{_signAlg}\"");
if (softSignRequired)
{
string softAuthString = $"ent_id=\"{_enterpriseId}\",enterprise_serial_number=\"{_enterpriseCertSn}\",signature=\"{softSignText}\",signature_algorithm=\"{_signAlg}\"";
flurlCall.Request.Headers.Remove(HTTP_HEADER_ENTERPRISE_AUTHORIZATION);
flurlCall.Request.WithHeader(HTTP_HEADER_ENTERPRISE_AUTHORIZATION, softAuthString);
context.FlurlCall.Request.WithHeader(HTTP_HEADER_ENTERPRISE_AUTHORIZATION, $"ent_id=\"{_enterpriseId}\",enterprise_serial_number=\"{_enterpriseCertSn}\",signature=\"{softSignText}\",signature_algorithm=\"{_signAlg}\"");
}
}
}

View File

@ -1,3 +1,5 @@
using System.Linq;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
{
/// <summary>
@ -7,7 +9,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
{
public override bool IsSuccessful()
{
return base.IsSuccessful() && RawBytes?.Length > 0;
return base.IsSuccessful() && GetRawBytes().Any();
}
}
}

View File

@ -32,9 +32,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置关单时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("close_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("close_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset CloseTime { get; set; }
}
}

View File

@ -163,9 +163,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置下单时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("create_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("create_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? CreateTime { get; set; }
/// <summary>
@ -254,9 +254,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置过期时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("expire_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("expire_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset ExpireTime { get; set; }
/// <summary>

View File

@ -125,9 +125,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置付款时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("pay_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("pay_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? SucceedTime { get; set; }
}
}

View File

@ -22,9 +22,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置链接过期时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("expire_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("expire_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset ExpireTime { get; set; }
}
}

View File

@ -64,9 +64,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置分账成功时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("succeeded_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("succeeded_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? SucceedTime { get; set; }
/// <summary>
@ -117,9 +117,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置分账完成时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("finished_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("finished_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? FinishTime { get; set; }
/// <summary>

View File

@ -67,18 +67,18 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置退款成功时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("succeeded_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("succeeded_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? SucceedTime { get; set; }
/// <summary>
/// 获取或设置退款创建时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("created_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("created_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset CreateTime { get; set; }
}
}

View File

@ -46,9 +46,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models
/// 获取或设置分账成功时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("succeeded_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("succeeded_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset? SucceedTime { get; set; }
/// <summary>

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461; net47; netstandard2.0; net6.0</TargetFrameworks>
<LangVersion>8.0</LangVersion>
<TargetFrameworks>net461; net471; netstandard2.0; net6.0</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
</PropertyGroup>
@ -14,7 +14,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat</PackageProjectUrl>
<PackageTags>Flurl.Http Tencent Tenpay FiT 腾讯金融科技服务平台 腾讯金融科技 腾讯金融 腾讯商企付 腾讯微企付 商企付 微企付</PackageTags>
<Version>2.2.0</Version>
<Version>3.0.0-preview.1</Version>
<Description>基于 Flurl.Http 的微企付 API 客户端。</Description>
<Authors>Fu Diwei</Authors>
<RepositoryType>git</RepositoryType>
@ -36,13 +36,13 @@
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net461' Or '$(TargetFramework)' == 'net47'" />
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net461' Or '$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" Condition="'$(TargetFramework)' == 'net461'" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="2.6.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0-preview.1" />
</ItemGroup>
</Project>

View File

@ -61,7 +61,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Settings
internal Credentials(WechatTenpayBusinessClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options is null) throw new ArgumentNullException(nameof(options));
PlatformId = options.PlatformId;
PlatformCertificateSerialNumber = options.PlatformCertificateSerialNumber;

View File

@ -72,8 +72,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>签名字节数组。</returns>
public static byte[] SignWithSHA256(byte[] privateKeyBytes, byte[] msgBytes)
{
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
if (privateKeyBytes is null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
RsaKeyParameters rsaPrivateKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return SignWithSHA256(rsaPrivateKeyParams, msgBytes);
@ -87,8 +87,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>经 Base64 编码的签名。</returns>
public static string SignWithSHA256(string privateKey, string message)
{
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (message == null) throw new ArgumentNullException(nameof(message));
if (privateKey is null) throw new ArgumentNullException(nameof(privateKey));
if (message is null) throw new ArgumentNullException(nameof(message));
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
@ -105,9 +105,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>验证结果。</returns>
public static bool VerifyWithSHA256(byte[] publicKeyBytes, byte[] msgBytes, byte[] signBytes)
{
if (publicKeyBytes == null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
if (signBytes == null) throw new ArgumentNullException(nameof(signBytes));
if (publicKeyBytes is null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
if (signBytes is null) throw new ArgumentNullException(nameof(signBytes));
RsaKeyParameters rsaPublicKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
return VerifyWithSHA256(rsaPublicKeyParams, msgBytes, signBytes);
@ -122,9 +122,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>验证结果。</returns>
public static bool VerifyWithSHA256(string publicKey, string message, string signature)
{
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
if (message == null) throw new ArgumentNullException(nameof(message));
if (signature == null) throw new ArgumentNullException(nameof(signature));
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
if (message is null) throw new ArgumentNullException(nameof(message));
if (signature is null) throw new ArgumentNullException(nameof(signature));
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
@ -141,8 +141,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>解密后的数据字节数组。</returns>
public static byte[] DecryptWithECB(byte[] privateKeyBytes, byte[] cipherBytes, string paddingAlgorithm = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (privateKeyBytes == null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (cipherBytes == null) throw new ArgumentNullException(nameof(cipherBytes));
if (privateKeyBytes is null) throw new ArgumentNullException(nameof(privateKeyBytes));
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
RsaKeyParameters rsaPrivateKeyParams = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privateKeyBytes);
return DecryptWithECB(rsaPrivateKeyParams, cipherBytes, paddingAlgorithm);
@ -157,8 +157,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>解密后的文本数据。</returns>
public static string DecryptWithECB(string privateKey, string cipherText, string paddingAlgorithm = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (privateKey == null) throw new ArgumentNullException(nameof(privateKey));
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
if (privateKey is null) throw new ArgumentNullException(nameof(privateKey));
if (cipherText is null) throw new ArgumentNullException(nameof(cipherText));
byte[] privateKeyBytes = ConvertPrivateKeyPkcs8PemToByteArray(privateKey);
byte[] cipherBytes = Convert.FromBase64String(cipherText);
@ -175,8 +175,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>加密后的数据字节数组。</returns>
public static byte[] EncryptWithECB(byte[] publicKeyBytes, byte[] plainBytes, string paddingAlgorithm = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (publicKeyBytes == null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
if (publicKeyBytes is null) throw new ArgumentNullException(nameof(publicKeyBytes));
if (plainBytes is null) throw new ArgumentNullException(nameof(plainBytes));
RsaKeyParameters rsaPublicKeyParams = (RsaKeyParameters)PublicKeyFactory.CreateKey(publicKeyBytes);
return EncryptWithECB(rsaPublicKeyParams, plainBytes, paddingAlgorithm);
@ -191,8 +191,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>经 Base64 编码的加密数据。</returns>
public static string EncryptWithECB(string publicKey, string plainText, string paddingAlgorithm = RSA_CIPHER_PADDING_OAEP_WITH_SHA1_AND_MGF1)
{
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
if (plainText is null) throw new ArgumentNullException(nameof(plainText));
byte[] publicKeyBytes = ConvertPublicKeyPkcs8PemToByteArray(publicKey);
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);

View File

@ -16,7 +16,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>信息摘要字节数组。</returns>
public static byte[] Hash(byte[] bytes)
{
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
if (bytes is null) throw new ArgumentNullException(nameof(bytes));
using SHA256 sha = SHA256.Create();
return sha.ComputeHash(bytes);
@ -29,7 +29,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>信息摘要。</returns>
public static string Hash(string message)
{
if (message == null) throw new ArgumentNullException(nameof(message));
if (message is null) throw new ArgumentNullException(nameof(message));
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] hashBytes = Hash(msgBytes);

View File

@ -16,7 +16,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>哈希字节数组。</returns>
public static byte[] Hash(byte[] bytes)
{
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
if (bytes is null) throw new ArgumentNullException(nameof(bytes));
SM3Digest sm3 = new SM3Digest();
sm3.BlockUpdate(bytes, 0, bytes.Length);
@ -32,7 +32,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>哈希值。</returns>
public static string Hash(string message)
{
if (message == null) throw new ArgumentNullException(nameof(message));
if (message is null) throw new ArgumentNullException(nameof(message));
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
byte[] hashBytes = Hash(msgBytes);

View File

@ -39,8 +39,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>加密后的数据字节数组。</returns>
public static byte[] EncryptWithCBC(byte[] keyBytes, byte[] ivBytes, byte[] plainBytes, string paddingMode = SM4_CIPHER_PADDING_PKCS7PADDING)
{
if (keyBytes == null) throw new ArgumentNullException(nameof(keyBytes));
if (plainBytes == null) throw new ArgumentNullException(nameof(plainBytes));
if (keyBytes is null) throw new ArgumentNullException(nameof(keyBytes));
if (plainBytes is null) throw new ArgumentNullException(nameof(plainBytes));
KeyParameter sm4KeyParams = ParameterUtilities.CreateKeyParameter(SM4_ALGORITHM_NAME, keyBytes);
ParametersWithIV sm4keyParamsWithIv = new ParametersWithIV(sm4KeyParams, ivBytes);
@ -57,8 +57,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>经 Base64 编码的加密数据。</returns>
public static string EncryptWithCBC(string key, string iv, string plainText, string paddingMode = SM4_CIPHER_PADDING_PKCS7PADDING)
{
if (key == null) throw new ArgumentNullException(nameof(key));
if (plainText == null) throw new ArgumentNullException(nameof(plainText));
if (key is null) throw new ArgumentNullException(nameof(key));
if (plainText is null) throw new ArgumentNullException(nameof(plainText));
byte[] keyBytes = Convert.FromBase64String(key);
byte[] ivBytes = Convert.FromBase64String(iv);
@ -77,8 +77,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>解密后的数据字节数组。</returns>
public static byte[] DecryptWithCBC(byte[] keyBytes, byte[] ivBytes, byte[] cipherBytes, string paddingMode = SM4_CIPHER_PADDING_PKCS7PADDING)
{
if (keyBytes == null) throw new ArgumentNullException(nameof(keyBytes));
if (cipherBytes == null) throw new ArgumentNullException(nameof(cipherBytes));
if (keyBytes is null) throw new ArgumentNullException(nameof(keyBytes));
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
KeyParameter sm4KeyParams = ParameterUtilities.CreateKeyParameter(SM4_ALGORITHM_NAME, keyBytes);
ParametersWithIV sm4keyParamsWithIv = new ParametersWithIV(sm4KeyParams, ivBytes);
@ -95,8 +95,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
/// <returns>解密后的文本数据。</returns>
public static string DecryptWithCBC(string key, string iv, string cipherText, string paddingMode = SM4_CIPHER_PADDING_PKCS7PADDING)
{
if (key == null) throw new ArgumentNullException(nameof(key));
if (cipherText == null) throw new ArgumentNullException(nameof(cipherText));
if (key is null) throw new ArgumentNullException(nameof(key));
if (cipherText is null) throw new ArgumentNullException(nameof(cipherText));
byte[] keyBytes = Convert.FromBase64String(key);
byte[] ivBytes = Convert.FromBase64String(iv);

View File

@ -15,10 +15,10 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string fileMetaJson, string formDataName, Action<HttpContent> configureMetaHttpContent, Action<HttpContent> configureFileHttpContent)
{
if (fileName == null) throw new ArgumentNullException(nameof(fileName));
if (fileMetaJson == null) throw new ArgumentNullException(nameof(fileMetaJson));
if (formDataName == null) throw new ArgumentNullException(nameof(formDataName));
if (configureFileHttpContent == null) throw new ArgumentNullException(nameof(configureFileHttpContent));
if (fileName is null) throw new ArgumentNullException(nameof(fileName));
if (fileMetaJson is null) throw new ArgumentNullException(nameof(fileMetaJson));
if (formDataName is null) throw new ArgumentNullException(nameof(formDataName));
if (configureFileHttpContent is null) throw new ArgumentNullException(nameof(configureFileHttpContent));
fileBytes = fileBytes ?? Array.Empty<byte>();
fileContentType = string.IsNullOrEmpty(fileContentType) ? "application/octet-stream" : fileContentType;

View File

@ -15,8 +15,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
private static void InnerReplacePropertyStringValue<T>(ref T obj, ReplacePropertyStringValueReplacementHandler replacement)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
if (replacement == null) throw new ArgumentNullException(nameof(replacement));
if (obj is null) throw new ArgumentNullException(nameof(obj));
if (replacement is null) throw new ArgumentNullException(nameof(replacement));
Type objType = obj.GetType();
if (!objType.IsClass)
@ -82,7 +82,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
Type elementType = element.GetType();
if (elementType == typeof(string))
{
if (currentProp == null)
if (currentProp is null)
continue;
if (!currentProp.CanWrite)
continue;
@ -121,7 +121,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
Type elementType = element.GetType();
if (elementType == typeof(string))
{
if (currentProp == null)
if (currentProp is null)
continue;
if (!currentProp.CanWrite)
continue;
@ -160,7 +160,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
Type entryValueType = entryValue.GetType();
if (entryValueType == typeof(string))
{
if (currentProp == null)
if (currentProp is null)
continue;
if (!currentProp.CanWrite)
continue;

View File

@ -35,20 +35,32 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// </summary>
/// <param name="options">配置项。</param>
public WechatTenpayBusinessClient(WechatTenpayBusinessClientOptions options)
: this(options, null)
{
if (options == null) throw new ArgumentNullException(nameof(options));
}
/// <summary>
///
/// </summary>
/// <param name="options"></param>
/// <param name="httpClient"></param>
/// <param name="disposeClient"></param>
internal protected WechatTenpayBusinessClient(WechatTenpayBusinessClientOptions options, HttpClient? httpClient, bool disposeClient = true)
: base(httpClient, disposeClient)
{
if (options is null) throw new ArgumentNullException(nameof(options));
Credentials = new Settings.Credentials(options);
AutoEncryptRequestSensitiveProperty = options.AutoEncryptRequestSensitiveProperty;
AutoDecryptResponseSensitiveProperty = options.AutoDecryptResponseSensitiveProperty;
FlurlClient.BaseUrl = options.Endpoint ?? WechatTenpayBusinessEndpoints.DEFAULT;
FlurlClient.Headers.Remove(FlurlHttpClient.Constants.HttpHeaders.Accept);
FlurlClient.Headers.Remove(FlurlHttpClient.Constants.HttpHeaders.AcceptLanguage);
FlurlClient.WithHeader(FlurlHttpClient.Constants.HttpHeaders.Accept, "application/json");
FlurlClient.WithTimeout(TimeSpan.FromMilliseconds(options.Timeout));
FlurlClient.Headers.Remove(HttpHeaders.Accept);
FlurlClient.Headers.Remove(HttpHeaders.AcceptLanguage);
FlurlClient.WithHeader(HttpHeaders.Accept, "application/json");
FlurlClient.WithTimeout(options.Timeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(options.Timeout));
Interceptors.Add(new Interceptors.WechatTenpayBusinessRequestSignatureInterceptor(
Interceptors.Add(new Interceptors.WechatTenpayBusinessRequestSigningInterceptor(
signAlg: options.SignAlgorithm,
platformId: options.PlatformId,
platformCertSn: options.PlatformCertificateSerialNumber,
@ -63,27 +75,22 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// 使用当前客户端生成一个新的 <see cref="IFlurlRequest"/> 对象。
/// </summary>
/// <param name="request"></param>
/// <param name="method"></param>
/// <param name="httpMethod"></param>
/// <param name="urlSegments"></param>
/// <returns></returns>
public IFlurlRequest CreateRequest(WechatTenpayBusinessRequest request, HttpMethod method, params object[] urlSegments)
public IFlurlRequest CreateFlurlRequest(WechatTenpayBusinessRequest request, HttpMethod httpMethod, params object[] urlSegments)
{
IFlurlRequest flurlRequest = FlurlClient.Request(urlSegments).WithVerb(method);
IFlurlRequest flurlRequest = base.CreateFlurlRequest(request, httpMethod, urlSegments);
if (AutoEncryptRequestSensitiveProperty)
{
this.EncryptRequestSensitiveProperty(request);
}
if (request.Timeout != null)
{
flurlRequest.WithTimeout(TimeSpan.FromMilliseconds(request.Timeout.Value));
}
if (request.Encryption != null)
if (request.WechatpayEncryption is not null)
{
flurlRequest.Headers.Remove("TBEP-Encrypt");
flurlRequest.WithHeader("TBEP-Encrypt", $"enc_key=\"{request.Encryption.EncryptedKey}\",iv=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes(request.Encryption.IV))}\",tbep_serial_number=\"{request.Encryption.SerialNumber}\",algorithm=\"{request.Encryption.Algorithm}\"");
flurlRequest.WithHeader("TBEP-Encrypt", $"enc_key=\"{request.WechatpayEncryption.EncryptedKey}\",iv=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes(request.WechatpayEncryption.IV))}\",tbep_serial_number=\"{request.WechatpayEncryption.SerialNumber}\",algorithm=\"{request.WechatpayEncryption.Algorithm}\"");
}
return flurlRequest;
@ -97,30 +104,19 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <param name="httpContent"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<T> SendRequestAsync<T>(IFlurlRequest flurlRequest, HttpContent? httpContent = null, CancellationToken cancellationToken = default)
public async Task<T> SendFlurlRequestAsync<T>(IFlurlRequest flurlRequest, HttpContent? httpContent = null, CancellationToken cancellationToken = default)
where T : WechatTenpayBusinessResponse, new()
{
if (flurlRequest == null) throw new ArgumentNullException(nameof(flurlRequest));
if (flurlRequest is null) throw new ArgumentNullException(nameof(flurlRequest));
if (httpContent != null)
if (httpContent is not null)
{
if (string.IsNullOrEmpty(httpContent.Headers.ContentType?.MediaType))
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
try
{
using IFlurlResponse flurlResponse = await base.SendRequestAsync(flurlRequest, httpContent, cancellationToken);
return await WrapResponseWithJsonAsync<T>(flurlResponse, cancellationToken);
}
catch (FlurlHttpTimeoutException ex)
{
throw new Exceptions.WechatTenpayBusinessRequestTimeoutException(ex.Message, ex);
}
catch (FlurlHttpException ex)
{
throw new WechatTenpayBusinessException(ex.Message, ex);
}
using IFlurlResponse flurlResponse = await base.SendFlurlRequestAsync(flurlRequest, httpContent, cancellationToken);
return await WrapFlurlResponseAsJsonAsync<T>(flurlResponse, cancellationToken);
}
/// <summary>
@ -131,36 +127,25 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <param name="data"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<T> SendRequestWithJsonAsync<T>(IFlurlRequest flurlRequest, object? data = null, CancellationToken cancellationToken = default)
public async Task<T> SendFlurlRequestAsJsonAsync<T>(IFlurlRequest flurlRequest, object? data = null, CancellationToken cancellationToken = default)
where T : WechatTenpayBusinessResponse, new()
{
if (flurlRequest == null) throw new ArgumentNullException(nameof(flurlRequest));
if (flurlRequest is null) throw new ArgumentNullException(nameof(flurlRequest));
try
{
bool isSimpleRequest = data == null ||
flurlRequest.Verb == HttpMethod.Get ||
flurlRequest.Verb == HttpMethod.Head ||
flurlRequest.Verb == HttpMethod.Options;
using IFlurlResponse flurlResponse = isSimpleRequest ?
await base.SendRequestAsync(flurlRequest, null, cancellationToken) :
await base.SendRequestWithJsonAsync(flurlRequest, data, cancellationToken);
return await WrapResponseWithJsonAsync<T>(flurlResponse, cancellationToken);
}
catch (FlurlHttpTimeoutException ex)
{
throw new Exceptions.WechatTenpayBusinessRequestTimeoutException(ex.Message, ex);
}
catch (FlurlHttpException ex)
{
throw new WechatTenpayBusinessException(ex.Message, ex);
}
bool isSimpleRequest = data is null ||
flurlRequest.Verb == HttpMethod.Get ||
flurlRequest.Verb == HttpMethod.Head ||
flurlRequest.Verb == HttpMethod.Options;
using IFlurlResponse flurlResponse = isSimpleRequest ?
await base.SendFlurlRequestAsync(flurlRequest, null, cancellationToken) :
await base.SendFlurlRequestAsJsonAsync(flurlRequest, data, cancellationToken);
return await WrapFlurlResponseAsJsonAsync<T>(flurlResponse, cancellationToken);
}
private new async Task<TResponse> WrapResponseWithJsonAsync<TResponse>(IFlurlResponse flurlResponse, CancellationToken cancellationToken = default)
private new async Task<TResponse> WrapFlurlResponseAsJsonAsync<TResponse>(IFlurlResponse flurlResponse, CancellationToken cancellationToken = default)
where TResponse : WechatTenpayBusinessResponse, new()
{
TResponse result = await base.WrapResponseWithJsonAsync<TResponse>(flurlResponse, cancellationToken);
TResponse result = await base.WrapFlurlResponseAsJsonAsync<TResponse>(flurlResponse, cancellationToken);
string? strEncryption = flurlResponse.Headers.FirstOrDefault("TBEP-Encrypt");
if (!string.IsNullOrEmpty(strEncryption))
@ -172,13 +157,13 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
k => k[0],
v => v.Length > 1 ? v[1].TrimStart('\"').TrimEnd('\"') : null
);
result.Encryption = new WechatTenpayBusinessResponseEncryption();
result.Encryption.PlatformId = dictEncryption.GetValueOrDefault("platform_id");
result.Encryption.EnterpriseId = dictEncryption.GetValueOrDefault("ent_id");
result.Encryption.EncryptedKey = dictEncryption.GetValueOrDefault("enc_key");
result.Encryption.IV = dictEncryption.GetValueOrDefault("iv");
result.Encryption.SerialNumber = dictEncryption.GetValueOrDefault("platform_serial_number") ?? dictEncryption.GetValueOrDefault("enterprise_serial_number");
result.Encryption.Algorithm = dictEncryption.GetValueOrDefault("algorithm");
result.WechatpayEncryption = new WechatTenpayBusinessResponseEncryption();
result.WechatpayEncryption.PlatformId = dictEncryption.GetValueOrDefault("platform_id");
result.WechatpayEncryption.EnterpriseId = dictEncryption.GetValueOrDefault("ent_id");
result.WechatpayEncryption.EncryptedKey = dictEncryption.GetValueOrDefault("enc_key");
result.WechatpayEncryption.IV = dictEncryption.GetValueOrDefault("iv");
result.WechatpayEncryption.SerialNumber = dictEncryption.GetValueOrDefault("platform_serial_number") ?? dictEncryption.GetValueOrDefault("enterprise_serial_number");
result.WechatpayEncryption.Algorithm = dictEncryption.GetValueOrDefault("algorithm");
if (AutoDecryptResponseSensitiveProperty && result.IsSuccessful())
{

View File

@ -7,19 +7,25 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
/// <summary>
/// 获取或设置请求超时时间(单位:毫秒)。
/// <para>默认值30000</para>
/// <para>
/// 默认值30000
/// </para>
/// </summary>
public int Timeout { get; set; } = 30 * 1000;
/// <summary>
/// 获取或设置微企付 API 入口点。
/// <para>默认值:<see cref="WechatTenpayBusinessEndpoints.DEFAULT"/></para>
/// <para>
/// 默认值:<see cref="WechatTenpayBusinessEndpoints.DEFAULT"/>
/// </para>
/// </summary>
public string Endpoint { get; set; } = WechatTenpayBusinessEndpoints.DEFAULT;
/// <summary>
/// 获取或设置微企付 API 签名认证方式。
/// <para>默认值:<see cref="Constants.SignAlgorithms.SHA245_WITH_RSA"/></para>
/// <para>
/// 默认值:<see cref="Constants.SignAlgorithms.SHA245_WITH_RSA"/>
/// </para>
/// </summary>
public string SignAlgorithm { get; set; } = Constants.SignAlgorithms.SHA245_WITH_RSA;
@ -63,15 +69,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// </summary>
public string TBEPCertificatePublicKey { get; set; } = default!;
/// <summary>
/// 获取或设置是否自动加密请求中的敏感字段数据。
/// <para>注意:启用该功能需配合 <see cref="SensitivePropertyEncryptionSM4Key"/> 和 <see cref="SensitivePropertyEncryptionSM4IV"/> 使用。</para>
/// </summary>
public bool AutoEncryptRequestSensitiveProperty { get; set; }
/// <summary>
/// 获取或设置自动加密请求重敏感字段数据时使用的算法。
/// <para>默认值:<see cref="Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC"/></para>
/// <para>
/// 默认值:<see cref="Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC"/>
/// </para>
/// </summary>
public string SensitivePropertyEncryptionAlgorithm { get; set; } = Constants.EncryptionAlgorithms.RSA_OAEP_WITH_SM4_128_CBC;
@ -85,6 +87,14 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// </summary>
public string? SensitivePropertyEncryptionSM4IV { get; set; }
/// <summary>
/// 获取或设置是否自动加密请求中的敏感字段数据。
/// <para>
/// 注意:启用该功能需配合 <see cref="SensitivePropertyEncryptionSM4Key"/> 和 <see cref="SensitivePropertyEncryptionSM4IV"/> 使用。
/// </para>
/// </summary>
public bool AutoEncryptRequestSensitiveProperty { get; set; }
/// <summary>
/// 获取或设置是否自动解密响应中的敏感字段数据。
/// </summary>

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
@ -6,7 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// 表示微企付 API 回调通知事件的基类。
/// </summary>
[Serializable]
public class WechatTenpayBusinessEvent
public class WechatTenpayBusinessEvent : ICommonWebhookEvent
{
/// <summary>
/// 获取或设置 API 版本。
@ -48,9 +48,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// 获取或设置通知创建时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("create_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("create_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
public DateTimeOffset CreateTime { get; set; }
}

View File

@ -1,11 +1,11 @@
using System;
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
/// <summary>
/// 当调用微企付 API 出错时引发的异常。
/// </summary>
public class WechatTenpayBusinessException : CommonExceptionBase
public class WechatTenpayBusinessException : CommonException
{
/// <inheritdoc/>
public WechatTenpayBusinessException()

View File

@ -3,22 +3,15 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <summary>
/// 表示微企付 API 请求的基类。
/// </summary>
public abstract class WechatTenpayBusinessRequest : ICommonRequest
public abstract class WechatTenpayBusinessRequest : CommonRequestBase, ICommonRequest
{
/// <summary>
/// 获取或设置请求超时时间(单位:毫秒)。如果不指定将使用构造 <see cref="WechatTenpayBusinessClient"/> 时的 <see cref="WechatTenpayBusinessClientOptions.Timeout"/> 参数,这在需要指定特定耗时请求(比如上传或下载文件)的超时时间时很有用。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public virtual int? Timeout { get; set; }
/// <summary>
/// 获取或设置请求使用的微企付敏感字段加密参数。
/// <para>如果启用了 <see cref="WechatTenpayBusinessClientOptions.AutoEncryptRequestSensitiveProperty"/> 参数,将由系统自动生成。</para>
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public virtual WechatTenpayBusinessRequestEncryption? Encryption { get; set; }
public virtual WechatTenpayBusinessRequestEncryption? WechatpayEncryption { get; set; }
}
public sealed class WechatTenpayBusinessRequestEncryption

View File

@ -5,62 +5,14 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <summary>
/// 表示微企付 API 响应的基类。
/// </summary>
public abstract class WechatTenpayBusinessResponse : ICommonResponse
public abstract class WechatTenpayBusinessResponse : CommonResponseBase, ICommonResponse
{
/// <summary>
///
/// </summary>
int ICommonResponse.RawStatus { get; set; }
/// <summary>
///
/// </summary>
IDictionary<string, string> ICommonResponse.RawHeaders { get; set; } = default!;
/// <summary>
///
/// </summary>
byte[] ICommonResponse.RawBytes { get; set; } = default!;
/// <summary>
/// 获取原始的 HTTP 响应状态码。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public int RawStatus
{
get { return ((ICommonResponse)this).RawStatus; }
internal set { ((ICommonResponse)this).RawStatus = value; }
}
/// <summary>
/// 获取原始的 HTTP 响应表头集合。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public IDictionary<string, string> RawHeaders
{
get { return ((ICommonResponse)this).RawHeaders; }
internal set { ((ICommonResponse)this).RawHeaders = value; }
}
/// <summary>
/// 获取原始的 HTTP 响应正文。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public byte[] RawBytes
{
get { return ((ICommonResponse)this).RawBytes; }
internal set { ((ICommonResponse)this).RawBytes = value; }
}
/// <summary>
/// 获取微企付 API 返回的敏感字段加密参数。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public WechatTenpayBusinessResponseEncryption? Encryption { get; internal set; }
public WechatTenpayBusinessResponseEncryption? WechatpayEncryption { get; internal set; }
/// <summary>
/// 获取微企付请求链路 ID。
@ -77,12 +29,15 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
public virtual WechatTenpayBusinessResponseError? Error { get; set; }
/// <summary>
/// 获取一个值,该值指示调用微企付 API 是否成功(即 HTTP 状态码为 200、202 或 204
/// 获取一个值,该值指示调用微企付 API 是否成功。
/// <para>
/// (即 HTTP 状态码为 200/202/204且 <see cref="Error.Code"/> 值为空)
/// </para>
/// </summary>
/// <returns></returns>
public virtual bool IsSuccessful()
public override bool IsSuccessful()
{
return (RawStatus == 200 || RawStatus == 202 || RawStatus == 204) && string.IsNullOrEmpty(Error?.Code);
return GetRawStatus() >= 200 && GetRawStatus() <= 204 && string.IsNullOrEmpty(Error?.Code);
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.IO;
using System.Reflection;
using SKIT.FlurlHttpClient.Tools.CodeAnalyzer;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.UnitTests
{
public class CodeAnalyzeTests
{
[Fact(DisplayName = "代码质量分析")]
public void TestCodeAnalyzer()
{
Assert.Null(Record.Exception(() =>
{
var options = new TypeDeclarationAnalyzerOptions()
{
SdkAssembly = Assembly.GetAssembly(typeof(WechatTenpayBusinessClient))!,
SdkRequestModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models",
SdkResponseModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models",
SdkExecutingExtensionDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness",
SdkWebhookEventDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Events",
ThrowOnNotFoundRequestModelTypes = true,
ThrowOnNotFoundResponseModelTypes = true,
ThrowOnNotFoundExecutingExtensionTypes = true,
ThrowOnNotFoundWebhookEventTypes = true
};
new TypeDeclarationAnalyzer(options).AssertNoIssues();
}));
Assert.Null(Record.Exception(() =>
{
string workdir = Environment.CurrentDirectory;
string projdir = Path.Combine(workdir, "../../../../../");
var options = new SourceFileAnalyzerOptions()
{
SdkAssembly = Assembly.GetAssembly(typeof(WechatTenpayBusinessClient))!,
SdkRequestModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models",
SdkResponseModelDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Models",
SdkWebhookEventDeclarationNamespace = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Events",
ProjectSourceRootDirectory = Path.Combine(projdir, "./src/SKIT.FlurlHttpClient.Wechat.TenpayBusiness/"),
ProjectTestRootDirectory = Path.Combine(projdir, "./test/SKIT.FlurlHttpClient.Wechat.TenpayBusiness.UnitTests/"),
ThrowOnNotFoundRequestModelClassCodeFiles = true,
ThrowOnNotFoundResponseModelClassCodeFiles = true,
ThrowOnNotFoundExecutingExtensionClassCodeFiles = true,
ThrowOnNotFoundWebhookEventClassCodeFiles = true,
ThrowOnNotFoundRequestModelSerializationSampleFiles = true,
ThrowOnNotFoundResponseModelSerializationSampleFiles = true,
ThrowOnNotFoundWebhookEventSerializationSampleFiles = true
};
new SourceFileAnalyzer(options).AssertNoIssues();
}));
}
}
}

View File

@ -11,22 +11,22 @@
<ItemGroup>
<None Remove=".gitignore" />
<Content Include="appsettings.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
<Content Include="appsettings.*.json" Condition="'$(Configuration)' == 'Debug'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="appsettings.*.json" Condition="'$(Configuration)' == 'Debug'">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</Content>
<Content Include="ModelSamples/**/*.json" />
<Content Include="EventSamples/**/*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Tools.CodeAnalyzer" Version="0.1.0-alpha.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Tools.CodeAnalyzer" Version="0.3.0-preview.1" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,26 +0,0 @@
using SKIT.FlurlHttpClient.Tools.CodeAnalyzer;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.UnitTests
{
public class TestCase_CodeReview
{
[Fact(DisplayName = "测试用例:代码质量分析")]
public void TestCodeAnalyzer()
{
Assert.Null(Record.Exception(() =>
{
CodeAnalyzerOptions options = new CodeAnalyzerOptions()
{
AssemblyName = "SKIT.FlurlHttpClient.Wechat.TenpayBusiness",
WorkDirectoryForSourceCode = TestConfigs.WorkDirectoryForSdk,
WorkDirectoryForTestSample = TestConfigs.WorkDirectoryForTest
};
CodeAnalyzer analyzer = new CodeAnalyzer(options);
analyzer.Start();
analyzer.Assert();
analyzer.Flush();
}));
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Text.Json;
@ -16,15 +16,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.UnitTests
using var stream = File.OpenRead("appsettings.local.json");
using var jdoc = JsonDocument.Parse(stream);
var config = jdoc.RootElement.GetProperty("TestConfig");
var config = jdoc.RootElement.GetProperty("TestConfigs");
WechatPlatformId = config.GetProperty("PlatformId").GetString()!;
WechatPlatformCertSerialNumber = config.GetProperty("PlatformCertSerialNumber").GetString()!;
WechatPlatformCertPrivateKey = config.GetProperty("PlatformCertPrivateKey").GetString()!;
WechatTBEPCertSerialNumber = config.GetProperty("TBEPCertSerialNumber").GetString()!;
WechatTBEPCertPrivateKey = config.GetProperty("TBEPCertPrivateKey").GetString()!;
WorkDirectoryForSdk = jdoc.RootElement.GetProperty("WorkDirectoryForSdk").GetString()!;
WorkDirectoryForTest = jdoc.RootElement.GetProperty("WorkDirectoryForTest").GetString()!;
}
catch (Exception ex)
{
@ -37,8 +34,5 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.UnitTests
public static readonly string WechatPlatformCertPrivateKey;
public static readonly string WechatTBEPCertSerialNumber;
public static readonly string WechatTBEPCertPrivateKey;
public static readonly string WorkDirectoryForSdk;
public static readonly string WorkDirectoryForTest;
}
}

View File

@ -1,11 +1,9 @@
{
"TestConfig": {
"TestConfigs": {
"PlatformId": "请在此填写用于测试的微企付商户号",
"PlatformCertSerialNumber": "请在此填写用于测试的微企付平台 API 证书序列号",
"PlatformCertPrivateKey": "请在此填写用于测试的微企付平台 API 证书私钥(字符串格式)",
"TBEPCertSerialNumber": "请在此填写用于测试的微企付证书序列号",
"TBEPCertPrivateKey": "请在此填写用于测试的微企付证书私钥(字符串格式)"
},
"WorkDirectoryForSdk": "请输入当前 SDK 项目所在的目录完整路径,如 C:\\Project\\src\\SKIT.FlurlHttpClient.Wechat.TenpayBusiness\\",
"WorkDirectoryForTest": "请输入当前测试项目所在的目录完整路径,如 C:\\Project\\test\\SKIT.FlurlHttpClient.Wechat.TenpayBusiness.UnitTests\\"
}
}