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; } } }