feat(wxapi): 新增小游戏虚拟支付 2.0 相关接口

This commit is contained in:
Fu Diwei
2023-02-15 21:50:56 +08:00
parent 32bc3f6994
commit ccf19fc20e
41 changed files with 918 additions and 177 deletions

View File

@@ -0,0 +1,7 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Constants
{
public static class MidasSignMethods
{
public const string HMAC_SHA256 = "hmac_sha256";
}
}

View File

@@ -0,0 +1,47 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Events
{
/// <summary>
/// <para>表示 EVENT.minigame_coin_deliver_completed 事件的数据。</para>
/// <para>https://docs.qq.com/doc/DVVZZdHFsYkttYmxl </para>
/// </summary>
public class MiniGameCoinDeliverCompletedEvent : WechatApiEvent, WechatApiEvent.Serialization.IJsonSerializable, WechatApiEvent.Serialization.IXmlSerializable
{
public static class Types
{
public class EventData
{
/// <summary>
/// 获取或设置携带的具体内容。
/// </summary>
[Newtonsoft.Json.JsonProperty("Payload")]
[System.Text.Json.Serialization.JsonPropertyName("Payload")]
[System.Xml.Serialization.XmlElement("Payload")]
public string Payload { get; set; } = default!;
/// <summary>
/// 获取或设置支付签名。
/// </summary>
[Newtonsoft.Json.JsonProperty("PayEventSig")]
[System.Text.Json.Serialization.JsonPropertyName("PayEventSig")]
[System.Xml.Serialization.XmlElement("PayEventSig")]
public string PaySign { get; set; } = default!;
/// <summary>
/// 获取或设置是否是模拟数据。
/// </summary>
[Newtonsoft.Json.JsonProperty("IsMock")]
[System.Text.Json.Serialization.JsonPropertyName("IsMock")]
[System.Xml.Serialization.XmlElement("IsMock")]
public bool IsMock { get; set; }
}
}
/// <summary>
/// 获取或设置事件数据。
/// </summary>
[Newtonsoft.Json.JsonProperty("MiniGame")]
[System.Text.Json.Serialization.JsonPropertyName("MiniGame")]
[System.Xml.Serialization.XmlElement("MiniGame")]
public Types.EventData EventData { get; set; } = default!;
}
}

View File

