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