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 +}