@@ -9,7 +9,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
{
public static class WechatApiClientExecuteCgibinExpressExtensions
{
private static T InitRequest<T>(WechatApiClient client, ref T request)
private static T PreprocessRequest<T>(WechatApiClient client, ref T request)
where T : Models.CgibinExpressLocalBusinessRequestBase, new()
{
if (client == null) throw new ArgumentNullException(nameof(request));
@@ -22,8 +22,8 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (request.DeliverySignature == null)
{
string plainText = $"{request.ShopId}{request.ShopOrderId}{client.Credentials.ImmeDeliveryAppSecret}";
request.DeliverySignature = Utilities.SHA1Utility.Hash(plainText).ToLower();
string msgText = $"{request.ShopId}{request.ShopOrderId}{client.Credentials.ImmeDeliveryAppSecret}";
request.DeliverySignature = Utilities.SHA1Utility.Hash(msgText).ToLower();
}
return request;
@@ -43,7 +43,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "pre_add")
@@ -65,7 +65,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "add")
@@ -87,7 +87,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "readd")
@@ -109,7 +109,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "addtips")
@@ -131,7 +131,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "get")
@@ -153,7 +153,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "precancel")
@@ -175,7 +175,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "cancel")
@@ -197,7 +197,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "order", "confirm_return")
@@ -279,7 +279,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "test_update_order")
@@ -301,7 +301,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "realmock_update_order")
@@ -343,7 +343,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "express", "local", "business", "update_order")

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
@@ -12,12 +12,10 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
{
public static class WechatApiClientExecuteCgibinMidasExtensions
{
private static T InitRequest<T>(WechatApiClient client, string method, string reqLoc, ref T request)
private static T PreprocessRequest<T>(WechatApiClient client, ref T request)
where T : Models.CgibinMidasRequestBase, new()
{
if (client == null) throw new ArgumentNullException(nameof(request));
if (method == null) throw new ArgumentNullException(nameof(method));
if (reqLoc == null) throw new ArgumentNullException(nameof(reqLoc));
if (request == null) throw new ArgumentNullException(nameof(request));
if (request.AppId == null)
@@ -25,6 +23,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
request.AppId = client.Credentials.AppId;
}
if (request.MidasOfferId == null)
{
request.MidasOfferId = client.Credentials.MidasOfferId;
}
if (request.Timestamp == null)
{
request.Timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds();
@@ -43,38 +46,16 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
{ "ts", request.Timestamp.ToString()! }
}
);
string plainText = string.Join("&", paramMap.Select(e => $"{e.Key}={e.Value}"))
+ $"&org_loc={reqLoc}"
+ $"&method={method.ToUpper()}"
string msgText = string.Join("&", paramMap.Select(e => $"{e.Key}={e.Value}"))
+ $"&org_loc={request.GetRequestPath()}"
+ $"&method={request.GetRequestMethod()}"
+ $"&secret={client.Credentials.MidasAppKey}";
request.Signature = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKey ?? string.Empty, plainText).ToLower();
request.Signature = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKey ?? string.Empty, msgText).ToLower();
}
return request;
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/cancelpay 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.cancelPay.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.CgibinMidasCancelPayResponse> ExecuteCgibinMidasCancelPayAsync(this WechatApiClient client, Models.CgibinMidasCancelPayRequest request, CancellationToken cancellationToken = default)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/cancelpay", ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "cancelpay")
.SetQueryParam("access_token", request.AccessToken);
return await client.SendRequestWithJsonAsync<Models.CgibinMidasCancelPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/getbalance 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.getBalance.html </para>
@@ -88,7 +69,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/getbalance", ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "getbalance")
@@ -110,7 +91,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/pay", ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "pay")
@@ -119,6 +100,28 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
return await client.SendRequestWithJsonAsync<Models.CgibinMidasPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/cancelpay 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.cancelPay.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.CgibinMidasCancelPayResponse> ExecuteCgibinMidasCancelPayAsync(this WechatApiClient client, Models.CgibinMidasCancelPayRequest 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, "cgi-bin", "midas", "cancelpay")
.SetQueryParam("access_token", request.AccessToken);
return await client.SendRequestWithJsonAsync<Models.CgibinMidasCancelPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/present 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.present.html </para>
@@ -132,7 +135,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/present", ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "present")
@@ -142,28 +145,6 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
}
#region Sandbox
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/sandbox/cancelpay 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.cancelPay.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.CgibinMidasSandboxCancelPayResponse> ExecuteCgibinMidasSandboxCancelPayAsync(this WechatApiClient client, Models.CgibinMidasSandboxCancelPayRequest request, CancellationToken cancellationToken = default)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/sandbox/cancelpay", ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "sandbox", "cancelpay")
.SetQueryParam("access_token", request.AccessToken);
return await client.SendRequestWithJsonAsync<Models.CgibinMidasSandboxCancelPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/sandbox/getbalance 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.getBalance.html </para>
@@ -177,7 +158,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/sandbox/getbalance", ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "sandbox", "getbalance")
@@ -199,7 +180,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/sandbox/pay", ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "sandbox", "pay")
@@ -208,6 +189,28 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
return await client.SendRequestWithJsonAsync<Models.CgibinMidasSandboxPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/sandbox/cancelpay 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.cancelPay.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.CgibinMidasSandboxCancelPayResponse> ExecuteCgibinMidasSandboxCancelPayAsync(this WechatApiClient client, Models.CgibinMidasSandboxCancelPayRequest 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, "cgi-bin", "midas", "sandbox", "cancelpay")
.SetQueryParam("access_token", request.AccessToken);
return await client.SendRequestWithJsonAsync<Models.CgibinMidasSandboxCancelPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /cgi-bin/midas/sandbox/present 接口。</para>
/// <para>REF: https://developers.weixin.qq.com/minigame/dev/api-backend/midas-payment/midas.present.html </para>
@@ -221,7 +224,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
InitRequest(client, HttpMethod.Post.Method, "/cgi-bin/midas/sandbox/present", ref request);
PreprocessRequest(client, ref request);
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "cgi-bin", "midas", "sandbox", "present")

View File

