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