From 8dc9ee123cb79ffc143df9ad11b7e3397bcb2684 Mon Sep 17 00:00:00 2001 From: zpc Date: Tue, 27 Aug 2024 22:11:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=9B=E5=BB=BA=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=9B=9E=E8=B0=83=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Utility/MD5Encryption.cs | 43 ++++++++ .../HuanMeng.MiaoYu.Code/Order/OrderBLL.cs | 2 +- .../Payment/Alipay/AlipayPayment.cs | 11 ++- .../Payment/Contract/IPayment.cs | 6 +- .../Payment/PaymentExtend.cs | 97 ++++++++++++++++++- .../Payment/WeChat/WeChatPayment.cs | 21 ++-- .../Controllers/PayController.cs | 4 +- 7 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 src/0-core/HuanMeng.DotNetCore/Utility/MD5Encryption.cs diff --git a/src/0-core/HuanMeng.DotNetCore/Utility/MD5Encryption.cs b/src/0-core/HuanMeng.DotNetCore/Utility/MD5Encryption.cs new file mode 100644 index 0000000..496aabd --- /dev/null +++ b/src/0-core/HuanMeng.DotNetCore/Utility/MD5Encryption.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.DotNetCore.Utility +{ + /// + /// + /// + public static class MD5Encryption + { + /// + /// md5加密 + /// + /// + /// + public static string ComputeMD5Hash(string input) + { + // 创建一个 MD5 哈希算法的实例 + using (MD5 md5 = MD5.Create()) + { + // 将输入字符串转换为字节数组 + byte[] inputBytes = Encoding.UTF8.GetBytes(input); + + // 计算 MD5 哈希值 + byte[] hashBytes = md5.ComputeHash(inputBytes); + + // 将字节数组转换为十六进制字符串 + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hashBytes.Length; i++) + { + sb.Append(hashBytes[i].ToString("x2")); + } + + // 返回哈希值字符串 + return sb.ToString(); + } + } + } +} diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Order/OrderBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Order/OrderBLL.cs index 4752b7b..bf0c7bb 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Order/OrderBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Order/OrderBLL.cs @@ -55,7 +55,7 @@ namespace HuanMeng.MiaoYu.Code.Order { price = (decimal)0.01; } - (var orderId, var order) = await payment.CreateOrder(product.ProductName, price, product, ip); + (var orderId, var order) = await payment.CreateOrder(product.Id,product.ProductName, price, product, ip); var t = product.ToIntentOrder(paymentMethod, orderId); t.UserId = _UserId; Dao.daoDbMiaoYu.context.Add(t); diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs index b8e49b4..6c02c0a 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs @@ -11,15 +11,16 @@ using Alipay.EasySDK.Kernel.Util; using Alipay.EasySDK.Payment.FaceToFace.Models; using StackExchange.Redis; using HuanMeng.DotNetCore.MultiTenant.Contract; +using HuanMeng.MiaoYu.Code.Payment.WeChat; namespace HuanMeng.MiaoYu.Code.Payment.Alipay { /// /// /// - public class AlipayPayment(Config config, ITenantInfo tenantInfo) : IPayment + public class AlipayPayment(Config config, ITenantInfo tenantInfo, int userId) : IPayment { - public Task<(string orderId, string order)> CreateOrder(string productName, decimal price, params object[] args) + public Task<(string orderId, string order)> CreateOrder(int productId, string productName, decimal price, params object[] args) { if (string.IsNullOrEmpty(productName)) { @@ -30,11 +31,13 @@ namespace HuanMeng.MiaoYu.Code.Payment.Alipay { throw new ArgumentNullException($"价格不能为空!"); } - var orderId = GenerateTimestampIdWithOffset(); + var orderId = PaymentExtend.GenerateCustomString("ZFB", userId, productId, "001"); //.SetOptions(GetConfig()); //AsyncNotify //https://pay.shhuanmeng.com/api/${tenant}/zfb/${orderId} - var notifyUrl = config.NotifyUrl.Replace("${pay}", "zfb").Replace("${orderId}", orderId).Replace("${tenant}", tenantInfo.Identifier); ; + string sign = $"{tenantInfo.Identifier}{orderId}{userId}"; + sign = MD5Encryption.ComputeMD5Hash(sign); + var notifyUrl = config.NotifyUrl.Replace("${pay}", "zfb").Replace("${orderId}", orderId).Replace("${tenant}", tenantInfo.Identifier).Replace("${sign}", sign); var response = Factory.Payment.App().AsyncNotify(notifyUrl).Optional("passback_params", "PaymentType%3Dzfb").Pay(productName, orderId, priceStr); if (!ResponseChecker.Success(response)) { diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/Contract/IPayment.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/Contract/IPayment.cs index da8d257..6ff73b5 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/Contract/IPayment.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/Contract/IPayment.cs @@ -14,11 +14,15 @@ namespace HuanMeng.MiaoYu.Code.Payment.Contract /// /// 创建订单 /// + /// 产品id /// 订单id /// 产品名称 /// 价格 /// 其它参数 /// - Task<(string orderId, string order)> CreateOrder(string productName, decimal price, params object[] args); + Task<(string orderId, string order)> CreateOrder(int productId, string productName, decimal price, params object[] args); + + + } } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs index e505aa0..ede5d5f 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs @@ -111,9 +111,9 @@ namespace HuanMeng.MiaoYu.Code.Payment if (payment == "zfb") { - return new AlipayPayment(AlipayConfig, miaoYuBase.TenantInfo); + return new AlipayPayment(AlipayConfig, miaoYuBase.TenantInfo, miaoYuBase._UserId); } - return new WeChatPayment(wxClient, weChatConfig, miaoYuBase.TenantInfo); + return new WeChatPayment(wxClient, weChatConfig, miaoYuBase.TenantInfo, miaoYuBase._UserId); } /// @@ -140,5 +140,98 @@ namespace HuanMeng.MiaoYu.Code.Payment }; 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/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs index e7f34a2..9059c2b 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs @@ -4,20 +4,25 @@ using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models; using SKIT.FlurlHttpClient.Wechat.TenpayV3; using Newtonsoft.Json; using HuanMeng.DotNetCore.MultiTenant.Contract; +using System.Security.Cryptography; namespace HuanMeng.MiaoYu.Code.Payment.WeChat { /// /// 微信支付 /// - public class WeChatPayment(WechatTenpayClient client, WeChatConfig weChatConfig, ITenantInfo tenantInfo) : IPayment + public class WeChatPayment(WechatTenpayClient client, WeChatConfig weChatConfig, ITenantInfo tenantInfo, int userId) : IPayment { - public async Task<(string orderId, string order)> CreateOrder(string productName, decimal price, params object[] args) + public async Task<(string orderId, string order)> CreateOrder(int productId, string productName, decimal price, params object[] args) { - var orderId = GenerateTimestampIdWithOffset(); + //var orderId = GenerateTimestampIdWithOffset(); + var orderId = PaymentExtend.GenerateCustomString("WX", userId, productId, "001"); //var client = new WechatTenpayClient(wechatTenpayClientOptions); /* 以 JSAPI 统一下单接口为例 */ - var notifyUrl = weChatConfig.NotifyUrl.Replace("${pay}", "wx").Replace("${orderId}", orderId).Replace("${tenant}", tenantInfo.Identifier); + string sign = $"{tenantInfo.Identifier}{orderId}{userId}"; + sign = MD5Encryption.ComputeMD5Hash(sign); + var notifyUrl = weChatConfig.NotifyUrl.Replace("${pay}", "wx").Replace("${orderId}", orderId).Replace("${tenant}", tenantInfo.Identifier).Replace("${sign}", sign); + var request = new CreatePayTransactionAppRequest() { OutTradeNumber = orderId, @@ -34,16 +39,10 @@ namespace HuanMeng.MiaoYu.Code.Payment.WeChat if (response.IsSuccessful()) { var paramMap = client.GenerateParametersForAppPayRequest(request.AppId, response.PrepayId); - Console.WriteLine("PrepayId:" + response.PrepayId); + //Console.WriteLine("PrepayId:" + response.PrepayId); return new(orderId, JsonConvert.SerializeObject(paramMap)); } throw new Exception("微信下单失败"); } - private string GenerateTimestampIdWithOffset() - { - var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); // 获取Unix时间戳(毫秒) - var random = new Random().Next(1000, 9999); // 生成四位随机数 - return $"WX0{timestamp}J001C{random}"; - } } } diff --git a/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs b/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs index 77eeed3..981c964 100644 --- a/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs +++ b/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs @@ -31,8 +31,8 @@ namespace HuanMeng.MiaoYu.WebPayApi.Controllers /// ${tenant}/zfb/${orderId} /// /// - [HttpPost("{tenant?}/{pay?}/{orderId?}")] - public async Task Post(string? tenant, string? pay, string? orderId) + [HttpPost("{tenant?}/{pay?}/{orderId?}/{sign?}")] + public async Task Post(string? tenant, string? pay, string? orderId,string? sign) { var context = httpContextAccessor.HttpContext; context.Request.EnableBuffering(); // Enable buffering to allow the body to be read multiple times