@@ -0,0 +1,171 @@
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
{
public static class WechatApiClientExecuteWxaGameExtensions
{
private static T PreprocessRequest<T>(WechatApiClient client, ref T request)
where T : Models.WxaGameRequestBase, new()
{
if (client == null) throw new ArgumentNullException(nameof(request));
if (request == null) throw new ArgumentNullException(nameof(request));
string? tmpRawData = null; // 用于缓存待签名数据中的请求正文部分,避免序列化多次浪费性能
if (request.OfferId == null)
{
request.OfferId = client.Credentials.MidasOfferIdV2;
}
if (request.Timestamp == null)
{
request.Timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds();
}
if (request.SignMethod == null)
{
request.SignMethod = Constants.MidasSignMethods.HMAC_SHA256;
}
if (request.Signature == null)
{
tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request);
switch (request.SignMethod)
{
case Constants.MidasSignMethods.HMAC_SHA256:
{
string msgText = tmpRawData;
request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey ?? string.Empty, msgText).ToLower();
}
break;
default:
{
request.Signature = string.Empty;
}
break;
}
}
if (request.PaySign == null)
{
tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request);
string msgText = $"{request.GetRequestPath()}&{tmpRawData}";
request.PaySign = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKeyV2 ?? string.Empty, msgText).ToLower();
}
return request;
}
/// <summary>
/// <para>异步调用 [POST] /wxa/game/getbalance 接口。</para>
/// <para>REF: https://docs.qq.com/doc/DVUN0QWJja0J5c2x4 </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.WxaGameGetBalanceResponse> ExecuteWxaGameGetBalanceAsync(this WechatApiClient client, Models.WxaGameGetBalanceRequest 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, "wxa", "game", "getbalance")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("sig_method", request.SignMethod)
.SetQueryParam("signature", request.Signature)
.SetQueryParam("pay_sig", request.PaySign);
return await client.SendRequestWithJsonAsync<Models.WxaGameGetBalanceResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /wxa/game/pay 接口。</para>
/// <para>REF: https://docs.qq.com/doc/DVUN0QWJja0J5c2x4 </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.WxaGamePayResponse> ExecuteWxaGamePayAsync(this WechatApiClient client, Models.WxaGamePayRequest 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, "wxa", "game", "pay")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("sig_method", request.SignMethod)
.SetQueryParam("signature", request.Signature)
.SetQueryParam("pay_sig", request.PaySign);
return await client.SendRequestWithJsonAsync<Models.WxaGamePayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /wxa/game/cancelpay 接口。</para>
/// <para>REF: https://docs.qq.com/doc/DVUN0QWJja0J5c2x4 </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.WxaGameCancelPayResponse> ExecuteWxaGameCancelPayAsync(this WechatApiClient client, Models.WxaGameCancelPayRequest 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, "wxa", "game", "cancelpay")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("sig_method", request.SignMethod)
.SetQueryParam("signature", request.Signature)
.SetQueryParam("pay_sig", request.PaySign);
return await client.SendRequestWithJsonAsync<Models.WxaGameCancelPayResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /wxa/game/present 接口。</para>
/// <para>REF: https://docs.qq.com/doc/DVUN0QWJja0J5c2x4 </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.WxaGamePresentResponse> ExecuteWxaGamePresentAsync(this WechatApiClient client, Models.WxaGamePresentRequest 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, "wxa", "game", "present")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("sig_method", request.SignMethod)
.SetQueryParam("signature", request.Signature)
.SetQueryParam("pay_sig", request.PaySign);
return await client.SendRequestWithJsonAsync<Models.WxaGamePresentResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
public abstract class CgibinMidasRequestBase : WechatApiRequest
{
@@ -9,6 +9,21 @@
[System.Text.Json.Serialization.JsonPropertyName("appid")]
public string? AppId { get; set; }
/// <summary>
/// 获取或设置米大师分配的 OfferId。如果不指定将使用构造 <see cref="WechatApiClient"/> 时的 <see cref="WechatApiClientOptions.MidasOfferId"/> 参数。
/// </summary>
[Newtonsoft.Json.JsonProperty("offer_id")]
[System.Text.Json.Serialization.JsonPropertyName("offer_id")]
public string? MidasOfferId { get; set; }
/// <summary>
/// 获取或设置米大师分区 ID。
/// <para>默认值1</para>
/// </summary>
[Newtonsoft.Json.JsonProperty("zone_id")]
[System.Text.Json.Serialization.JsonPropertyName("zone_id")]
public string MidasZoneId { get; set; } = "1";
/// <summary>
/// 获取或设置用户唯一标识。
/// </summary>
@@ -16,27 +31,6 @@
[System.Text.Json.Serialization.JsonPropertyName("openid")]
public string OpenId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置米大师分配的 OfferId。
/// </summary>
[Newtonsoft.Json.JsonProperty("offer_id")]
[System.Text.Json.Serialization.JsonPropertyName("offer_id")]
public string MidasOfferId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置米大师分区。
/// </summary>
[Newtonsoft.Json.JsonProperty("zone_id")]
[System.Text.Json.Serialization.JsonPropertyName("zone_id")]
public string MidasZoneId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置米大师环境。
/// </summary>
[Newtonsoft.Json.JsonProperty("env")]
[System.Text.Json.Serialization.JsonPropertyName("env")]
public string MidasEnv { get; set; } = string.Empty;
/// <summary>
/// 获取或设置平台标识。
/// <para>默认值android</para>
@@ -65,5 +59,9 @@
[Newtonsoft.Json.JsonProperty("sig")]
[System.Text.Json.Serialization.JsonPropertyName("sig")]
public string? Signature { get; set; }
protected internal abstract string GetRequestMethod();
protected internal abstract string GetRequestPath();
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/cancelpay 接口的请求。</para>
@@ -18,5 +18,15 @@
[Newtonsoft.Json.JsonProperty("pay_item")]
[System.Text.Json.Serialization.JsonPropertyName("pay_item")]
public string? PayItem { get; set; }
protected internal override string GetRequestMethod()
{
return "POST";
}
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/cancelpay";
}
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/cancelpay 接口的响应。</para>
@@ -6,7 +6,7 @@
public class CgibinMidasCancelPayResponse : WechatApiResponse
{
/// <summary>
/// 获取或设置扣除游戏币的订单号。
/// 获取或设置订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]

View File

@@ -1,9 +1,18 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/getbalance 接口的请求。</para>
/// </summary>
public class CgibinMidasGetBalanceRequest : CgibinMidasRequestBase, IInferable<CgibinMidasGetBalanceRequest, CgibinMidasGetBalanceResponse>
{
protected internal override string GetRequestMethod()
{
return "POST";
}
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/getbalance";
}
}
}

