Skip to content

Commit 2c9a54d

Browse files
committed
feat(work): 新增收银台收款工具相关接口
1 parent 8db3a17 commit 2c9a54d

30 files changed

Lines changed: 1970 additions & 12 deletions
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Flurl;
8+
using Flurl.Http;
9+
using Newtonsoft.Json.Linq;
10+
11+
namespace SKIT.FlurlHttpClient.Wechat.Work
12+
{
13+
public static class WechatWorkClientExecuteCgibinPayToolExtensions
14+
{
15+
#region Invoice
16+
/// <summary>
17+
/// <para>异步调用 [POST] /cgi-bin/paytool/get_invoice_list 接口。</para>
18+
/// <para>
19+
/// REF: <br/>
20+
/// <![CDATA[ https://developer.work.weixin.qq.com/document/path/99436 ]]>
21+
/// </para>
22+
/// </summary>
23+
/// <param name="client"></param>
24+
/// <param name="request"></param>
25+
/// <param name="cancellationToken"></param>
26+
/// <returns></returns>
27+
public static async Task<Models.CgibinPayToolGetInvoiceListResponse> ExecuteCgibinPayToolGetInvoiceListAsync(this WechatWorkClient client, Models.CgibinPayToolGetInvoiceListRequest request, CancellationToken cancellationToken = default)
28+
{
29+
if (client is null) throw new ArgumentNullException(nameof(client));
30+
if (request is null) throw new ArgumentNullException(nameof(request));
31+
32+
IFlurlRequest flurlReq = client
33+
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "get_invoice_list")
34+
.SetQueryParam("provider_access_token", request.ProviderAccessToken);
35+
36+
return await client.SendFlurlRequestAsJsonAsync<Models.CgibinPayToolGetInvoiceListResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
37+
}
38+
39+
/// <summary>
40+
/// <para>异步调用 [POST] /cgi-bin/paytool/mark_invoice_status 接口。</para>
41+
/// <para>
42+
/// REF: <br/>
43+
/// <![CDATA[ https://developer.work.weixin.qq.com/document/path/99437 ]]>
44+
/// </para>
45+
/// </summary>
46+
/// <param name="client"></param>
47+
/// <param name="request"></param>
48+
/// <param name="cancellationToken"></param>
49+
/// <returns></returns>
50+
public static async Task<Models.CgibinPayToolMarkInvoiceStatusResponse> ExecuteCgibinPayToolMarkInvoiceStatusAsync(this WechatWorkClient client, Models.CgibinPayToolMarkInvoiceStatusRequest request, CancellationToken cancellationToken = default)
51+
{
52+
if (client is null) throw new ArgumentNullException(nameof(client));
53+
if (request is null) throw new ArgumentNullException(nameof(request));
54+
55+
IFlurlRequest flurlReq = client
56+
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "mark_invoice_status")
57+
.SetQueryParam("provider_access_token", request.ProviderAccessToken);
58+
59+
return await client.SendFlurlRequestAsJsonAsync<Models.CgibinPayToolMarkInvoiceStatusResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
60+
}
61+
#endregion
62+
63+
#region Order
64+
private static T PreprocessRequest<T>(WechatWorkClient client, ref T request)
65+
where T : Models.CgibinPayToolOrderRequestBase, new()
66+
{
67+
if (client is null) throw new ArgumentNullException(nameof(request));
68+
if (request is null) throw new ArgumentNullException(nameof(request));
69+
70+
if (request.NonceString is null)
71+
{
72+
request.NonceString = Guid.NewGuid().ToString("N");
73+
}
74+
75+
if (request.Timestamp is null)
76+
{
77+
request.Timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds();
78+
}
79+
80+
if (request.Signature is null)
81+
{
82+
if (string.IsNullOrEmpty(client.Credentials.PayToolApiSecret))
83+
throw new WechatWorkException("Could not sign request, because paytool API secret is missing.");
84+
85+
Func<string, JToken, List<string>> flatten = default!;
86+
flatten = (path, token) =>
87+
{
88+
List<string> results = new List<string>();
89+
90+
switch (token.Type)
91+
{
92+
case JTokenType.Null:
93+
break;
94+
95+
case JTokenType.Integer:
96+
case JTokenType.Float:
97+
case JTokenType.String:
98+
{
99+
string key = path;
100+
string val = token.ToString();
101+
if (string.IsNullOrEmpty(key))
102+
throw new InvalidOperationException();
103+
if (!string.IsNullOrEmpty(val))
104+
results.Add($"{key}={val}");
105+
}
106+
break;
107+
108+
case JTokenType.Boolean:
109+
{
110+
string key = path;
111+
string val = token.ToString().ToLower();
112+
if (string.IsNullOrEmpty(key))
113+
throw new InvalidOperationException();
114+
results.Add($"{key}={val}");
115+
}
116+
break;
117+
118+
case JTokenType.Array:
119+
{
120+
foreach (var item in (JArray)token)
121+
{
122+
results.AddRange(flatten(path, item));
123+
}
124+
}
125+
break;
126+
127+
case JTokenType.Object:
128+
{
129+
foreach (JProperty prop in ((JObject)token).Properties())
130+
{
131+
results.AddRange(flatten(prop.Name, prop.Value));
132+
}
133+
}
134+
break;
135+
136+
default:
137+
throw new InvalidOperationException();
138+
}
139+
140+
return results;
141+
};
142+
143+
List<string> tmp = flatten(string.Empty, JObject.Parse(client.JsonSerializer.Serialize(request)));
144+
tmp.Sort(StringComparer.Ordinal);
145+
146+
byte[] keyBytes = Encoding.UTF8.GetBytes(client.Credentials.PayToolApiSecret!);
147+
byte[] msgBytes = Encoding.UTF8.GetBytes(string.Join("&", tmp));
148+
byte[] signBytes = Utilities.HMACUtility.HashWithSHA256(keyBytes, msgBytes);
149+
request.Signature = Convert.ToBase64String(signBytes);
150+
}
151+
152+
return request;
153+
}
154+
155+
/// <summary>
156+
/// <para>异步调用 [POST] /cgi-bin/paytool/open_order 接口。</para>
157+
/// <para>
158+
/// REF: <br/>
159+
/// <![CDATA[ https://developer.work.weixin.qq.com/document/path/98045 ]]>
160+
/// </para>
161+
/// </summary>
162+
/// <param name="client"></param>
163+
/// <param name="request"></param>
164+
/// <param name="cancellationToken"></param>
165+
/// <returns></returns>
166+
public static async Task<Models.CgibinPayToolOpenOrderResponse> ExecuteCgibinPayToolOpenOrderAsync(this WechatWorkClient client, Models.CgibinPayToolOpenOrderRequest request, CancellationToken cancellationToken = default)
167+
{
168+
if (client is null) throw new ArgumentNullException(nameof(client));
169+
if (request is null) throw new ArgumentNullException(nameof(request));
170+
171+
PreprocessRequest(client, ref request);
172+
173+
IFlurlRequest flurlReq = client
174+
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "open_order")
175+
.SetQueryParam("provider_access_token", request.ProviderAccessToken);
176+
177+
return await client.SendFlurlRequestAsJsonAsync<Models.CgibinPayToolOpenOrderResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
178+
}
179+
180+
/// <summary>
181+
/// <para>异步调用 [POST] /cgi-bin/paytool/close_order 接口。</para>
182+
/// <para>
183+
/// REF: <br/>
184+
/// <![CDATA[ https://developer.work.weixin.qq.com/document/path/98046 ]]>
185+
/// </para>
186+
/// </summary>
187+
/// <param name="client"></param>
188+
/// <param name="request"></param>
189+
/// <param name="cancellationToken"></param>
190+
/// <returns></returns>
191+
public static async Task<Models.CgibinPayToolCloseOrderResponse> ExecuteCgibinPayToolCloseOrderAsync(this WechatWorkClient client, Models.CgibinPayToolCloseOrderRequest request, CancellationToken cancellationToken = default)
192+
{
193+
if (client is null) throw new ArgumentNullException(nameof(client));
194+
if (request is null) throw new ArgumentNullException(nameof(request));
195+
196+
PreprocessRequest(client, ref request);
197+
198+
IFlurlRequest flurlReq = client
199+
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "close_order")
200+
.SetQueryParam("provider_access_token", request.ProviderAccessToken);
201+
202+
return await client.SendFlurlRequestAsJsonAsync<Models.CgibinPayToolCloseOrderResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
203+
}
204+
205+
/// <summary>
206+
/// <para>异步调用 [POST] /cgi-bin/paytool/get_order_list 接口。</para>
207+
/// <para>
208+
/// REF: <br/>
209+
/// <![CDATA[ https://developer.work.weixin.qq.com/document/path/98053 ]]>
210+
/// </para>
211+
/// </summary>
212+
/// <param name="client"></param>
213+
/// <param name="request"></param>
214+
/// <param name="cancellationToken"></param>
215+
/// <returns></returns>
216+
public static async Task<Models.CgibinPayToolGetOrderListResponse> ExecuteCgibinPayToolGetOrderListAsync(this WechatWorkClient client, Models.CgibinPayToolGetOrderListRequest request, CancellationToken cancellationToken = default)
217+
{
218+
if (client is null) throw new ArgumentNullException(nameof(client));
219+
if (request is null) throw new ArgumentNullException(nameof(request));
220+
221+
PreprocessRequest(client, ref request);
222+
223+
IFlurlRequest flurlReq = client
224+
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "get_order_list")
225+
.SetQueryParam("provider_access_token", request.ProviderAccessToken);
226+
227+
return await client.SendFlurlRequestAsJsonAsync<Models.CgibinPayToolGetOrderListResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
228+
}
229+
230+
/// <summary>
231+
/// <para>异步调用 [POST] /cgi-bin/paytool/get_order_detail 接口。</para>
232+
/// <para>
233+
/// REF: <br/>
234+
/// <![CDATA[ https://developer.work.weixin.qq.com/document/path/98054 ]]>
235+
/// </para>
236+
/// </summary>
237+
/// <param name="client"></param>
238+
/// <param name="request"></param>
239+
/// <param name="cancellationToken"></param>
240+
/// <returns></returns>
241+
public static async Task<Models.CgibinPayToolGetOrderDetailResponse> ExecuteCgibinPayToolGetOrderDetailAsync(this WechatWorkClient client, Models.CgibinPayToolGetOrderDetailRequest request, CancellationToken cancellationToken = default)
242+
{
243+
if (client is null) throw new ArgumentNullException(nameof(client));
244+
if (request is null) throw new ArgumentNullException(nameof(request));
245+
246+
PreprocessRequest(client, ref request);
247+
248+
IFlurlRequest flurlReq = client
249+
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "paytool", "get_order_detail")
250+
.SetQueryParam("provider_access_token", request.ProviderAccessToken);
251+
252+
return await client.SendFlurlRequestAsJsonAsync<Models.CgibinPayToolGetOrderDetailResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
253+
}
254+
#endregion
255+
}
256+
}

