diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMessageDeviceSubscribeExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMessageDeviceSubscribeExtensions.cs
new file mode 100644
index 00000000..b067ed76
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMessageDeviceSubscribeExtensions.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Flurl;
+using Flurl.Http;
+
+namespace SKIT.FlurlHttpClient.Wechat.Api
+{
+ public static class WechatApiClientExecuteCgibinMessageDeviceSubscribeExtensions
+ {
+ ///
+ /// 异步调用 [POST] /cgi-bin/message/device/subscribe/send 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/hardware-device/hardwareDevice.send.html
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteCgibinMessageDeviceSubscribeSendAsync(this WechatApiClient client, Models.CgibinMessageDeviceSubscribeSendRequest 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, "cgi-bin", "message", "device", "subscribe", "send")
+ .SetQueryParam("access_token", request.AccessToken);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs
index 6107e337..a96e30b5 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs
@@ -1080,5 +1080,27 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
}
#endregion
+
+ #region HardwareDevice
+ ///
+ /// 异步调用 [POST] /wxa/getsnticket 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/hardware-device/hardwareDevice.getSnTicket.html
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteWxaGetSnTicketAsync(this WechatApiClient client, Models.WxaGetSnTicketRequest 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, "wxa", "getsnticket")
+ .SetQueryParam("access_token", request.AccessToken);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+ #endregion
}
}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendRequest.cs
new file mode 100644
index 00000000..a3509c16
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendRequest.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /cgi-bin/message/device/subscribe/send 接口的请求。
+ ///
+ public class CgibinMessageDeviceSubscribeSendRequest : WechatApiRequest
+ {
+ public static class Types
+ {
+ public class DataItem
+ {
+ ///
+ /// 获取或设置消息内容文本。
+ ///
+ [Newtonsoft.Json.JsonProperty("value")]
+ [System.Text.Json.Serialization.JsonPropertyName("value")]
+ public string Value { get; set; } = string.Empty;
+ }
+ }
+
+ ///
+ /// 获取或设置接收消息的用户 OpenId 列表。
+ ///
+ [Newtonsoft.Json.JsonProperty("to_openid_list")]
+ [System.Text.Json.Serialization.JsonPropertyName("to_openid_list")]
+ public IList ToUserOpenIdList { get; set; } = new List();
+
+ ///
+ /// 获取或设置订阅模板 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("template_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("template_id")]
+ public string TemplateId { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置设备序列号。
+ ///
+ [Newtonsoft.Json.JsonProperty("sn")]
+ [System.Text.Json.Serialization.JsonPropertyName("sn")]
+ public string SerialNumber { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置小程序页面路径。
+ ///
+ [Newtonsoft.Json.JsonProperty("page")]
+ [System.Text.Json.Serialization.JsonPropertyName("page")]
+ public string? MiniProgramPagePath { get; set; }
+
+ ///
+ /// 获取或设置小程序版本。
+ ///
+ [Newtonsoft.Json.JsonProperty("miniprogram_state")]
+ [System.Text.Json.Serialization.JsonPropertyName("miniprogram_state")]
+ public string? MiniProgramState { get; set; }
+
+ ///
+ /// 获取或设置语言类型。
+ ///
+ [Newtonsoft.Json.JsonProperty("lang")]
+ [System.Text.Json.Serialization.JsonPropertyName("lang")]
+ public string? Language { get; set; }
+
+ ///
+ /// 获取或设置消息正文。
+ ///
+ [Newtonsoft.Json.JsonProperty("data")]
+ [System.Text.Json.Serialization.JsonPropertyName("data")]
+ public IDictionary? Data { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendResponse.cs
new file mode 100644
index 00000000..9136777e
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendResponse.cs
@@ -0,0 +1,9 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /cgi-bin/message/device/subscribe/send 接口的响应。
+ ///
+ public class CgibinMessageDeviceSubscribeSendResponse : WechatApiResponse
+ {
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/Wxa/HardwareDevice/WxaGetSnTicketRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/Wxa/HardwareDevice/WxaGetSnTicketRequest.cs
new file mode 100644
index 00000000..f0ac84ca
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/Wxa/HardwareDevice/WxaGetSnTicketRequest.cs
@@ -0,0 +1,22 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /wxa/getsnticket 接口的请求。
+ ///
+ public class WxaGetSnTicketRequest : WechatApiRequest
+ {
+ ///
+ /// 获取或设置设备唯一序列号。
+ ///
+ [Newtonsoft.Json.JsonProperty("sn")]
+ [System.Text.Json.Serialization.JsonPropertyName("sn")]
+ public string SerialNumber { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置设备型号 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("model_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("model_id")]
+ public string ModelId { get; set; } = string.Empty;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/Wxa/HardwareDevice/WxaGetSnTicketResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/Wxa/HardwareDevice/WxaGetSnTicketResponse.cs
new file mode 100644
index 00000000..f59648ac
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/Wxa/HardwareDevice/WxaGetSnTicketResponse.cs
@@ -0,0 +1,15 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /wxa/getsnticket 接口的响应。
+ ///
+ public class WxaGetSnTicketResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置设备票据。
+ ///
+ [Newtonsoft.Json.JsonProperty("sn_ticket")]
+ [System.Text.Json.Serialization.JsonPropertyName("sn_ticket")]
+ public string SnTicket { get; set; } = default!;
+ }
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendRequest.json
new file mode 100644
index 00000000..8bfd6eff
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendRequest.json
@@ -0,0 +1,19 @@
+{
+ "touser": "OPENID",
+ "template_id": "TEMPLATE_ID",
+ "page": "index",
+ "data": {
+ "name01": {
+ "value": "某某"
+ },
+ "amount01": {
+ "value": "¥100"
+ },
+ "thing01": {
+ "value": "广州至北京"
+ },
+ "date01": {
+ "value": "2018-01-01"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendResponse.json
new file mode 100644
index 00000000..22fdca1b
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/CgibinMessageDeviceSubscribe/CgibinMessageDeviceSubscribeSendResponse.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/Wxa/HardwareDevice/WxaGetSnTicketRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/Wxa/HardwareDevice/WxaGetSnTicketRequest.json
new file mode 100644
index 00000000..02f44b67
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/Wxa/HardwareDevice/WxaGetSnTicketRequest.json
@@ -0,0 +1,4 @@
+{
+ "model_id": "XXXXX",
+ "sn": "XXXXX"
+}
\ No newline at end of file
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/Wxa/HardwareDevice/WxaGetSnTicketResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/Wxa/HardwareDevice/WxaGetSnTicketResponse.json
new file mode 100644
index 00000000..5099b1af
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/Wxa/HardwareDevice/WxaGetSnTicketResponse.json
@@ -0,0 +1,3 @@
+{
+ "sn_ticket": "XXXXX"
+}
\ No newline at end of file