View File

@@ -1,17 +1,10 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/pay 接口的请求。</para>
/// </summary>
public class CgibinMidasPayRequest : CgibinMidasRequestBase, IInferable<CgibinMidasPayRequest, CgibinMidasPayResponse>
{
/// <summary>
/// 获取或设置扣除游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("amt")]
[System.Text.Json.Serialization.JsonPropertyName("amt")]
public int Amount { get; set; }
/// <summary>
/// 获取或设置订单号。
/// </summary>
@@ -19,6 +12,13 @@
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = string.Empty;
/// <summary>
/// 获取或设置扣除游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("amt")]
[System.Text.Json.Serialization.JsonPropertyName("amt")]
public int Amount { get; set; }
/// <summary>
/// 获取或设置道具名称。
/// </summary>
@@ -32,5 +32,15 @@
[Newtonsoft.Json.JsonProperty("app_remark")]
[System.Text.Json.Serialization.JsonPropertyName("app_remark")]
public string? Remark { get; set; }
protected internal override string GetRequestMethod()
{
return "POST";
}
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/pay";
}
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/present 接口的请求。</para>
@@ -18,5 +18,15 @@
[Newtonsoft.Json.JsonProperty("present_counts")]
[System.Text.Json.Serialization.JsonPropertyName("present_counts")]
public int PresentAmount { get; set; }
protected internal override string GetRequestMethod()
{
return "POST";
}
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/present";
}
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/present 接口的响应。</para>
@@ -6,7 +6,7 @@
public class CgibinMidasPresentResponse : WechatApiResponse
{
/// <summary>
/// 获取或设置赠送游戏币的订单号。
/// 获取或设置订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]

View File

@@ -1,9 +1,13 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/sandbox/cancelpay 接口的请求。</para>
/// </summary>
public class CgibinMidasSandboxCancelPayRequest : CgibinMidasCancelPayRequest, IInferable<CgibinMidasSandboxCancelPayRequest, CgibinMidasSandboxCancelPayResponse>
{
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/sandbox/cancelpay";
}
}
}

View File

@@ -1,9 +1,13 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/sandbox/getbalance 接口的请求。</para>
/// </summary>
public class CgibinMidasSandboxGetBalanceRequest : CgibinMidasGetBalanceRequest, IInferable<CgibinMidasSandboxGetBalanceRequest, CgibinMidasSandboxGetBalanceResponse>
{
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/sandbox/getbalance";
}
}
}

View File

@@ -1,9 +1,13 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/sandbox/pay 接口的请求。</para>
/// </summary>
public class CgibinMidasSandboxPayRequest : CgibinMidasPayRequest, IInferable<CgibinMidasSandboxPayRequest, CgibinMidasSandboxPayResponse>
{
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/sandbox/pay";
}
}
}

View File

