diff --git a/src/CloudGaming/Api/CloudGaming.Api/Controllers/PaymentController.cs b/src/CloudGaming/Api/CloudGaming.Api/Controllers/PaymentController.cs
new file mode 100644
index 0000000..f38b127
--- /dev/null
+++ b/src/CloudGaming/Api/CloudGaming.Api/Controllers/PaymentController.cs
@@ -0,0 +1,49 @@
+using CloudGaming.Api.Base;
+using CloudGaming.Code.Mall;
+using CloudGaming.DtoModel.Mall;
+
+using HuanMeng.DotNetCore.Base;
+
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace CloudGaming.Api.Controllers;
+
+
+///
+///
+///
+public class PaymentController : CloudGamingControllerBase
+{
+ public PaymentController(IServiceProvider _serviceProvider) : base(_serviceProvider)
+ {
+ }
+
+ ///
+ /// 创建订单
+ ///
+ ///
+ ///
+ [HttpPost]
+ [Authorize]
+ public async Task CreateOrder([FromBody] IntentOrderRequest intentOrder)
+ {
+ OrderBLL orderBLL = new OrderBLL(ServiceProvider);
+ return await orderBLL.CreateOrder(intentOrder.PaymentMethod, intentOrder.ProductId);
+ }
+
+
+ ///
+ /// 获取订单状态
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Authorize]
+ public async Task> GetOrderRewardsInfo(string orderId)
+ {
+ OrderBLL orderBLL = new OrderBLL(ServiceProvider);
+ return await orderBLL.GetOrderRewardsInfo(orderId);
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
index 4229d9e..2c599ae 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountBLL.cs
@@ -389,7 +389,7 @@ namespace CloudGaming.Code.Account
// 将用户身份证号设置成 "前四位*后四位"
if (!string.IsNullOrEmpty(userInfoDto.IdCard) && userInfoDto.IdCard.Length >= 8)
{
- userInfo.IdCard = userInfoDto.IdCard.Substring(0, 4) + "*********" + userInfoDto.IdCard.Substring(userInfoDto.IdCard.Length - 4);
+ userInfoDto.IdCard = userInfoDto.IdCard.Substring(0, 4) + "*********" + userInfoDto.IdCard.Substring(userInfoDto.IdCard.Length - 4);
}
return userInfoDto;
}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs
index 09b808c..094e0ee 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs
@@ -181,6 +181,7 @@ namespace CloudGaming.Code.Account
userInfo.IsJuveniles = user.UserRealNameStatus == 2;
userInfo.UserName = user.UserName ?? "";
userInfo.IdCard = user.IDCard ?? "";
+ userInfo.IsTest = user.IsTest ?? false;
}
if (userData != null)
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlipayPayment.cs b/src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlipayPayment.cs
new file mode 100644
index 0000000..8436707
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/Aliyun/AlipayPayment.cs
@@ -0,0 +1,60 @@
+using Alipay.EasySDK.Factory;
+using Alipay.EasySDK.Kernel.Util;
+
+using CloudGaming.Code.Contract;
+using CloudGaming.Code.Payment;
+
+using HuanMeng.DotNetCore.MultiTenant;
+
+using Microsoft.Extensions.Options;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.Aliyun;
+
+public class AlipayPayment(AppConfig appConfig, int userId) : IPayment
+{
+ public Task<(string orderId, string order)> CreateOrder(int productId, string productName, decimal price, params object[] args)
+ {
+ if (string.IsNullOrEmpty(productName))
+ {
+ throw new ArgumentNullException($"产品名称不能为空!");
+ }
+ var priceStr = price.ToString();
+ if (string.IsNullOrEmpty(priceStr))
+ {
+ throw new ArgumentNullException($"价格不能为空!");
+ }
+ var orderId = PaymentExtend.GenerateCustomString("ZFB", userId, productId, "001");
+ //.SetOptions(GetConfig());
+ //AsyncNotify
+ //https://pay.shhuanmeng.com/api/${tenant}/zfb/${orderId}
+ string sign = $"{appConfig.Identifier}{orderId}{userId}";
+ sign = MD5Encryption.ComputeMD5Hash(sign);
+ var notifyUrl = appConfig.Payment?.AlipayConfig?.NotifyUrl;
+ if (string.IsNullOrEmpty(notifyUrl))
+ {
+ notifyUrl = "";
+ }
+ var app = appConfig.Payment?.App();
+ if (app == null)
+ {
+ throw new Exception("支付宝配置错误");
+ }
+ notifyUrl = notifyUrl.Replace("${pay}", "zfb").Replace("${orderId}", orderId).Replace("${tenant}", appConfig.Identifier).Replace("${sign}", sign);
+ var response = app.AsyncNotify(notifyUrl).Optional("passback_params", "PaymentType%3Dzfb").Pay(productName, orderId, priceStr);
+ if (!ResponseChecker.Success(response))
+ {
+ throw new Exception("创建订单失败!" + response.Body);
+ }
+ //.PreCreate("Apple iPhone11 128G", "2234567234890", "5799.00");
+ var zfbOrderId = response.Body;
+ return Task.FromResult((orderId, zfbOrderId));
+ }
+
+
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
index 1deee68..25d0a97 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfig.cs
@@ -1,4 +1,5 @@
-using CloudGaming.Code.AppExtend.Config;
+
+
using System;
using System.Collections.Generic;
@@ -61,7 +62,7 @@ public class AppConfig
///
/// 项目支付数据
///
- //public PaymentModel? Payment { get; set; }
+ public PaymentModel? Payment { get; set; }
///
/// oss阿里云配置
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
index 5a79bb9..06f3cca 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/AppConfigurationExtend.cs
@@ -186,12 +186,14 @@ namespace CloudGaming.Code.AppExtend
newAppConfig.ExtConnectionString = appConfig.ExtConnectionString;
newAppConfig.PhoneConnectionString = appConfig.PhoneConnectionString;
newAppConfig.AliyunConfig = appConfig.AliyunConfig;
- newAppConfig.DefaultLanguage = appConfig.DefaultLanguage;
+ newAppConfig.DefaultLanguage = appConfig.DefaultLanguage ?? "zh";
if (appConfig.UserConfig == null)
{
appConfig.UserConfig = new UserConfig();
}
newAppConfig.UserConfig = appConfig.UserConfig;
+ newAppConfig.Payment = appConfig.Payment;
+
return newAppConfig;
}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/Config/AliyunConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/AliyunConfig.cs
similarity index 96%
rename from src/CloudGaming/Code/CloudGaming.Code/AppExtend/Config/AliyunConfig.cs
rename to src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/AliyunConfig.cs
index fb7a6a0..9e43d35 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/Config/AliyunConfig.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/AliyunConfig.cs
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace CloudGaming.Code.AppExtend.Config;
+namespace CloudGaming.Code.AppExtend.ConfigModel;
///
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/PaymentModel.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/PaymentModel.cs
new file mode 100644
index 0000000..773d6be
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/PaymentModel.cs
@@ -0,0 +1,86 @@
+using Alipay.EasySDK.Kernel;
+
+using Newtonsoft.Json;
+
+using SKIT.FlurlHttpClient.Wechat.TenpayV3;
+using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
+using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+namespace CloudGaming.Code.AppExtend.ConfigModel;
+
+///
+///
+///
+public class PaymentModel
+{
+ ///
+ /// 微信支付数据
+ ///
+ public WeChatConfig? WeChatConfig { get; set; }
+
+ ///
+ /// 支付宝支付数据
+ ///
+ public Alipay.EasySDK.Kernel.Config? AlipayConfig { get; set; }
+
+ ///
+ ///
+ ///
+ private WechatTenpayClient _wxClient;
+ ///
+ /// 微信支付客户端
+ ///
+ [JsonIgnore]
+ public WechatTenpayClient? WxClient
+ {
+ get
+ {
+ if (_wxClient == null)
+ {
+ if (WeChatConfig == null)
+ {
+ return null;
+ }
+ var manager = new InMemoryCertificateManager();
+ /* 仅列出必须配置项。也包含一些诸如超时时间、UserAgent 等的配置项 */
+ var wechatTenpayClientOptions = new WechatTenpayClientOptions()
+ {
+
+ MerchantId = WeChatConfig.MchId,
+ MerchantV3Secret = WeChatConfig.Key,
+ MerchantCertificateSerialNumber = WeChatConfig.MerchantCertificateSerialNumber,
+ MerchantCertificatePrivateKey = WeChatConfig.MerchantCertificatePrivateKey,
+ PlatformCertificateManager = manager
+ };
+ _wxClient = new WechatTenpayClient(wechatTenpayClientOptions);
+ }
+ return _wxClient;
+ }
+ }
+
+ // var conf = appConfig.Payment?.AlipayConfig ?? new Alipay.EasySDK.Kernel.Config();
+ //var context = new Alipay.EasySDK.Kernel.Context(conf, "alipay-easysdk-net-2.1.0");
+ //var app = new Alipay.EasySDK.Payment.App.Client(new Alipay.EasySDK.Kernel.Client(context));
+
+ private Alipay.EasySDK.Kernel.Context _aliPayContext;
+ [JsonIgnore]
+ public Alipay.EasySDK.Kernel.Context AliPayContext
+ {
+ get
+ {
+ if (_aliPayContext == null)
+ {
+ if (AlipayConfig != null)
+ {
+ _aliPayContext = new Alipay.EasySDK.Kernel.Context(AlipayConfig, "alipay-easysdk-net-2.1.0");
+ }
+ }
+ return _aliPayContext;
+ }
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/Config/UserConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/UserConfig.cs
similarity index 91%
rename from src/CloudGaming/Code/CloudGaming.Code/AppExtend/Config/UserConfig.cs
rename to src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/UserConfig.cs
index 7d047b6..f08a95c 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/Config/UserConfig.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/UserConfig.cs
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace CloudGaming.Code.AppExtend.Config;
+namespace CloudGaming.Code.AppExtend.ConfigModel;
///
/// 用户默认配置
diff --git a/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/WeChatConfig.cs b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/WeChatConfig.cs
new file mode 100644
index 0000000..ed53567
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/AppExtend/ConfigModel/WeChatConfig.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.AppExtend.ConfigModel;
+
+///
+///
+///
+public class WeChatConfig
+{
+ ///
+ /// appId
+ ///
+ public string AppId { get; set; }
+ ///
+ /// appkey
+ ///
+ public string AppSecret { get; set; }
+ ///
+ ///
+ ///
+ public string Key { get; set; }
+ ///
+ /// 商户id
+ ///
+ public string MchId { get; set; }
+ ///
+ /// 回调地址
+ ///
+ public string NotifyUrl { get; set; }
+
+ ///
+ ///
+ ///
+ public string MerchantCertificateSerialNumber { get; set; }
+
+ ///
+ ///
+ ///
+ public string MerchantCertificatePrivateKey { get; set; }
+}
+
+
diff --git a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj
index b3eaae3..1567d09 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj
+++ b/src/CloudGaming/Code/CloudGaming.Code/CloudGaming.Code.csproj
@@ -12,11 +12,14 @@
+
+
+
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Contract/IPayment.cs b/src/CloudGaming/Code/CloudGaming.Code/Contract/IPayment.cs
new file mode 100644
index 0000000..6696635
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/Contract/IPayment.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.Contract;
+
+///
+/// 支付接口
+///
+public interface IPayment
+{
+ ///
+ /// 创建订单
+ ///
+ /// 产品id
+ /// 产品名称
+ /// 价格
+ /// 其它参数
+ ///
+ Task<(string orderId, string order)> CreateOrder(int productId, string productName, decimal price, params object[] args);
+}
\ No newline at end of file
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Extend/OrderExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Extend/OrderExtend.cs
new file mode 100644
index 0000000..f7327b3
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/Extend/OrderExtend.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.Extend
+{
+ internal class OrderExtend
+ {
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs b/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
index 2561ae3..5b642e7 100644
--- a/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
+++ b/src/CloudGaming/Code/CloudGaming.Code/GlobalUsings.cs
@@ -19,5 +19,6 @@ global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
-global using CloudGaming.Code.AppExtend.Config;
-global using System.Diagnostics;
\ No newline at end of file
+global using System.Diagnostics;
+
+global using CloudGaming.Code.AppExtend.ConfigModel;
\ No newline at end of file
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Mall/OrderBLL.cs b/src/CloudGaming/Code/CloudGaming.Code/Mall/OrderBLL.cs
new file mode 100644
index 0000000..32125e5
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/Mall/OrderBLL.cs
@@ -0,0 +1,108 @@
+using CloudGaming.Code.DataAccess;
+using CloudGaming.Code.Payment;
+using CloudGaming.DtoModel.Account.User;
+using CloudGaming.DtoModel.Mall;
+
+using HuanMeng.DotNetCore.Redis;
+
+using Microsoft.EntityFrameworkCore.Storage;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.Mall;
+
+
+///
+/// 订单数据
+///
+public class OrderBLL : CloudGamingBase
+{
+ public OrderBLL(IServiceProvider serviceProvider) : base(serviceProvider)
+ {
+ }
+
+ ///
+ /// 创建订单
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task CreateOrder(string paymentMethod, string productId)
+ {
+ if (_UserId == 0)
+ {
+ throw new ArgumentNullException("未登录");
+ }
+ if (string.IsNullOrEmpty(productId))
+ {
+ throw new ArgumentNullException("产品不能为空");
+ }
+
+ var products = Cache.ProductCacheList;
+ var product = products.FirstOrDefault(it => it.ProductId == productId);
+ if (product == null)
+ {
+ throw new NullReferenceException("未找到所属产品");
+ }
+ var redisLock = $"lock:payment:{_UserId}:{productId}";
+ if (!RedisCache.StringSetLock(redisLock, "", 5))
+ {
+ throw new ArgumentNullException("重复创建订单");
+ }
+
+ //productEntityCache.get
+ IntentOrderDto intentOrderDto = null;
+ //创建订单
+ try
+ {
+ var ip = HttpContextAccessor.HttpContext.GetClientIpAddress();
+ var price = product.Price;
+ var payment = PaymentExtend.GetPayment(paymentMethod, this);
+ //UserInfoBLL userInfo = new UserInfoBLL(Dao, _UserId);
+ if (UserInfo.IsTest)
+ {
+ price = (decimal)0.01;
+ }
+ (var orderId, var order) = await payment.CreateOrder(product.Id, product.ProductName, price, product, ip);
+ var t = product.ToIntentOrder(paymentMethod, orderId);
+ t.UserId = _UserId;
+ await Dao.DaoUser.Context.AddAsync(t);
+ await Dao.DaoUser.Context.SaveChangesAsync();
+ intentOrderDto = new IntentOrderDto()
+ {
+ OrderId = orderId,
+ Payment = order
+ };
+ RedisCache.KeyDelete(redisLock);
+ }
+ catch (Exception ex)
+ {
+ RedisCache.KeyDelete(redisLock);
+ throw new Exception("创建订单失败");
+ }
+
+ return intentOrderDto;
+ }
+
+ ///
+ /// 获取订单状态
+ ///
+ ///
+ ///
+ public async Task> GetOrderRewardsInfo(string orderId)
+ {
+ var tips = await Dao.DaoUser.Context.T_User_OrderItems.Where(it => it.OrderId == orderId).Select(it => it.RewardTips).FirstOrDefaultAsync();
+ if (string.IsNullOrEmpty(tips))
+ {
+ return new BaseResponse(ResonseCode.Success, "", false);
+ }
+ return new BaseResponse(ResonseCode.Success, tips, true);
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/Payment/PaymentExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Payment/PaymentExtend.cs
new file mode 100644
index 0000000..b111e78
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/Payment/PaymentExtend.cs
@@ -0,0 +1,278 @@
+using Alipay.EasySDK.Factory;
+
+using CloudGaming.Code.Aliyun;
+using CloudGaming.Code.Contract;
+using CloudGaming.Code.WeChat;
+using CloudGaming.DtoModel.Mall;
+using CloudGaming.DtoModel.Payment;
+
+using SKIT.FlurlHttpClient.Wechat.TenpayV3;
+using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.Payment;
+
+///
+///
+///
+public static class PaymentExtend
+{
+ private static WechatTenpayClientOptions wechatTenpayClientOptions { get; set; } = new WechatTenpayClientOptions();
+ private static WeChatConfig weChatConfig { get; set; }
+ private static WechatTenpayClient wxClient { get; set; }
+ ///
+ /// 支付
+ ///
+ ///
+ ///
+ public static IHostApplicationBuilder AddPayment(this IHostApplicationBuilder builder)
+ {
+
+ AddAlipay(builder.Configuration);
+ AddWeChat(builder.Configuration);
+ return builder;
+ }
+
+ ///
+ /// 加载微信支付
+ ///
+ ///
+ public static void AddWeChat(IConfiguration configuration)
+ {
+ var _weChatConfig = configuration.GetSection("Payment:WeChatConfig").Get();
+ if (_weChatConfig == null)
+ {
+ Console.WriteLine("微信支付失败");
+ //throw new Exception("微信支付失败");
+ return;
+ }
+ weChatConfig = _weChatConfig;
+ var manager = new InMemoryCertificateManager();
+ /* 仅列出必须配置项。也包含一些诸如超时时间、UserAgent 等的配置项 */
+ wechatTenpayClientOptions = new WechatTenpayClientOptions()
+ {
+
+ MerchantId = weChatConfig.MchId,
+ MerchantV3Secret = weChatConfig.Key,
+ MerchantCertificateSerialNumber = weChatConfig.MerchantCertificateSerialNumber,
+ MerchantCertificatePrivateKey = weChatConfig.MerchantCertificatePrivateKey,
+ PlatformCertificateManager = manager
+ };
+ wxClient = new WechatTenpayClient(wechatTenpayClientOptions);
+ }
+
+ ///
+ ///
+ ///
+ public static Alipay.EasySDK.Kernel.Config AlipayConfig { get; set; }
+ ///
+ /// 支付
+ ///
+ ///
+ public static void AddAlipay(IConfiguration configuration)
+ {
+ var _config = configuration.GetSection("Payment:AlipayConfig").Get();
+
+ if (_config == null)
+ {
+ Console.WriteLine("接入支付失败");
+ return;
+ }
+ var config = new Alipay.EasySDK.Kernel.Config()
+ {
+ Protocol = "https",
+ GatewayHost = "openapi.alipay.com",
+ SignType = "RSA2",
+ AppId = _config.AppId,
+ // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
+ MerchantPrivateKey = _config.MerchantPrivateKey,
+ // 如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
+ AlipayPublicKey = _config.AlipayPublicKey,
+ //可设置异步通知接收服务地址(可选)
+ NotifyUrl = _config.NotifyUrl,
+ };
+ AlipayConfig = config;
+ Factory.SetOptions(config);
+ }
+
+ ///
+ ///获取支付方式
+ ///
+ ///
+ ///
+ public static IPayment GetPayment(string payment, CloudGamingBase cloudGamingBase)
+ {
+ //miaoYuBase.AppConfig.Payment
+ if (payment == "zfb")
+ {
+ return new AlipayPayment(cloudGamingBase.AppConfig, cloudGamingBase._UserId);
+ }
+
+ if (cloudGamingBase.AppConfig != null)
+ {
+ if (cloudGamingBase.AppConfig.Payment == null)
+ {
+ cloudGamingBase.AppConfig.Payment = new PaymentModel()
+ {
+ WeChatConfig = weChatConfig
+ };
+ }
+ if (cloudGamingBase.AppConfig.Payment.WeChatConfig == null)
+ {
+ cloudGamingBase.AppConfig.Payment.WeChatConfig = weChatConfig;
+ }
+ }
+
+ var _weChatConfig = cloudGamingBase.AppConfig.Payment.WeChatConfig ?? weChatConfig;
+ var _wxClient = cloudGamingBase.AppConfig.Payment.WxClient ?? wxClient;
+ if (payment == "xcx")
+ {
+ if (cloudGamingBase._UserId == 0)
+ {
+ throw new Exception("请先登录");
+ }
+ var userAccount = cloudGamingBase.Dao.DaoUser.Context.T_User_MiniProgram_Account.FirstOrDefault(it => it.UserId == cloudGamingBase._UserId);
+ if (userAccount == null)
+ {
+ throw new Exception("未找到人员数据,请先登录");
+ }
+
+ //return new WeChatMiniProgram(_wxClient, _weChatConfig, miaoYuBase.TenantInfo, miaoYuBase._UserId, userAccount.OpenId);
+ }
+ return new WeChatPayment(_wxClient, _weChatConfig, cloudGamingBase.AppConfig, cloudGamingBase._UserId);
+ }
+
+
+ ///
+ /// 获取支付包支付接口
+ ///
+ ///
+ ///
+ public static Alipay.EasySDK.Payment.App.Client App(this PaymentModel payment)
+ {
+ return new Alipay.EasySDK.Payment.App.Client(new Alipay.EasySDK.Kernel.Client(payment.AliPayContext));
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static T_User_IntentOrder ToIntentOrder(this ProductCache productCache, string payment, string orderId)
+ {
+ T_User_IntentOrder t_User_IntentOrder = new T_User_IntentOrder()
+ {
+ Price = productCache.Price,
+ CreatedAt = DateTime.Now,
+ IntentAt = DateTime.Now,
+ Method = payment,
+ Notes = "",
+ ProductId = productCache.ProductId,
+ Quantity = 1,
+ Status = (int)OrderState.已下单,
+ UpdatedAt = DateTime.Now,
+ OrderId = orderId,
+ };
+ return t_User_IntentOrder;
+ }
+
+ ///
+ /// 生成订单编号
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string GenerateCustomString(string identifier, int userId, int producId, string machineCode)
+ {
+ // 确保标识符为3位,不足补0
+ string identifierPadded = identifier.PadRight(3, '0');
+
+ // 获取当前时间戳
+ long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+
+ // 将用户ID补足6位,不足补0
+ string userIdPadded = userId.ToString().PadLeft(6, '0');
+ // 产品id补足3位
+ string producIdPadded = producId.ToString().PadLeft(3, '0');
+ // 确保机器码为3位,不足补0
+ string machineCodePadded = machineCode.PadLeft(3, '0');
+
+ // 计算随机字符串应占用的位数
+ int randomStringLength = 32 - (identifierPadded.Length + 1 + 10 + 1 + userIdPadded.Length + 1 + machineCodePadded.Length + 1 + producIdPadded.Length + 1);
+
+ // 生成指定长度的随机字符串(字母和数字混合)
+ string randomPadded = GenerateRandomString(randomStringLength);
+
+ // 使用 StringBuilder 拼接成最终字符串
+ var sb = new StringBuilder(32);
+ sb.Append(identifierPadded)
+ .Append('T').Append(timestamp)
+ .Append('U').Append(userIdPadded)
+ .Append('P').Append(producIdPadded)
+ .Append('M').Append(machineCodePadded)
+ .Append('R').Append(randomPadded);
+
+ return sb.ToString();
+ }
+
+ private static string GenerateRandomString(int length)
+ {
+ const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ var random = new Random();
+ var result = new char[length];
+ for (int i = 0; i < length; i++)
+ {
+ result[i] = chars[random.Next(chars.Length)];
+ }
+ return new string(result);
+ }
+ ///
+ /// 解析订单号
+ ///
+ ///
+ ///
+ ///
+ public static (string Identifier, long Timestamp, int UserId, int ProductId, string MachineCode, string RandomString) ParseCustomString(string customString)
+ {
+ if (customString.Length != 32)
+ {
+ throw new ArgumentException("Custom string must be exactly 32 characters long.");
+ }
+
+ // 通过索引位置缓存各个部分的起始位置
+ int tIndex = customString.IndexOf('T');
+
+ int uIndex = customString.IndexOf('U');
+ int pIndex = customString.IndexOf('P');
+ int mIndex = customString.IndexOf('M');
+ int rIndex = customString.IndexOf('R');
+
+ // 提取并去掉标识符的末尾零
+ string identifier = customString.Substring(0, tIndex).TrimEnd('0');
+
+ // 提取时间戳
+ long timestamp = long.Parse(customString.Substring(tIndex + 1, uIndex - tIndex - 1));
+
+ // 提取用户ID
+ int userId = int.Parse(customString.Substring(uIndex + 1, pIndex - uIndex - 1));
+
+ // 提取产品ID
+ int productId = int.Parse(customString.Substring(pIndex + 1, mIndex - pIndex - 1));
+
+ // 提取机器码
+ string machineCode = customString.Substring(mIndex + 1, rIndex - mIndex - 1);
+
+ // 提取随机字符串
+ string randomString = customString.Substring(rIndex + 1);
+
+ return (identifier, timestamp, userId, productId, machineCode, randomString);
+ }
+}
diff --git a/src/CloudGaming/Code/CloudGaming.Code/WeChat/WeChatPayment.cs b/src/CloudGaming/Code/CloudGaming.Code/WeChat/WeChatPayment.cs
new file mode 100644
index 0000000..1626e61
--- /dev/null
+++ b/src/CloudGaming/Code/CloudGaming.Code/WeChat/WeChatPayment.cs
@@ -0,0 +1,53 @@
+using CloudGaming.Code.Contract;
+using CloudGaming.Code.Payment;
+using Newtonsoft.Json;
+using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
+using SKIT.FlurlHttpClient.Wechat.TenpayV3;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.Code.WeChat;
+
+
+///
+/// 微信支付
+///
+public class WeChatPayment(WechatTenpayClient client, WeChatConfig weChatConfig, AppConfig appConfig, int userId) : IPayment
+{
+ public async Task<(string orderId, string order)> CreateOrder(int productId, string productName, decimal price, params object[] args)
+ {
+ //var orderId = GenerateTimestampIdWithOffset();
+ var orderId = PaymentExtend.GenerateCustomString("WX", userId, productId, "001");
+ //var client = new WechatTenpayClient(wechatTenpayClientOptions);
+ /* 以 JSAPI 统一下单接口为例 */
+ string sign = $"{appConfig.Identifier}{orderId}{userId}";
+ sign = MD5Encryption.ComputeMD5Hash(sign);
+ var notifyUrl = weChatConfig.NotifyUrl.Replace("${pay}", "wx").Replace("${orderId}", orderId).Replace("${tenant}", appConfig.Identifier).Replace("${sign}", sign);
+
+ var request = new CreatePayTransactionAppRequest()
+ {
+ OutTradeNumber = orderId,
+ AppId = weChatConfig.AppId,
+ Description = productName,
+ ExpireTime = DateTimeOffset.Now.AddMinutes(20),
+ NotifyUrl = notifyUrl,
+ Amount = new CreatePayTransactionJsapiRequest.Types.Amount()
+ {
+ Total = (int)(price * 100)
+ },
+ };
+
+ var response = await client.ExecuteCreatePayTransactionAppAsync(request);
+ if (response.IsSuccessful())
+ {
+ var paramMap = client.GenerateParametersForAppPayRequest(request.AppId, response.PrepayId);
+ //Console.WriteLine("PrepayId:" + response.PrepayId);
+ return new(orderId, JsonConvert.SerializeObject(paramMap));
+ }
+ throw new Exception("微信下单失败");
+ }
+}
diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs
index e034737..2a21433 100644
--- a/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs
+++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Account/User/UserInfoCache.cs
@@ -44,6 +44,11 @@ namespace CloudGaming.DtoModel.Account.User
///
public List GameCollects { get; set; }
+ ///
+ /// 是否测试账号
+ ///
+ public bool IsTest { get; set; }
+
}
}
diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Mall/IntentOrderDto.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Mall/IntentOrderDto.cs
new file mode 100644
index 0000000..4495be2
--- /dev/null
+++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Mall/IntentOrderDto.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.DtoModel.Mall
+{
+ ///
+ ///
+ ///
+ public class IntentOrderDto
+ {
+ ///
+ /// 订单编号
+ ///
+ public string OrderId { get; set; }
+ ///
+ /// 支付信息
+ ///
+ public object Payment { get; set; }
+ }
+}
diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Mall/IntentOrderRequest.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Mall/IntentOrderRequest.cs
new file mode 100644
index 0000000..50ee681
--- /dev/null
+++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Mall/IntentOrderRequest.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.DtoModel.Mall;
+
+///
+///
+///
+public class IntentOrderRequest
+{
+ ///
+ /// 产品编号
+ ///
+ public string ProductId { get; set; }
+ ///
+ /// 支付方式
+ ///
+ public string PaymentMethod { get; set; }
+}
\ No newline at end of file
diff --git a/src/CloudGaming/Model/CloudGaming.DtoModel/Payment/OrderState.cs b/src/CloudGaming/Model/CloudGaming.DtoModel/Payment/OrderState.cs
new file mode 100644
index 0000000..bb7852a
--- /dev/null
+++ b/src/CloudGaming/Model/CloudGaming.DtoModel/Payment/OrderState.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudGaming.DtoModel.Payment;
+
+///
+/// 订单状态
+///
+public enum OrderState
+{
+ ///
+ /// 已下单
+ ///
+ 已下单 = 0,
+ ///
+ /// 正在发货
+ ///
+ 正在发货 = 1,
+ ///
+ /// 已完成
+ ///
+ 已完成 = 2,
+ ///
+ /// 发货失败
+ ///
+ 发货失败 = 3
+}