diff --git a/docs/WechatApi/README.md b/docs/WechatApi/README.md
index 7a34ca68..cef611fb 100644
--- a/docs/WechatApi/README.md
+++ b/docs/WechatApi/README.md
@@ -38,10 +38,11 @@ var options = new WechatApiClientOptions()
AppSecret = "微信 AppSecret",
ImmeDeliveryAppKey = "即时配送相关服务 AppKey,不用则不填",
ImmeDeliveryAppSecret = "即时配送相关服务 AppSecret,不用则不填",
- MidasOfferId = "米大师(虚拟支付 1.0)相关服务 OfferId,不用则不填",
- MidasOfferIdV2 = "米大师(虚拟支付 2.0)相关服务 OfferId,不用则不填",
- MidasAppKey = "米大师(虚拟支付 1.0)相关服务 AppKey,不用则不填",
- MidasAppKeyV2 = "米大师(虚拟支付 2.0)相关服务 AppKey,不用则不填"
+ VirtualPaymentAppKey = "虚拟支付相关服务 AppKey,不用则不填",
+ MidasOfferId = "米大师 1.0 相关服务 OfferId,不用则不填",
+ MidasAppKey = "米大师 1.0 相关服务 AppKey,不用则不填",
+ MidasOfferIdV2 = "米大师 2.0 相关服务 OfferId,不用则不填",
+ MidasAppKeyV2 = "米大师 2.0 相关服务 AppKey,不用则不填"
};
var client = new WechatApiClient(options);
```
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Events/XPay/XPayCoinPayNotifyEvent.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Events/XPay/XPayCoinPayNotifyEvent.cs
new file mode 100644
index 00000000..3b8b8dde
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Events/XPay/XPayCoinPayNotifyEvent.cs
@@ -0,0 +1,119 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Events
+{
+ ///
+ /// 表示 EVENT.xpay_coin_pay_notify 事件的数据。
+ /// https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html
+ ///
+ public class XPayCoinPayNotifyEvent : WechatApiEvent, WechatApiEvent.Serialization.IJsonSerializable, WechatApiEvent.Serialization.IXmlSerializable
+ {
+ public static class Types
+ {
+ public class WechatpayInfo
+ {
+ ///
+ /// 获取或设置商户订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("MchOrderNo")]
+ [System.Text.Json.Serialization.JsonPropertyName("MchOrderNo")]
+ [System.Xml.Serialization.XmlElement("MchOrderNo")]
+ public string MerchantOrderNumber { get; set; } = default!;
+
+ ///
+ /// 获取或设置微信支付订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("TransactionId")]
+ [System.Text.Json.Serialization.JsonPropertyName("TransactionId")]
+ [System.Xml.Serialization.XmlElement("TransactionId")]
+ public string TransactionId { get; set; } = default!;
+
+ ///
+ /// 获取或设置支付时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("PaidTime")]
+ [System.Text.Json.Serialization.JsonPropertyName("PaidTime")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("PaidTime")]
+ public long PayTimestamp { get; set; }
+ }
+
+ public class CoinInfo
+ {
+ ///
+ /// 获取或设置数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("Quantity")]
+ [System.Text.Json.Serialization.JsonPropertyName("Quantity")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("Quantity")]
+ public int Quantity { get; set; }
+
+ ///
+ /// 获取或设置原始价格(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("OrigPrice")]
+ [System.Text.Json.Serialization.JsonPropertyName("OrigPrice")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("OrigPrice")]
+ public int OriginalPrice { get; set; }
+
+ ///
+ /// 获取或设置实付价格(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("ActualPrice")]
+ [System.Text.Json.Serialization.JsonPropertyName("ActualPrice")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("ActualPrice")]
+ public int ActualPrice { get; set; }
+
+ ///
+ /// 获取或设置透传信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("Attach")]
+ [System.Text.Json.Serialization.JsonPropertyName("Attach")]
+ [System.Xml.Serialization.XmlElement("Attach", IsNullable = true)]
+ public string? Attachment { get; set; }
+ }
+ }
+
+ ///
+ /// 获取或设置用户 OpenId。
+ ///
+ [Newtonsoft.Json.JsonProperty("OpenId")]
+ [System.Text.Json.Serialization.JsonPropertyName("OpenId")]
+ [System.Xml.Serialization.XmlElement("OpenId")]
+ public string OpenId { get; set; } = default!;
+
+ ///
+ /// 获取或设置业务订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("OutTradeNo")]
+ [System.Text.Json.Serialization.JsonPropertyName("OutTradeNo")]
+ [System.Xml.Serialization.XmlElement("OutTradeNo")]
+ public string OutTradeNumber { get; set; } = default!;
+
+ ///
+ /// 获取或设置虚拟支付环境。
+ ///
+ [Newtonsoft.Json.JsonProperty("Env")]
+ [System.Text.Json.Serialization.JsonPropertyName("Env")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("Env")]
+ public int Environment { get; set; }
+
+ ///
+ /// 获取或设置微信支付信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("WeChatPayInfo")]
+ [System.Text.Json.Serialization.JsonPropertyName("WeChatPayInfo")]
+ [System.Xml.Serialization.XmlElement("WeChatPayInfo", IsNullable = true)]
+ public Types.WechatpayInfo? WechatpayInfo { get; set; }
+
+ ///
+ /// 获取或设置代币信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("CoinInfo")]
+ [System.Text.Json.Serialization.JsonPropertyName("CoinInfo")]
+ [System.Xml.Serialization.XmlElement("CoinInfo", IsNullable = true)]
+ public Types.CoinInfo? CoinInfo { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Events/XPay/XPayGoodsDeliverNotifyEvent.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Events/XPay/XPayGoodsDeliverNotifyEvent.cs
new file mode 100644
index 00000000..ec034e7b
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Events/XPay/XPayGoodsDeliverNotifyEvent.cs
@@ -0,0 +1,127 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Events
+{
+ ///
+ /// 表示 EVENT.xpay_goods_deliver_notify 事件的数据。
+ /// https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html
+ ///
+ public class XPayGoodsDeliverNotifyEvent : WechatApiEvent, WechatApiEvent.Serialization.IJsonSerializable, WechatApiEvent.Serialization.IXmlSerializable
+ {
+ public static class Types
+ {
+ public class WechatpayInfo
+ {
+ ///
+ /// 获取或设置商户订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("MchOrderNo")]
+ [System.Text.Json.Serialization.JsonPropertyName("MchOrderNo")]
+ [System.Xml.Serialization.XmlElement("MchOrderNo")]
+ public string MerchantOrderNumber { get; set; } = default!;
+
+ ///
+ /// 获取或设置微信支付订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("TransactionId")]
+ [System.Text.Json.Serialization.JsonPropertyName("TransactionId")]
+ [System.Xml.Serialization.XmlElement("TransactionId")]
+ public string TransactionId { get; set; } = default!;
+
+ ///
+ /// 获取或设置支付时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("PaidTime")]
+ [System.Text.Json.Serialization.JsonPropertyName("PaidTime")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("PaidTime")]
+ public long PayTimestamp { get; set; }
+ }
+
+ public class GoodsInfo
+ {
+ ///
+ /// 获取或设置道具 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("ProductId")]
+ [System.Text.Json.Serialization.JsonPropertyName("ProductId")]
+ [System.Xml.Serialization.XmlElement("ProductId")]
+ public string ProductId { get; set; } = default!;
+
+ ///
+ /// 获取或设置数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("Quantity")]
+ [System.Text.Json.Serialization.JsonPropertyName("Quantity")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("Quantity")]
+ public int Quantity { get; set; }
+
+ ///
+ /// 获取或设置原始价格(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("OrigPrice")]
+ [System.Text.Json.Serialization.JsonPropertyName("OrigPrice")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("OrigPrice")]
+ public int OriginalPrice { get; set; }
+
+ ///
+ /// 获取或设置实付价格(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("ActualPrice")]
+ [System.Text.Json.Serialization.JsonPropertyName("ActualPrice")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("ActualPrice")]
+ public int ActualPrice { get; set; }
+
+ ///
+ /// 获取或设置透传信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("Attach")]
+ [System.Text.Json.Serialization.JsonPropertyName("Attach")]
+ [System.Xml.Serialization.XmlElement("Attach", IsNullable = true)]
+ public string? Attachment { get; set; }
+ }
+ }
+
+ ///
+ /// 获取或设置用户 OpenId。
+ ///
+ [Newtonsoft.Json.JsonProperty("OpenId")]
+ [System.Text.Json.Serialization.JsonPropertyName("OpenId")]
+ [System.Xml.Serialization.XmlElement("OpenId")]
+ public string OpenId { get; set; } = default!;
+
+ ///
+ /// 获取或设置业务订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("OutTradeNo")]
+ [System.Text.Json.Serialization.JsonPropertyName("OutTradeNo")]
+ [System.Xml.Serialization.XmlElement("OutTradeNo")]
+ public string OutTradeNumber { get; set; } = default!;
+
+ ///
+ /// 获取或设置虚拟支付环境。
+ ///
+ [Newtonsoft.Json.JsonProperty("Env")]
+ [System.Text.Json.Serialization.JsonPropertyName("Env")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ [System.Xml.Serialization.XmlElement("Env")]
+ public int Environment { get; set; }
+
+ ///
+ /// 获取或设置微信支付信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("WeChatPayInfo")]
+ [System.Text.Json.Serialization.JsonPropertyName("WeChatPayInfo")]
+ [System.Xml.Serialization.XmlElement("WeChatPayInfo", IsNullable = true)]
+ public Types.WechatpayInfo? WechatpayInfo { get; set; }
+
+ ///
+ /// 获取或设置道具信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("GoodsInfo")]
+ [System.Text.Json.Serialization.JsonPropertyName("GoodsInfo")]
+ [System.Xml.Serialization.XmlElement("GoodsInfo", IsNullable = true)]
+ public Types.GoodsInfo? GoodsInfo { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs
index e9c56752..17039397 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs
@@ -1,13 +1,9 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using System.Web;
using Flurl;
using Flurl.Http;
-using Newtonsoft.Json.Converters;
namespace SKIT.FlurlHttpClient.Wechat.Api
{
@@ -36,7 +32,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
request.SignMethod = Constants.MidasSignMethods.HMAC_SHA256;
}
- if (request.Signature == null)
+ if (request.Signature == null && request.SessionKey != null)
{
tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request);
@@ -45,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
case Constants.MidasSignMethods.HMAC_SHA256:
{
string msgText = tmpRawData;
- request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey ?? string.Empty, msgText).ToLower();
+ request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey, msgText).ToLower();
}
break;
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs
new file mode 100644
index 00000000..85821e26
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs
@@ -0,0 +1,364 @@
+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 WechatApiClientExecuteXPayExtensions
+ {
+ private static T PreprocessRequest(WechatApiClient client, ref T request)
+ where T : Models.XPayRequestBase, new()
+ {
+ if (client == null) throw new ArgumentNullException(nameof(request));
+ if (request == null) throw new ArgumentNullException(nameof(request));
+
+ string? tmpRawData = null; // 用于缓存待签名数据中的请求正文部分,避免序列化多次浪费性能
+
+ if (request.Signature == null && request.SessionKey != null)
+ {
+ tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request);
+
+ string msgText = tmpRawData;
+ request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey, msgText).ToLower();
+ }
+
+ if (request.PaySign == null)
+ {
+ tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request);
+
+ string msgText = $"{request.GetRequestPath()}&{tmpRawData}";
+ request.PaySign = Utilities.HMACUtility.HashWithSHA256(client.Credentials.VirtualPaymentAppKey ?? string.Empty, msgText).ToLower();
+ }
+
+ return request;
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/query_user_balance 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayQueryUserBalanceAsync(this WechatApiClient client, Models.XPayQueryUserBalanceRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "query_user_balance")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("signature", request.Signature)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/currency_pay 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayCurrencyPayAsync(this WechatApiClient client, Models.XPayCurrencyPayRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "currency_pay")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("signature", request.Signature)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/cancel_currency_pay 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayCancelCurrencyPayAsync(this WechatApiClient client, Models.XPayCancelCurrencyPayRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "cancel_currency_pay")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("signature", request.Signature)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/notify_provide_goods 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayNotifyProvideGoodsAsync(this WechatApiClient client, Models.XPayNotifyProvideGoodsRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "notify_provide_goods")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/present_currency 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayPresentCurrencyAsync(this WechatApiClient client, Models.XPayPresentCurrencyRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "present_currency")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/download_bill 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayDownloadBillAsync(this WechatApiClient client, Models.XPayDownloadBillRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "download_bill")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/query_order 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayQueryOrderAsync(this WechatApiClient client, Models.XPayQueryOrderRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "query_order")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/refund_order 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayRefundOrderAsync(this WechatApiClient client, Models.XPayRefundOrderRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "refund_order")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/create_withdraw_order 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayCreateWithdrawOrderAsync(this WechatApiClient client, Models.XPayCreateWithdrawOrderRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "create_withdraw_order")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/query_withdraw_order 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayQueryWithdrawOrderAsync(this WechatApiClient client, Models.XPayQueryWithdrawOrderRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "query_withdraw_order")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/start_upload_goods 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayStartUploadGoodsAsync(this WechatApiClient client, Models.XPayStartUploadGoodsRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "start_upload_goods")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/query_upload_goods 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayQueryUploadGoodsAsync(this WechatApiClient client, Models.XPayQueryUploadGoodsRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "query_upload_goods")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/start_publish_goods 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayStartPublishGoodsAsync(this WechatApiClient client, Models.XPayStartPublishGoodsRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "start_publish_goods")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 异步调用 [POST] /xpay/query_publish_goods 接口。
+ /// REF: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/virtual-payment.html#_2-3-%E6%9C%8D%E5%8A%A1%E5%99%A8API
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteXPayQueryPublishGoodsAsync(this WechatApiClient client, Models.XPayQueryPublishGoodsRequest 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
+ .CreateRequest(request, HttpMethod.Post, "xpay", "query_publish_goods")
+ .SetQueryParam("access_token", request.AccessToken)
+ .SetQueryParam("pay_sig", request.PaySign);
+
+ return await client.SendRequestWithJsonAsync(flurlReq, data: request, cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/Abstractions/WxaGameRequestBase.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/Abstractions/WxaGameRequestBase.cs
index 07863192..4589d2b1 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/Abstractions/WxaGameRequestBase.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/Abstractions/WxaGameRequestBase.cs
@@ -49,7 +49,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Models
///
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
- public string SessionKey { get; set; } = string.Empty;
+ public string? SessionKey { get; set; }
///
/// 获取或设置用于用户登录态签名的方式。
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGameCancelPayRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGameCancelPayRequest.cs
index 36c95d5b..3775fa37 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGameCancelPayRequest.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGameCancelPayRequest.cs
@@ -20,7 +20,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Models
public string BillNumber { get; set; } = string.Empty;
///
- /// 获取或设置本次退回的退游戏币的数量。
+ /// 获取或设置本次退回的游戏币的数量。
///
[Newtonsoft.Json.JsonProperty("amount")]
[System.Text.Json.Serialization.JsonPropertyName("amount")]
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGamePayResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGamePayResponse.cs
index 5321d196..20bfff72 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGamePayResponse.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/WxaGame/WxaGamePayResponse.cs
@@ -20,7 +20,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Models
public int Balance { get; set; }
///
- /// 获取或设置本次扣的赠送币的数量。
+ /// 获取或设置本次扣除的赠送币的数量。
///
[Newtonsoft.Json.JsonProperty("used_present_amount")]
[System.Text.Json.Serialization.JsonPropertyName("used_present_amount")]
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/Abstractions/XPayRequestBase.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/Abstractions/XPayRequestBase.cs
new file mode 100644
index 00000000..9f410ebc
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/Abstractions/XPayRequestBase.cs
@@ -0,0 +1,56 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ public abstract class XPayRequestBase : WechatApiRequest
+ {
+ ///
+ /// 获取或设置用户唯一标识。
+ ///
+ [Newtonsoft.Json.JsonProperty("openid")]
+ [System.Text.Json.Serialization.JsonPropertyName("openid")]
+ public string? OpenId { get; set; }
+
+ ///
+ /// 获取或设置虚拟支付环境。
+ ///
+ [Newtonsoft.Json.JsonProperty("env")]
+ [System.Text.Json.Serialization.JsonPropertyName("env")]
+ public int Environment { get; set; }
+
+ ///
+ /// 获取或设置用户 IP。
+ ///
+ [Newtonsoft.Json.JsonProperty("user_ip")]
+ [System.Text.Json.Serialization.JsonPropertyName("user_ip")]
+ public string? UserIp { get; set; }
+
+ ///
+ /// 获取或设置平台类型。
+ ///
+ [Newtonsoft.Json.JsonProperty("device_type")]
+ [System.Text.Json.Serialization.JsonPropertyName("device_type")]
+ public int? DeviceType { get; set; }
+
+ ///
+ /// 获取或设置用于用户登录态签名的会话密钥。
+ ///
+ [Newtonsoft.Json.JsonIgnore]
+ [System.Text.Json.Serialization.JsonIgnore]
+ public string? SessionKey { get; set; }
+
+ ///
+ /// 获取或设置用户登录态签名。如果不指定将由系统自动生成。
+ ///
+ [Newtonsoft.Json.JsonIgnore]
+ [System.Text.Json.Serialization.JsonIgnore]
+ public string? Signature { get; set; }
+
+ ///
+ /// 获取或设置支付签名。如果不指定将由系统自动生成。
+ ///
+ [Newtonsoft.Json.JsonIgnore]
+ [System.Text.Json.Serialization.JsonIgnore]
+ public string? PaySign { get; set; }
+
+ protected internal abstract string GetRequestPath();
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCancelCurrencyPayRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCancelCurrencyPayRequest.cs
new file mode 100644
index 00000000..aab821bc
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCancelCurrencyPayRequest.cs
@@ -0,0 +1,34 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/cancel_currency_pay 接口的请求。
+ ///
+ public class XPayCancelCurrencyPayRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置扣除代币的订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("pay_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("pay_order_id")]
+ public string PayOrderId { 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("amount")]
+ [System.Text.Json.Serialization.JsonPropertyName("amount")]
+ public int Amount { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/cancel_currency_pay";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCancelCurrencyPayResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCancelCurrencyPayResponse.cs
new file mode 100644
index 00000000..4699188f
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCancelCurrencyPayResponse.cs
@@ -0,0 +1,15 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/cancel_currency_pay 接口的响应。
+ ///
+ public class XPayCancelCurrencyPayResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置退回代币的订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string OrderId { get; set; } = default!;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCreateWithdrawOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCreateWithdrawOrderRequest.cs
new file mode 100644
index 00000000..49f22435
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCreateWithdrawOrderRequest.cs
@@ -0,0 +1,29 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/create_withdraw_order 接口的请求。
+ ///
+ public class XPayCreateWithdrawOrderRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置提现单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_no")]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_no")]
+ public string WithdrawNumber { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置提现金额(单位:元)。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_amount")]
+ [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.TextualDoubleConverter))]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_amount")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString)]
+ public double? Amount { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/create_withdraw_order";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCreateWithdrawOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCreateWithdrawOrderResponse.cs
new file mode 100644
index 00000000..aa09e887
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCreateWithdrawOrderResponse.cs
@@ -0,0 +1,22 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/create_withdraw_order 接口的响应。
+ ///
+ public class XPayCreateWithdrawOrderResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置提现单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_no")]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_no")]
+ public string WithdrawNumber { get; set; } = default!;
+
+ ///
+ /// 获取或设置微信内部提现单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("wx_withdraw_no")]
+ [System.Text.Json.Serialization.JsonPropertyName("wx_withdraw_no")]
+ public string WxWithdrawNumber { get; set; } = default!;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCurrencyPayRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCurrencyPayRequest.cs
new file mode 100644
index 00000000..d42d47d3
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCurrencyPayRequest.cs
@@ -0,0 +1,41 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/currency_pay 接口的请求。
+ ///
+ public class XPayCurrencyPayRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置扣除代币的订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string OrderId { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置扣除代币数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("amount")]
+ [System.Text.Json.Serialization.JsonPropertyName("amount")]
+ public int Amount { get; set; }
+
+ ///
+ /// 获取或设置物品信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("payitem")]
+ [System.Text.Json.Serialization.JsonPropertyName("payitem")]
+ public string? PayItem { get; set; }
+
+ ///
+ /// 获取或设置备注。
+ ///
+ [Newtonsoft.Json.JsonProperty("remark")]
+ [System.Text.Json.Serialization.JsonPropertyName("remark")]
+ public string? Remark { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/currency_pay";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCurrencyPayResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCurrencyPayResponse.cs
new file mode 100644
index 00000000..05cd3eec
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayCurrencyPayResponse.cs
@@ -0,0 +1,29 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/currency_pay 接口的响应。
+ ///
+ public class XPayCurrencyPayResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置扣除代币的订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string OrderId { get; set; } = default!;
+
+ ///
+ /// 获取或设置预扣后的余额。
+ ///
+ [Newtonsoft.Json.JsonProperty("balance")]
+ [System.Text.Json.Serialization.JsonPropertyName("balance")]
+ public int Balance { get; set; }
+
+ ///
+ /// 获取或设置本次扣除的赠送币的数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("used_present_amount")]
+ [System.Text.Json.Serialization.JsonPropertyName("used_present_amount")]
+ public int UsedPresentAmount { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayDownloadBillRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayDownloadBillRequest.cs
new file mode 100644
index 00000000..8521c541
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayDownloadBillRequest.cs
@@ -0,0 +1,27 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/download_bill 接口的请求。
+ ///
+ public class XPayDownloadBillRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置起始日期字符串(格式:yyyyMMdd)。
+ ///
+ [Newtonsoft.Json.JsonProperty("begin_ds")]
+ [System.Text.Json.Serialization.JsonPropertyName("begin_ds")]
+ public string BeginDateString { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置截止日期字符串(格式:yyyyMMdd)。
+ ///
+ [Newtonsoft.Json.JsonProperty("end_ds")]
+ [System.Text.Json.Serialization.JsonPropertyName("end_ds")]
+ public string EndDateString { get; set; } = string.Empty;
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/download_bill";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayDownloadBillResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayDownloadBillResponse.cs
new file mode 100644
index 00000000..1f1ecf4b
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayDownloadBillResponse.cs
@@ -0,0 +1,15 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/download_bill 接口的响应。
+ ///
+ public class XPayDownloadBillResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置下载地址。
+ ///
+ [Newtonsoft.Json.JsonProperty("url")]
+ [System.Text.Json.Serialization.JsonPropertyName("url")]
+ public string Url { get; set; } = default!;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayNotifyProvideGoodsRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayNotifyProvideGoodsRequest.cs
new file mode 100644
index 00000000..1e93639d
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayNotifyProvideGoodsRequest.cs
@@ -0,0 +1,27 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/notify_provide_goods 接口的请求。
+ ///
+ public class XPayNotifyProvideGoodsRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置订单号。与字段 二选一。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string? OrderId { get; set; }
+
+ ///
+ /// 获取或设置微信内部订单号。与字段 二选一。
+ ///
+ [Newtonsoft.Json.JsonProperty("wx_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("wx_order_id")]
+ public string? WxOrderId { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/notify_provide_goods";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayNotifyProvideGoodsResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayNotifyProvideGoodsResponse.cs
new file mode 100644
index 00000000..7099a792
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayNotifyProvideGoodsResponse.cs
@@ -0,0 +1,9 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/notify_provide_goods 接口的响应。
+ ///
+ public class XPayNotifyProvideGoodsResponse : WechatApiResponse
+ {
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayPresentCurrencyRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayPresentCurrencyRequest.cs
new file mode 100644
index 00000000..220b9489
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayPresentCurrencyRequest.cs
@@ -0,0 +1,27 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/present_currency 接口的请求。
+ ///
+ public class XPayPresentCurrencyRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置赠送代币的订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string OrderId { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置赠送代币的个数。
+ ///
+ [Newtonsoft.Json.JsonProperty("amount")]
+ [System.Text.Json.Serialization.JsonPropertyName("amount")]
+ public int Amount { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/present_currency";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayPresentCurrencyResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayPresentCurrencyResponse.cs
new file mode 100644
index 00000000..eacfbd48
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayPresentCurrencyResponse.cs
@@ -0,0 +1,29 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/present_currency 接口的响应。
+ ///
+ public class XPayPresentCurrencyResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置赠送代币的订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string OrderId { get; set; } = default!;
+
+ ///
+ /// 获取或设置赠送后的余额。
+ ///
+ [Newtonsoft.Json.JsonProperty("balance")]
+ [System.Text.Json.Serialization.JsonPropertyName("balance")]
+ public int Balance { get; set; }
+
+ ///
+ /// 获取或设置赠送后的代币余额。
+ ///
+ [Newtonsoft.Json.JsonProperty("present_balance")]
+ [System.Text.Json.Serialization.JsonPropertyName("present_balance")]
+ public int PresentBalance { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryOrderRequest.cs
new file mode 100644
index 00000000..27694f27
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryOrderRequest.cs
@@ -0,0 +1,27 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_order 接口的请求。
+ ///
+ public class XPayQueryOrderRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置订单号。与字段 二选一。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string? OrderId { get; set; }
+
+ ///
+ /// 获取或设置微信内部订单号。与字段 二选一。
+ ///
+ [Newtonsoft.Json.JsonProperty("wx_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("wx_order_id")]
+ public string? WxOrderId { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/query_order";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryOrderResponse.cs
new file mode 100644
index 00000000..795af64f
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryOrderResponse.cs
@@ -0,0 +1,154 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_order 接口的响应。
+ ///
+ public class XPayQueryOrderResponse : WechatApiResponse
+ {
+ 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!;
+
+ ///
+ /// 获取或设置虚拟支付环境。
+ ///
+ [Newtonsoft.Json.JsonProperty("env_type")]
+ [System.Text.Json.Serialization.JsonPropertyName("env_type")]
+ public int Environment { get; set; }
+
+ ///
+ /// 获取或设置订单类型。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_type")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_type")]
+ public int OrderType { get; set; }
+
+ ///
+ /// 获取或设置订单状态。
+ ///
+ [Newtonsoft.Json.JsonProperty("status")]
+ [System.Text.Json.Serialization.JsonPropertyName("status")]
+ public int Status { get; set; }
+
+ ///
+ /// 获取或设置业务类型。
+ ///
+ [Newtonsoft.Json.JsonProperty("biz_type")]
+ [System.Text.Json.Serialization.JsonPropertyName("biz_type")]
+ public int BusinessType { get; set; }
+
+ ///
+ /// 获取或设置订单金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_fee")]
+ public int OrderFee { get; set; }
+
+ ///
+ /// 获取或设置订单优惠金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("coupon_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("coupon_fee")]
+ public int CouponFee { get; set; }
+
+ ///
+ /// 获取或设置用户支付金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("paid_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("paid_fee")]
+ public int PaidFee { get; set; }
+
+ ///
+ /// 获取或设置退款金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("refund_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("refund_fee")]
+ public int? RefundFee { get; set; }
+
+ ///
+ /// 获取或设置剩余金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("left_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("left_fee")]
+ public int? LeftFee { get; set; }
+
+ ///
+ /// 获取或设置付款时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("paid_time")]
+ [System.Text.Json.Serialization.JsonPropertyName("paid_time")]
+ public long? PayTimestamp { get; set; }
+
+ ///
+ /// 获取或设置发货时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("provide_time")]
+ [System.Text.Json.Serialization.JsonPropertyName("provide_time")]
+ public long? ProvideTimestamp { get; set; }
+
+ ///
+ /// 获取或设置创建时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("create_time")]
+ [System.Text.Json.Serialization.JsonPropertyName("create_time")]
+ public long CreateTimestamp { get; set; }
+
+ ///
+ /// 获取或设置更新时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("update_time")]
+ [System.Text.Json.Serialization.JsonPropertyName("update_time")]
+ public long UpdateTimestamp { get; set; }
+
+ ///
+ /// 获取或设置订单创建时传的信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("biz_meta")]
+ [System.Text.Json.Serialization.JsonPropertyName("biz_meta")]
+ public string? BusinessMeta { get; set; }
+
+ ///
+ /// 获取或设置下单时米大师返回的 Token。
+ ///
+ [Newtonsoft.Json.JsonProperty("token")]
+ [System.Text.Json.Serialization.JsonPropertyName("token")]
+ public string? MidasToken { get; set; }
+
+ ///
+ /// 获取或设置微信内部单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("wx_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("wx_order_id")]
+ public string WxOrderId { get; set; } = default!;
+
+ ///
+ /// 获取或设置渠道单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("channel_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("channel_order_id")]
+ public string? ChannelOrderId { get; set; }
+
+ ///
+ /// 获取或设置微信支付交易单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("wxpay_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("wxpay_order_id")]
+ public string? TransactionId { get; set; }
+ }
+ }
+
+ ///
+ /// 获取或设置订单信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("order")]
+ [System.Text.Json.Serialization.JsonPropertyName("order")]
+ public Types.Order Order { get; set; } = default!;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryPublishGoodsRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryPublishGoodsRequest.cs
new file mode 100644
index 00000000..fab7e1e5
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryPublishGoodsRequest.cs
@@ -0,0 +1,13 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_publish_goods 接口的请求。
+ ///
+ public class XPayQueryPublishGoodsRequest : XPayRequestBase, IInferable
+ {
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/query_publish_goods";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryPublishGoodsResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryPublishGoodsResponse.cs
new file mode 100644
index 00000000..9422d736
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryPublishGoodsResponse.cs
@@ -0,0 +1,49 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_publish_goods 接口的响应。
+ ///
+ public class XPayQueryPublishGoodsResponse : WechatApiResponse
+ {
+ public static class Types
+ {
+ public class PublishItem
+ {
+ ///
+ /// 获取或设置道具 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("id")]
+ [System.Text.Json.Serialization.JsonPropertyName("id")]
+ public string Id { get; set; } = default!;
+
+ ///
+ /// 获取或设置发布状态。
+ ///
+ [Newtonsoft.Json.JsonProperty("publish_status")]
+ [System.Text.Json.Serialization.JsonPropertyName("publish_status")]
+ public int PublishStatus { get; set; }
+
+ ///
+ /// 获取或设置发布失败原因。
+ ///
+ [Newtonsoft.Json.JsonProperty("errmsg")]
+ [System.Text.Json.Serialization.JsonPropertyName("errmsg")]
+ public string? FailReason { get; set; }
+ }
+ }
+
+ ///
+ /// 获取或设置道具列表。
+ ///
+ [Newtonsoft.Json.JsonProperty("publish_item")]
+ [System.Text.Json.Serialization.JsonPropertyName("publish_item")]
+ public Types.PublishItem[] PublishItemList { get; set; } = default!;
+
+ ///
+ /// 获取或设置发布状态。
+ ///
+ [Newtonsoft.Json.JsonProperty("status")]
+ [System.Text.Json.Serialization.JsonPropertyName("status")]
+ public int Status { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUploadGoodsRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUploadGoodsRequest.cs
new file mode 100644
index 00000000..4691ecac
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUploadGoodsRequest.cs
@@ -0,0 +1,13 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_upload_goods 接口的请求。
+ ///
+ public class XPayQueryUploadGoodsRequest : XPayRequestBase, IInferable
+ {
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/query_upload_goods";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUploadGoodsResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUploadGoodsResponse.cs
new file mode 100644
index 00000000..a8efb7b2
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUploadGoodsResponse.cs
@@ -0,0 +1,77 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_upload_goods 接口的响应。
+ ///
+ public class XPayQueryUploadGoodsResponse : WechatApiResponse
+ {
+ public static class Types
+ {
+ public class UploadItem
+ {
+ ///
+ /// 获取或设置道具 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("id")]
+ [System.Text.Json.Serialization.JsonPropertyName("id")]
+ public string Id { get; set; } = default!;
+
+ ///
+ /// 获取或设置道具名称。
+ ///
+ [Newtonsoft.Json.JsonProperty("name")]
+ [System.Text.Json.Serialization.JsonPropertyName("name")]
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 获取或设置道具单价(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("price")]
+ [System.Text.Json.Serialization.JsonPropertyName("price")]
+ public int Price { get; set; }
+
+ ///
+ /// 获取或设置备注。
+ ///
+ [Newtonsoft.Json.JsonProperty("remark")]
+ [System.Text.Json.Serialization.JsonPropertyName("remark")]
+ public string? Remark { get; set; }
+
+ ///
+ /// 获取或设置图片 URL。
+ ///
+ [Newtonsoft.Json.JsonProperty("item_url")]
+ [System.Text.Json.Serialization.JsonPropertyName("item_url")]
+ public string? PictureUrl { get; set; }
+
+ ///
+ /// 获取或设置上传状态。
+ ///
+ [Newtonsoft.Json.JsonProperty("upload_status")]
+ [System.Text.Json.Serialization.JsonPropertyName("upload_status")]
+ public int UploadStatus { get; set; }
+
+ ///
+ /// 获取或设置上传失败原因。
+ ///
+ [Newtonsoft.Json.JsonProperty("errmsg")]
+ [System.Text.Json.Serialization.JsonPropertyName("errmsg")]
+ public string? FailReason { get; set; }
+ }
+ }
+
+ ///
+ /// 获取或设置道具列表。
+ ///
+ [Newtonsoft.Json.JsonProperty("upload_item")]
+ [System.Text.Json.Serialization.JsonPropertyName("upload_item")]
+ public Types.UploadItem[] UploadItemList { get; set; } = default!;
+
+ ///
+ /// 获取或设置上传状态。
+ ///
+ [Newtonsoft.Json.JsonProperty("status")]
+ [System.Text.Json.Serialization.JsonPropertyName("status")]
+ public int Status { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUserBalanceRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUserBalanceRequest.cs
new file mode 100644
index 00000000..7e2d6373
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUserBalanceRequest.cs
@@ -0,0 +1,13 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_user_balance 接口的请求。
+ ///
+ public class XPayQueryUserBalanceRequest : XPayRequestBase, IInferable
+ {
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/query_user_balance";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUserBalanceResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUserBalanceResponse.cs
new file mode 100644
index 00000000..109d561e
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryUserBalanceResponse.cs
@@ -0,0 +1,59 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_user_balance 接口的响应。
+ ///
+ public class XPayQueryUserBalanceResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置代币总余额。
+ ///
+ [Newtonsoft.Json.JsonProperty("balance")]
+ [System.Text.Json.Serialization.JsonPropertyName("balance")]
+ public int Balance { get; set; }
+
+ ///
+ /// 获取或设置赠送账户的代币余额。
+ ///
+ [Newtonsoft.Json.JsonProperty("present_balance")]
+ [System.Text.Json.Serialization.JsonPropertyName("present_balance")]
+ public int PresentBalance { get; set; }
+
+ ///
+ /// 获取或设置是否满足首充活动。
+ ///
+ [Newtonsoft.Json.JsonProperty("first_save_flag")]
+ [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.NumericalBooleanConverter))]
+ [System.Text.Json.Serialization.JsonPropertyName("first_save_flag")]
+ [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.NumericalBooleanConverter))]
+ public bool IsFirstSave { get; set; }
+
+ ///
+ /// 获取或设置累计现金充值获得的代币数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("sum_save")]
+ [System.Text.Json.Serialization.JsonPropertyName("sum_save")]
+ public int SumSave { get; set; }
+
+ ///
+ /// 获取或设置累计赠送的代币数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("sum_present")]
+ [System.Text.Json.Serialization.JsonPropertyName("sum_present")]
+ public int SumPresent { get; set; }
+
+ ///
+ /// 获取或设置累计获得的代币数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("sum_balance")]
+ [System.Text.Json.Serialization.JsonPropertyName("sum_balance")]
+ public int SumBalance { get; set; }
+
+ ///
+ /// 获取或设置累计总消耗代币数量。
+ ///
+ [Newtonsoft.Json.JsonProperty("sum_cost")]
+ [System.Text.Json.Serialization.JsonPropertyName("sum_cost")]
+ public int SumCost { get; set; }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryWithdrawOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryWithdrawOrderRequest.cs
new file mode 100644
index 00000000..1189d5fe
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryWithdrawOrderRequest.cs
@@ -0,0 +1,20 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_withdraw_order 接口的请求。
+ ///
+ public class XPayQueryWithdrawOrderRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置提现单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_no")]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_no")]
+ public string WithdrawNumber { get; set; } = string.Empty;
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/query_withdraw_order";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryWithdrawOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryWithdrawOrderResponse.cs
new file mode 100644
index 00000000..72968308
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayQueryWithdrawOrderResponse.cs
@@ -0,0 +1,60 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/query_withdraw_order 接口的响应。
+ ///
+ public class XPayQueryWithdrawOrderResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置提现单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_no")]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_no")]
+ public string WithdrawNumber { get; set; } = default!;
+
+ ///
+ /// 获取或设置提现金额(单位:元)。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_amount")]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_amount")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString | System.Text.Json.Serialization.JsonNumberHandling.WriteAsString)]
+ public double Amount { get; set; }
+
+ ///
+ /// 获取或设置提现状态。
+ ///
+ [Newtonsoft.Json.JsonProperty("status")]
+ [System.Text.Json.Serialization.JsonPropertyName("status")]
+ public int Status { get; set; }
+
+ ///
+ /// 获取或设置成功时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("withdraw_success_timestamp")]
+ [System.Text.Json.Serialization.JsonPropertyName("withdraw_success_timestamp")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ public long? SuccessTimestamp { get; set; }
+
+ ///
+ /// 获取或设置失败原因。
+ ///
+ [Newtonsoft.Json.JsonProperty("fail_reason")]
+ [System.Text.Json.Serialization.JsonPropertyName("fail_reason")]
+ public string? FailReason { get; set; }
+
+ ///
+ /// 获取或设置创建时间戳。
+ ///
+ [Newtonsoft.Json.JsonProperty("create_time")]
+ [System.Text.Json.Serialization.JsonPropertyName("create_time")]
+ [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
+ public long CreateTimestamp { get; set; }
+
+ ///
+ /// 获取或设置微信内部提现单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("wx_withdraw_no")]
+ [System.Text.Json.Serialization.JsonPropertyName("wx_withdraw_no")]
+ public string WxWithdrawNumber { get; set; } = default!;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayRefundOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayRefundOrderRequest.cs
new file mode 100644
index 00000000..b2faf4e7
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayRefundOrderRequest.cs
@@ -0,0 +1,69 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/refund_order 接口的请求。
+ ///
+ public class XPayRefundOrderRequest : XPayRequestBase, IInferable
+ {
+ ///
+ /// 获取或设置订单号。与字段 二选一。
+ ///
+ [Newtonsoft.Json.JsonProperty("order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("order_id")]
+ public string? PayOrderId { get; set; }
+
+ ///
+ /// 获取或设置微信内部订单号。与字段 二选一。
+ ///
+ [Newtonsoft.Json.JsonProperty("wx_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("wx_order_id")]
+ public string? PayWxOrderId { get; set; }
+
+ ///
+ /// 获取或设置退款订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("refund_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("refund_order_id")]
+ public string RefundOrderId { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置退款金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("refund_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("refund_fee")]
+ public int RefundFee { get; set; }
+
+ ///
+ /// 获取或设置剩余金额(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("left_fee")]
+ [System.Text.Json.Serialization.JsonPropertyName("left_fee")]
+ public int LeftFee { get; set; }
+
+ ///
+ /// 获取或设置自定义信息。
+ ///
+ [Newtonsoft.Json.JsonProperty("biz_meta")]
+ [System.Text.Json.Serialization.JsonPropertyName("biz_meta")]
+ public string? BusinessMeta { get; set; }
+
+ ///
+ /// 获取或设置退款原因。
+ ///
+ [Newtonsoft.Json.JsonProperty("refund_reason")]
+ [System.Text.Json.Serialization.JsonPropertyName("refund_reason")]
+ public string? RefundReason { get; set; }
+
+ ///
+ /// 获取或设置退款来源。
+ ///
+ [Newtonsoft.Json.JsonProperty("req_from")]
+ [System.Text.Json.Serialization.JsonPropertyName("req_from")]
+ public string? RefundFrom { get; set; }
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/refund_order";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayRefundOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayRefundOrderResponse.cs
new file mode 100644
index 00000000..22a310d1
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayRefundOrderResponse.cs
@@ -0,0 +1,36 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/refund_order 接口的响应。
+ ///
+ public class XPayRefundOrderResponse : WechatApiResponse
+ {
+ ///
+ /// 获取或设置订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("pay_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("pay_order_id")]
+ public string PayOrderId { get; set; } = default!;
+
+ ///
+ /// 获取或设置微信内部订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("pay_wx_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("pay_wx_order_id")]
+ public string PayWxOrderId { get; set; } = default!;
+
+ ///
+ /// 获取或设置退款订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("refund_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("refund_order_id")]
+ public string RefundOrderId { get; set; } = default!;
+
+ ///
+ /// 获取或设置微信内部退款订单号。
+ ///
+ [Newtonsoft.Json.JsonProperty("refund_wx_order_id")]
+ [System.Text.Json.Serialization.JsonPropertyName("refund_wx_order_id")]
+ public string RefundWxOrderId { get; set; } = default!;
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartPublishGoodsRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartPublishGoodsRequest.cs
new file mode 100644
index 00000000..dfae9eaa
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartPublishGoodsRequest.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/start_publish_goods 接口的请求。
+ ///
+ public class XPayStartPublishGoodsRequest : XPayRequestBase, IInferable
+ {
+ public static class Types
+ {
+ public class PublishItem
+ {
+ ///
+ /// 获取或设置道具 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("id")]
+ [System.Text.Json.Serialization.JsonPropertyName("id")]
+ public string Id { get; set; } = string.Empty;
+ }
+ }
+
+ ///
+ /// 获取或设置道具列表。
+ ///
+ [Newtonsoft.Json.JsonProperty("publish_item")]
+ [System.Text.Json.Serialization.JsonPropertyName("publish_item")]
+ public IList PublishItemList { get; set; } = new List();
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/start_publish_goods";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartPublishGoodsResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartPublishGoodsResponse.cs
new file mode 100644
index 00000000..0dd37f20
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartPublishGoodsResponse.cs
@@ -0,0 +1,9 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/start_publish_goods 接口的响应。
+ ///
+ public class XPayStartPublishGoodsResponse : WechatApiResponse
+ {
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartUploadGoodsRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartUploadGoodsRequest.cs
new file mode 100644
index 00000000..c46644c8
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartUploadGoodsRequest.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/start_upload_goods 接口的请求。
+ ///
+ public class XPayStartUploadGoodsRequest : XPayRequestBase, IInferable
+ {
+ public static class Types
+ {
+ public class UploadItem
+ {
+ ///
+ /// 获取或设置道具 ID。
+ ///
+ [Newtonsoft.Json.JsonProperty("id")]
+ [System.Text.Json.Serialization.JsonPropertyName("id")]
+ public string Id { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置道具名称。
+ ///
+ [Newtonsoft.Json.JsonProperty("name")]
+ [System.Text.Json.Serialization.JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 获取或设置道具单价(单位:分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("price")]
+ [System.Text.Json.Serialization.JsonPropertyName("price")]
+ public int Price { get; set; }
+
+ ///
+ /// 获取或设置备注。
+ ///
+ [Newtonsoft.Json.JsonProperty("remark")]
+ [System.Text.Json.Serialization.JsonPropertyName("remark")]
+ public string? Remark { get; set; }
+
+ ///
+ /// 获取或设置图片 URL。
+ ///
+ [Newtonsoft.Json.JsonProperty("item_url")]
+ [System.Text.Json.Serialization.JsonPropertyName("item_url")]
+ public string? PictureUrl { get; set; }
+ }
+ }
+
+ ///
+ /// 获取或设置道具列表。
+ ///
+ [Newtonsoft.Json.JsonProperty("upload_item")]
+ [System.Text.Json.Serialization.JsonPropertyName("upload_item")]
+ public IList UploadItemList { get; set; } = new List();
+
+ protected internal override string GetRequestPath()
+ {
+ return "/xpay/start_upload_goods";
+ }
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartUploadGoodsResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartUploadGoodsResponse.cs
new file mode 100644
index 00000000..f5ced799
--- /dev/null
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Models/XPay/XPayStartUploadGoodsResponse.cs
@@ -0,0 +1,9 @@
+namespace SKIT.FlurlHttpClient.Wechat.Api.Models
+{
+ ///
+ /// 表示 [POST] /xpay/start_upload_goods 接口的响应。
+ ///
+ public class XPayStartUploadGoodsResponse : WechatApiResponse
+ {
+ }
+}
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Settings/Credentials.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Settings/Credentials.cs
index 2c0be034..17ab9923 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/Settings/Credentials.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Settings/Credentials.cs
@@ -34,6 +34,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Settings
///
public string? ImmeDeliveryAppSecret { get; }
+ ///
+ /// 初始化客户端时 的副本。
+ ///
+ public string? VirtualPaymentAppKey { get; }
+
///
/// 初始化客户端时 的副本。
///
@@ -64,6 +69,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Settings
PushToken = options.PushToken;
ImmeDeliveryAppKey = options.ImmeDeliveryAppKey;
ImmeDeliveryAppSecret = options.ImmeDeliveryAppSecret;
+ VirtualPaymentAppKey = options.VirtualPaymentAppKey;
MidasAppKey = options.MidasAppKey;
MidasOfferId = options.MidasOfferId;
MidasAppKeyV2 = options.MidasAppKeyV2;
diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/WechatApiClientOptions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/WechatApiClientOptions.cs
index 8ea76b11..2e0d4904 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Api/WechatApiClientOptions.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Api/WechatApiClientOptions.cs
@@ -47,6 +47,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
///
public string? ImmeDeliveryAppSecret { get; set; }
+ ///
+ /// 获取或设置虚拟支付 AppKey(用于小程序虚拟支付相关接口)。
+ ///
+ public string? VirtualPaymentAppKey { get; set; }
+
///
/// 获取或设置米大师平台 OfferId(用于小游戏虚拟支付 1.0 相关接口)。
///
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/EventSamples/XPay/XPayCoinPayNotifyEvent.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/EventSamples/XPay/XPayCoinPayNotifyEvent.json
new file mode 100644
index 00000000..9eccf852
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/EventSamples/XPay/XPayCoinPayNotifyEvent.json
@@ -0,0 +1,21 @@
+{
+ "ToUserName": "",
+ "FromUserName": "",
+ "CreateTime": 0,
+ "MsgType": "event",
+ "Event": "xpay_coin_pay_notify",
+ "OpenId": "",
+ "OutTradeNo": "",
+ "Env": 0,
+ "WeChatPayInfo": {
+ "MchOrderNo": "",
+ "TransactionId": "",
+ "PaidTime": 0
+ },
+ "CoinInfo": {
+ "Quantity": 0,
+ "OrigPrice": 0,
+ "ActualPrice": 0,
+ "Attach": ""
+ }
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/EventSamples/XPay/XPayGoodsDeliverNotifyEvent.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/EventSamples/XPay/XPayGoodsDeliverNotifyEvent.json
new file mode 100644
index 00000000..4f76883a
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/EventSamples/XPay/XPayGoodsDeliverNotifyEvent.json
@@ -0,0 +1,22 @@
+{
+ "ToUserName": "",
+ "FromUserName": "",
+ "CreateTime": 0,
+ "MsgType": "event",
+ "Event": "xpay_goods_deliver_notify",
+ "OpenId": "",
+ "OutTradeNo": "",
+ "Env": 0,
+ "WeChatPayInfo": {
+ "MchOrderNo": "",
+ "TransactionId": "",
+ "PaidTime": 0
+ },
+ "GoodsInfo": {
+ "ProductId": "",
+ "Quantity": 0,
+ "OrigPrice": 0,
+ "ActualPrice": 0,
+ "Attach": ""
+ }
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCancelCurrencyPayRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCancelCurrencyPayRequest.json
new file mode 100644
index 00000000..79ce8ec5
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCancelCurrencyPayRequest.json
@@ -0,0 +1,9 @@
+{
+ "openid": "",
+ "env": 0,
+ "user_ip": "",
+ "pay_order_id": "",
+ "order_id": "",
+ "amount": 0,
+ "device_type": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCancelCurrencyPayResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCancelCurrencyPayResponse.json
new file mode 100644
index 00000000..0952bae3
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCancelCurrencyPayResponse.json
@@ -0,0 +1,5 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "order_id": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCreateWithdrawOrderRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCreateWithdrawOrderRequest.json
new file mode 100644
index 00000000..1b9efa22
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCreateWithdrawOrderRequest.json
@@ -0,0 +1,5 @@
+{
+ "withdraw_no": "",
+ "withdraw_amount": "0",
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCreateWithdrawOrderResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCreateWithdrawOrderResponse.json
new file mode 100644
index 00000000..97c36cc8
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCreateWithdrawOrderResponse.json
@@ -0,0 +1,6 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "withdraw_no": "",
+ "wx_withdraw_no": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCurrencyPayRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCurrencyPayRequest.json
new file mode 100644
index 00000000..ba110e43
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCurrencyPayRequest.json
@@ -0,0 +1,10 @@
+{
+ "openid": "",
+ "env": 0,
+ "user_ip": "",
+ "amount": 0,
+ "order_id": "",
+ "payitem": "",
+ "remark": "",
+ "device_type": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCurrencyPayResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCurrencyPayResponse.json
new file mode 100644
index 00000000..46874af0
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayCurrencyPayResponse.json
@@ -0,0 +1,7 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "order_id": "",
+ "balance": 0,
+ "used_present_amount": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayDownloadBillRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayDownloadBillRequest.json
new file mode 100644
index 00000000..005ffe5c
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayDownloadBillRequest.json
@@ -0,0 +1,4 @@
+{
+ "begin_ds": "20230801",
+ "end_ds": "20230810"
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayDownloadBillResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayDownloadBillResponse.json
new file mode 100644
index 00000000..327559ad
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayDownloadBillResponse.json
@@ -0,0 +1,5 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "url": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayNotifyProvideGoodsRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayNotifyProvideGoodsRequest.json
new file mode 100644
index 00000000..aeca925e
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayNotifyProvideGoodsRequest.json
@@ -0,0 +1,5 @@
+{
+ "order_id": "",
+ "wx_order_id": "",
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayPresentCurrencyRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayPresentCurrencyRequest.json
new file mode 100644
index 00000000..9edfa2db
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayPresentCurrencyRequest.json
@@ -0,0 +1,7 @@
+{
+ "openid": "",
+ "env": 0,
+ "order_id": "",
+ "amount": 0,
+ "device_type": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayPresentCurrencyResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayPresentCurrencyResponse.json
new file mode 100644
index 00000000..af08a262
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayPresentCurrencyResponse.json
@@ -0,0 +1,7 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "order_id": "",
+ "balance": 0,
+ "present_balance": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryOrderRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryOrderRequest.json
new file mode 100644
index 00000000..2fa3b205
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryOrderRequest.json
@@ -0,0 +1,6 @@
+{
+ "openid": "",
+ "env": 0,
+ "order_id": "",
+ "wx_order_id": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryOrderResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryOrderResponse.json
new file mode 100644
index 00000000..fcb5cdfa
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryOrderResponse.json
@@ -0,0 +1,25 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "order": {
+ "order_id": "",
+ "create_time": 0,
+ "update_time": 0,
+ "status": 0,
+ "biz_type": 0,
+ "order_fee": 0,
+ "coupon_fee": 0,
+ "paid_fee": 0,
+ "order_type": 0,
+ "refund_fee": 0,
+ "paid_time": 0,
+ "provide_time": 0,
+ "biz_meta": "",
+ "env_type": 0,
+ "token": "",
+ "left_fee": 0,
+ "wx_order_id": "",
+ "channel_order_id": "",
+ "wxpay_order_id": ""
+ }
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryPublishGoodsRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryPublishGoodsRequest.json
new file mode 100644
index 00000000..1f9f9e45
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryPublishGoodsRequest.json
@@ -0,0 +1,3 @@
+{
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryPublishGoodsResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryPublishGoodsResponse.json
new file mode 100644
index 00000000..9eb82e90
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryPublishGoodsResponse.json
@@ -0,0 +1,12 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "publish_item": [
+ {
+ "id": "",
+ "publish_status": 0,
+ "errmsg": ""
+ }
+ ],
+ "status": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUploadGoodsRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUploadGoodsRequest.json
new file mode 100644
index 00000000..1f9f9e45
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUploadGoodsRequest.json
@@ -0,0 +1,3 @@
+{
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUploadGoodsResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUploadGoodsResponse.json
new file mode 100644
index 00000000..4ed17404
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUploadGoodsResponse.json
@@ -0,0 +1,16 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "upload_item": [
+ {
+ "id": "",
+ "name": "",
+ "price": 0,
+ "remark": "",
+ "item_url": "",
+ "upload_status": 0,
+ "errmsg": ""
+ }
+ ],
+ "status": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUserBalanceRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUserBalanceRequest.json
new file mode 100644
index 00000000..c98e4912
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUserBalanceRequest.json
@@ -0,0 +1,5 @@
+{
+ "openid": "",
+ "env": 0,
+ "user_ip": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUserBalanceResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUserBalanceResponse.json
new file mode 100644
index 00000000..1c59f79f
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryUserBalanceResponse.json
@@ -0,0 +1,11 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "balance": 0,
+ "present_balance": 0,
+ "sum_save": 0,
+ "sum_present": 0,
+ "sum_balance": 0,
+ "sum_cost": 0,
+ "first_save_flag": 1
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryWithdrawOrderRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryWithdrawOrderRequest.json
new file mode 100644
index 00000000..1c286dc0
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryWithdrawOrderRequest.json
@@ -0,0 +1,4 @@
+{
+ "withdraw_no": "",
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryWithdrawOrderResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryWithdrawOrderResponse.json
new file mode 100644
index 00000000..fe7c5ac7
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayQueryWithdrawOrderResponse.json
@@ -0,0 +1,11 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "withdraw_no": "",
+ "status": 0,
+ "withdraw_amount": "0",
+ "wx_withdraw_no": "",
+ "withdraw_success_timestamp": 0,
+ "create_time": 0,
+ "fail_reason": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayRefundOrderRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayRefundOrderRequest.json
new file mode 100644
index 00000000..09b4fdea
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayRefundOrderRequest.json
@@ -0,0 +1,12 @@
+{
+ "openid": "",
+ "order_id": "",
+ "wx_order_id": "",
+ "refund_order_id": "",
+ "left_fee": 0,
+ "refund_fee": 0,
+ "biz_meta": "",
+ "refund_reason": "",
+ "req_from": "",
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayRefundOrderResponse.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayRefundOrderResponse.json
new file mode 100644
index 00000000..f8bc4c79
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayRefundOrderResponse.json
@@ -0,0 +1,8 @@
+{
+ "errcode": 0,
+ "errmsg": "ok",
+ "refund_order_id": "",
+ "refund_wx_order_id": "",
+ "pay_order_id": "",
+ "pay_wx_order_id": ""
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayStartPublishGoodsRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayStartPublishGoodsRequest.json
new file mode 100644
index 00000000..84766fb1
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayStartPublishGoodsRequest.json
@@ -0,0 +1,8 @@
+{
+ "publish_item": [
+ {
+ "id": ""
+ }
+ ],
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayStartUploadGoodsRequest.json b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayStartUploadGoodsRequest.json
new file mode 100644
index 00000000..7bc149b1
--- /dev/null
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/ModelSamples/XPay/XPayStartUploadGoodsRequest.json
@@ -0,0 +1,12 @@
+{
+ "upload_item": [
+ {
+ "id": "",
+ "name": "",
+ "price": 0,
+ "remark": "",
+ "item_url": ""
+ }
+ ],
+ "env": 0
+}
diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSignatureTests.cs b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSignatureTests.cs
index 88691145..d2d6dedf 100644
--- a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSignatureTests.cs
+++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_RequestSignatureTests.cs
@@ -25,7 +25,38 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
Assert.Equal("a93d8d6bae9a9483c1b1d4e8670e7f6226ec94cb", request.DeliverySignature, ignoreCase: true);
}
- [Fact(DisplayName = "测试用例:米大师虚拟支付 1.0 请求签名")]
+ [Fact(DisplayName = "测试用例:虚拟支付请求签名")]
+ public async Task TestVirtualPaymentRequestSignature()
+ {
+ var mockClient = new WechatApiClient(new WechatApiClientOptions()
+ {
+ AppId = "wxtest",
+ AppSecret = "",
+ VirtualPaymentAppKey = "12345"
+ });
+ mockClient.Configure(settings =>
+ {
+ var jsonOptions = FlurlSystemTextJsonSerializer.GetDefaultSerializerOptions();
+ jsonOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
+ jsonOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict;
+ jsonOptions.WriteIndented = false;
+ settings.JsonSerializer = new FlurlSystemTextJsonSerializer(jsonOptions);
+ });
+
+ var request = new Models.XPayQueryUserBalanceRequest()
+ {
+ OpenId = "xxx",
+ Environment = 0,
+ UserIp = "127.0.0.1",
+ SessionKey = "9hAb/NEYUlkaMBEsmFgzig=="
+ };
+ var response = await mockClient.ExecuteXPayQueryUserBalanceAsync(request); // 这里不关心响应结果,只为获得预处理请求
+
+ Assert.Equal("e690cc22b6378ca9d70fe61727fabf65f0273d436813447816951989867a134c", request.Signature, ignoreCase: true);
+ Assert.Equal("fb4703e6b7da545a7f43428f02f2a9fa42e895704c6319d01a0aa859cf63253c", request.PaySign, ignoreCase: true);
+ }
+
+ [Fact(DisplayName = "测试用例:米大师 1.0 请求签名")]
public async Task TestMidasRequestSignature()
{
var mockClient = new WechatApiClient(new WechatApiClientOptions()
@@ -48,8 +79,8 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
Assert.Equal("1ad64e8dcb2ec1dc486b7fdf01f4a15159fc623dc3422470e51cf6870734726b", request.Signature, ignoreCase: true);
}
- [Fact(DisplayName = "测试用例:米大师虚拟支付 2.0 请求签名")]
- public async Task TestMidasRequestSignatureV2()
+ [Fact(DisplayName = "测试用例:米大师 2.0 请求签名")]
+ public async Task TestMidasV2RequestSignature()
{
var mockClient = new WechatApiClient(new WechatApiClientOptions()
{