Skip to content

Commit 4030fc5

Browse files
committed
feat(tenpayv3): 新增微工卡投保相关接口
1 parent 1e3a7d4 commit 4030fc5

9 files changed

+317
-0
lines changed

src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientExecutePayrollCardExtensions.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,51 @@ public static class WechatTenpayClientExecutePayrollCardExtensions
177177
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePayrollCardTransferBatchResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
178178
}
179179
#endregion
180+
181+
#region Wesure
182+
/// <summary>
183+
/// <para>异步调用 [POST] /payroll-card/wesure/insurance 接口。</para>
184+
/// <para>
185+
/// REF: <br/>
186+
/// <![CDATA[ https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_32.shtml ]]>
187+
/// </para>
188+
/// </summary>
189+
/// <param name="client"></param>
190+
/// <param name="request"></param>
191+
/// <param name="cancellationToken"></param>
192+
/// <returns></returns>
193+
public static async Task<Models.CreatePayrollCardWesureInsuranceResponse> ExecuteCreatePayrollCardWesureInsuranceAsync(this WechatTenpayClient client, Models.CreatePayrollCardWesureInsuranceRequest request, CancellationToken cancellationToken = default)
194+
{
195+
if (client is null) throw new ArgumentNullException(nameof(client));
196+
if (request is null) throw new ArgumentNullException(nameof(request));
197+
198+
IFlurlRequest flurlReq = client
199+
.CreateFlurlRequest(request, HttpMethod.Post, "payroll-card", "wesure", "insurance");
200+
201+
return await client.SendFlurlRequestAsJsonAsync<Models.CreatePayrollCardWesureInsuranceResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
202+
}
203+
204+
/// <summary>
205+
/// <para>异步调用 [POST] /payroll-card/wesure/token-validations 接口。</para>
206+
/// <para>
207+
/// REF: <br/>
208+
/// <![CDATA[ https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_1_31.shtml ]]>
209+
/// </para>
210+
/// </summary>
211+
/// <param name="client"></param>
212+
/// <param name="request"></param>
213+
/// <param name="cancellationToken"></param>
214+
/// <returns></returns>
215+
public static async Task<Models.GetPayrollCardWesureTokenValidationResponse> ExecuteGetPayrollCardWesureTokenValidationAsync(this WechatTenpayClient client, Models.GetPayrollCardWesureTokenValidationRequest request, CancellationToken cancellationToken = default)
216+
{
217+
if (client is null) throw new ArgumentNullException(nameof(client));
218+
if (request is null) throw new ArgumentNullException(nameof(request));
219+
220+
IFlurlRequest flurlReq = client
221+
.CreateFlurlRequest(request, HttpMethod.Post, "payroll-card", "wesure", "token-validations");
222+
223+
return await client.SendFlurlRequestAsJsonAsync<Models.GetPayrollCardWesureTokenValidationResponse>(flurlReq, data: request, cancellationToken: cancellationToken).ConfigureAwait(false);
224+
}
225+
#endregion
180226
}
181227
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System;
2+
3+
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
4+
{
5+
/// <summary>
6+
/// <para>表示 [POST] /payroll-card/wesure/insurance 接口的请求。</para>
7+
/// </summary>
8+
public class CreatePayrollCardWesureInsuranceRequest : WechatTenpayRequest
9+
{
10+
/// <summary>
11+
/// 获取或设置微信商户号。
12+
/// </summary>
13+
[Newtonsoft.Json.JsonProperty("sp_mchid")]
14+
[System.Text.Json.Serialization.JsonPropertyName("sp_mchid")]
15+
public string MerchantId { get; set; } = string.Empty;
16+
17+
/// <summary>
18+
/// 获取或设置子商户号。
19+
/// </summary>
20+
[Newtonsoft.Json.JsonProperty("sub_mchid")]
21+
[System.Text.Json.Serialization.JsonPropertyName("sub_mchid")]
22+
public string? SubMerchantId { get; set; }
23+
24+
/// <summary>
25+
/// 获取或设置微保保单号。
26+
/// </summary>
27+
[Newtonsoft.Json.JsonProperty("wesure_order_no")]
28+
[System.Text.Json.Serialization.JsonPropertyName("wesure_order_no")]
29+
public string WesureOrderNumber { get; set; } = string.Empty;
30+
31+
/// <summary>
32+
/// 获取或设置保险公司保单号。
33+
/// </summary>
34+
[Newtonsoft.Json.JsonProperty("insurance_company_order_no")]
35+
[System.Text.Json.Serialization.JsonPropertyName("insurance_company_order_no")]
36+
public string InsuranceCompanyOrderNumber { get; set; } = string.Empty;
37+
38+
/// <summary>
39+
/// 获取或设置保险公司名称。
40+
/// </summary>
41+
[Newtonsoft.Json.JsonProperty("insurance_company_name")]
42+
[System.Text.Json.Serialization.JsonPropertyName("insurance_company_name")]
43+
public string InsuranceCompanyName { get; set; } = string.Empty;
44+
45+
/// <summary>
46+
/// 获取或设置授权 Token。
47+
/// </summary>
48+
[Newtonsoft.Json.JsonProperty("token")]
49+
[System.Text.Json.Serialization.JsonPropertyName("token")]
50+
public string? Token { get; set; }
51+
52+
/// <summary>
53+
/// 获取或设置用户唯一标识。
54+
/// </summary>
55+
[Newtonsoft.Json.JsonProperty("openid")]
56+
[System.Text.Json.Serialization.JsonPropertyName("openid")]
57+
public string OpenId { get; set; } = string.Empty;
58+
59+
/// <summary>
60+
/// 获取或设置投保状态。
61+
/// </summary>
62+
[Newtonsoft.Json.JsonProperty("insure_state")]
63+
[System.Text.Json.Serialization.JsonPropertyName("insure_state")]
64+
public string InsureState { get; set; } = string.Empty;
65+
66+
/// <summary>
67+
/// 获取或设置保障生效时间。
68+
/// </summary>
69+
[Newtonsoft.Json.JsonProperty("insure_effect_time")]
70+
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
71+
[System.Text.Json.Serialization.JsonPropertyName("insure_effect_time")]
72+
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
73+
public DateTimeOffset InsureEffectTime { get; set; }
74+
75+
/// <summary>
76+
/// 获取或设置保障失效时间。
77+
/// </summary>
78+
[Newtonsoft.Json.JsonProperty("insure_invalid_time")]
79+
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
80+
[System.Text.Json.Serialization.JsonPropertyName("insure_invalid_time")]
81+
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
82+
public DateTimeOffset InsureInvalidTime { get; set; }
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
2+
{
3+
/// <summary>
4+
/// <para>表示 [POST] /payroll-card/wesure/insurance 接口的响应。</para>
5+
/// </summary>
6+
public class CreatePayrollCardWesureInsuranceResponse : WechatTenpayResponse
7+
{
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
3+
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
4+
{
5+
/// <summary>
6+
/// <para>表示 [POST] /payroll-card/wesure/token-validations 接口的请求。</para>
7+
/// </summary>
8+
[WechatTenpaySensitive]
9+
public class GetPayrollCardWesureTokenValidationRequest : WechatTenpayRequest
10+
{
11+
/// <summary>
12+
/// 获取或设置微信商户号。
13+
/// </summary>
14+
[Newtonsoft.Json.JsonProperty("sp_mchid")]
15+
[System.Text.Json.Serialization.JsonPropertyName("sp_mchid")]
16+
public string MerchantId { get; set; } = string.Empty;
17+
18+
/// <summary>
19+
/// 获取或设置子商户号。
20+
/// </summary>
21+
[Newtonsoft.Json.JsonProperty("sub_mchid")]
22+
[System.Text.Json.Serialization.JsonPropertyName("sub_mchid")]
23+
public string? SubMerchantId { get; set; }
24+
25+
/// <summary>
26+
/// 获取或设置授权 Token。
27+
/// </summary>
28+
[Newtonsoft.Json.JsonProperty("token")]
29+
[System.Text.Json.Serialization.JsonPropertyName("token")]
30+
public string? Token { get; set; }
31+
32+
/// <summary>
33+
/// 获取或设置用户姓名(需使用平台公钥/证书加密)。
34+
/// </summary>
35+
[Newtonsoft.Json.JsonProperty("user_name")]
36+
[System.Text.Json.Serialization.JsonPropertyName("user_name")]
37+
[WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256, algorithm: Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1)]
38+
[WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3, algorithm: Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1)]
39+
public string? UserName { get; set; }
40+
41+
/// <summary>
42+
/// 获取或设置身份证号码(需使用平台公钥/证书加密)。
43+
/// </summary>
44+
[Newtonsoft.Json.JsonProperty("id_card_number")]
45+
[System.Text.Json.Serialization.JsonPropertyName("id_card_number")]
46+
[WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256, algorithm: Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1)]
47+
[WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3, algorithm: Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1)]
48+
public string? IdCardNumber { get; set; }
49+
50+
/// <summary>
51+
/// 获取或设置核身类型。
52+
/// </summary>
53+
[Newtonsoft.Json.JsonProperty("authenticate_type")]
54+
[System.Text.Json.Serialization.JsonPropertyName("authenticate_type")]
55+
public string AuthenticateType { get; set; } = string.Empty;
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
3+
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
4+
{
5+
/// <summary>
6+
/// <para>表示 [POST] /payroll-card/wesure/token-validations 接口的响应。</para>
7+
/// </summary>
8+
public class GetPayrollCardWesureTokenValidationResponse : WechatTenpayResponse
9+
{
10+
/// <summary>
11+
/// 获取或设置校验结果。
12+
/// </summary>
13+
[Newtonsoft.Json.JsonProperty("result")]
14+
[System.Text.Json.Serialization.JsonPropertyName("result")]
15+
public string Result { get; set; } = default!;
16+
17+
/// <summary>
18+
/// 获取或设置用户唯一标识。
19+
/// </summary>
20+
[Newtonsoft.Json.JsonProperty("openid")]
21+
[System.Text.Json.Serialization.JsonPropertyName("openid")]
22+
public string? OpenId { get; set; }
23+
24+
/// <summary>
25+
/// 获取或设置核身成功时间。
26+
/// </summary>
27+
[Newtonsoft.Json.JsonProperty("authenticate_success_time")]
28+
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.Common.Rfc3339DateTimeOffsetConverter))]
29+
[System.Text.Json.Serialization.JsonPropertyName("authenticate_success_time")]
30+
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.Common.Rfc3339DateTimeOffsetConverter))]
31+
public DateTimeOffset? SuccessTime { get; set; }
32+
33+
/// <summary>
34+
/// 获取或设置失败原因。
35+
/// </summary>
36+
[Newtonsoft.Json.JsonProperty("fail_reason")]
37+
[System.Text.Json.Serialization.JsonPropertyName("fail_reason")]
38+
public string? FailReason { get; set; }
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"insurance_company_name": "平安",
3+
"insurance_company_order_no": "5fds5345dfd",
4+
"insure_effect_time": "2018-06-08T10:34:56+08:00",
5+
"insure_invalid_time": "2018-06-08T10:34:56+08:00",
6+
"insure_state": "INSURE_SUCCESS",
7+
"openid": "11111111",
8+
"sp_mchid": "1900000109",
9+
"sub_mchid": "1900000109",
10+
"wesure_order_no": "5fds5345dfd"
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"authenticate_type": "NORMAL",
3+
"id_card_number": "7FzH5XksJG3a8HLLsaaUV6K54y1OnPMY5",
4+
"sp_mchid": "1900000109",
5+
"sub_mchid": "1900000109",
6+
"token": "abcdefghijklmn",
7+
"user_name": "LP7bT4hQXUsOZCEvK2YrSiqFsnP0oRMfeoLN0vBg"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"authenticate_success_time": "2015-05-20T13:29:35+08:00",
3+
"fail_reason": "SP_MCH_NO_PERMISSION",
4+
"openid": "11111111",
5+
"result": "SUCCESS"
6+
}