@@ -1,9 +1,13 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/sandbox/present 接口的请求。</para>
/// </summary>
public class CgibinMidasSandboxPresentRequest : CgibinMidasPresentRequest, IInferable<CgibinMidasSandboxPresentRequest, CgibinMidasSandboxPresentResponse>
{
protected internal override string GetRequestPath()
{
return "/cgi-bin/midas/sandbox/present";
}
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /cgi-bin/midas/sandbox/present 接口的响应。</para>

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/business/getpayfororder 接口的请求。</para>
@@ -12,6 +12,13 @@
[System.Text.Json.Serialization.JsonPropertyName("appid")]
public string? AppId { get; set; }
/// <summary>
/// 获取或设置米大师环境。
/// </summary>
[Newtonsoft.Json.JsonProperty("env")]
[System.Text.Json.Serialization.JsonPropertyName("env")]
public int MidasEnvironment { get; set; }
/// <summary>
/// 获取或设置微信索要单号。
/// </summary>

View File

@@ -0,0 +1,78 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
public abstract class WxaGameRequestBase : WechatApiRequest
{
/// <summary>
/// 获取或设置虚拟支付应用 ID。如果不指定将使用构造 <see cref="WechatApiClient"/> 时的 <see cref="WechatApiClientOptions.MidasOfferIdV2"/> 参数。
/// </summary>
[Newtonsoft.Json.JsonProperty("offer_id")]
[System.Text.Json.Serialization.JsonPropertyName("offer_id")]
public string? OfferId { get; set; }
/// <summary>
/// 获取或设置虚拟支付分区 ID。
/// </summary>
[Newtonsoft.Json.JsonProperty("zone_id")]
[System.Text.Json.Serialization.JsonPropertyName("zone_id")]
public string ZoneId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置用户唯一标识。
/// </summary>
[Newtonsoft.Json.JsonProperty("openid")]
[System.Text.Json.Serialization.JsonPropertyName("openid")]
public string OpenId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置虚拟支付环境。
/// </summary>
[Newtonsoft.Json.JsonProperty("env")]
[System.Text.Json.Serialization.JsonPropertyName("env")]
public int Environment { get; set; }
/// <summary>
/// 获取或设置用户 IP。
/// </summary>
[Newtonsoft.Json.JsonProperty("user_ip")]
[System.Text.Json.Serialization.JsonPropertyName("user_ip")]
public string? UserIp { get; set; }
/// <summary>
/// 获取或设置请求时间戳。如果不指定将由系统自动生成。
/// </summary>
[Newtonsoft.Json.JsonProperty("ts")]
[System.Text.Json.Serialization.JsonPropertyName("ts")]
public long? Timestamp { get; set; }
/// <summary>
/// 获取或设置用于用户登录态签名的会话密钥。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string SessionKey { get; set; } = string.Empty;
/// <summary>
/// 获取或设置用于用户登录态签名的方式。
/// <para>默认值:<see cref="Constants.MidasSignMethods.HMAC_SHA256"/></para>
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string SignMethod { get; set; } = Constants.MidasSignMethods.HMAC_SHA256;
/// <summary>
/// 获取或设置用户登录态签名。如果不指定将由系统自动生成。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string? Signature { get; set; }
/// <summary>
/// 获取或设置支付签名。如果不指定将由系统自动生成。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string? PaySign { get; set; }
protected internal abstract string GetRequestPath();
}
}

View File

@@ -0,0 +1,34 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/cancelpay 接口的请求。</para>
/// </summary>
public class WxaGameCancelPayRequest : WxaGameRequestBase, IInferable<WxaGameCancelPayRequest, WxaGameCancelPayResponse>
{
/// <summary>
/// 获取或设置扣除游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("pay_bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("pay_bill_no")]
public string PayBillNumber { get; set; } = string.Empty;
/// <summary>
/// 获取或设置退回游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = string.Empty;
/// <summary>
/// 获取或设置本次退回的退游戏币的数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("amount")]
[System.Text.Json.Serialization.JsonPropertyName("amount")]
public int Amount { get; set; }
protected internal override string GetRequestPath()
{
return "/wxa/game/cancelpay";
}
}
}

View File

@@ -0,0 +1,15 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/cancelpay 接口的响应。</para>
/// </summary>
public class WxaGameCancelPayResponse : WechatApiResponse
{
/// <summary>
/// 获取或设置退回游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = default!;
}
}

View File

@@ -0,0 +1,13 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/getbalance 接口的请求。</para>
/// </summary>
public class WxaGameGetBalanceRequest : WxaGameRequestBase, IInferable<WxaGameGetBalanceRequest, WxaGameGetBalanceResponse>
{
protected internal override string GetRequestPath()
{
return "/wxa/game/getbalance";
}
}
}

View File

@@ -0,0 +1,57 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/getbalance 接口的响应。</para>
/// </summary>
public class WxaGameGetBalanceResponse : WechatApiResponse
{
/// <summary>
/// 获取或设置游戏币总余额。
/// </summary>
[Newtonsoft.Json.JsonProperty("balance")]
[System.Text.Json.Serialization.JsonPropertyName("balance")]
public int Balance { get; set; }
/// <summary>
/// 获取或设置赠送账户的游戏币余额。
/// </summary>
[Newtonsoft.Json.JsonProperty("present_balance")]
[System.Text.Json.Serialization.JsonPropertyName("present_balance")]
public int PresentBalance { get; set; }
/// <summary>
/// 获取或设置是否满足首充活动。
/// </summary>
[Newtonsoft.Json.JsonProperty("first_save")]
[System.Text.Json.Serialization.JsonPropertyName("first_save")]
public bool IsFirstSave { get; set; }
/// <summary>
/// 获取或设置累计现金充值获得的游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("sum_save")]
[System.Text.Json.Serialization.JsonPropertyName("sum_save")]
public int SumSave { get; set; }
/// <summary>
/// 获取或设置累计赠送的游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("sum_present")]
[System.Text.Json.Serialization.JsonPropertyName("sum_present")]
public int SumPresent { get; set; }
/// <summary>
/// 获取或设置累计获得的游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("sum_balance")]
[System.Text.Json.Serialization.JsonPropertyName("sum_balance")]
public int SumBalance { get; set; }
/// <summary>
/// 获取或设置累计总消耗游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("sum_cost")]
[System.Text.Json.Serialization.JsonPropertyName("sum_cost")]
public int SumCost { get; set; }
}
}

