using System;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
{
using SKIT.FlurlHttpClient.Internal;
///
/// 一个微信支付 API HTTP 客户端。
///
public partial class WechatTenpayClient : CommonClientBase, ICommonClient
{
///
/// 获取当前客户端使用的微信商户平台凭证。
///
public Settings.Credentials Credentials { get; }
///
/// 用指定的配置项初始化 类的新实例。
///
/// 配置项。
public WechatTenpayClient(WechatTenpayClientOptions options)
: this(options, null)
{
}
///
///
///
///
///
///
internal protected WechatTenpayClient(WechatTenpayClientOptions options, HttpClient? httpClient, bool disposeClient = true)
: base(httpClient ?? new HttpClient(new Settings.WechatpayHttpHandler(options.MerchantCertificateBytes, options.MerchantCertificatePassword ?? options.MerchantId)), disposeClient)
{
if (options is null) throw new ArgumentNullException(nameof(options));
Credentials = new Settings.Credentials(options);
FlurlClient.BaseUrl = options.Endpoint ?? WechatTenpayEndpoints.DEFAULT;
FlurlClient.WithTimeout(options.Timeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(options.Timeout));
}
///
/// 使用当前客户端生成一个新的 对象。
///
///
///
///
///
public IFlurlRequest CreateFlurlRequest(WechatTenpayRequest request, HttpMethod httpMethod, params object[] urlSegments)
{
IFlurlRequest flurlRequest = base.CreateFlurlRequest(request, httpMethod, urlSegments);
if (request.MerchantId is null)
{
request.MerchantId = Credentials.MerchantId;
}
if (request.AppId is null)
{
request.AppId = Credentials.AppId;
}
if (request is WechatTenpaySignableRequest signableRequest)
{
if (signableRequest.NonceString is null)
{
signableRequest.NonceString = Guid.NewGuid().ToString("N");
}
if (signableRequest.Signature is null)
{
try
{
signableRequest.Signature = Utilities.RequestSigner.SignFromJson(
json: JsonSerializer.Serialize(signableRequest),
secret: Credentials.MerchantSecret,
signType: signableRequest.SignType
);
}
catch (Exception ex)
{
throw new WechatTenpayException("Failed to sign request. Please see the inner exception for more details.", ex);
}
}
}
return flurlRequest;
}
///
/// 异步发起请求。
///
///
///
///
///
///
public async Task SendFlurlRequestAsync(IFlurlRequest flurlRequest, HttpContent? httpContent = null, CancellationToken cancellationToken = default)
where T : WechatTenpayResponse, new()
{
if (flurlRequest is null) throw new ArgumentNullException(nameof(flurlRequest));
using IFlurlResponse flurlResponse = await base.SendFlurlRequestAsync(flurlRequest, httpContent, cancellationToken).ConfigureAwait(false);
return await WrapFlurlResponseAsJsonAsync(flurlResponse, cancellationToken).ConfigureAwait(false);
}
///
/// 异步发起请求。
///
///
///
///
///
///
public async Task SendFlurlRequestAsXmlAsync(IFlurlRequest flurlRequest, object? data = null, CancellationToken cancellationToken = default)
where T : WechatTenpayResponse, new()
{
if (flurlRequest is null) throw new ArgumentNullException(nameof(flurlRequest));
bool isSimpleRequest = data is null ||
flurlRequest.Verb == HttpMethod.Get ||
flurlRequest.Verb == HttpMethod.Head ||
flurlRequest.Verb == HttpMethod.Options;
if (isSimpleRequest)
{
using IFlurlResponse flurlResponse = await base.SendFlurlRequestAsync(flurlRequest, null, cancellationToken).ConfigureAwait(false);
return await WrapFlurlResponseAsXmlAsync(flurlResponse, cancellationToken).ConfigureAwait(false);
}
else
{
HttpContent httpContent;
try
{
string json = JsonSerializer.Serialize(data);
string xml = Utilities.XmlHelper.ConvertFromJson(json);
httpContent = new StringContent(xml, Encoding.UTF8, "application/xml");
}
catch (Exception ex)
{
throw new CommonSerializationException(ex.Message, ex);
}
try
{
using IFlurlResponse flurlResponse = await SendFlurlRequestAsync(flurlRequest, httpContent: httpContent, cancellationToken).ConfigureAwait(false);
return await WrapFlurlResponseAsXmlAsync(flurlResponse, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
throw;
}
catch (CommonException)
{
throw;
}
catch (Exception ex)
{
throw new CommonException(ex.Message, ex);
}
finally
{
httpContent.Dispose();
}
}
}
private async Task WrapFlurlResponseAsXmlAsync(IFlurlResponse flurlResponse, CancellationToken cancellationToken = default)
where TResponse : WechatTenpayResponse, new()
{
var tmp = await WrapFlurlResponseAsync(flurlResponse, cancellationToken).ConfigureAwait(false);
var tmpBytes = tmp.GetRawBytes();
var result = default(TResponse);
if (_StringSyntaxAssert.MaybeXml(tmpBytes))
{
try
{
string xml = Encoding.UTF8.GetString(tmpBytes);
string json = Utilities.XmlHelper.ConvertToJson(xml);
result = JsonSerializer.Deserialize(json);
}
catch (Exception ex)
{
throw new CommonSerializationException(ex.Message, ex);
}
var accessor = _UnsafeAccessor.VisitCommonResponse(result);
accessor.RawStatus = tmp.GetRawStatus();
accessor.RawHeaders = tmp.GetRawHeaders();
accessor.RawBytes = tmpBytes;
}
else
{
result = tmp;
}
return result;
}
}
}