test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_RequestEncryptionTests.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,62 @@ static void AssertMockRequestModel(Models.CreatePayrollCardTokenRequest request,
13361336
}
13371337
}
13381338

1339+
[Fact(DisplayName = "测试用例:加密请求中的敏感数据([POST] /payroll-card/wesure/token-validations)")]
1340+
public async Task TestEncryptRequestSensitiveProperty_GetPayrollCardWesureTokenValidationRequest()
1341+
{
1342+
static Models.GetPayrollCardWesureTokenValidationRequest GenerateMockRequestModel()
1343+
{
1344+
return new Models.GetPayrollCardWesureTokenValidationRequest()
1345+
{
1346+
UserName = MOCK_PLAIN_STR,
1347+
IdCardNumber = MOCK_PLAIN_STR
1348+
};
1349+
}
1350+
1351+
static void AssertMockRequestModel(Models.GetPayrollCardWesureTokenValidationRequest request, Func<string, string> decryptor)
1352+
{
1353+
Assert.NotEqual(MOCK_PLAIN_STR, request.UserName!);
1354+
Assert.NotEqual(MOCK_PLAIN_STR, request.IdCardNumber!);
1355+
Assert.Equal(MOCK_PLAIN_STR, decryptor.Invoke(request.UserName!));
1356+
Assert.Equal(MOCK_PLAIN_STR, decryptor.Invoke(request.IdCardNumber!));
1357+
Assert.Equal(MOCK_CERT_SN, request.WechatpayCertificateSerialNumber!, ignoreCase: true);
1358+
}
1359+
1360+
if (!string.IsNullOrEmpty(TestConfigs.WechatMerchantRSACertificatePrivateKey))
1361+
{
1362+
using (var client = CreateMockClientUseRSA(autoEncrypt: false))
1363+
{
1364+
var request = GenerateMockRequestModel();
1365+
client.EncryptRequestSensitiveProperty(request);
1366+
AssertMockRequestModel(request, (cipher) => Utilities.RSAUtility.DecryptWithECB(RSA_PEM_PRIVATE_KEY, (EncodedString)cipher)!);
1367+
}
1368+
1369+
using (var client = CreateMockClientUseRSA(autoEncrypt: true))
1370+
{
1371+
var request = GenerateMockRequestModel();
1372+
await client.ExecuteGetPayrollCardWesureTokenValidationAsync(request);
1373+
AssertMockRequestModel(request, (cipher) => Utilities.RSAUtility.DecryptWithECB(RSA_PEM_PRIVATE_KEY, (EncodedString)cipher)!);
1374+
}
1375+
}
1376+
1377+
if (!string.IsNullOrEmpty(TestConfigs.WechatMerchantSM2CertificatePrivateKey))
1378+
{
1379+
using (var client = CreateMockClientUseSM2(autoEncrypt: false))
1380+
{
1381+
var request = GenerateMockRequestModel();
1382+
client.EncryptRequestSensitiveProperty(request);
1383+
AssertMockRequestModel(request, (cipher) => Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, (EncodedString)cipher)!);
1384+
}
1385+
1386+
using (var client = CreateMockClientUseSM2(autoEncrypt: true))
1387+
{
1388+
var request = GenerateMockRequestModel();
1389+
await client.ExecuteGetPayrollCardWesureTokenValidationAsync(request);
1390+
AssertMockRequestModel(request, (cipher) => Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, (EncodedString)cipher)!);
1391+
}
1392+
}
1393+
}
1394+
13391395
[Fact(DisplayName = "测试用例:加密请求中的敏感数据([POST] /profitsharing/orders)")]
13401396
public async Task TestEncryptRequestSensitiveProperty_CreateProfitSharingOrderRequest()
13411397
{

0 commit comments

Comments
 (0)