diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientExecuteCgibinPayToolExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientExecuteCgibinPayToolExtensions.cs new file mode 100644 index 00000000..3264b588 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientExecuteCgibinPayToolExtensions.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Flurl; +using Flurl.Http; +using Newtonsoft.Json.Linq; + +namespace SKIT.FlurlHttpClient.Wechat.Work +{ + public static class WechatWorkClientExecuteCgibinPayToolExtensions + { + #region Invoice + /// + /// 异步调用 [POST] /cgi-bin/paytool/get_invoice_list 接口。 + /// + /// REF:
+ /// + ///
+ ///
+ /// + /// + /// + /// + public static async Task ExecuteCgibinPayToolGetInvoiceListAsync(this WechatWorkClient client, Models.CgibinPayToolGetInvoiceListRequest request, CancellationToken cancellationToken = default) + { + if (client is null) throw new ArgumentNullException(nameof(client)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + IFlurlRequest flurlReq = client + .CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "get_invoice_list") + .SetQueryParam("provider_access_token", request.ProviderAccessToken); + + return await client.SendFlurlRequestAsJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// 异步调用 [POST] /cgi-bin/paytool/mark_invoice_status 接口。 + /// + /// REF:
+ /// + ///
+ ///
+ /// + /// + /// + /// + public static async Task ExecuteCgibinPayToolMarkInvoiceStatusAsync(this WechatWorkClient client, Models.CgibinPayToolMarkInvoiceStatusRequest request, CancellationToken cancellationToken = default) + { + if (client is null) throw new ArgumentNullException(nameof(client)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + IFlurlRequest flurlReq = client + .CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "mark_invoice_status") + .SetQueryParam("provider_access_token", request.ProviderAccessToken); + + return await client.SendFlurlRequestAsJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false); + } + #endregion + + #region Order + private static T PreprocessRequest(WechatWorkClient client, ref T request) + where T : Models.CgibinPayToolOrderRequestBase, new() + { + if (client is null) throw new ArgumentNullException(nameof(request)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + if (request.NonceString is null) + { + request.NonceString = Guid.NewGuid().ToString("N"); + } + + if (request.Timestamp is null) + { + request.Timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds(); + } + + if (request.Signature is null) + { + if (string.IsNullOrEmpty(client.Credentials.PayToolApiSecret)) + throw new WechatWorkException("Could not sign request, because paytool API secret is missing."); + + Func> flatten = default!; + flatten = (path, token) => + { + List results = new List(); + + switch (token.Type) + { + case JTokenType.Null: + break; + + case JTokenType.Integer: + case JTokenType.Float: + case JTokenType.String: + { + string key = path; + string val = token.ToString(); + if (string.IsNullOrEmpty(key)) + throw new InvalidOperationException(); + if (!string.IsNullOrEmpty(val)) + results.Add($"{key}={val}"); + } + break; + + case JTokenType.Boolean: + { + string key = path; + string val = token.ToString().ToLower(); + if (string.IsNullOrEmpty(key)) + throw new InvalidOperationException(); + results.Add($"{key}={val}"); + } + break; + + case JTokenType.Array: + { + foreach (var item in (JArray)token) + { + results.AddRange(flatten(path, item)); + } + } + break; + + case JTokenType.Object: + { + foreach (JProperty prop in ((JObject)token).Properties()) + { + results.AddRange(flatten(prop.Name, prop.Value)); + } + } + break; + + default: + throw new InvalidOperationException(); + } + + return results; + }; + + List tmp = flatten(string.Empty, JObject.Parse(client.JsonSerializer.Serialize(request))); + tmp.Sort(StringComparer.Ordinal); + + byte[] keyBytes = Encoding.UTF8.GetBytes(client.Credentials.PayToolApiSecret!); + byte[] msgBytes = Encoding.UTF8.GetBytes(string.Join("&", tmp)); + byte[] signBytes = Utilities.HMACUtility.HashWithSHA256(keyBytes, msgBytes); + request.Signature = Convert.ToBase64String(signBytes); + } + + return request; + } + + /// + /// 异步调用 [POST] /cgi-bin/paytool/open_order 接口。 + /// + /// REF:
+ /// + ///
+ ///
+ /// + /// + /// + /// + public static async Task ExecuteCgibinPayToolOpenOrderAsync(this WechatWorkClient client, Models.CgibinPayToolOpenOrderRequest request, CancellationToken cancellationToken = default) + { + if (client is null) throw new ArgumentNullException(nameof(client)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + PreprocessRequest(client, ref request); + + IFlurlRequest flurlReq = client + .CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "open_order") + .SetQueryParam("provider_access_token", request.ProviderAccessToken); + + return await client.SendFlurlRequestAsJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// 异步调用 [POST] /cgi-bin/paytool/close_order 接口。 + /// + /// REF:
+ /// + ///
+ ///
+ /// + /// + /// + /// + public static async Task ExecuteCgibinPayToolCloseOrderAsync(this WechatWorkClient client, Models.CgibinPayToolCloseOrderRequest request, CancellationToken cancellationToken = default) + { + if (client is null) throw new ArgumentNullException(nameof(client)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + PreprocessRequest(client, ref request); + + IFlurlRequest flurlReq = client + .CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "close_order") + .SetQueryParam("provider_access_token", request.ProviderAccessToken); + + return await client.SendFlurlRequestAsJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// 异步调用 [POST] /cgi-bin/paytool/get_order_list 接口。 + /// + /// REF:
+ /// + ///
+ ///
+ /// + /// + /// + /// + public static async Task ExecuteCgibinPayToolGetOrderListAsync(this WechatWorkClient client, Models.CgibinPayToolGetOrderListRequest request, CancellationToken cancellationToken = default) + { + if (client is null) throw new ArgumentNullException(nameof(client)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + PreprocessRequest(client, ref request); + + IFlurlRequest flurlReq = client + .CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "get_order_list") + .SetQueryParam("provider_access_token", request.ProviderAccessToken); + + return await client.SendFlurlRequestAsJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// 异步调用 [POST] /cgi-bin/paytool/get_order_detail 接口。 + /// + /// REF:
+ /// + ///
+ ///
+ /// + /// + /// + /// + public static async Task ExecuteCgibinPayToolGetOrderDetailAsync(this WechatWorkClient client, Models.CgibinPayToolGetOrderDetailRequest request, CancellationToken cancellationToken = default) + { + if (client is null) throw new ArgumentNullException(nameof(client)); + if (request is null) throw new ArgumentNullException(nameof(request)); + + PreprocessRequest(client, ref request); + + IFlurlRequest flurlReq = client + .CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "get_order_detail") + .SetQueryParam("provider_access_token", request.ProviderAccessToken); + + return await client.SendFlurlRequestAsJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false); + } + #endregion + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinExternalContact/CgibinExternalContactGetResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinExternalContact/CgibinExternalContactGetResponse.cs index a5d21ce3..a5cd22d5 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinExternalContact/CgibinExternalContactGetResponse.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinExternalContact/CgibinExternalContactGetResponse.cs @@ -259,11 +259,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Models public string? State { get; set; } /// - /// 获取或设置发起添加的成员账号。 + /// 获取或设置操作人的成员账号。 /// [Newtonsoft.Json.JsonProperty("oper_userid")] [System.Text.Json.Serialization.JsonPropertyName("oper_userid")] - public string? OperateUserId { get; set; } + public string? OperatorUserId { get; set; } /// /// 获取或设置视频号信息。 diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListRequest.cs new file mode 100644 index 00000000..3436764e --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListRequest.cs @@ -0,0 +1,43 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/get_invoice_list 接口的请求。 + /// + public class CgibinPayToolGetInvoiceListRequest : WechatWorkRequest + { + /// + /// 获取或设置服务商 AccessToken。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string ProviderAccessToken { get; set; } = string.Empty; + + /// + /// 获取或设置开票时间开始时间戳。 + /// + [Newtonsoft.Json.JsonProperty("start_time")] + [System.Text.Json.Serialization.JsonPropertyName("start_time")] + public long? StartTimestamp { get; set; } + + /// + /// 获取或设置开票时间结束时间戳。 + /// + [Newtonsoft.Json.JsonProperty("end_time")] + [System.Text.Json.Serialization.JsonPropertyName("end_time")] + public long? EndTimestamp { get; set; } + + /// + /// 获取或设置分页游标。 + /// + [Newtonsoft.Json.JsonProperty("cursor")] + [System.Text.Json.Serialization.JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// + /// 获取或设置分页每页数量。 + /// + [Newtonsoft.Json.JsonProperty("limit")] + [System.Text.Json.Serialization.JsonPropertyName("limit")] + public int? Limit { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListResponse.cs new file mode 100644 index 00000000..0119d63b --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListResponse.cs @@ -0,0 +1,170 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/get_invoice_list 接口的响应。 + /// + public class CgibinPayToolGetInvoiceListResponse : WechatWorkResponse + { + public static class Types + { + public class Invoice + { + /// + /// 获取或设置开票订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = default!; + + /// + /// 获取或设置客户企业 CorpId。 + /// + [Newtonsoft.Json.JsonProperty("custom_corpid")] + [System.Text.Json.Serialization.JsonPropertyName("custom_corpid")] + public string? CustomCorpId { get; set; } + + /// + /// 获取或设置申请开票时间戳。 + /// + [Newtonsoft.Json.JsonProperty("apply_time")] + [System.Text.Json.Serialization.JsonPropertyName("apply_time")] + public long ApplyTimestamp { get; set; } + + /// + /// 获取或设置发票类型。 + /// + [Newtonsoft.Json.JsonProperty("invoice_type")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_type")] + public int InvoiceType { get; set; } + + /// + /// 获取或设置实付金额(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("paid_price")] + [System.Text.Json.Serialization.JsonPropertyName("paid_price")] + public int PaidPrice { get; set; } + + /// + /// 获取或设置开票状态。 + /// + [Newtonsoft.Json.JsonProperty("invoice_status")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_status")] + public int InvoiceStatus { get; set; } + + /// + /// 获取或设置发票抬头。 + /// + [Newtonsoft.Json.JsonProperty("invoice_title")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_title")] + public string InvoiceTitle { get; set; } = default!; + + /// + /// 获取或设置纳税人识别号。 + /// + [Newtonsoft.Json.JsonProperty("tax_number")] + [System.Text.Json.Serialization.JsonPropertyName("tax_number")] + public string TaxNumber { get; set; } = default!; + + /// + /// 获取或设置发票收取方式。 + /// + [Newtonsoft.Json.JsonProperty("send_way")] + [System.Text.Json.Serialization.JsonPropertyName("send_way")] + public int SendWay { get; set; } + + /// + /// 获取或设置联系人姓名。 + /// + [Newtonsoft.Json.JsonProperty("contact_name")] + [System.Text.Json.Serialization.JsonPropertyName("contact_name")] + public string? ContactName { get; set; } + + /// + /// 获取或设置联系电话。 + /// + [Newtonsoft.Json.JsonProperty("contact_tel")] + [System.Text.Json.Serialization.JsonPropertyName("contact_tel")] + public string? ContactTeleNumber { get; set; } + + /// + /// 获取或设置收件地址。 + /// + [Newtonsoft.Json.JsonProperty("contact_addr")] + [System.Text.Json.Serialization.JsonPropertyName("contact_addr")] + public string? ContactAddress { get; set; } + + /// + /// 获取或设置邮政编码。 + /// + [Newtonsoft.Json.JsonProperty("contact_postcode")] + [System.Text.Json.Serialization.JsonPropertyName("contact_postcode")] + public string? ContactPostCode { get; set; } + + /// + /// 获取或设置电子邮箱。 + /// + [Newtonsoft.Json.JsonProperty("receive_email")] + [System.Text.Json.Serialization.JsonPropertyName("receive_email")] + public string? ReceiveEmail { get; set; } + + /// + /// 获取或设置公司地址。 + /// + [Newtonsoft.Json.JsonProperty("company_addr")] + [System.Text.Json.Serialization.JsonPropertyName("company_addr")] + public string CompanyAddress { get; set; } = default!; + + /// + /// 获取或设置公司电话。 + /// + [Newtonsoft.Json.JsonProperty("company_tel")] + [System.Text.Json.Serialization.JsonPropertyName("company_tel")] + public string CompanyTeleNumber { get; set; } = default!; + + /// + /// 获取或设置开户行。 + /// + [Newtonsoft.Json.JsonProperty("bank_name")] + [System.Text.Json.Serialization.JsonPropertyName("bank_name")] + public string BankName { get; set; } = default!; + + /// + /// 获取或设置银行账号。 + /// + [Newtonsoft.Json.JsonProperty("bank_account_number")] + [System.Text.Json.Serialization.JsonPropertyName("bank_account_number")] + public string BankAccountNumber { get; set; } = default!; + + /// + /// 获取或设置开票备注。 + /// + [Newtonsoft.Json.JsonProperty("invoice_note")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_note")] + public string InvoiceNotes { get; set; } = default!; + } + } + + /// + /// 获取或设置发票列表。 + /// + [Newtonsoft.Json.JsonProperty("invoice_list")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_list")] + public Types.Invoice[] InvoiceList { get; set; } = default!; + + /// + /// 获取或设置分页游标。 + /// + [Newtonsoft.Json.JsonProperty("next_cursor")] + [System.Text.Json.Serialization.JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } + + /// + /// 获取或设置是否还有更多数据。 + /// + [Newtonsoft.Json.JsonProperty("has_more")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.NumericalBooleanConverter))] + [System.Text.Json.Serialization.JsonPropertyName("has_more")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.NumericalBooleanConverter))] + public bool HasMore { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusRequest.cs new file mode 100644 index 00000000..3ea8400d --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusRequest.cs @@ -0,0 +1,43 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/mark_invoice_status 接口的请求。 + /// + public class CgibinPayToolMarkInvoiceStatusRequest : WechatWorkRequest + { + /// + /// 获取或设置服务商 AccessToken。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string ProviderAccessToken { get; set; } = string.Empty; + + /// + /// 获取或设置开票订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + + /// + /// 获取或设置操作人的成员账号。 + /// + [Newtonsoft.Json.JsonProperty("oper_userid")] + [System.Text.Json.Serialization.JsonPropertyName("oper_userid")] + public string? OperatorUserId { get; set; } + + /// + /// 获取或设置开票状态。 + /// + [Newtonsoft.Json.JsonProperty("invoice_status")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_status")] + public int InvoiceStatus { get; set; } + + /// + /// 获取或设置开票备注。 + /// + [Newtonsoft.Json.JsonProperty("invoice_note")] + [System.Text.Json.Serialization.JsonPropertyName("invoice_note")] + public string InvoiceNotes { get; set; } = string.Empty; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusResponse.cs new file mode 100644 index 00000000..f089bb2b --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusResponse.cs @@ -0,0 +1,9 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/mark_invoice_status 接口的响应。 + /// + public class CgibinPayToolMarkInvoiceStatusResponse : WechatWorkResponse + { + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolCloseOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolCloseOrderRequest.cs new file mode 100644 index 00000000..1c22b341 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolCloseOrderRequest.cs @@ -0,0 +1,15 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/close_order 接口的请求。 + /// + public class CgibinPayToolCloseOrderRequest : CgibinPayToolOrderRequestBase + { + /// + /// 获取或设置收款订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolCloseOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolCloseOrderResponse.cs new file mode 100644 index 00000000..8510ffaa --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolCloseOrderResponse.cs @@ -0,0 +1,9 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/close_order 接口的响应。 + /// + public class CgibinPayToolCloseOrderResponse : WechatWorkResponse + { + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderDetailRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderDetailRequest.cs new file mode 100644 index 00000000..ada23dc2 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderDetailRequest.cs @@ -0,0 +1,15 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/get_order_detail 接口的请求。 + /// + public class CgibinPayToolGetOrderDetailRequest : CgibinPayToolOrderRequestBase + { + /// + /// 获取或设置收款订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = string.Empty; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderDetailResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderDetailResponse.cs new file mode 100644 index 00000000..bcd8032c --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderDetailResponse.cs @@ -0,0 +1,446 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/get_order_detail 接口的响应。 + /// + public class CgibinPayToolGetOrderDetailResponse : WechatWorkResponse + { + public static class Types + { + public class Order + { + public static class Types + { + public class Product + { + public static class Types + { + public class ProductBase + { + /// + /// 获取或设置购买类型。 + /// + [Newtonsoft.Json.JsonProperty("order_type")] + [System.Text.Json.Serialization.JsonPropertyName("order_type")] + public int OrderType { get; set; } + + /// + /// 获取或设置是否推送确认提醒。 + /// + [Newtonsoft.Json.JsonProperty("notify_custom_corp")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.NumericalBooleanConverter))] + [System.Text.Json.Serialization.JsonPropertyName("notify_custom_corp")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.NumericalBooleanConverter))] + public bool? IsNotifyCustomCorp { get; set; } + } + + public class ThirdPartyApp : ProductBase + { + public static class Types + { + public class BuyInfo + { + public static class Types + { + public class DiscountInfo + { + /// + /// 获取或设置优惠类型。 + /// + [Newtonsoft.Json.JsonProperty("discount_type")] + [System.Text.Json.Serialization.JsonPropertyName("discount_type")] + public int DiscountType { get; set; } + + /// + /// 获取或设置优惠金额(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("discount_amount")] + [System.Text.Json.Serialization.JsonPropertyName("discount_amount")] + public int? DiscountAmount { get; set; } + + /// + /// 获取或设置优惠折扣(单位:百分数)。 + /// + [Newtonsoft.Json.JsonProperty("discount_ratio")] + [System.Text.Json.Serialization.JsonPropertyName("discount_ratio")] + public int? DiscountRatio { get; set; } + + /// + /// 获取或设置优惠原因。 + /// + [Newtonsoft.Json.JsonProperty("discount_remarks")] + [System.Text.Json.Serialization.JsonPropertyName("discount_remarks")] + public string? DiscountRemarks { get; set; } + } + } + + /// + /// 获取或设置套件 ID。 + /// + [Newtonsoft.Json.JsonProperty("suiteid")] + [System.Text.Json.Serialization.JsonPropertyName("suiteid")] + public string SuiteId { get; set; } = default!; + + /// + /// 获取或设置旧版应用 ID。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public int? AppId { get; set; } + + /// + /// 获取或设置版本号 ID。 + /// + [Newtonsoft.Json.JsonProperty("edition_id")] + [System.Text.Json.Serialization.JsonPropertyName("edition_id")] + public string EditionId { get; set; } = default!; + + /// + /// 获取或设置购买人数。 + /// + [Newtonsoft.Json.JsonProperty("user_count")] + [System.Text.Json.Serialization.JsonPropertyName("user_count")] + public int? UserCount { get; set; } + + /// + /// 获取或设置购买天数。 + /// + [Newtonsoft.Json.JsonProperty("duration_days")] + [System.Text.Json.Serialization.JsonPropertyName("duration_days")] + public int? DurationDays { get; set; } + + /// + /// 获取或设置生效日期字符串(格式:yyyyMMdd)。 + /// + [Newtonsoft.Json.JsonProperty("take_effect_date")] + [System.Text.Json.Serialization.JsonPropertyName("take_effect_date")] + public string? TakeEffectDateString { get; set; } + + /// + /// 获取或设置原价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("origin_price")] + [System.Text.Json.Serialization.JsonPropertyName("origin_price")] + public int OriginPrice { get; set; } + + /// + /// 获取或设置现价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("paid_price")] + [System.Text.Json.Serialization.JsonPropertyName("paid_price")] + public int PaidPrice { get; set; } + } + } + + /// + /// 获取或设置购买应用列表。 + /// + [Newtonsoft.Json.JsonProperty("buy_info_list")] + [System.Text.Json.Serialization.JsonPropertyName("buy_info_list")] + public Types.BuyInfo[] BuyInfoList { get; set; } = default!; + } + + public class CustomizedApp : ProductBase + { + public static class Types + { + public class BuyInfo + { + /// + /// 获取或设置套件 ID。 + /// + [Newtonsoft.Json.JsonProperty("suiteid")] + [System.Text.Json.Serialization.JsonPropertyName("suiteid")] + public string SuiteId { get; set; } = default!; + + /// + /// 获取或设置旧版应用 ID。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public int? AppId { get; set; } + + /// + /// 获取或设置购买人数。 + /// + [Newtonsoft.Json.JsonProperty("user_count")] + [System.Text.Json.Serialization.JsonPropertyName("user_count")] + public int? UserCount { get; set; } + + /// + /// 获取或设置购买天数。 + /// + [Newtonsoft.Json.JsonProperty("duration_days")] + [System.Text.Json.Serialization.JsonPropertyName("duration_days")] + public int? DurationDays { get; set; } + + /// + /// 获取或设置生效日期字符串(格式:yyyyMMdd)。 + /// + [Newtonsoft.Json.JsonProperty("take_effect_date")] + [System.Text.Json.Serialization.JsonPropertyName("take_effect_date")] + public string? TakeEffectDateString { get; set; } + + /// + /// 获取或设置原价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("origin_price")] + [System.Text.Json.Serialization.JsonPropertyName("origin_price")] + public int OriginPrice { get; set; } + + /// + /// 获取或设置现价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("paid_price")] + [System.Text.Json.Serialization.JsonPropertyName("paid_price")] + public int PaidPrice { get; set; } + } + } + + /// + /// 获取或设置购买应用列表。 + /// + [Newtonsoft.Json.JsonProperty("buy_info_list")] + [System.Text.Json.Serialization.JsonPropertyName("buy_info_list")] + public Types.BuyInfo[] BuyInfoList { get; set; } = default!; + } + + public class PromotionCase : ProductBase + { + public static class Types + { + public class BuyInfo + { + /// + /// 获取或设置套件 ID。 + /// + [Newtonsoft.Json.JsonProperty("suiteid")] + [System.Text.Json.Serialization.JsonPropertyName("suiteid")] + public string SuiteId { get; set; } = default!; + + /// + /// 获取或设置旧版应用 ID。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public int? AppId { get; set; } + + /// + /// 获取或设置版本号 ID。 + /// + [Newtonsoft.Json.JsonProperty("edition_id")] + [System.Text.Json.Serialization.JsonPropertyName("edition_id")] + public string EditionId { get; set; } = default!; + + /// + /// 获取或设置购买人数。 + /// + [Newtonsoft.Json.JsonProperty("user_count")] + [System.Text.Json.Serialization.JsonPropertyName("user_count")] + public int? UserCount { get; set; } + + /// + /// 获取或设置生效日期字符串(格式:yyyyMMdd)。 + /// + [Newtonsoft.Json.JsonProperty("take_effect_date")] + [System.Text.Json.Serialization.JsonPropertyName("take_effect_date")] + public string? TakeEffectDateString { get; set; } + } + } + + /// + /// 获取或设置行业方案 ID。 + /// + [Newtonsoft.Json.JsonProperty("case_id")] + [System.Text.Json.Serialization.JsonPropertyName("case_id")] + public string CaseId { get; set; } = default!; + + /// + /// 获取或设置行业方案版本名称 + /// + [Newtonsoft.Json.JsonProperty("promotion_edition_name")] + [System.Text.Json.Serialization.JsonPropertyName("promotion_edition_name")] + public string PromotionEditionName { get; set; } = default!; + + /// + /// 获取或设置购买天数。 + /// + [Newtonsoft.Json.JsonProperty("duration_days")] + [System.Text.Json.Serialization.JsonPropertyName("duration_days")] + public int? DurationDays { get; set; } + + /// + /// 获取或设置购买应用列表。 + /// + [Newtonsoft.Json.JsonProperty("buy_info_list")] + [System.Text.Json.Serialization.JsonPropertyName("buy_info_list")] + public Types.BuyInfo[]? BuyInfoList { get; set; } + } + } + + /// + /// 获取或设置第三方应用商品信息。 + /// + [Newtonsoft.Json.JsonProperty("third_app")] + [System.Text.Json.Serialization.JsonPropertyName("third_app")] + public Types.ThirdPartyApp? ThirdPartyApp { get; set; } + + /// + /// 获取或设置代开发应用商品信息。 + /// + [Newtonsoft.Json.JsonProperty("customized_app")] + [System.Text.Json.Serialization.JsonPropertyName("customized_app")] + public Types.CustomizedApp? CustomizedApp { get; set; } + + /// + /// 获取或设置行业解决方案商品信息。 + /// + [Newtonsoft.Json.JsonProperty("promotion_case")] + [System.Text.Json.Serialization.JsonPropertyName("promotion_case")] + public Types.PromotionCase? PromotionCase { get; set; } + } + } + + /// + /// 获取或设置收款订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = default!; + + /// + /// 获取或设置客户企业 CorpId。 + /// + [Newtonsoft.Json.JsonProperty("custom_corpid")] + [System.Text.Json.Serialization.JsonPropertyName("custom_corpid")] + public string? CustomCorpId { get; set; } + + /// + /// 获取或设置客户企业简称。 + /// + [Newtonsoft.Json.JsonProperty("custom_corp_name")] + [System.Text.Json.Serialization.JsonPropertyName("custom_corp_name")] + public string? CustomCorpName { get; set; } + + /// + /// 获取或设置创建订单时间戳。 + /// + [Newtonsoft.Json.JsonProperty("create_time")] + [System.Text.Json.Serialization.JsonPropertyName("create_time")] + public long CreateTimestamp { get; set; } + + /// + /// 获取或设置业务类型。 + /// + [Newtonsoft.Json.JsonProperty("business_type")] + [System.Text.Json.Serialization.JsonPropertyName("business_type")] + public int BusinessType { get; set; } + + /// + /// 获取或设置购买内容。 + /// + [Newtonsoft.Json.JsonProperty("buy_content")] + [System.Text.Json.Serialization.JsonPropertyName("buy_content")] + public string BuyContent { get; set; } = default!; + + /// + /// 获取或设置原价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("origin_price")] + [System.Text.Json.Serialization.JsonPropertyName("origin_price")] + public int OriginPrice { get; set; } + + /// + /// 获取或设置现价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("paid_price")] + [System.Text.Json.Serialization.JsonPropertyName("paid_price")] + public int PaidPrice { get; set; } + + /// + /// 获取或设置订单状态。 + /// + [Newtonsoft.Json.JsonProperty("order_status")] + [System.Text.Json.Serialization.JsonPropertyName("order_status")] + public int OrderStatus { get; set; } + + /// + /// 获取或设置订单来源。 + /// + [Newtonsoft.Json.JsonProperty("order_from")] + [System.Text.Json.Serialization.JsonPropertyName("order_from")] + public int OrderFrom { get; set; } + + /// + /// 获取或设置订单创建人成员账号。 + /// + [Newtonsoft.Json.JsonProperty("creator")] + [System.Text.Json.Serialization.JsonPropertyName("creator")] + public string? CreatorUserId { get; set; } + + /// + /// 获取或设置支付方式。 + /// + [Newtonsoft.Json.JsonProperty("pay_type")] + [System.Text.Json.Serialization.JsonPropertyName("pay_type")] + public int PayType { get; set; } + + /// + /// 获取或设置付款方式。 + /// + [Newtonsoft.Json.JsonProperty("pay_channel")] + [System.Text.Json.Serialization.JsonPropertyName("pay_channel")] + public int? PayChannel { get; set; } + + /// + /// 获取或设置付款流水号。 + /// + [Newtonsoft.Json.JsonProperty("channel_order_id")] + [System.Text.Json.Serialization.JsonPropertyName("channel_order_id")] + public string? ChannelOrderId { get; set; } + + /// + /// 获取或设置付款时间戳。 + /// + [Newtonsoft.Json.JsonProperty("paid_time")] + [System.Text.Json.Serialization.JsonPropertyName("paid_time")] + public long? PaidTimestamp { get; set; } + + /// + /// 获取或设置收入到账类型。 + /// + [Newtonsoft.Json.JsonProperty("income_type")] + [System.Text.Json.Serialization.JsonPropertyName("income_type")] + public int? IncomeType { get; set; } + + /// + /// 获取或设置收入到账时间戳。 + /// + [Newtonsoft.Json.JsonProperty("income_time")] + [System.Text.Json.Serialization.JsonPropertyName("income_time")] + public long? IncomeTimestamp { get; set; } + + /// + /// 获取或设置收入到账金额(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("income_amount")] + [System.Text.Json.Serialization.JsonPropertyName("income_amount")] + public int? IncomeAmount { get; set; } + + /// + /// 获取或设置商品信息。 + /// + [Newtonsoft.Json.JsonProperty("product_list")] + [System.Text.Json.Serialization.JsonPropertyName("product_list")] + public Types.Product Product { get; set; } = default!; + } + } + + /// + /// 获取或设置订单信息。 + /// + [Newtonsoft.Json.JsonProperty("pay_order")] + [System.Text.Json.Serialization.JsonPropertyName("pay_order")] + public Types.Order Order { get; set; } = default!; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderListRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderListRequest.cs new file mode 100644 index 00000000..0682a4ac --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderListRequest.cs @@ -0,0 +1,44 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/get_order_list 接口的请求。 + /// + public class CgibinPayToolGetOrderListRequest : CgibinPayToolOrderRequestBase + { + /// + /// 获取或设置业务类型。 + /// + [Newtonsoft.Json.JsonProperty("business_type")] + [System.Text.Json.Serialization.JsonPropertyName("business_type")] + public int? BusinessType { get; set; } + + /// + /// 获取或设置创建订单时间开始时间戳。 + /// + [Newtonsoft.Json.JsonProperty("start_time")] + [System.Text.Json.Serialization.JsonPropertyName("start_time")] + public long? StartTimestamp { get; set; } + + /// + /// 获取或设置创建订单时间结束时间戳。 + /// + [Newtonsoft.Json.JsonProperty("end_time")] + [System.Text.Json.Serialization.JsonPropertyName("end_time")] + public long? EndTimestamp { get; set; } + + /// + /// 获取或设置分页游标。 + /// + [Newtonsoft.Json.JsonProperty("cursor")] + [System.Text.Json.Serialization.JsonPropertyName("cursor")] + public string? Cursor { get; set; } + + /// + /// 获取或设置分页每页数量。 + /// 默认值:100 + /// + [Newtonsoft.Json.JsonProperty("limit")] + [System.Text.Json.Serialization.JsonPropertyName("limit")] + public int Limit { get; set; } = 100; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderListResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderListResponse.cs new file mode 100644 index 00000000..f0998560 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolGetOrderListResponse.cs @@ -0,0 +1,107 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/get_order_list 接口的响应。 + /// + public class CgibinPayToolGetOrderListResponse : WechatWorkResponse + { + public static class Types + { + public class Order + { + /// + /// 获取或设置收款订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = default!; + + /// + /// 获取或设置客户企业 CorpId。 + /// + [Newtonsoft.Json.JsonProperty("custom_corpid")] + [System.Text.Json.Serialization.JsonPropertyName("custom_corpid")] + public string? CustomCorpId { get; set; } + + /// + /// 获取或设置创建订单时间戳。 + /// + [Newtonsoft.Json.JsonProperty("create_time")] + [System.Text.Json.Serialization.JsonPropertyName("create_time")] + public long CreateTimestamp { get; set; } + + /// + /// 获取或设置购买内容。 + /// + [Newtonsoft.Json.JsonProperty("buy_content")] + [System.Text.Json.Serialization.JsonPropertyName("buy_content")] + public string BuyContent { get; set; } = default!; + + /// + /// 获取或设置原价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("origin_price")] + [System.Text.Json.Serialization.JsonPropertyName("origin_price")] + public int OriginPrice { get; set; } + + /// + /// 获取或设置现价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("paid_price")] + [System.Text.Json.Serialization.JsonPropertyName("paid_price")] + public int PaidPrice { get; set; } + + /// + /// 获取或设置订单状态。 + /// + [Newtonsoft.Json.JsonProperty("order_status")] + [System.Text.Json.Serialization.JsonPropertyName("order_status")] + public int OrderStatus { get; set; } + + /// + /// 获取或设置订单来源。 + /// + [Newtonsoft.Json.JsonProperty("order_from")] + [System.Text.Json.Serialization.JsonPropertyName("order_from")] + public int OrderFrom { get; set; } + + /// + /// 获取或设置订单创建人成员账号。 + /// + [Newtonsoft.Json.JsonProperty("creator")] + [System.Text.Json.Serialization.JsonPropertyName("creator")] + public string? CreatorUserId { get; set; } + + /// + /// 获取或设置支付方式。 + /// + [Newtonsoft.Json.JsonProperty("pay_type")] + [System.Text.Json.Serialization.JsonPropertyName("pay_type")] + public int PayType { get; set; } + } + } + + /// + /// 获取或设置订单列表。 + /// + [Newtonsoft.Json.JsonProperty("pay_order_list")] + [System.Text.Json.Serialization.JsonPropertyName("pay_order_list")] + public Types.Order[] OrderList { get; set; } = default!; + + /// + /// 获取或设置分页游标。 + /// + [Newtonsoft.Json.JsonProperty("next_cursor")] + [System.Text.Json.Serialization.JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } + + /// + /// 获取或设置是否还有更多数据。 + /// + [Newtonsoft.Json.JsonProperty("has_more")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.NumericalBooleanConverter))] + [System.Text.Json.Serialization.JsonPropertyName("has_more")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.NumericalBooleanConverter))] + public bool HasMore { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolOpenOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolOpenOrderRequest.cs new file mode 100644 index 00000000..be688ff5 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolOpenOrderRequest.cs @@ -0,0 +1,323 @@ +using System.Collections.Generic; + +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/open_order 接口的请求。 + /// + public class CgibinPayToolOpenOrderRequest : CgibinPayToolOrderRequestBase + { + public static class Types + { + public class Product + { + public static class Types + { + public class ProductBase + { + /// + /// 获取或设置购买类型。 + /// + [Newtonsoft.Json.JsonProperty("order_type")] + [System.Text.Json.Serialization.JsonPropertyName("order_type")] + public int OrderType { get; set; } + + /// + /// 获取或设置是否推送确认提醒。 + /// + [Newtonsoft.Json.JsonProperty("notify_custom_corp")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.NumericalBooleanConverter))] + [System.Text.Json.Serialization.JsonPropertyName("notify_custom_corp")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.NumericalBooleanConverter))] + public bool? IsNotifyCustomCorp { get; set; } + } + + public class ThirdPartyApp : ProductBase + { + public static class Types + { + public class BuyInfo + { + public static class Types + { + public class DiscountInfo + { + /// + /// 获取或设置优惠类型。 + /// + [Newtonsoft.Json.JsonProperty("discount_type")] + [System.Text.Json.Serialization.JsonPropertyName("discount_type")] + public int DiscountType { get; set; } + + /// + /// 获取或设置优惠金额(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("discount_amount")] + [System.Text.Json.Serialization.JsonPropertyName("discount_amount")] + public int? DiscountAmount { get; set; } + + /// + /// 获取或设置优惠折扣(单位:百分数)。 + /// + [Newtonsoft.Json.JsonProperty("discount_ratio")] + [System.Text.Json.Serialization.JsonPropertyName("discount_ratio")] + public int? DiscountRatio { get; set; } + + /// + /// 获取或设置优惠原因。 + /// + [Newtonsoft.Json.JsonProperty("discount_remarks")] + [System.Text.Json.Serialization.JsonPropertyName("discount_remarks")] + public string? DiscountRemarks { get; set; } + } + } + + /// + /// 获取或设置套件 ID。 + /// + [Newtonsoft.Json.JsonProperty("suiteid")] + [System.Text.Json.Serialization.JsonPropertyName("suiteid")] + public string SuiteId { get; set; } = string.Empty; + + /// + /// 获取或设置旧版应用 ID。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public int? AppId { get; set; } + + /// + /// 获取或设置版本号 ID。 + /// + [Newtonsoft.Json.JsonProperty("edition_id")] + [System.Text.Json.Serialization.JsonPropertyName("edition_id")] + public string EditionId { get; set; } = string.Empty; + + /// + /// 获取或设置购买人数。 + /// + [Newtonsoft.Json.JsonProperty("user_count")] + [System.Text.Json.Serialization.JsonPropertyName("user_count")] + public int? UserCount { get; set; } + + /// + /// 获取或设置购买天数。 + /// + [Newtonsoft.Json.JsonProperty("duration_days")] + [System.Text.Json.Serialization.JsonPropertyName("duration_days")] + public int? DurationDays { get; set; } + + /// + /// 获取或设置生效日期字符串(格式:yyyyMMdd)。 + /// + [Newtonsoft.Json.JsonProperty("take_effect_date")] + [System.Text.Json.Serialization.JsonPropertyName("take_effect_date")] + public string? TakeEffectDateString { get; set; } + + /// + /// 获取或设置优惠信息。 + /// + [Newtonsoft.Json.JsonProperty("discount_info")] + [System.Text.Json.Serialization.JsonPropertyName("discount_info")] + public Types.DiscountInfo? DiscountInfo { get; set; } + } + } + + /// + /// 获取或设置购买应用列表。 + /// + [Newtonsoft.Json.JsonProperty("buy_info_list")] + [System.Text.Json.Serialization.JsonPropertyName("buy_info_list")] + public IList BuyInfoList { get; set; } = new List(); + } + + public class CustomizedApp : ProductBase + { + public static class Types + { + public class BuyInfo + { + /// + /// 获取或设置套件 ID。 + /// + [Newtonsoft.Json.JsonProperty("suiteid")] + [System.Text.Json.Serialization.JsonPropertyName("suiteid")] + public string SuiteId { get; set; } = string.Empty; + + /// + /// 获取或设置旧版应用 ID。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public int? AppId { get; set; } + + /// + /// 获取或设置购买人数。 + /// + [Newtonsoft.Json.JsonProperty("user_count")] + [System.Text.Json.Serialization.JsonPropertyName("user_count")] + public int? UserCount { get; set; } + + /// + /// 获取或设置购买天数。 + /// + [Newtonsoft.Json.JsonProperty("duration_days")] + [System.Text.Json.Serialization.JsonPropertyName("duration_days")] + public int? DurationDays { get; set; } + + /// + /// 获取或设置生效日期字符串(格式:yyyyMMdd)。 + /// + [Newtonsoft.Json.JsonProperty("take_effect_date")] + [System.Text.Json.Serialization.JsonPropertyName("take_effect_date")] + public string? TakeEffectDateString { get; set; } + + /// + /// 获取或设置总价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("total_price")] + [System.Text.Json.Serialization.JsonPropertyName("total_price")] + public int TotalPrice { get; set; } + } + } + + /// + /// 获取或设置购买应用列表。 + /// + [Newtonsoft.Json.JsonProperty("buy_info_list")] + [System.Text.Json.Serialization.JsonPropertyName("buy_info_list")] + public IList BuyInfoList { get; set; } = new List(); + } + + public class PromotionCase : ProductBase + { + public static class Types + { + public class BuyInfo + { + /// + /// 获取或设置套件 ID。 + /// + [Newtonsoft.Json.JsonProperty("suiteid")] + [System.Text.Json.Serialization.JsonPropertyName("suiteid")] + public string SuiteId { get; set; } = string.Empty; + + /// + /// 获取或设置旧版应用 ID。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public int? AppId { get; set; } + + /// + /// 获取或设置购买人数。 + /// + [Newtonsoft.Json.JsonProperty("user_count")] + [System.Text.Json.Serialization.JsonPropertyName("user_count")] + public int? UserCount { get; set; } + } + } + + /// + /// 获取或设置行业方案 ID。 + /// + [Newtonsoft.Json.JsonProperty("case_id")] + [System.Text.Json.Serialization.JsonPropertyName("case_id")] + public string CaseId { get; set; } = string.Empty; + + /// + /// 获取或设置行业方案版本名称 + /// + [Newtonsoft.Json.JsonProperty("promotion_edition_name")] + [System.Text.Json.Serialization.JsonPropertyName("promotion_edition_name")] + public string PromotionEditionName { get; set; } = string.Empty; + + /// + /// 获取或设置购买天数。 + /// + [Newtonsoft.Json.JsonProperty("duration_days")] + [System.Text.Json.Serialization.JsonPropertyName("duration_days")] + public int? DurationDays { get; set; } + + /// + /// 获取或设置生效日期字符串(格式:yyyyMMdd)。 + /// + [Newtonsoft.Json.JsonProperty("take_effect_date")] + [System.Text.Json.Serialization.JsonPropertyName("take_effect_date")] + public string? TakeEffectDateString { get; set; } + + /// + /// 获取或设置购买应用列表。 + /// + [Newtonsoft.Json.JsonProperty("buy_info_list")] + [System.Text.Json.Serialization.JsonPropertyName("buy_info_list")] + public IList? BuyInfoList { get; set; } + } + } + + /// + /// 获取或设置第三方应用商品信息。 + /// + [Newtonsoft.Json.JsonProperty("third_app")] + [System.Text.Json.Serialization.JsonPropertyName("third_app")] + public Types.ThirdPartyApp? ThirdPartyApp { get; set; } + + /// + /// 获取或设置代开发应用商品信息。 + /// + [Newtonsoft.Json.JsonProperty("customized_app")] + [System.Text.Json.Serialization.JsonPropertyName("customized_app")] + public Types.CustomizedApp? CustomizedApp { get; set; } + + /// + /// 获取或设置行业解决方案商品信息。 + /// + [Newtonsoft.Json.JsonProperty("promotion_case")] + [System.Text.Json.Serialization.JsonPropertyName("promotion_case")] + public Types.PromotionCase? PromotionCase { get; set; } + } + } + + /// + /// 获取或设置业务类型。 + /// + [Newtonsoft.Json.JsonProperty("business_type")] + [System.Text.Json.Serialization.JsonPropertyName("business_type")] + public int BusinessType { get; set; } + + /// + /// 获取或设置客户企业 CorpId。 + /// + [Newtonsoft.Json.JsonProperty("custom_corpid")] + [System.Text.Json.Serialization.JsonPropertyName("custom_corpid")] + public string? CustomCorpId { get; set; } + + /// + /// 获取或设置支付方式。 + /// + [Newtonsoft.Json.JsonProperty("pay_type")] + [System.Text.Json.Serialization.JsonPropertyName("pay_type")] + public int PayType { get; set; } + + /// + /// 获取或设置服务商订单费用凭证 MediaId。 + /// + [Newtonsoft.Json.JsonProperty("bank_receipt_media_id")] + [System.Text.Json.Serialization.JsonPropertyName("bank_receipt_media_id")] + public string? BandReceiptMediaId { get; set; } + + /// + /// 获取或设置订单创建人成员账号。 + /// + [Newtonsoft.Json.JsonProperty("creator")] + [System.Text.Json.Serialization.JsonPropertyName("creator")] + public string? CreatorUserId { get; set; } + + /// + /// 获取或设置商品信息。 + /// + [Newtonsoft.Json.JsonProperty("product_list")] + [System.Text.Json.Serialization.JsonPropertyName("product_list")] + public Types.Product Product { get; set; } = new Types.Product(); + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolOpenOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolOpenOrderResponse.cs new file mode 100644 index 00000000..9c558bd0 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/CgibinPayToolOpenOrderResponse.cs @@ -0,0 +1,36 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + /// + /// 表示 [POST] /cgi-bin/paytool/open_order 接口的响应。 + /// + public class CgibinPayToolOpenOrderResponse : WechatWorkResponse + { + /// + /// 获取或设置收款订单号。 + /// + [Newtonsoft.Json.JsonProperty("order_id")] + [System.Text.Json.Serialization.JsonPropertyName("order_id")] + public string OrderId { get; set; } = default!; + + /// + /// 获取或设置收款订单链接。 + /// + [Newtonsoft.Json.JsonProperty("order_url")] + [System.Text.Json.Serialization.JsonPropertyName("order_url")] + public string OrderUrl { get; set; } = default!; + + /// + /// 获取或设置原价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("origin_price")] + [System.Text.Json.Serialization.JsonPropertyName("origin_price")] + public int? OriginPrice { get; set; } + + /// + /// 获取或设置现价(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("paid_price")] + [System.Text.Json.Serialization.JsonPropertyName("paid_price")] + public int? PaidPrice { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/__Abstractions/CgibinPayToolOrderRequestBase.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/__Abstractions/CgibinPayToolOrderRequestBase.cs new file mode 100644 index 00000000..6964f28f --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinPayTool/Order/__Abstractions/CgibinPayToolOrderRequestBase.cs @@ -0,0 +1,33 @@ +namespace SKIT.FlurlHttpClient.Wechat.Work.Models +{ + public abstract class CgibinPayToolOrderRequestBase : WechatWorkRequest + { + /// + /// 获取或设置服务商 AccessToken。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string ProviderAccessToken { get; set; } = string.Empty; + + /// + /// 获取或设置随机字符串。如果不指定将由系统自动生成。 + /// + [Newtonsoft.Json.JsonProperty("nonce_str")] + [System.Text.Json.Serialization.JsonPropertyName("nonce_str")] + public string? NonceString { get; set; } + + /// + /// 获取或设置请求时间戳。如果不指定将由系统自动生成。 + /// + [Newtonsoft.Json.JsonProperty("ts")] + [System.Text.Json.Serialization.JsonPropertyName("ts")] + public long? Timestamp { get; set; } + + /// + /// 获取或设置请求数字签名。如果不指定将由系统自动生成。 + /// + [Newtonsoft.Json.JsonProperty("sig")] + [System.Text.Json.Serialization.JsonPropertyName("sig")] + public string? Signature { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Settings/Credentials.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Settings/Credentials.cs index 413030e9..a6bfa447 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Work/Settings/Credentials.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Settings/Credentials.cs @@ -54,6 +54,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Settings /// public string? PushToken { get; } + /// + /// 初始化客户端时 的副本。 + /// + public string? PayToolApiSecret { get; } + internal Credentials(WechatWorkClientOptions options) { if (options is null) throw new ArgumentNullException(nameof(options)); @@ -68,6 +73,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Settings ModelSecret = options.ModelSecret; PushEncodingAESKey = options.PushEncodingAESKey; PushToken = options.PushToken; + PayToolApiSecret = options.PayToolApiSecret; } } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/WechatWorkClientOptions.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/WechatWorkClientOptions.cs index 808c292f..19ad593a 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Work/WechatWorkClientOptions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Work/WechatWorkClientOptions.cs @@ -66,5 +66,10 @@ namespace SKIT.FlurlHttpClient.Wechat.Work /// 获取或设置企业微信服务器推送的 Token。 /// public string? PushToken { get; set; } + + /// + /// 获取或设置企业微信收银台 API 调用密钥。 + /// + public string? PayToolApiSecret { get; set; } } } diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSigningTests.cs b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSigningTests.cs index 02aaf69c..b0867cd1 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSigningTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSigningTests.cs @@ -1,26 +1,31 @@ +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; using System.Threading.Tasks; using Xunit; namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests { - public class TestCase_RequestSigningTests + public partial class TestCase_RequestSigningTests { [Fact(DisplayName = "测试用例:即时配送请求签名")] public async Task TestImmeDeliveryRequestSignature() { + var httpClient = new HttpClient(new MockHttpMessageHandler(new HttpClientHandler())); var mockClient = new WechatApiClient(new WechatApiClientOptions() { AppId = "", AppSecret = "", ImmeDeliveryAppKey = "test_shop_id", ImmeDeliveryAppSecret = "test_app_secrect" - }); + }, httpClient); var request = new Models.CgibinExpressLocalBusinessTestUpdateOrderRequest() { ShopOrderId = "test_shop_order_id" }; - var response = await mockClient.ExecuteCgibinExpressLocalBusinessTestUpdateOrderAsync(request); // 这里不关心响应结果,只为获得预处理请求 + _ = await mockClient.ExecuteCgibinExpressLocalBusinessTestUpdateOrderAsync(request); Assert.Equal("a93d8d6bae9a9483c1b1d4e8670e7f6226ec94cb", request.DeliverySignature, ignoreCase: true); } @@ -28,12 +33,13 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests [Fact(DisplayName = "测试用例:虚拟支付请求签名")] public async Task TestVirtualPaymentRequestSignature() { + var httpClient = new HttpClient(new MockHttpMessageHandler(new HttpClientHandler())); var mockClient = new WechatApiClient(new WechatApiClientOptions() { AppId = "wxtest", AppSecret = "", VirtualPaymentAppKey = "12345" - }); + }, httpClient); mockClient.Configure(settings => { var jsonOptions = SystemTextJsonSerializer.GetDefaultSerializerOptions(); @@ -50,7 +56,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests UserIp = "127.0.0.1", SessionKey = "9hAb/NEYUlkaMBEsmFgzig==" }; - var response = await mockClient.ExecuteXPayQueryUserBalanceAsync(request); // 这里不关心响应结果,只为获得预处理请求 + _ = await mockClient.ExecuteXPayQueryUserBalanceAsync(request); Assert.Equal("e690cc22b6378ca9d70fe61727fabf65f0273d436813447816951989867a134c", request.Signature, ignoreCase: true); Assert.Equal("fb4703e6b7da545a7f43428f02f2a9fa42e895704c6319d01a0aa859cf63253c", request.PaySign, ignoreCase: true); @@ -59,13 +65,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests [Fact(DisplayName = "测试用例:米大师 1.0 请求签名")] public async Task TestMidasRequestSignature() { + var httpClient = new HttpClient(new MockHttpMessageHandler(new HttpClientHandler())); var mockClient = new WechatApiClient(new WechatApiClientOptions() { AppId = "wx1234567", AppSecret = "", MidasOfferId = "12345678", MidasAppKey = "zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u" - }); + }, httpClient); var request = new Models.CgibinMidasGetBalanceRequest() { @@ -74,7 +81,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests Platform = "android", Timestamp = 1507530737 }; - var response = await mockClient.ExecuteCgibinMidasGetBalanceAsync(request); // 这里不关心响应结果,只为获得预处理请求 + _ = await mockClient.ExecuteCgibinMidasGetBalanceAsync(request); Assert.Equal("1ad64e8dcb2ec1dc486b7fdf01f4a15159fc623dc3422470e51cf6870734726b", request.Signature, ignoreCase: true); } @@ -82,13 +89,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests [Fact(DisplayName = "测试用例:米大师 2.0 请求签名")] public async Task TestMidasV2RequestSignature() { + var httpClient = new HttpClient(new MockHttpMessageHandler(new HttpClientHandler())); var mockClient = new WechatApiClient(new WechatApiClientOptions() { AppId = "wx1234567", AppSecret = "", MidasOfferIdV2 = "12345678", MidasAppKeyV2 = "12345" - }); + }, httpClient); mockClient.Configure(settings => { var jsonOptions = SystemTextJsonSerializer.GetDefaultSerializerOptions(); @@ -107,10 +115,43 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests Timestamp = 1668136271, SessionKey = "9hAb/NEYUlkaMBEsmFgzig==" }; - var response = await mockClient.ExecuteWxaGameGetBalanceAsync(request); // 这里不关心响应结果,只为获得预处理请求 + _ = await mockClient.ExecuteWxaGameGetBalanceAsync(request); Assert.Equal("7ec6d737f118b0c898de39ef1b6b199c48290e699495364fe9d069597a7da125", request.Signature, ignoreCase: true); Assert.Equal("5fc5460b23b2589efb8adbc2cf08d7a9cd8892b33e084249a26455b7880741a8", request.PaySign, ignoreCase: true); } } + + partial class TestCase_RequestSigningTests + { + public class MockHttpClient : HttpClient + { + public MockHttpClient() + : base(new MockHttpMessageHandler(new HttpClientHandler())) + { + } + } + + public class MockHttpMessageHandler : DelegatingHandler + { + public MockHttpMessageHandler(HttpMessageHandler innerHandler) + : base(innerHandler) + { + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + string mockResponseContent = "{\"errcode\":0}"; + byte[] mockResponseBytes = Encoding.UTF8.GetBytes(mockResponseContent); + var resp = new HttpResponseMessage + { + RequestMessage = request, + StatusCode = HttpStatusCode.NoContent, + Content = new ByteArrayContent(mockResponseBytes), + }; + resp.Headers.TryAddWithoutValidation("Content-Length", mockResponseBytes.Length.ToString()); + return Task.FromResult(resp); + } + } + } } diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListRequest.json new file mode 100644 index 00000000..723bb9f2 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListRequest.json @@ -0,0 +1,6 @@ +{ + "start_time": 1680000000, + "end_time": 1680003600, + "cursor": "CURSOR", + "limit": 50 +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListResponse.json new file mode 100644 index 00000000..737af075 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolGetInvoiceListResponse.json @@ -0,0 +1,29 @@ +{ + "errcode": 0, + "errmsg": "ok", + "has_more": 1, + "next_cursor": "CURSOR", + "invoice_list": [ + { + "order_id": "ORDERID", + "custom_corpid": "wwxx", + "apply_time": 1680000000, + "invoice_type": 0, + "paid_price": 9000, + "invoice_status": 1, + "invoice_title": "发票抬头", + "tax_number": "TAXPAYERID", + "send_way": 1, + "contact_name": "张三", + "contact_tel": "12345678901", + "contact_addr": "广东省|广州市|海珠区", + "contact_postcode": "200240", + "receive_email": "jack@qq.com", + "company_addr": "广东省广州市海珠区媒体港", + "company_tel": "187XXXXXXXX", + "bank_name": "中国邮政储蓄银行股份有限公司广州市天河支行", + "bank_account_number": "17631364174811341", + "invoice_note": "开票备注" + } + ] +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusRequest.json new file mode 100644 index 00000000..e8aaf7d4 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Invoice/CgibinPayToolMarkInvoiceStatusRequest.json @@ -0,0 +1,6 @@ +{ + "order_id": "ORDERID", + "oper_userid": "USERID", + "invoice_status": 1, + "invoice_note": "NOTE" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolCloseOrderRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolCloseOrderRequest.json new file mode 100644 index 00000000..8b8e66b9 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolCloseOrderRequest.json @@ -0,0 +1,6 @@ +{ + "order_id": "T0Rkc2g3dWlQNzFLNTRXMDJBV25BNi95OVZZbjBPOE11N2FCWkc0STJRdz0=", + "nonce_str": "129031823", + "ts": 1548302135, + "sig": "mPOwVW/vQ74xN+b+Yu1KMa9RrmhKJaJjAtXHTof+EpU=" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderDetailRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderDetailRequest.json new file mode 100644 index 00000000..8b8e66b9 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderDetailRequest.json @@ -0,0 +1,6 @@ +{ + "order_id": "T0Rkc2g3dWlQNzFLNTRXMDJBV25BNi95OVZZbjBPOE11N2FCWkc0STJRdz0=", + "nonce_str": "129031823", + "ts": 1548302135, + "sig": "mPOwVW/vQ74xN+b+Yu1KMa9RrmhKJaJjAtXHTof+EpU=" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderDetailResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderDetailResponse.json new file mode 100644 index 00000000..de75869d --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderDetailResponse.json @@ -0,0 +1,67 @@ +{ + "errcode": 0, + "errmsg": "ok", + "pay_order": { + "order_id": "ORDERID", + "create_time": 1671161338, + "custom_corpid": "ww0***********7e21", + "buy_content": "BUYCONTENT", + "origin_price": 10000, + "paid_price": 9000, + "order_status": 2, + "order_from": 1, + "creator": "CREATOR", + "pay_type": 1, + "custom_corp_name": "测试企业", + "pay_channel": 1, + "channel_order_id": "4200001711202212169932901772", + "paid_time": 1671161378, + "business_type": 2, + "income_type": 1, + "income_time": 1672111778, + "income_amount": 2, + "product_list": { + "third_app": { + "order_type": 0, + "buy_info_list": [ + { + "suiteid": "SUITEID", + "appid": 1, + "edition_id": "版本名字", + "user_count": 10, + "duration_days": 365, + "origin_price": 10000, + "paid_price": 9000, + "take_effect_date": "20221220" + } + ] + }, + "customized_app": { + "order_type": 2, + "buy_info_list": [ + { + "suiteid": "SUITEID", + "user_count": 10, + "duration_days": 365, + "origin_price": 10000, + "paid_price": 9000, + "take_effect_date": "20221220" + } + ] + }, + "promotion_case": { + "order_type": 0, + "case_id": "CASEID", + "promotion_edition_name": "版本名字", + "buy_info_list": [ + { + "suiteid": "SUITEID", + "appid": 1, + "user_count": 10, + "take_effect_date": "20221220" + } + ] + } + } + } +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderListRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderListRequest.json new file mode 100644 index 00000000..653e0761 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderListRequest.json @@ -0,0 +1,10 @@ +{ + "business_type": 1, + "start_time": 1670643437, + "end_time": 1671507437, + "cursor": "tJzlB9tdqfh-g7i_J-ehOz_TWcd7dSKa39_AqCIeMFw", + "limit": 0, + "nonce_str": "129031823", + "ts": 1548302135, + "sig": "mPOwVW/vQ74xN+b+Yu1KMa9RrmhKJaJjAtXHTof+EpU=" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderListResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderListResponse.json new file mode 100644 index 00000000..548b05f3 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolGetOrderListResponse.json @@ -0,0 +1,20 @@ +{ + "errcode": 0, + "errmsg": "ok", + "next_cursor": "5f0uN2RNNsR8Dj0G08XUSg==", + "has_more": 1, + "pay_order_list": [ + { + "order_id": "ORDERID", + "create_time": 1672220217, + "custom_corpid": "wx9***********65f9", + "buy_content": "BUYCONTENT", + "origin_price": 1000, + "paid_price": 9000, + "order_status": 1, + "order_from": 1, + "creator": "CREATOR", + "pay_type": 2 + } + ] +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolOpenOrderRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolOpenOrderRequest.json new file mode 100644 index 00000000..4edd087a --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolOpenOrderRequest.json @@ -0,0 +1,78 @@ +{ + "business_type": 1, + "custom_corpid": "wx979abee1572365f9", + "pay_type": 1, + "bank_receipt_media_id": "MEDIA_ID_XXX", + "creator": "CREATOR", + "product_list": { + "third_app": { + "order_type": 0, + "buy_info_list": [ + { + "suiteid": "wx63bea8582b858ee7", + "appid": 1, + "edition_id": "sp7fd5170a8e807a44", + "duration_days": 1, + "take_effect_date": "20221220", + "user_count": 10, + "discount_info": { + "discount_type": 2, + "discount_amount": 0, + "discount_ratio": 75, + "discount_remarks": "老客户优惠" + } + }, + { + "suiteid": "wx46b0217691d307f7", + "appid": 1, + "edition_id": "spb5625fc505870d09", + "duration_days": 1, + "user_count": 10 + } + ], + "notify_custom_corp": 1 + }, + "customized_app": { + "order_type": 0, + "buy_info_list": [ + { + "suiteid": "dk6e2ed1be411f395a", + "duration_days": 30, + "take_effect_date": "20221220", + "user_count": 10, + "total_price": 250000 + }, + { + "suiteid": "dk9b5e63db2d5d98a0", + "duration_days": 10, + "take_effect_date": "20221220", + "user_count": 20, + "total_price": 2500 + } + ], + "notify_custom_corp": 1 + }, + "promotion_case": { + "order_type": 0, + "case_id": "slne7508097f40c4f8e", + "promotion_edition_name": "固定单价", + "duration_days": 30, + "take_effect_date": "20221230", + "buy_info_list": [ + { + "suiteid": "wx8838a73b0f57a7b5", + "appid": 2, + "user_count": 10 + }, + { + "suiteid": "wx15ab15f7b1ced876", + "user_count": 20 + } + ], + "notify_custom_corp": 1 + } + }, + "nonce_str": "129031823", + "ts": 1548302135, + "sig": "mPOwVW/vQ74xN+b+Yu1KMa9RrmhKJaJjAtXHTof+EpU=" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolOpenOrderResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolOpenOrderResponse.json new file mode 100644 index 00000000..d312e831 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/ModelSamples/CgibinPayTool/Order/CgibinPayToolOpenOrderResponse.json @@ -0,0 +1,8 @@ +{ + "errcode": 0, + "errmsg": "ok", + "order_id": "N00000F24364063FC57FACFC5C6A0", + "order_url": "https://open.work.weixin.qq.com/payforapp/PREORDERT0Rkc2g3dWlQNzFLNTRXMDJBV25BNi95OVZZbjBPOE11N2FCWkc0STJRdz0=", + "origin_price": 1000, + "paid_price": 900 +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_ApiExecuteCgibinMediaTests.cs b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_ApiExecuteCgibinMediaTests.cs index 71a098b4..cb476d65 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_ApiExecuteCgibinMediaTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_ApiExecuteCgibinMediaTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Xunit; diff --git a/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_RequestSigningTests.cs b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_RequestSigningTests.cs new file mode 100644 index 00000000..4ac6f795 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.Work.UnitTests/TestCase_RequestSigningTests.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace SKIT.FlurlHttpClient.Wechat.Work.UnitTests +{ + public partial class TestCase_RequestSigningTests + { + [Fact(DisplayName = "测试用例:收银台收款工具接口请求签名")] + public async Task TestPayToolRequestSignature() + { + var httpClient = new HttpClient(new MockHttpMessageHandler(new HttpClientHandler())); + var mockClient = new WechatWorkClient(new WechatWorkClientOptions() + { + PayToolApiSecret = "at23pxnPBNQY3JiA8N5U1gabiQqxZwqH_Gihg7a_wrULmlOPVP-iiRjv9JWYPrDk" + }, new MockHttpClient()); + + // 简单数据结构 + { + var request = new Models.CgibinPayToolCloseOrderRequest() + { + OrderId = "T0Rkc2g3dWlQNzFLNTRXMDJBV25BNi95OVZZbjBPOE11N2FCWkc0STJRdz0=", + NonceString = "129031823", + Timestamp = 1548302135 + }; + _ = await mockClient.ExecuteCgibinPayToolCloseOrderAsync(request); + + Assert.Equal("jzjAGcoLbquKfNVlpqzBJDSdUASP8J7PebdFB6CUnRg=", request.Signature); + } + + // 复杂数据结构 + { + var request = new Models.CgibinPayToolOpenOrderRequest() + { + BusinessType = 1, + CustomCorpId = "wx979abee1572365f9", + PayType = 1, + BandReceiptMediaId = "MEDIA_ID_XXX", + CreatorUserId = "CREATOR", + Product = new Models.CgibinPayToolOpenOrderRequest.Types.Product() + { + ThirdPartyApp = new Models.CgibinPayToolOpenOrderRequest.Types.Product.Types.ThirdPartyApp() + { + OrderType = 0, + BuyInfoList = new List() + { + new Models.CgibinPayToolOpenOrderRequest.Types.Product.Types.ThirdPartyApp.Types.BuyInfo() + { + SuiteId = "wx63bea8582b858ee7", + AppId = 1, + EditionId = "sp7fd5170a8e807a44", + DurationDays = 1, + TakeEffectDateString = "20221220", + UserCount = 10, + DiscountInfo = new Models.CgibinPayToolOpenOrderRequest.Types.Product.Types.ThirdPartyApp.Types.BuyInfo.Types.DiscountInfo() + { + DiscountType = 2, + DiscountAmount = 0, + DiscountRatio = 75, + DiscountRemarks = "老客户优惠" + } + }, + new Models.CgibinPayToolOpenOrderRequest.Types.Product.Types.ThirdPartyApp.Types.BuyInfo() + { + SuiteId = "wx46b0217691d307f7", + AppId = 1, + EditionId = "spb5625fc505870d09", + DurationDays = 1, + UserCount = 10, + } + }, + IsNotifyCustomCorp = true + } + }, + NonceString = "129031823", + Timestamp = 1548302135 + }; + _ = await mockClient.ExecuteCgibinPayToolOpenOrderAsync(request); + + Assert.Equal("5c6ZV2YFUWSA+2t4i92wUi4INQPYj3m6DArzLXkwxxk=", request.Signature); + } + } + } + + partial class TestCase_RequestSigningTests + { + public class MockHttpClient : HttpClient + { + public MockHttpClient() + : base(new MockHttpMessageHandler(new HttpClientHandler())) + { + } + } + + public class MockHttpMessageHandler : DelegatingHandler + { + public MockHttpMessageHandler(HttpMessageHandler innerHandler) + : base(innerHandler) + { + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + string mockResponseContent = "{\"errcode\":0}"; + byte[] mockResponseBytes = Encoding.UTF8.GetBytes(mockResponseContent); + var resp = new HttpResponseMessage + { + RequestMessage = request, + StatusCode = HttpStatusCode.NoContent, + Content = new ByteArrayContent(mockResponseBytes), + }; + resp.Headers.TryAddWithoutValidation("Content-Length", mockResponseBytes.Length.ToString()); + return Task.FromResult(resp); + } + } + } +}