View File

@@ -0,0 +1,41 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/pay 接口的请求。</para>
/// </summary>
public class WxaGamePayRequest : WxaGameRequestBase, IInferable<WxaGamePayRequest, WxaGamePayResponse>
{
/// <summary>
/// 获取或设置扣除游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = string.Empty;
/// <summary>
/// 获取或设置扣除游戏币数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("amount")]
[System.Text.Json.Serialization.JsonPropertyName("amount")]
public int Amount { get; set; }
/// <summary>
/// 获取或设置道具名称。
/// </summary>
[Newtonsoft.Json.JsonProperty("payitem")]
[System.Text.Json.Serialization.JsonPropertyName("payitem")]
public string? PayItem { get; set; }
/// <summary>
/// 获取或设置备注。
/// </summary>
[Newtonsoft.Json.JsonProperty("remark")]
[System.Text.Json.Serialization.JsonPropertyName("remark")]
public string? Remark { get; set; }
protected internal override string GetRequestPath()
{
return "/wxa/game/pay";
}
}
}

View File

@@ -0,0 +1,29 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/pay 接口的响应。</para>
/// </summary>
public class WxaGamePayResponse : WechatApiResponse
{
/// <summary>
/// 获取或设置扣除游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = default!;
/// <summary>
/// 获取或设置预扣后的余额。
/// </summary>
[Newtonsoft.Json.JsonProperty("balance")]
[System.Text.Json.Serialization.JsonPropertyName("balance")]
public int Balance { get; set; }
/// <summary>
/// 获取或设置本次扣的赠送币的数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("used_present_amount")]
[System.Text.Json.Serialization.JsonPropertyName("used_present_amount")]
public int UsedPresentAmount { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/present 接口的请求。</para>
/// </summary>
public class WxaGamePresentRequest : WxaGameRequestBase, IInferable<WxaGamePresentRequest, WxaGamePresentResponse>
{
/// <summary>
/// 获取或设置赠送游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = string.Empty;
/// <summary>
/// 获取或设置赠送游戏币的个数。
/// </summary>
[Newtonsoft.Json.JsonProperty("amount")]
[System.Text.Json.Serialization.JsonPropertyName("amount")]
public int Amount { get; set; }
protected internal override string GetRequestPath()
{
return "/wxa/game/present";
}
}
}

View File

@@ -0,0 +1,22 @@
namespace SKIT.FlurlHttpClient.Wechat.Api.Models
{
/// <summary>
/// <para>表示 [POST] /wxa/game/present 接口的响应。</para>
/// </summary>
public class WxaGamePresentResponse : WechatApiResponse
{
/// <summary>
/// 获取或设置赠送游戏币的订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("bill_no")]
[System.Text.Json.Serialization.JsonPropertyName("bill_no")]
public string BillNumber { get; set; } = default!;
/// <summary>
/// 获取或设置赠送后的余额。
/// </summary>
[Newtonsoft.Json.JsonProperty("balance")]
[System.Text.Json.Serialization.JsonPropertyName("balance")]
public int Balance { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace SKIT.FlurlHttpClient.Wechat.Api.Settings
{
@@ -39,6 +39,21 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Settings
/// </summary>
public string? MidasAppKey { get; }
/// <summary>
/// 初始化客户端时 <see cref="WechatApiClientOptions.MidasOfferId"/> 的副本。
/// </summary>
public string? MidasOfferId { get; }
/// <summary>
/// 初始化客户端时 <see cref="WechatApiClientOptions.MidasAppKeyV2"/> 的副本。
/// </summary>
public string? MidasAppKeyV2 { get; }
/// <summary>
/// 初始化客户端时 <see cref="WechatApiClientOptions.MidasOfferIdV2"/> 的副本。
/// </summary>
public string? MidasOfferIdV2 { get; }
internal Credentials(WechatApiClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -50,6 +65,9 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Settings
ImmeDeliveryAppKey = options.ImmeDeliveryAppKey;
ImmeDeliveryAppSecret = options.ImmeDeliveryAppSecret;
MidasAppKey = options.MidasAppKey;
MidasOfferId = options.MidasOfferId;
MidasAppKeyV2 = options.MidasAppKeyV2;
MidasOfferIdV2 = options.MidasOfferIdV2;
}
}
}

View File

@@ -1,4 +1,4 @@
namespace SKIT.FlurlHttpClient.Wechat.Api
namespace SKIT.FlurlHttpClient.Wechat.Api
{
/// <summary>
/// 一个用于构造 <see cref="WechatApiClient"/> 时使用的配置项。
@@ -38,18 +38,33 @@
public string? PushToken { get; set; }
/// <summary>
/// 获取或设置即时配送公司帐号 AppKey。
/// 获取或设置即时配送公司帐号 AppKey(用于小程序即使配送相关接口)
/// </summary>
public string? ImmeDeliveryAppKey { get; set; }
/// <summary>
/// 获取或设置即时配送公司帐号 AppSecret。
/// 获取或设置即时配送公司帐号 AppSecret(用于小程序即使配送相关接口)
/// </summary>
public string? ImmeDeliveryAppSecret { get; set; }
/// <summary>
/// 获取或设置米大师平台 AppKey
/// 获取或设置米大师平台 OfferId用于小游戏虚拟支付 1.0 相关接口)
/// </summary>
public string? MidasOfferId { get; set; }
/// <summary>
/// 获取或设置米大师平台 AppKey用于小游戏虚拟支付 1.0 相关接口)。
/// </summary>
public string? MidasAppKey { get; set; }
/// <summary>
/// 获取或设置米大师平台 OfferId用于小游戏虚拟支付 2.0 相关接口)。
/// </summary>
public string? MidasOfferIdV2 { get; set; }
/// <summary>
/// 获取或设置米大师平台 AppKey用于小游戏虚拟支付 2.0 相关接口)。
/// </summary>
public string? MidasAppKeyV2 { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
<xml>
<CreateTime>1583202606</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[minigame_coin_deliver_completed]]></Event>
<MiniGame>
<Payload>{"OpenId":"to_user_openid","OutTradeNo":"xxxxxxx","WeChatPayInfo":{"MchOrderNo":"xxxxxxx","TransactionId":"xxxxxxx"},"Env":0,"CoinInfo":{"ZoneId":"1","TotalPrice":100,"BuyQuantity":1,"OrigPrice":100}}</Payload>
<PayEventSig>f749f67b751fa80f27ddc0b7c8d2821aeda162ea22b323cd64a2c8056c2736f0</PayEventSig>
<IsMock>true</IsMock>
</MiniGame>
</xml>

View File

@@ -0,0 +1,10 @@
{
"offer_id": "12345678",
"openid": "oUrsfxxxxxxxxxx",
"ts": 1668512806,
"zone_id": "1",
"env": 0,
"bill_no": "test_cancel_pay_1668512806",
"pay_bill_no": "test_pay_1668512428",
"amount": 1
}

View File

@@ -0,0 +1,5 @@
{
"errcode": 0,
"errmsg": "ok",
"bill_no": "test_cancel_pay_1668512806"
}

View File

@@ -0,0 +1,7 @@
{
"offer_id": "12345678",
"openid": "oUrsfxxxxxxxxxx",
"ts": 1668512543,
"zone_id": "1",
"env": 0
}

View File

@@ -0,0 +1,11 @@
{
"errcode": 0,
"errmsg": "ok",
"balance": 11,
"present_balance": 1,
"sum_save": 10,
"sum_present": 1,
"sum_balance": 11,
"sum_cost": 0,
"first_save": false
}

View File

@@ -0,0 +1,11 @@
{
"offer_id": "12345678",
"openid": "oUrsfxxxxxxxxxx",
"ts": 1668512428,
"zone_id": "1",
"env": 0,
"bill_no": "test_pay_1668512428",
"amount": 1,
"payitem": "钻石",
"remark": "测试"
}

View File

@@ -0,0 +1,7 @@
{
"errcode": 0,
"errmsg": "ok",
"bill_no": "test_pay_1668512428",
"balance": 10,
"used_present_amount": 1
}

View File

@@ -0,0 +1,9 @@
{
"offer_id": "12345678",
"openid": "oUrsfxxxxxxxxxx",
"ts": 1668512716,
"zone_id": "1",
"env": 0,
"bill_no": "test_present_1668512716",
"amount": 1
}

View File

@@ -0,0 +1,6 @@
{
"errcode": 0,
"errmsg": "ok",
"bill_no": "test_present_1668512716",
"balance": 11
}

View File

@@ -1,27 +0,0 @@
using System.Threading.Tasks;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
{
public class TestCase_DeliveryRequestSignatureTests
{
[Fact(DisplayName = "测试用例:即时配送请求签名")]
public async Task TestDeliveryRequestSignature()
{
var client = new WechatApiClient(new WechatApiClientOptions()
{
AppId = "",
AppSecret = "",
ImmeDeliveryAppKey = "test_shop_id",
ImmeDeliveryAppSecret = "test_app_secrect"
});
var request = new Models.CgibinExpressLocalBusinessTestUpdateOrderRequest()
{
ShopOrderId = "test_shop_order_id"
};
var response = await client.ExecuteCgibinExpressLocalBusinessTestUpdateOrderAsync(request);
Assert.Equal("a93d8d6bae9a9483c1b1d4e8670e7f6226ec94cb", request.DeliverySignature, ignoreCase: true);
}
}
}

View File

@@ -1,30 +0,0 @@
using System.Threading.Tasks;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
{
public class TestCase_MidasRequestSignatureTests
{
[Fact(DisplayName = "测试用例:米大师请求签名")]
public async Task TestMidasRequestSignature()
{
var client = new WechatApiClient(new WechatApiClientOptions()
{
AppId = "wx1234567",
AppSecret = "",
MidasAppKey = "zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u"
});
var request = new Models.CgibinMidasGetBalanceRequest()
{
OpenId = "odkx20ENSNa2w5y3g_qOkOvBNM1g",
MidasOfferId = "12345678",
MidasZoneId = "1",
Platform = "android",
Timestamp = 1507530737
};
var response = await client.ExecuteCgibinMidasGetBalanceAsync(request);
Assert.Equal("1ad64e8dcb2ec1dc486b7fdf01f4a15159fc623dc3422470e51cf6870734726b", request.Signature, ignoreCase: true);
}
}
}

View File

@@ -0,0 +1,85 @@
using System.Threading.Tasks;
using Xunit;
namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests
{
public class TestCase_RequestSignatureTests
{
[Fact(DisplayName = "测试用例:即时配送请求签名")]
public async Task TestImmeDeliveryRequestSignature()
{
var mockClient = new WechatApiClient(new WechatApiClientOptions()
{
AppId = "",
AppSecret = "",
ImmeDeliveryAppKey = "test_shop_id",
ImmeDeliveryAppSecret = "test_app_secrect"
});
var request = new Models.CgibinExpressLocalBusinessTestUpdateOrderRequest()
{
ShopOrderId = "test_shop_order_id"
};
var response = await mockClient.ExecuteCgibinExpressLocalBusinessTestUpdateOrderAsync(request); // 这里不关心响应结果,只为获得预处理请求
Assert.Equal("a93d8d6bae9a9483c1b1d4e8670e7f6226ec94cb", request.DeliverySignature, ignoreCase: true);
}
[Fact(DisplayName = "测试用例:米大师虚拟支付 1.0 请求签名")]
public async Task TestMidasRequestSignature()
{
var mockClient = new WechatApiClient(new WechatApiClientOptions()
{
AppId = "wx1234567",
AppSecret = "",
MidasOfferId = "12345678",
MidasAppKey = "zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u"
});
var request = new Models.CgibinMidasGetBalanceRequest()
{
OpenId = "odkx20ENSNa2w5y3g_qOkOvBNM1g",
MidasZoneId = "1",
Platform = "android",
Timestamp = 1507530737
};
var response = await mockClient.ExecuteCgibinMidasGetBalanceAsync(request); // 这里不关心响应结果,只为获得预处理请求
Assert.Equal("1ad64e8dcb2ec1dc486b7fdf01f4a15159fc623dc3422470e51cf6870734726b", request.Signature, ignoreCase: true);
}
[Fact(DisplayName = "测试用例:米大师虚拟支付 2.0 请求签名")]
public async Task TestMidasRequestSignatureV2()
{
var mockClient = new WechatApiClient(new WechatApiClientOptions()
{
AppId = "wx1234567",
AppSecret = "",
MidasOfferIdV2 = "12345678",
MidasAppKeyV2 = "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.WxaGameGetBalanceRequest()
{
OpenId = "oUrsfxxxxxxxxxx",
OfferId = "12345678",
ZoneId = "1",
Environment = 0,
Timestamp = 1668136271,
SessionKey = "9hAb/NEYUlkaMBEsmFgzig=="
};
var response = await mockClient.ExecuteWxaGameGetBalanceAsync(request); // 这里不关心响应结果,只为获得预处理请求
Assert.Equal("7ec6d737f118b0c898de39ef1b6b199c48290e699495364fe9d069597a7da125", request.Signature, ignoreCase: true);
Assert.Equal("5fc5460b23b2589efb8adbc2cf08d7a9cd8892b33e084249a26455b7880741a8", request.PaySign, ignoreCase: true);
}
}
}