src/SKIT.FlurlHttpClient.Wechat.Work/Models/CgibinExternalContact/CgibinExternalContactGetResponse.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,11 @@ public class WechatChannels
259259
public string? State { get; set; }
260260

261261
/// <summary>
262-
/// 获取或设置发起添加的成员账号
262+
/// 获取或设置操作人的成员账号
263263
/// </summary>
264264
[Newtonsoft.Json.JsonProperty("oper_userid")]
265265
[System.Text.Json.Serialization.JsonPropertyName("oper_userid")]
266-
public string? OperateUserId { get; set; }
266+
public string? OperatorUserId { get; set; }
267267

268268
/// <summary>
269269
/// 获取或设置视频号信息。
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
namespace SKIT.FlurlHttpClient.Wechat.Work.Models
2+
{
3+
/// <summary>
4+
/// <para>表示 [POST] /cgi-bin/paytool/get_invoice_list 接口的请求。</para>
5+
/// </summary>
6+
public class CgibinPayToolGetInvoiceListRequest : WechatWorkRequest
7+
{
8+
/// <summary>
9+
/// 获取或设置服务商 AccessToken。
10+
/// </summary>
11+
[Newtonsoft.Json.JsonIgnore]
12+
[System.Text.Json.Serialization.JsonIgnore]
13+
public string ProviderAccessToken { get; set; } = string.Empty;
14+
15+
/// <summary>
16+
/// 获取或设置开票时间开始时间戳。
17+
/// </summary>
18+
[Newtonsoft.Json.JsonProperty("start_time")]
19+
[System.Text.Json.Serialization.JsonPropertyName("start_time")]
20+
public long? StartTimestamp { get; set; }
21+
22+
/// <summary>
23+
/// 获取或设置开票时间结束时间戳。
24+
/// </summary>
25+
[Newtonsoft.Json.JsonProperty("end_time")]
26+
[System.Text.Json.Serialization.JsonPropertyName("end_time")]
27+
public long? EndTimestamp { get; set; }
28+
29+
/// <summary>
30+
/// 获取或设置分页游标。
31+
/// </summary>
32+
[Newtonsoft.Json.JsonProperty("cursor")]
33+
[System.Text.Json.Serialization.JsonPropertyName("cursor")]
34+
public string? Cursor { get; set; }
35+
36+
/// <summary>
37+
/// 获取或设置分页每页数量。
38+
/// </summary>
39+
[Newtonsoft.Json.JsonProperty("limit")]
40+
[System.Text.Json.Serialization.JsonPropertyName("limit")]
41+
public int? Limit { get; set; }
42+
}
43+
}

0 commit comments

Comments
 (0)