diff --git a/docs/WechatTenpayV3/Basic_ModelDefinition.md b/docs/WechatTenpayV3/Basic_ModelDefinition.md index eeeda2c8..f301af3e 100644 --- a/docs/WechatTenpayV3/Basic_ModelDefinition.md +++ b/docs/WechatTenpayV3/Basic_ModelDefinition.md @@ -49,6 +49,7 @@ | √ | 行业方案:微信支付分停车服务 | 直连商户 & 服务商 | | | √ | 行业方案:租用充电宝隔夜归还 | 直连商户 | | | √ | 行业方案:微信点餐订单 | 服务商 | | +| √ | 行业方案:ETC 扣费 | 服务商 | | | √ | 营销工具:代金券 | 直连商户 & 服务商 | | | √ | 营销工具:商家券 | 直连商户 & 服务商 | | | √ | 营销工具:委托营销 | 直连商户 & 服务商 | | diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleETCTransactionResource.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleETCTransactionResource.cs new file mode 100644 index 00000000..abb00a48 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleETCTransactionResource.cs @@ -0,0 +1,71 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Events +{ + /// + /// 表示 TRANSACTION.SUCCESS(仅限 ETC 扣费)通知的数据。 + /// + public class VehicleETCTransactionResource : WechatTenpayEvent.Types.IDecryptedResource + { + /// + /// 获取或设置微信商户号。 + /// + [Newtonsoft.Json.JsonProperty("sp_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sp_mchid")] + public string MerchantId { get; set; } = default!; + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonProperty("sub_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_mchid")] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置微信 AppId。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public string AppId { get; set; } = default!; + + /// + /// 获取或设置子商户 AppId。 + /// + [Newtonsoft.Json.JsonProperty("sub_appid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_appid")] + public string? SubAppId { get; set; } + + /// + /// 获取或设置用户唯一标识。 + /// + [Newtonsoft.Json.JsonProperty("sp_openid")] + [System.Text.Json.Serialization.JsonPropertyName("sp_openid")] + public string? OpenId { get; set; } + + /// + /// 获取或设置用户在子商户下的唯一标识。 + /// + [Newtonsoft.Json.JsonProperty("sub_openid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_openid")] + public string? SubOpenId { get; set; } + + /// + /// 获取或设置 ETC 扣费授权协议号。 + /// + [Newtonsoft.Json.JsonProperty("contract_id")] + [System.Text.Json.Serialization.JsonPropertyName("contract_id")] + public string ContractId { get; set; } = default!; + + /// + /// 获取或设置车牌号。 + /// + [Newtonsoft.Json.JsonProperty("plate_number")] + [System.Text.Json.Serialization.JsonPropertyName("plate_number")] + public string PlateNumber { get; set; } = default!; + + /// + /// 获取或设置绑定状态。 + /// + [Newtonsoft.Json.JsonProperty("bind_state")] + [System.Text.Json.Serialization.JsonPropertyName("bind_state")] + public string BindState { get; set; } = default!; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/VehicleParking/VehicleEntranceStateChangeResource.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleEntranceStateChangeResource.cs similarity index 100% rename from src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/VehicleParking/VehicleEntranceStateChangeResource.cs rename to src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleEntranceStateChangeResource.cs diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/VehicleParking/VehicleTransactionResource.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleTransactionResource.cs similarity index 99% rename from src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/VehicleParking/VehicleTransactionResource.cs rename to src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleTransactionResource.cs index 7958da0d..54574152 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/VehicleParking/VehicleTransactionResource.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Events/Vehicle/VehicleTransactionResource.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Events { diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientExecuteVehicleExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientExecuteVehicleExtensions.cs index 100bf8d6..26e11f8f 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientExecuteVehicleExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientExecuteVehicleExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -12,6 +12,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 /// public static class WechatTenpayClientExecuteVehicleExtensions { + #region Parking /// /// 异步调用 [GET] /vehicle/parking/services/find 接口。 /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_8_1.shtml @@ -58,7 +59,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); } + #endregion + #region Transactions /// /// 异步调用 [POST] /vehicle/transactions/parking 接口。 /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_8_3.shtml @@ -101,5 +104,136 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); } + #endregion + + #region ETC + /// + /// 异步调用 [POST] /vehicle/etc/preopen 接口。 + /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_4_1.shtml + /// + /// + /// + /// + /// + public static async Task ExecutePreopenVehicleETCAsync(this WechatTenpayClient client, Models.PreopenVehicleETCRequest 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 + .CreateRequest(request, HttpMethod.Post, "vehicle", "etc", "preopen"); + + return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); + } + + /// + /// 异步调用 [POST] /vehicle/etc/transactions/highway 接口。 + /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_4_3.shtml + /// + /// + /// + /// + /// + public static async Task ExecuteCreateVehicleETCTransactionHighwayAsync(this WechatTenpayClient client, Models.CreateVehicleETCTransactionHighwayRequest 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 + .CreateRequest(request, HttpMethod.Post, "vehicle", "etc", "transactions", "highway"); + + return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); + } + + /// + /// 异步调用 [GET] /vehicle/etc/users/{openid}/contracts 接口。 + /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_4_3.shtml + /// + /// + /// + /// + /// + public static async Task ExecuteGetVehicleETCContractByOpenIdAsync(this WechatTenpayClient client, Models.GetVehicleETCContractByOpenIdRequest 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 + .CreateRequest(request, HttpMethod.Get, "vehicle", "etc", "users", request.OpenId, "contracts") + .SetQueryParam("plan_id", request.PlanId) + .SetQueryParam("plate_number", request.PlateNumber); + + if (request.SubMerchantId != null) + flurlReq.SetQueryParam("sub_mchid", request.SubMerchantId); + + return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); + } + + /// + /// 异步调用 [GET] /vehicle/etc/contracts/{contract_id} 接口。 + /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_4_3.shtml + /// + /// + /// + /// + /// + public static async Task ExecuteGetVehicleETCContractByContractIdAsync(this WechatTenpayClient client, Models.GetVehicleETCContractByContractIdRequest 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 + .CreateRequest(request, HttpMethod.Get, "vehicle", "etc", "contracts", request.ContractId); + + if (request.SubMerchantId != null) + flurlReq.SetQueryParam("sub_mchid", request.SubMerchantId); + + return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); + } + + /// + /// 异步调用 [GET] /vehicle/etc/transactions/out-trade-no/{out_trade_no} 接口。 + /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_4_8.shtml + /// + /// + /// + /// + /// + public static async Task ExecuteGetVehicleETCTransactionByOutTradeNumberAsync(this WechatTenpayClient client, Models.GetVehicleETCTransactionByOutTradeNumberRequest 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 + .CreateRequest(request, HttpMethod.Get, "vehicle", "etc", "transactions", "out-trade-no", request.OutTradeNumber); + + if (request.SubMerchantId != null) + flurlReq.SetQueryParam("sub_mchid", request.SubMerchantId); + + return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); + } + + /// + /// 异步调用 [GET] /vehicle/etc/transactions/id/{transaction_id} 接口。 + /// REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter4_4_8.shtml + /// + /// + /// + /// + /// + public static async Task ExecuteGetVehicleETCTransactionByTransactionIdAsync(this WechatTenpayClient client, Models.GetVehicleETCTransactionByTransactionIdRequest 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 + .CreateRequest(request, HttpMethod.Get, "vehicle", "etc", "transactions", "id", request.TransactionId); + + if (request.SubMerchantId != null) + flurlReq.SetQueryParam("sub_mchid", request.SubMerchantId); + + return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken); + } + #endregion } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/CreateNewTaxControlFapiaoApplicationRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/CreateNewTaxControlFapiaoApplicationRequest.cs index 9a852c83..d7283eed 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/CreateNewTaxControlFapiaoApplicationRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/CreateNewTaxControlFapiaoApplicationRequest.cs @@ -63,7 +63,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string? BankAccountNumber { get; set; } /// - /// 获取或设置用户手机号码。 + /// 获取或设置用户手机号码(需使用平台公钥/证书加密)。 /// [Newtonsoft.Json.JsonProperty("phone")] [System.Text.Json.Serialization.JsonPropertyName("phone")] @@ -72,7 +72,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string? UserMobileNumber { get; set; } /// - /// 获取或设置用户邮箱地址。 + /// 获取或设置用户邮箱地址(需使用平台公钥/证书加密)。 /// [Newtonsoft.Json.JsonProperty("email")] [System.Text.Json.Serialization.JsonPropertyName("email")] diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/GetNewTaxControlFapiaoApplicationByFapiaoApplyIdResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/GetNewTaxControlFapiaoApplicationByFapiaoApplyIdResponse.cs index d379f8fe..70c61458 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/GetNewTaxControlFapiaoApplicationByFapiaoApplyIdResponse.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/FapiaoApplications/GetNewTaxControlFapiaoApplicationByFapiaoApplyIdResponse.cs @@ -144,7 +144,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string? BankAccountNumber { get; set; } /// - /// 获取或设置用户手机号码。 + /// 获取或设置用户手机号码(需使用商户私钥解密)。 /// [Newtonsoft.Json.JsonProperty("phone")] [System.Text.Json.Serialization.JsonPropertyName("phone")] @@ -153,7 +153,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string? UserMobileNumber { get; set; } /// - /// 获取或设置用户邮箱地址。 + /// 获取或设置用户邮箱地址(需使用商户私钥解密)。 /// [Newtonsoft.Json.JsonProperty("email")] [System.Text.Json.Serialization.JsonPropertyName("email")] diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/UserTitle/GetNewTaxControlFapiaoUserTitleResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/UserTitle/GetNewTaxControlFapiaoUserTitleResponse.cs index d7c74a03..aac095f8 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/UserTitle/GetNewTaxControlFapiaoUserTitleResponse.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/NewTaxControlFapiao/UserTitle/GetNewTaxControlFapiaoUserTitleResponse.cs @@ -56,7 +56,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string? BankAccountNumber { get; set; } /// - /// 获取或设置用户手机号码。 + /// 获取或设置用户手机号码(需使用平台公钥/证书加密)。 /// [Newtonsoft.Json.JsonProperty("phone")] [System.Text.Json.Serialization.JsonPropertyName("phone")] @@ -65,7 +65,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string? UserMobileNumber { get; set; } /// - /// 获取或设置用户邮箱地址。 + /// 获取或设置用户邮箱地址(需使用平台公钥/证书加密)。 /// [Newtonsoft.Json.JsonProperty("email")] [System.Text.Json.Serialization.JsonPropertyName("email")] diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/CreateVehicleETCTransactionHighwayRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/CreateVehicleETCTransactionHighwayRequest.cs new file mode 100644 index 00000000..6f1dc3b2 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/CreateVehicleETCTransactionHighwayRequest.cs @@ -0,0 +1,195 @@ +using System; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [POST] /vehicle/etc/transactions/highway 接口的请求。 + /// + public class CreateVehicleETCTransactionHighwayRequest : WechatTenpayRequest + { + public static class Types + { + public class Amount + { + /// + /// 获取或设置总金额(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("total")] + [System.Text.Json.Serialization.JsonPropertyName("total")] + public int Total { get; set; } + + /// + /// 获取或设置币种。 + /// 默认值:CNY + /// + [Newtonsoft.Json.JsonProperty("currency")] + [System.Text.Json.Serialization.JsonPropertyName("currency")] + public string Currency { get; set; } = "CNY"; + } + + public class Highway + { + /// + /// 获取或设置车牌号。 + /// + [Newtonsoft.Json.JsonProperty("plate_number")] + [System.Text.Json.Serialization.JsonPropertyName("plate_number")] + public string PlateNumber { get; set; } = string.Empty; + + /// + /// 获取或设置入站时间。 + /// + [Newtonsoft.Json.JsonProperty("start_time")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))] + [System.Text.Json.Serialization.JsonPropertyName("start_time")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))] + public DateTimeOffset StartTime { get; set; } + + /// + /// 获取或设置出站时间。 + /// + [Newtonsoft.Json.JsonProperty("end_time")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))] + [System.Text.Json.Serialization.JsonPropertyName("end_time")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))] + public DateTimeOffset EndTime { get; set; } + + /// + /// 获取或设置通道类型。 + /// + [Newtonsoft.Json.JsonProperty("channel_type")] + [System.Text.Json.Serialization.JsonPropertyName("channel_type")] + public string ChannelType { get; set; } = string.Empty; + + /// + /// 获取或设置车辆类型。 + /// + [Newtonsoft.Json.JsonProperty("car_type")] + [System.Text.Json.Serialization.JsonPropertyName("car_type")] + public string CarType { get; set; } = string.Empty; + + /// + /// 获取或设置入口名称。 + /// + [Newtonsoft.Json.JsonProperty("entrance")] + [System.Text.Json.Serialization.JsonPropertyName("entrance")] + public string? Entrance { get; set; } + + /// + /// 获取或设置出口名称。 + /// + [Newtonsoft.Json.JsonProperty("exit")] + [System.Text.Json.Serialization.JsonPropertyName("exit")] + public string? Exit { get; set; } + + /// + /// 获取或设置承载人数。 + /// + [Newtonsoft.Json.JsonProperty("carrying_capacity")] + [System.Text.Json.Serialization.JsonPropertyName("carrying_capacity")] + public int? CarryingCapacity { get; set; } + + /// + /// 获取或设置荷载范围字符串。 + /// + [Newtonsoft.Json.JsonProperty("carrying_range")] + [System.Text.Json.Serialization.JsonPropertyName("carrying_range")] + public string? CarryingRangeString { get; set; } + } + + public class Device + { + /// + /// 获取或设置商户端设备号。 + /// + [Newtonsoft.Json.JsonProperty("device_id")] + [System.Text.Json.Serialization.JsonPropertyName("device_id")] + public string? DeviceId { get; set; } + + /// + /// 获取或设置商户端设备 IP。 + /// + [Newtonsoft.Json.JsonProperty("device_ip")] + [System.Text.Json.Serialization.JsonPropertyName("device_ip")] + public string? DeviceIp { get; set; } + } + } + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonProperty("sub_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_mchid")] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置服务商 AppId。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public string AppId { get; set; } = string.Empty; + + /// + /// 获取或设置子商户 AppId。 + /// + [Newtonsoft.Json.JsonProperty("sub_appid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_appid")] + public string? SubAppId { get; set; } + + /// + /// 获取或设置商户订单号。 + /// + [Newtonsoft.Json.JsonProperty("out_trade_no")] + [System.Text.Json.Serialization.JsonPropertyName("out_trade_no")] + public string OutTradeNumber { get; set; } = string.Empty; + + /// + /// 获取或设置 ETC 扣费授权协议号。 + /// + [Newtonsoft.Json.JsonProperty("contract_id")] + [System.Text.Json.Serialization.JsonPropertyName("contract_id")] + public string ContractId { get; set; } = string.Empty; + + /// + /// 获取或设置商品信息。 + /// + [Newtonsoft.Json.JsonProperty("description")] + [System.Text.Json.Serialization.JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// + /// 获取或设置附加信息。 + /// + [Newtonsoft.Json.JsonProperty("attach")] + [System.Text.Json.Serialization.JsonPropertyName("attach")] + public string? Attachment { get; set; } + + /// + /// 获取或设置订单优惠标记。 + /// + [Newtonsoft.Json.JsonProperty("goods_tag")] + [System.Text.Json.Serialization.JsonPropertyName("goods_tag")] + public string? GoodsTag { get; set; } + + /// + /// 获取或设置金额信息。 + /// + [Newtonsoft.Json.JsonProperty("amount")] + [System.Text.Json.Serialization.JsonPropertyName("amount")] + public Types.Amount Amount { get; set; } = new Types.Amount(); + + /// + /// 获取或设置高速信息。 + /// + [Newtonsoft.Json.JsonProperty("highway_info")] + [System.Text.Json.Serialization.JsonPropertyName("highway_info")] + public Types.Highway Highway { get; set; } = new Types.Highway(); + + /// + /// 获取或设置设备信息。 + /// + [Newtonsoft.Json.JsonProperty("device_info")] + [System.Text.Json.Serialization.JsonPropertyName("device_info")] + public Types.Device? Device { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/CreateVehicleETCTransactionHighwayResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/CreateVehicleETCTransactionHighwayResponse.cs new file mode 100644 index 00000000..a56ece9f --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/CreateVehicleETCTransactionHighwayResponse.cs @@ -0,0 +1,11 @@ +using System; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [POST] /vehicle/etc/transactions/highway 接口的响应。 + /// + public class CreateVehicleETCTransactionHighwayResponse : GetVehicleETCTransactionByOutTradeNumberResponse + { + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByContractIdRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByContractIdRequest.cs new file mode 100644 index 00000000..3fcf7574 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByContractIdRequest.cs @@ -0,0 +1,22 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/contracts/{contract_id} 接口的请求。 + /// + public class GetVehicleETCContractByContractIdRequest : WechatTenpayRequest + { + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置 ETC 扣费授权协议号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string ContractId { get; set; } = string.Empty; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByContractIdResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByContractIdResponse.cs new file mode 100644 index 00000000..3741850a --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByContractIdResponse.cs @@ -0,0 +1,9 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/contracts/{contract_id} 接口的响应。 + /// + public class GetVehicleETCContractByContractIdResponse : GetVehicleETCContractByOpenIdResponse + { + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByOpenIdRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByOpenIdRequest.cs new file mode 100644 index 00000000..1bffba37 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByOpenIdRequest.cs @@ -0,0 +1,36 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/users/{openid}/contracts 接口的请求。 + /// + public class GetVehicleETCContractByOpenIdRequest : WechatTenpayRequest + { + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置用户唯一标识。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string OpenId { get; set; } = string.Empty; + + /// + /// 获取或设置 ETC 授权扣费模板 ID。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string PlanId { get; set; } = string.Empty; + + /// + /// 获取或设置车牌号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string PlateNumber { get; set; } = string.Empty; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByOpenIdResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByOpenIdResponse.cs new file mode 100644 index 00000000..6a333c4f --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCContractByOpenIdResponse.cs @@ -0,0 +1,71 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/users/{openid}/contracts 接口的响应。 + /// + public class GetVehicleETCContractByOpenIdResponse : WechatTenpayResponse + { + /// + /// 获取或设置微信商户号。 + /// + [Newtonsoft.Json.JsonProperty("sp_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sp_mchid")] + public string MerchantId { get; set; } = default!; + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonProperty("sub_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_mchid")] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置微信 AppId。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public string AppId { get; set; } = default!; + + /// + /// 获取或设置子商户 AppId。 + /// + [Newtonsoft.Json.JsonProperty("sub_appid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_appid")] + public string? SubAppId { get; set; } + + /// + /// 获取或设置用户唯一标识。 + /// + [Newtonsoft.Json.JsonProperty("sp_openid")] + [System.Text.Json.Serialization.JsonPropertyName("sp_openid")] + public string? OpenId { get; set; } + + /// + /// 获取或设置用户在子商户下的唯一标识。 + /// + [Newtonsoft.Json.JsonProperty("sub_openid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_openid")] + public string? SubOpenId { get; set; } + + /// + /// 获取或设置 ETC 扣费授权协议号。 + /// + [Newtonsoft.Json.JsonProperty("contract_id")] + [System.Text.Json.Serialization.JsonPropertyName("contract_id")] + public string ContractId { get; set; } = default!; + + /// + /// 获取或设置车牌号。 + /// + [Newtonsoft.Json.JsonProperty("plate_number")] + [System.Text.Json.Serialization.JsonPropertyName("plate_number")] + public string PlateNumber { get; set; } = default!; + + /// + /// 获取或设置绑定状态。 + /// + [Newtonsoft.Json.JsonProperty("bind_state")] + [System.Text.Json.Serialization.JsonPropertyName("bind_state")] + public string BindState { get; set; } = default!; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberRequest.cs new file mode 100644 index 00000000..1f78c672 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberRequest.cs @@ -0,0 +1,22 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/transactions/out-trade-no/{out_trade_no} 接口的请求。 + /// + public class GetVehicleETCTransactionByOutTradeNumberRequest : WechatTenpayRequest + { + /// + /// 获取或设置商户订单号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string OutTradeNumber { get; set; } = string.Empty; + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string? SubMerchantId { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberResponse.cs new file mode 100644 index 00000000..fb2538ec --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberResponse.cs @@ -0,0 +1,218 @@ +using System; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/transactions/out-trade-no/{out_trade_no} 接口的响应。 + /// + public class GetVehicleETCTransactionByOutTradeNumberResponse : WechatTenpayResponse + { + public static class Types + { + public class Payer : GetVehicleTransactionByOutTradeNumberResponse.Types.Payer + { + } + + public class Amount : GetVehicleTransactionByOutTradeNumberResponse.Types.Amount + { + } + + public class Device + { + /// + /// 获取或设置商户端设备号。 + /// + [Newtonsoft.Json.JsonProperty("device_id")] + [System.Text.Json.Serialization.JsonPropertyName("device_id")] + public string? DeviceId { get; set; } + + /// + /// 获取或设置商户端设备 IP。 + /// + [Newtonsoft.Json.JsonProperty("device_ip")] + [System.Text.Json.Serialization.JsonPropertyName("device_ip")] + public string? DeviceIp { get; set; } + } + + public class Promotion + { + /// + /// 获取或设置券 ID。 + /// + [Newtonsoft.Json.JsonProperty("promotion_id")] + [System.Text.Json.Serialization.JsonPropertyName("promotion_id")] + public string PromotionId { get; set; } = default!; + + /// + /// 获取或设置优惠名称。 + /// + [Newtonsoft.Json.JsonProperty("name")] + [System.Text.Json.Serialization.JsonPropertyName("name")] + public string? Name { get; set; } + + /// + /// 获取或设置优惠范围。 + /// + [Newtonsoft.Json.JsonProperty("scope")] + [System.Text.Json.Serialization.JsonPropertyName("scope")] + public string? Scope { get; set; } + + /// + /// 获取或设置优惠类型。 + /// + [Newtonsoft.Json.JsonProperty("type")] + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string? Type { get; set; } + + /// + /// 获取或设置优惠券面额(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("amount")] + [System.Text.Json.Serialization.JsonPropertyName("amount")] + public int Amount { get; set; } + + /// + /// 获取或设置活动 ID。 + /// + [Newtonsoft.Json.JsonProperty("activity_id")] + [System.Text.Json.Serialization.JsonPropertyName("activity_id")] + public string? ActivityId { get; set; } + + /// + /// 获取或设置微信出资(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("wxpay_contribute")] + [System.Text.Json.Serialization.JsonPropertyName("wxpay_contribute")] + public int? WechatpayContribute { get; set; } + + /// + /// 获取或设置商户出资(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("merchant_contribute")] + [System.Text.Json.Serialization.JsonPropertyName("merchant_contribute")] + public int? MerchantContribute { get; set; } + + /// + /// 获取或设置其他出资(单位:分)。 + /// + [Newtonsoft.Json.JsonProperty("other_contribute")] + [System.Text.Json.Serialization.JsonPropertyName("other_contribute")] + public int? OtherContribute { get; set; } + } + } + + /// + /// 获取或设置微信商户号。 + /// + [Newtonsoft.Json.JsonProperty("mchid")] + [System.Text.Json.Serialization.JsonPropertyName("mchid")] + public string MerchantId { get; set; } = default!; + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonProperty("sub_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_mchid")] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置微信 AppId。 + /// + [Newtonsoft.Json.JsonProperty("appid")] + [System.Text.Json.Serialization.JsonPropertyName("appid")] + public string AppId { get; set; } = default!; + + /// + /// 获取或设置子商户 AppId。 + /// + [Newtonsoft.Json.JsonProperty("sub_appid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_appid")] + public string? SubAppId { get; set; } + + /// + /// 获取或设置商户订单号。 + /// + [Newtonsoft.Json.JsonProperty("out_trade_no")] + [System.Text.Json.Serialization.JsonPropertyName("out_trade_no")] + public string OutTradeNumber { get; set; } = default!; + + /// + /// 获取或设置微信支付订单号。 + /// + [Newtonsoft.Json.JsonProperty("transaction_id")] + [System.Text.Json.Serialization.JsonPropertyName("transaction_id")] + public string? TransactionId { get; set; } + + /// + /// 获取或设置交易类型。 + /// + [Newtonsoft.Json.JsonProperty("trade_type")] + [System.Text.Json.Serialization.JsonPropertyName("trade_type")] + public string TradeType { get; set; } = default!; + + /// + /// 获取或设置交易状态。 + /// + [Newtonsoft.Json.JsonProperty("trade_state")] + [System.Text.Json.Serialization.JsonPropertyName("trade_state")] + public string TradeState { get; set; } = default!; + + /// + /// 获取或设置交易状态描述。 + /// + [Newtonsoft.Json.JsonProperty("trade_state_desc")] + [System.Text.Json.Serialization.JsonPropertyName("trade_state_desc")] + public string TradeStateDescription { get; set; } = default!; + + /// + /// 获取或设置付款银行类型。 + /// + [Newtonsoft.Json.JsonProperty("bank_type")] + [System.Text.Json.Serialization.JsonPropertyName("bank_type")] + public string? BankType { get; set; } + + /// + /// 获取或设置附加数据。 + /// + [Newtonsoft.Json.JsonProperty("attach")] + [System.Text.Json.Serialization.JsonPropertyName("attach")] + public string? Attachment { get; set; } + + /// + /// 获取或设置支付完成时间。 + /// + [Newtonsoft.Json.JsonProperty("success_time")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))] + [System.Text.Json.Serialization.JsonPropertyName("success_time")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))] + public DateTimeOffset? SuccessTime { get; set; } + + /// + /// 获取或设置支付者信息。 + /// + [Newtonsoft.Json.JsonProperty("payer")] + [System.Text.Json.Serialization.JsonPropertyName("payer")] + public Types.Payer? Payer { get; set; } + + /// + /// 获取或设置金额信息。 + /// + [Newtonsoft.Json.JsonProperty("amount")] + [System.Text.Json.Serialization.JsonPropertyName("amount")] + public Types.Amount Amount { get; set; } = default!; + + /// + /// 获取或设置设备信息。 + /// + [Newtonsoft.Json.JsonProperty("device_info")] + [System.Text.Json.Serialization.JsonPropertyName("device_info")] + public Types.Device? Device { get; set; } + + /// + /// 获取或设置优惠信息。 + /// + [Newtonsoft.Json.JsonProperty("promotion_detail")] + [System.Text.Json.Serialization.JsonPropertyName("promotion_detail")] + public Types.Promotion[]? PromotionList { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdRequest.cs new file mode 100644 index 00000000..c0c2bba6 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdRequest.cs @@ -0,0 +1,22 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/transactions/id/{transaction_id} 接口的请求。 + /// + public class GetVehicleETCTransactionByTransactionIdRequest : WechatTenpayRequest + { + /// + /// 获取或设置微信支付订单号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string TransactionId { get; set; } = string.Empty; + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public string? SubMerchantId { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdResponse.cs new file mode 100644 index 00000000..653cdaae --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdResponse.cs @@ -0,0 +1,9 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [GET] /vehicle/etc/transactions/id/{transaction_id} 接口的响应。 + /// + public class GetVehicleETCTransactionByTransactionIdResponse : GetVehicleETCTransactionByOutTradeNumberResponse + { + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/PreopenVehicleETCRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/PreopenVehicleETCRequest.cs new file mode 100644 index 00000000..fc858740 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/PreopenVehicleETCRequest.cs @@ -0,0 +1,116 @@ +using System; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [POST] /vehicle/etc/preopen 接口的请求。 + /// + [WechatTenpaySensitive] + public class PreopenVehicleETCRequest : WechatTenpayRequest + { + public static class Types + { + public class Identify + { + /// + /// 获取或设置真实姓名(需使用平台公钥/证书加密)。 + /// + [Newtonsoft.Json.JsonProperty("encrypted_real_name")] + [System.Text.Json.Serialization.JsonPropertyName("encrypted_real_name")] + [WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256, algorithm: Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1)] + [WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3, algorithm: Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1)] + public string EncryptedRealName { get; set; } = string.Empty; + + /// + /// 获取或设置证件号码(需使用平台公钥/证书加密)。 + /// + [Newtonsoft.Json.JsonProperty("encrypted_credential_id")] + [System.Text.Json.Serialization.JsonPropertyName("encrypted_credential_id")] + [WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256, algorithm: Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1)] + [WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3, algorithm: Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1)] + public string EncryptedCredentialNumber { get; set; } = string.Empty; + + /// + /// 获取或设置证件类型。 + /// + [Newtonsoft.Json.JsonProperty("credential_type")] + [System.Text.Json.Serialization.JsonPropertyName("credential_type")] + public string? CredentialType { get; set; } + } + } + + /// + /// 获取或设置子商户号。 + /// + [Newtonsoft.Json.JsonProperty("sub_mchid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_mchid")] + public string? SubMerchantId { get; set; } + + /// + /// 获取或设置服务商 AppId。 + /// + [Newtonsoft.Json.JsonProperty("sp_appid")] + [System.Text.Json.Serialization.JsonPropertyName("sp_appid")] + public string AppId { get; set; } = string.Empty; + + /// + /// 获取或设置子商户 AppId。 + /// + [Newtonsoft.Json.JsonProperty("sub_appid")] + [System.Text.Json.Serialization.JsonPropertyName("sub_appid")] + public string? SubAppId { get; set; } + + /// + /// 获取或设置用户唯一标识。 + /// + [Newtonsoft.Json.JsonProperty("openid")] + [System.Text.Json.Serialization.JsonPropertyName("openid")] + public string OpenId { get; set; } = string.Empty; + + /// + /// 获取或设置 ETC 授权扣费模板 ID。 + /// + [Newtonsoft.Json.JsonProperty("plan_id")] + [System.Text.Json.Serialization.JsonPropertyName("plan_id")] + public string PlanId { get; set; } = string.Empty; + + /// + /// 获取或设置 ETC 设备号。 + /// + [Newtonsoft.Json.JsonProperty("etc_device_id")] + [System.Text.Json.Serialization.JsonPropertyName("etc_device_id")] + public string? DeviceId { get; set; } + + /// + /// 获取或设置车牌号。 + /// + [Newtonsoft.Json.JsonProperty("plate_number")] + [System.Text.Json.Serialization.JsonPropertyName("plate_number")] + public string PlateNumber { get; set; } = string.Empty; + + /// + /// 获取或设置入场时间。 + /// + [Newtonsoft.Json.JsonProperty("start_time")] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339DateTimeOffsetConverter))] + [System.Text.Json.Serialization.JsonPropertyName("start_time")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339DateTimeOffsetConverter))] + public DateTimeOffset StartTime { get; set; } + + /// + /// 获取或设置实名认证信息。 + /// + [Newtonsoft.Json.JsonProperty("identify")] + [System.Text.Json.Serialization.JsonPropertyName("identify")] + public Types.Identify Identify { get; set; } = new Types.Identify(); + + /// + /// 获取或设置银行卡号(需使用平台公钥/证书加密)。 + /// + [Newtonsoft.Json.JsonProperty("bank_card_no")] + [System.Text.Json.Serialization.JsonPropertyName("bank_card_no")] + [WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256, algorithm: Constants.EncryptionAlgorithms.RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1)] + [WechatTenpaySensitiveProperty(scheme: Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3, algorithm: Constants.EncryptionAlgorithms.SM2_C1C3C2_ASN1)] + public string? BankCardNumber { get; set; } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/PreopenVehicleETCResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/PreopenVehicleETCResponse.cs new file mode 100644 index 00000000..4de8694c --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Vehicle/ETC/PreopenVehicleETCResponse.cs @@ -0,0 +1,15 @@ +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models +{ + /// + /// 表示 [POST] /vehicle/etc/preopen 接口的响应。 + /// + public class PreopenVehicleETCResponse : WechatTenpayResponse + { + /// + /// 获取或设置预开通 ID。 + /// + [Newtonsoft.Json.JsonProperty("preopen_id")] + [System.Text.Json.Serialization.JsonPropertyName("preopen_id")] + public string PreopenId { get; set; } = default!; + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/SKIT.FlurlHttpClient.Wechat.TenpayV3.csproj b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/SKIT.FlurlHttpClient.Wechat.TenpayV3.csproj index 24a528dd..89d43fc7 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/SKIT.FlurlHttpClient.Wechat.TenpayV3.csproj +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/SKIT.FlurlHttpClient.Wechat.TenpayV3.csproj @@ -15,7 +15,7 @@ https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat Flurl.Http Wechat Weixin MicroMessage Tenpay WechatPay WeixinPay Wxpay 微信 微信支付 微信商户 2.16.0 - 基于 Flurl.Http 的微信支付 API v3 版客户端,支持直连商户、服务商模式,支持基础支付、代金券、商家券、委托营销、消费卡、支付有礼、银行定向促活、微信支付分、微信先享卡、支付即服务、点金计划、智慧商圈、电商收付通、二级商户进件、小微商户进件、消费者投诉、商户违规通知、批量转账到零钱、银行组件、海关报关、融合钱包、微工卡、电子发票等功能。 + 基于 Flurl.Http 的微信支付 API v3 版客户端,支持直连商户、服务商模式,支持基础支付、代金券、商家券、委托营销、消费卡、支付有礼、银行定向促活、微信支付分、微信先享卡、支付即服务、点金计划、智慧商圈、电商收付通、二级商户进件、小微商户进件、消费者投诉、商户违规通知、批量转账到零钱、银行组件、海关报关、融合钱包、微工卡、电子发票、ETC 扣费等功能。 Fu Diwei git https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleETCTransactionResource.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleETCTransactionResource.json new file mode 100644 index 00000000..adddbc92 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleETCTransactionResource.json @@ -0,0 +1,9 @@ +{ + "appid": "wxcbda96de0b165486", + "sp_mchid": "10000098", + "sp_openid": "onqOjjmM1tad-3ROpncN-yUfa6ua", + "sub_mchid": "1900000109", + "contract_id": "aAfixCs13LsdKPpfZfDkk2189ssXjfx", + "bind_state": "DELETED", + "plate_number": "浙ASB945" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/VehicleParking/VehicleEntranceStateChangeResource.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleEntranceStateChangeResource.json similarity index 100% rename from test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/VehicleParking/VehicleEntranceStateChangeResource.json rename to test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleEntranceStateChangeResource.json diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/VehicleParking/VehicleTransactionResource.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleTransactionResource.json similarity index 100% rename from test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/VehicleParking/VehicleTransactionResource.json rename to test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/EventSamples/Vehicle/VehicleTransactionResource.json diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/CreateVehicleETCTransactionHighwayRequest.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/CreateVehicleETCTransactionHighwayRequest.json new file mode 100644 index 00000000..2d2ab4bd --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/CreateVehicleETCTransactionHighwayRequest.json @@ -0,0 +1,29 @@ +{ + "out_trade_no": "121775250124070332", + "sub_mchid": "1900000109", + "appid": "wxd678efh567hg6787", + "sub_appid": "wxd678efh567hg6999", + "contract_id": "wxd678ef4Fwxd678ef4Fwxd678ef4F21", + "description": "商品信息", + "attach": "自定义数据", + "goods_tag": "test", + "highway_info": { + "plate_number": "粤B888888", + "start_time": "2017-08-26T09:43:39+08:00", + "end_time": "2017-08-26T09:43:39+08:00", + "channel_type": "ETC", + "car_type": "CAR", + "entrance": "沿江深圳-大铲湾", + "exit": "广珠东线-珠海站", + "carrying_capacity": 3, + "carrying_range": "6-12" + }, + "amount": { + "total": 888, + "currency": "CNY" + }, + "device_info": { + "device_id": "POS1:1", + "device_ip": "123.12.12.123" + } +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/CreateVehicleETCTransactionHighwayResponse.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/CreateVehicleETCTransactionHighwayResponse.json new file mode 100644 index 00000000..2bceafd5 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/CreateVehicleETCTransactionHighwayResponse.json @@ -0,0 +1,40 @@ +{ + "mchid": "1230000109", + "sub_mchid": "1900000109", + "appid": "wxd678efh567hg6787", + "sub_appid": "wxd678efh567hg6999", + "out_trade_no": "121775250124070332", + "transaction_id": "25012014070332333018", + "attach": "自定义数据", + "trade_type": "AUTH", + "bank_type": "CMC", + "success_time": "2017-08-26T10:43:39+08:00", + "trade_state": "SUCCESS", + "trade_state_desc": "请重新下单支付", + "payer": { + "openid": "oUpF8uMuAJOM2pxb1Q" + }, + "amount": { + "total": 888, + "currency": "CNY", + "payer_total": 888, + "discount_total": 100 + }, + "device_info": { + "device_id": "POS1:1", + "device_ip": "123.12.12.123" + }, + "promotion_detail": [ + { + "promotion_id": "109519", + "name": "单品惠-6", + "scope": "SINGLE", + "type": "DISCOUNT", + "activity_id": "931386", + "amount": 5, + "wxpay_contribute": 0, + "merchant_contribute": 0, + "other_contribute": 5 + } + ] +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCContractByContractIdResponse.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCContractByContractIdResponse.json new file mode 100644 index 00000000..adddbc92 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCContractByContractIdResponse.json @@ -0,0 +1,9 @@ +{ + "appid": "wxcbda96de0b165486", + "sp_mchid": "10000098", + "sp_openid": "onqOjjmM1tad-3ROpncN-yUfa6ua", + "sub_mchid": "1900000109", + "contract_id": "aAfixCs13LsdKPpfZfDkk2189ssXjfx", + "bind_state": "DELETED", + "plate_number": "浙ASB945" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCContractByOpenIdResponse.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCContractByOpenIdResponse.json new file mode 100644 index 00000000..adddbc92 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCContractByOpenIdResponse.json @@ -0,0 +1,9 @@ +{ + "appid": "wxcbda96de0b165486", + "sp_mchid": "10000098", + "sp_openid": "onqOjjmM1tad-3ROpncN-yUfa6ua", + "sub_mchid": "1900000109", + "contract_id": "aAfixCs13LsdKPpfZfDkk2189ssXjfx", + "bind_state": "DELETED", + "plate_number": "浙ASB945" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberResponse.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberResponse.json new file mode 100644 index 00000000..2bceafd5 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCTransactionByOutTradeNumberResponse.json @@ -0,0 +1,40 @@ +{ + "mchid": "1230000109", + "sub_mchid": "1900000109", + "appid": "wxd678efh567hg6787", + "sub_appid": "wxd678efh567hg6999", + "out_trade_no": "121775250124070332", + "transaction_id": "25012014070332333018", + "attach": "自定义数据", + "trade_type": "AUTH", + "bank_type": "CMC", + "success_time": "2017-08-26T10:43:39+08:00", + "trade_state": "SUCCESS", + "trade_state_desc": "请重新下单支付", + "payer": { + "openid": "oUpF8uMuAJOM2pxb1Q" + }, + "amount": { + "total": 888, + "currency": "CNY", + "payer_total": 888, + "discount_total": 100 + }, + "device_info": { + "device_id": "POS1:1", + "device_ip": "123.12.12.123" + }, + "promotion_detail": [ + { + "promotion_id": "109519", + "name": "单品惠-6", + "scope": "SINGLE", + "type": "DISCOUNT", + "activity_id": "931386", + "amount": 5, + "wxpay_contribute": 0, + "merchant_contribute": 0, + "other_contribute": 5 + } + ] +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdResponse.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdResponse.json new file mode 100644 index 00000000..2bceafd5 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/GetVehicleETCTransactionByTransactionIdResponse.json @@ -0,0 +1,40 @@ +{ + "mchid": "1230000109", + "sub_mchid": "1900000109", + "appid": "wxd678efh567hg6787", + "sub_appid": "wxd678efh567hg6999", + "out_trade_no": "121775250124070332", + "transaction_id": "25012014070332333018", + "attach": "自定义数据", + "trade_type": "AUTH", + "bank_type": "CMC", + "success_time": "2017-08-26T10:43:39+08:00", + "trade_state": "SUCCESS", + "trade_state_desc": "请重新下单支付", + "payer": { + "openid": "oUpF8uMuAJOM2pxb1Q" + }, + "amount": { + "total": 888, + "currency": "CNY", + "payer_total": 888, + "discount_total": 100 + }, + "device_info": { + "device_id": "POS1:1", + "device_ip": "123.12.12.123" + }, + "promotion_detail": [ + { + "promotion_id": "109519", + "name": "单品惠-6", + "scope": "SINGLE", + "type": "DISCOUNT", + "activity_id": "931386", + "amount": 5, + "wxpay_contribute": 0, + "merchant_contribute": 0, + "other_contribute": 5 + } + ] +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/PreopenVehicleETCRequest.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/PreopenVehicleETCRequest.json new file mode 100644 index 00000000..eefa77d0 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/PreopenVehicleETCRequest.json @@ -0,0 +1,14 @@ +{ + "sp_appid": "wx47c677487ce3ac47", + "sub_appid": "wxcc603d9f0d54eaf0", + "sub_mchid": "1510770601", + "openid": "ot35_4gNgXsUuh2sQY0lT9N08AU0", + "plan_id": "97619", + "etc_device_id": "xxxxxxx", + "plate_number": "藏ZZZZZZ", + "identify": { + "encrypted_real_name": "jUCOynVR0fvpLOX6jBeRlK/TErW6DqEM8JFHX4iL3Rlj2vqaEqktvw1KVjgmQ==", + "encrypted_credential_id": "l5CfRt85SAX3GxVYqGQm2Q8cfeFd1dWy40j4s7XpIds8Glw83HG3vN9Q==", + "credential_type": "ID_CARD" + } +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/PreopenVehicleETCResponse.json b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/PreopenVehicleETCResponse.json new file mode 100644 index 00000000..84a142cc --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/ModelSamples/Vehicle/ETC/PreopenVehicleETCResponse.json @@ -0,0 +1,3 @@ +{ + "preopen_id": "sdjf23kdsHJfdksj8djsK98HJio5hdI0" +} diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_RequestEncryptionTests.cs b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_RequestEncryptionTests.cs index cbea80a2..1b895402 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_RequestEncryptionTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/TestCase_RequestEncryptionTests.cs @@ -1073,6 +1073,50 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests await CreateMockClientUseSM2(autoEncrypt: true).ExecuteCreateTransferBatchAsync(reqB2); AssertMockRequestModel(reqB2, (cipher) => Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, cipher)); } + + [Fact(DisplayName = "测试用例:加密请求中的敏感数据([POST] /vehicle/etc/preopen)")] + public async Task TestEncryptRequestSensitiveProperty_PreopenVehicleETCRequest() + { + static Models.PreopenVehicleETCRequest GenerateMockRequestModel() + { + return new Models.PreopenVehicleETCRequest() + { + Identify = new Models.PreopenVehicleETCRequest.Types.Identify() + { + EncryptedRealName = MOCK_PLAIN_STR, + EncryptedCredentialNumber = MOCK_PLAIN_STR + }, + BankCardNumber = MOCK_PLAIN_STR + }; + } + + static void AssertMockRequestModel(Models.PreopenVehicleETCRequest request, Func decryptor) + { + Assert.NotEqual(MOCK_PLAIN_STR, request.Identify!.EncryptedRealName!); + Assert.NotEqual(MOCK_PLAIN_STR, request.Identify!.EncryptedCredentialNumber!); + Assert.NotEqual(MOCK_PLAIN_STR, request.BankCardNumber!); + Assert.Equal(MOCK_PLAIN_STR, decryptor.Invoke(request.Identify!.EncryptedRealName!)); + Assert.Equal(MOCK_PLAIN_STR, decryptor.Invoke(request.Identify!.EncryptedCredentialNumber!)); + Assert.Equal(MOCK_PLAIN_STR, decryptor.Invoke(request.BankCardNumber!)); + Assert.Equal(MOCK_CERT_SN, request.WechatpayCertificateSerialNumber!, ignoreCase: true); + } + + var reqA1 = GenerateMockRequestModel(); + CreateMockClientUseRSA(autoEncrypt: false).EncryptRequestSensitiveProperty(reqA1); + AssertMockRequestModel(reqA1, (cipher) => Utilities.RSAUtility.DecryptWithECB(RSA_PEM_PRIVATE_KEY, cipher)); + + var reqA2 = GenerateMockRequestModel(); + CreateMockClientUseSM2(autoEncrypt: false).EncryptRequestSensitiveProperty(reqA2); + AssertMockRequestModel(reqA2, (cipher) => Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, cipher)); + + var reqB1 = GenerateMockRequestModel(); + await CreateMockClientUseRSA(autoEncrypt: true).ExecutePreopenVehicleETCAsync(reqB1); + AssertMockRequestModel(reqB1, (cipher) => Utilities.RSAUtility.DecryptWithECB(RSA_PEM_PRIVATE_KEY, cipher)); + + var reqB2 = GenerateMockRequestModel(); + await CreateMockClientUseSM2(autoEncrypt: true).ExecutePreopenVehicleETCAsync(reqB2); + AssertMockRequestModel(reqB2, (cipher) => Utilities.SM2Utility.Decrypt(SM2_PEM_PRIVATE_KEY, cipher)); + } } partial class TestCase_RequestEncryptionTests