diff --git a/Admin.NET/Admin.NET.Application/Configuration/Alipay.json b/Admin.NET/Admin.NET.Application/Configuration/Alipay.json
new file mode 100644
index 00000000..3c1e24fc
--- /dev/null
+++ b/Admin.NET/Admin.NET.Application/Configuration/Alipay.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
+
+ // 支付宝支付配置,文档地址:https://openhome.alipay.com/develop/sandbox/app
+ "Alipay": {
+ "AppId": "9000000000000000", // 支付宝 APPID
+ "AlipayWebsocketUrl": "openchannel-sandbox.dl.alipaydev.com", // websocket服务地址
+ "ServerUrl": "https://openapi-sandbox.dl.alipaydev.com/gateway.do", // 支付宝网关地址
+ "AuthUrl": "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm", // 授权回调地址
+ "NotifyUrl": "http://xxxx.xxx/api/Alipay/Notify", // 应用网关地址
+ "PrivateKey": "xxxxxxxxx", // 应用私钥
+ "SignType": "RSA2", // 加密算法
+ "EncryptKey": "xxxxxxxx", // 从支付宝获取敏感信息时的加密密钥
+ "AlipayPublicCertPath": "/AlipayCrt/alipayPublicCert.crt", // 支付宝公钥证书存放路径
+ "RootCertPath": "/AlipayCrt/alipayRootCert.crt", // 支付宝根证书存放路径
+ "AppCertPath": "/AlipayCrt/appPublicCert.crt" // 应用公钥证书存放路径
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj b/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
index 0b588e35..5b5a24f9 100644
--- a/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
+++ b/Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
@@ -14,9 +14,11 @@
+
+
@@ -37,6 +39,7 @@
+
@@ -58,7 +61,6 @@
-
@@ -68,7 +70,6 @@
-
diff --git a/Admin.NET/Admin.NET.Core/Attribute/CommonValidationAttribute.cs b/Admin.NET/Admin.NET.Core/Attribute/CommonValidationAttribute.cs
new file mode 100644
index 00000000..e58d08a4
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Attribute/CommonValidationAttribute.cs
@@ -0,0 +1,92 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 通用接口参数验证特性类
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class CommonValidationAttribute : ValidationAttribute
+{
+ private readonly Dictionary _conditions;
+ private static readonly Dictionary CompiledConditions = new();
+
+ ///
+ ///
+ /// 条件对参数,长度必须为偶数
+ /// 奇数字符串参数:动态条件
+ /// 偶数字符串参数:提示消息
+ ///
+ ///
+ ///
+ /// public class ModelInput {
+ ///
+ ///
+ /// public string A { get; set; }
+ ///
+ ///
+ /// [CommonValidation(
+ /// "A == 1 && B == null", "当 A == 1时,B不能为空",
+ /// "C == 2 && B == null", "当 C == 2时,B不能为空"
+ /// )]
+ /// public string B { get; set; }
+ /// }
+ ///
+ ///
+ public CommonValidationAttribute(params string[] conditionPairs)
+ {
+ if (conditionPairs.Length % 2 != 0) throw new ArgumentException("条件对必须以偶数个字符串的形式提供。");
+
+ var conditions = new Dictionary();
+ for (int i = 0; i < conditionPairs.Length; i += 2)
+ conditions.Add(conditionPairs[i], conditionPairs[i + 1]);
+
+ _conditions = conditions;
+ }
+
+ protected override ValidationResult IsValid(object value, ValidationContext validationContext)
+ {
+ foreach (var (expr, errorMessage) in _conditions)
+ {
+ var conditionKey = $"{validationContext.ObjectType.FullName}.{expr}";
+ if (!CompiledConditions.TryGetValue(conditionKey, out var condition))
+ {
+ condition = CreateCondition(validationContext.ObjectType, expr);
+ CompiledConditions[conditionKey] = condition;
+ }
+
+ if ((bool)condition.DynamicInvoke(validationContext.ObjectInstance)!)
+ {
+ return new ValidationResult(errorMessage ?? $"[{validationContext.MemberName}]校验失败");
+ }
+ }
+
+ return ValidationResult.Success;
+ }
+
+ private Delegate CreateCondition(Type modelType, string expression)
+ {
+ try
+ {
+ // 创建参数表达式
+ var parameter = Expression.Parameter(typeof(object), "x");
+
+ // 构建 Lambda 表达式
+ var lambda = DynamicExpressionParser.ParseLambda(new[] { Expression.Parameter(modelType, "x") }, typeof(bool), expression);
+
+ // 创建新的 Lambda 表达式,接受 object 参数并调用编译后的表达式
+ var invokeExpression = Expression.Invoke(lambda, Expression.Convert(parameter, modelType));
+ var finalLambda = Expression.Lambda>(invokeExpression, parameter);
+
+ return finalLambda.Compile();
+ }
+ catch (Exception ex)
+ {
+ throw new ArgumentException($"无法解析表达式 '{expression}': {ex.Message}", ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Const/AlipayConst.cs b/Admin.NET/Admin.NET.Core/Const/AlipayConst.cs
new file mode 100644
index 00000000..d6c4dda2
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Const/AlipayConst.cs
@@ -0,0 +1,39 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 支付宝支付常量
+///
+[SuppressSniffer]
+public class AlipayConst
+{
+ ///
+ /// 单笔无密转账【业务场景】固定值
+ ///
+ public const string BizScene = "DIRECT_TRANSFER";
+
+ ///
+ /// 单笔无密转账【销售产品码】固定值
+ ///
+ public const string ProductCode = "TRANS_ACCOUNT_NO_PWD";
+
+ ///
+ /// 交易状态参数名
+ ///
+ public const string TradeStatus = "trade_status";
+
+ ///
+ /// 交易成功标识
+ ///
+ public const string TradeSuccess = "TRADE_SUCCESS";
+
+ ///
+ /// 授权类型
+ ///
+ public const string GrantType = "authorization_code";
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Const/ConfigConst.cs b/Admin.NET/Admin.NET.Core/Const/ConfigConst.cs
index 23afcc74..2906f00b 100644
--- a/Admin.NET/Admin.NET.Core/Const/ConfigConst.cs
+++ b/Admin.NET/Admin.NET.Core/Const/ConfigConst.cs
@@ -121,6 +121,11 @@ public class ConfigConst
///
public const string SysUpgrade = "sys_upgrade";
+ ///
+ /// 支付宝授权页面地址
+ ///
+ public const string AlipayAuthPageUrl = "alipay_auth_page_url_";
+
///
/// Default 分组
///
diff --git a/Admin.NET/Admin.NET.Core/Enum/AlipayCertTypeEnum.cs b/Admin.NET/Admin.NET.Core/Enum/AlipayCertTypeEnum.cs
new file mode 100644
index 00000000..8bc1c342
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Enum/AlipayCertTypeEnum.cs
@@ -0,0 +1,21 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 参与方的证件类型枚举
+///
+[SuppressSniffer]
+[Description("参与方的证件类型枚举")]
+public enum AlipayCertTypeEnum
+{
+ [Description("身份证")]
+ IDENTITY_CARD,
+
+ [Description("护照")]
+ PASSPORT
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Enum/AlipayIdentityTypeEnum.cs b/Admin.NET/Admin.NET.Core/Enum/AlipayIdentityTypeEnum.cs
new file mode 100644
index 00000000..4b2d864b
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Enum/AlipayIdentityTypeEnum.cs
@@ -0,0 +1,21 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 参与方的标识类型枚举
+///
+[SuppressSniffer]
+[Description("参与方的标识类型枚举")]
+public enum AlipayIdentityTypeEnum
+{
+ [Description("支付宝用户UID")]
+ ALIPAY_USER_ID,
+
+ [Description("支付宝登录号")]
+ ALIPAY_LOGON_ID
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs b/Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs
index 8f6c106c..5bd96d01 100644
--- a/Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs
+++ b/Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs
@@ -4,6 +4,8 @@
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+using System.Text.Json;
+
namespace Admin.NET.Core;
///
@@ -446,4 +448,16 @@ public static partial class ObjectExtension
}
}
}
+
+ ///
+ /// 对象深复制
+ ///
+ /// 深复制源对象
+ /// 对象
+ ///
+ public static T DeepCopy(this T obj)
+ {
+ var json = JsonSerializer.Serialize(obj);
+ return JsonSerializer.Deserialize(json);
+ }
}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Option/AlipayOptions.cs b/Admin.NET/Admin.NET.Core/Option/AlipayOptions.cs
new file mode 100644
index 00000000..2ece6bb6
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Option/AlipayOptions.cs
@@ -0,0 +1,68 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+///
+/// 支付宝支付配置选项
+///
+public sealed class AlipayOptions : IConfigurableOptions
+{
+ ///
+ /// 支付宝 APPID(必填)
+ ///
+ public string AppId { get; set; }
+
+ ///
+ /// 支付宝 websocket 服务地址 (必填)
+ ///
+ public string AlipayWebsocketUrl { get; set; }
+
+ ///
+ /// 支付宝网关地址 (必填)
+ ///
+ public string ServerUrl { get; set; }
+
+ ///
+ /// 支付宝授权回调地址 (必填)
+ ///
+ public string AuthUrl { get; set; }
+
+ ///
+ /// 应用回调地址
+ ///
+ public string NotifyUrl { get; set; }
+
+ ///
+ /// 加密算法(必填)
+ ///
+ public string SignType { get; set; }
+
+ ///
+ /// 从支付宝获取敏感信息时的加密密钥(可选)
+ ///
+ public string EncryptKey { get; set; }
+
+ ///
+ /// 应用私钥 (必填)
+ ///
+ public string PrivateKey { get; set; }
+
+ ///
+ /// 支付宝公钥证书存放路径(证书加签方式必填)
+ ///
+ public string AlipayPublicCertPath { get; set; }
+
+ ///
+ /// 支付宝根证书存放路径(证书加签方式必填)
+ ///
+ public string RootCertPath { get; set; }
+
+ ///
+ /// 应用公钥证书存放路径(证书加签方式必填)
+ ///
+ public string AppCertPath { get; set; }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/Alipay/AlipayErrorCodes.cs b/Admin.NET/Admin.NET.Core/Service/Alipay/AlipayErrorCodes.cs
new file mode 100644
index 00000000..a5d04a0d
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/Alipay/AlipayErrorCodes.cs
@@ -0,0 +1,143 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core.Service;
+
+///
+/// 支付宝支付错误码
+///
+public class AlipayErrorCode
+{
+ ///
+ /// 错误代码
+ ///
+ public string Code { get; private set; }
+
+ ///
+ /// 错误消息
+ ///
+ public string Message { get; private set; }
+
+ ///
+ /// 解决方案
+ ///
+ public string Solution { get; private set; }
+
+ ///
+ /// 错误码集
+ ///
+ private static readonly List StatusCodes = new()
+ {
+ new AlipayErrorCode { Code="SYSTEM_ERROR", Message="系统繁忙", Solution="可能是由于网络或者系统故障,请与技术人员联系以解决该问题。" },
+ new AlipayErrorCode { Code="INVALID_PARAMETER", Message="参数有误或没有参数", Solution="请检查并确认查询请求参数合法性。" },
+ new AlipayErrorCode { Code="AUTHORISE_NOT_MATCH", Message="授权失败,无法获取用户信息", Solution="检查账户与支付方关系主表关系,确认是否正确配置。" },
+ new AlipayErrorCode { Code="BALANCE_IS_NOT_ENOUGH", Message="余额不足,建议尽快充值。后续登录电银通或支付宝,自主设置余额预警提醒功能。", Solution="余额不足,建议尽快充值。商户后续登录电银通或支付宝,自主设置余额预警提醒功能或登录Alipay-资金管理->产品一览->右上角功能按钮进行设置。" },
+ new AlipayErrorCode { Code="BIZ_UNIQUE_EXCEPTION", Message="商户订单号冲突。", Solution="商户订单号冲突。" },
+ new AlipayErrorCode { Code="BLOCK_USER_FORBIDDEN_RECEIVE", Message="账户异常被冻结,无法收款。", Solution="账户异常被冻结,无法收款。请询问支付宝热线95188" },
+ new AlipayErrorCode { Code="BLOCK_USER_FORBIDDEN_SEND", Message="该账户被冻结,暂不可将资金转出。", Solution="该账户被冻结,暂不可将资金转出。" },
+ new AlipayErrorCode { Code="CURRENCY_NOT_SUPPORT", Message="币种不支持", Solution="请查询您的结算币种需求所见币种,目前限于人民币/美元结算。" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_DC_R_ECEIVED", Message="收款方单日收款笔数超限", Solution="收款方向同一个收款账户单日只能收款固定的笔数,超过后让收款人第二天再收。" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_DM_AMOUNT", Message="日累计额度超限", Solution="今日转账金额已上限,日累计额度需满5000元以上,可使用企业支付宝付款点【立即付款】申请,日累计额度需满5000元以上可点击【联系客服】咨询:转账到支付宝客户窗口" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_DM_MAX_AMOUNT", Message="超出单日转账限额,如有疑问请询问支付宝热线95188", Solution="今日转账金额已上限,日累计额度需满5000元以上,可使用企业支付宝付款点【立即付款】申请,日累计额度需满5000元以上可点击【联系客服】咨询:转账到支付宝客户窗口" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_ENT_SM_AMOUNT", Message="转账给企业用户超过单笔限额(默认10w)", Solution="1. 10w以下的快速转账给企业用户。2. 联系800电话协助修改,修改转账限额" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_MM_AMOUNT", Message="月累计金额超限", Solution="本月转账金额已上限,月转账额度需满10000元以上,可使用企业支付宝付款点【立即付款】申请,月转账额度需满10000元以上可点击【联系客服】咨询:转账到支付宝客户窗口" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_MMM_MAX_AMOUNT", Message="超出单月转账限额,如有疑问请询问支付宝热线95188", Solution="本月转账金额已上限,月转账额度需满10000元以上,可使用企业支付宝付款点【立即付款】申请,月转账额度需满10000元以上可点击【联系客服】咨询:转账到支付宝客户窗口" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_PERSONAL_SM_AMOUNT", Message="超出转账给个人支付宝账户的单笔限额", Solution="超出转账给个人支付宝账户的单笔限额" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_SM_AMOUNT", Message="单笔额度超限", Solution="请根据接入文档填写amount字段" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_SM_MIN_AMOUNT", Message="请求金额不能低于0.1元", Solution="请修改转账金额。" },
+ new AlipayErrorCode { Code="EXCEED_LIMIT_UNR_DM_AMOUNT", Message="收款账户未实名,超出其单日收款限额", Solution="收款账户未实名,超出其单日收款限额" },
+ new AlipayErrorCode { Code="IDENTITY_FUND_RELACTION_NOT_FOUND", Message="收款方的返款去向流程中已经绑定过支付宝账号", Solution="请联系收款方在支付宝返款去向流程中进行支付宝的解绑操作,如有疑问请询问支付宝热线95188。" },
+ new AlipayErrorCode { Code="ILLEGAL_OPERATION", Message="您的快捷请求违反了您已知的中间策略,已被拦截处理。请直接联系收款客户信息再次发起交易。", Solution="您的快捷请求违反了您已知的中间策略,已被拦截处理。请直接联系收款客户信息再次发起交易。" },
+ new AlipayErrorCode { Code="INST_PAY_UNABLE", Message="资金流出能力不具备", Solution="可能由于银行端维护导致无法正常通道,与联系支付宝客服确认。" },
+ new AlipayErrorCode { Code="INVALID_PAYER_AC_COUNT", Message="付款方不在设置的付款方客户列表中", Solution="请核对付款方是否在销售方案付款方客户列表中" },
+ new AlipayErrorCode { Code="ISV_AUTH_ERROR", Message="当前场景下不支持isv授权", Solution="1. 检查商户产品和场景范围,当前场景下不支持isv授权权。2. 去删除isv授权模板,改为自调用。" },
+ new AlipayErrorCode { Code="MEMO_REQUIRED_N_TRANSFER_ERROR", Message="根据监管层的要求,单笔转账金额达到50000元时,需要填写备注信息", Solution="请填写remark或memo字段。" },
+ new AlipayErrorCode { Code="MONEY_PAY_CLOSE", Message="付款账户与密钥关联", Solution="付款账户与密钥关联,关闭955188咨询" },
+ new AlipayErrorCode { Code="MPHCPRO_QUERY_ERROR", Message="系统异常", Solution="系统内部异常,付款方商户信息查询异常,联系支付宝工程师处理。" },
+ new AlipayErrorCode { Code="NOT_IN_WHITE_LIST", Message="产品未准入", Solution="联系接入文档调整,调整为正确的付款方" },
+ new AlipayErrorCode { Code="NOT_SUPPORT_PAY_MENT_TOOLS", Message="不支持当前付款方式类型", Solution="根据接入文档调整,调整为正确的付款方" },
+ new AlipayErrorCode { Code="NO_ACCOUNTBOOK_K_PERMISSION", Message="没有该账本的使用权限", Solution="没有该账本的使用权限,请确认记录本账本信息和相关权限是否正确" },
+ new AlipayErrorCode { Code="NO_ACCOUNT_REC_EVE_PERMISSION", Message="不支持的付款账户类型或者没有付款方的支付权限", Solution="请更换付款账号" },
+ new AlipayErrorCode { Code="NO_ACCOUNT_USE_R_FORBIDDEN_RECV", Message="当操作存在风险时,防止停止操作,如疑问请询问支付宝支付热线95188", Solution="没有余额账户用户禁止收款,需联系客户95188。" },
+ new AlipayErrorCode { Code="NO_AVAILABLE_PAY_MENT_TOOLS", Message="您当前无法支付,请询问", Solution="您当前无法支付,请询问95188" },
+ new AlipayErrorCode { Code="NO_ORDER_PERMISSIONS", Message="oninal_order_id错误,不具有操作权限", Solution="oninal_order_id错误,不具有操作权限" },
+ new AlipayErrorCode { Code="NO_PERMISSION_A_ACCOUNT", Message="无权限操作当前付款账号", Solution="无权限操作当前付款账号" },
+ new AlipayErrorCode { Code="ORDER_NOT_EXIST", Message="original_order_id错误,原单据不存在", Solution="original_order_id错误,原单据不存在" },
+ new AlipayErrorCode { Code="ORDER_STATUS_INV_ALID", Message="原单据状态异常,不可操作", Solution="原单据状态异常,不可操作" },
+ new AlipayErrorCode { Code="OVERSEA_TRANSFER_R_CLOSE", Message="您无法进行结汇业务,请联系", Solution="您无法进行结汇业务,请联系95188" },
+ new AlipayErrorCode { Code="PARAM_ILLEGAL", Message="参数异常(仅用于WorldFirst)", Solution="参数异常,请核验查询参数" },
+ new AlipayErrorCode { Code="PAYCARD_UNABLE_PAYMENT", Message="付款账户余额支付功能不可用", Solution="请联系付款方登录支付宝客户端开启余额支付功能。" },
+ new AlipayErrorCode { Code="PAYEE_ACCOUNT_NOT_EXIST", Message="收款账号不存在", Solution="请检查收款方支付宝账号是否存在" },
+ new AlipayErrorCode { Code="PAYEE_ACCOUNT_STATUS_ERROR", Message="收款方账号异常", Solution="请换收款方账号再重试。" },
+ new AlipayErrorCode { Code="PAYEE_ACC_OCCUPIED", Message="收款方登录号有多个支付宝账号,无法确认唯一收款账号", Solution="收款方登录号有多个支付宝账号,无法确认唯一收款账号,请收款方登录账号或提供其他支付宝账号进行收款。" },
+ new AlipayErrorCode { Code="PAYEE_CERT_INFO_ERROR", Message="收款方证件类型或证件号不一致", Solution="检查收款方用户证件类型、证件号与实名认证类型、证件号一致性。" },
+ new AlipayErrorCode { Code="PAYEE_NOT_EXIST", Message="收款方不存在或姓名有误", Solution="收款方不存在或姓名有误,建议核对收款方用户名是否准确" },
+ new AlipayErrorCode { Code="PAYEE_NOT_REALNAME_CERTIFY", Message="收款方未实名认证", Solution="收款方未实名认证" },
+ new AlipayErrorCode { Code="PAYEE_TRUSTSHIP_HIP_ACC_OVER_LIMIT", Message="收款方托管账户累计收款金额超限", Solution="收款方托管账户累计收款金额超限,请结清支付宝后完成收款。" },
+ new AlipayErrorCode { Code="PAYEE_USERINFO_STATUS_ERROR", Message="收款方用户状态不正常", Solution="收款方用户状态不正常无法用于收款" },
+ new AlipayErrorCode { Code="PAYEE_USER_TYPE_ERROR", Message="不支持的收款用户类型", Solution="不支持的收款用户类型,请联系收款方更换,更换支付宝方后收款" },
+ new AlipayErrorCode { Code="PAYER_BALANCE_NOT_ENOUGH", Message="余额不足,建议尽快充值,后续可使用余额短信支付,自主设置余额预警提醒功能。", Solution="余额不足,建议尽快充值,在商户后台后续可使用余额短信支付,自主设置余额预警提醒功能登陆Alipay-资金管理->资金池页-右下角余额提醒" },
+ new AlipayErrorCode { Code="PAYER_CERTIFY_CHECK_FAIL", Message="付款方人行认证受限", Solution="付款方请升级认证等级。" },
+ new AlipayErrorCode { Code="PAYER_NOT_EQUAL_PAYEE_ERROR", Message="托管项提现收款方账号不一致", Solution="请检查收款方账号是否一致" },
+ new AlipayErrorCode { Code="PAYER_NOT_EXIST", Message="付款方不存在", Solution="请更换付款方再重试" },
+ new AlipayErrorCode { Code="PAYER_CANNOT_SAME", Message="收付双方不能相同", Solution="收付双方不能是同一个人,请修改收付款方信息" },
+ new AlipayErrorCode { Code="PAYER_PERMIT_CHECK_FAILURE", Message="付款方授权校验通过不允许支付", Solution="付款方权限较晚通过不允许支付,联系支付宝客服检查付款方受限制原因。" },
+ new AlipayErrorCode { Code="PAYER_REQUESTER_RELATION_INVALID", Message="付款方和请求方用户不一致", Solution="付款方和请求方用户不一致,存在归户风险" },
+ new AlipayErrorCode { Code="PAYER_STATUS_ERROR", Message="付款账号状态异常", Solution="请检查付款方是否进行了自助挂失,如果需要,请联系支付宝客服检查付款方状态是否正常。" },
+ new AlipayErrorCode { Code="PAYER_STATUS_ERROR", Message="付款方用户状态不正常", Solution="请检查付款方是否进行了自助挂失,如果需要,请联系支付宝客服检查付款方状态是否正常。" },
+ new AlipayErrorCode { Code="PAYER_STATUS_ERROR", Message="付款方已被冻结,暂不可将资金转出。", Solution="1. 联系支付宝客户询问用户冻结原因以及协助解冻办法状态。" },
+ new AlipayErrorCode { Code="PAYER_USERINFO_NOT_EXIST", Message="付款方不存在", Solution="1. 检查付款方是否已销户,若销户请联系销户后重新发起业务。2. 检查参入是否有误。" },
+ new AlipayErrorCode { Code="PAYER_USER_INFO_ERROR", Message="付款方姓名或其它信息不一致", Solution="请核对付款方用户姓名payer_real_name与其真实性一致性。" },
+ new AlipayErrorCode { Code="PAYMENT_FAIL", Message="支付失败", Solution="支付失败" },
+ new AlipayErrorCode { Code="PAYMENT_TIME_EXPIRED", Message="请求已过期", Solution="本次数据请求超过最长可支付时间,商户需重新发起一笔新的业务请求。" },
+ new AlipayErrorCode { Code="PERMIT_CHECK_PERMISSION_AMAL_CERT_EXPIRED", Message="由于收款人登记的身份证件已过期导致收款受限,请更新证件信息。", Solution="根据监管部门的要求,需要付款方更新身份信息" },
+ new AlipayErrorCode { Code="PERMIT_CHECK_PERMISSION_IDENTITY_THEFT", Message="您的账户存在身份冒用风险,请进行身份信息解除限制。", Solution="您的账户存在身份冒用风险,请进行身份信息解除限制。" },
+ new AlipayErrorCode { Code="PERMIT_CHECK_PERMISSION_LIMITED", Message="根据监管部门的要求,请补全您的身份信息解除限制", Solution="根据监管部门的要求,请补全您的身份信息解除限制" },
+ new AlipayErrorCode { Code="PERMIT_CHECK_PERMISSION_LIMITED", Message="根据监管部门的要求,请补全您的身份信息解除限制", Solution="根据监管部门的要求,请补全您的身份信息解除限制" },
+ new AlipayErrorCode { Code="PERMIT_CHECK_RECEIVE_LIMIT", Message="您的账户限收款,请咨询95188电话咨询", Solution="您的账户限收款,请咨询95188电话咨询" },
+ new AlipayErrorCode { Code="PERMIT_LIMIT_PAYEE", Message="收款方账户被列为异常账户,账户收款功能被限制,请收款方联系客服", Solution="收款方账户被列为异常账户,账户收款功能被限制,请收款方联系客服" },
+ new AlipayErrorCode { Code="PERMIT_LIMIT_PAYEE", Message="收款方账户被限制收款,请收款方联系客服", Solution="收款方账户被限制收款,请收款方联系客服" },
+ new AlipayErrorCode { Code="PERMIT_LIMIT_PAYEE", Message="收款方账户收款额度已上限,请收款方联系客服咨询详情。", Solution="收款方账户收款额度已上限,请收款方联系客服咨询详情。" },
+ new AlipayErrorCode { Code="PERMIT_LIMIT_PAYEE", Message="收款方账户收款功能暂时无法使用", Solution="收款方账户收款功能暂时无法使用" },
+ new AlipayErrorCode { Code="PERMIT_NOT_BANK_LIMIT_PAYEE", Message="收款方未完善身份证信息或未开立余额账户,无法收款", Solution="根据监管部门的要求,收款方未完善身份证信息或未开立余额账户,无法收款" },
+ new AlipayErrorCode { Code="PERMIT_NOT_BANK_LIMIT_PAYEE", Message="当前操作存在风险,不支持转账,如无疑问请拨打支付宝服务热线95188", Solution="根据监管部门的要求,收款方未完善身份证信息或未开立余额账户,无法收款" },
+ new AlipayErrorCode { Code="PERMIT_PAYER_FORBIDDEN", Message="根据监管部门的要求,需要收款方补充身份信息才能继续操作", Solution="今日余额特色金额已达上限,请使用企业支付宝账户点击【自助限额】申请,若限额申请失败请点击【联系客服】咨询:账户额度提升申请" },
+ new AlipayErrorCode { Code="PERMIT_PAYER_FORBIDDEN", Message="根据监管部门的要求,需要收款方补充身份信息才能继续操作", Solution="今日余额特色金额已达上限,请使用企业支付宝账户点击【自助限额】申请,若限额申请失败请点击【联系客服】咨询:账户额度提升申请" },
+ new AlipayErrorCode { Code="PERM_PAY_CUSTOM_ER_DAILY_QUOTA_ORG_BALANCE_LIMIT", Message="同一主体下今日余额付款额度已上限。", Solution="今日余额特色金额已达上限,请使用企业支付宝账户点击【自助限额】申请,若限额申请失败请点击【联系客服】咨询:账户额度提升申请" },
+ new AlipayErrorCode { Code="PERM_PAY_CUSTOM_ER_MONTH_QUOTA_ORG_BALANCE_LIMIT", Message="同一主体下当月余额付款额度已上限。", Solution="今日余额特色金额已达上限,请使用企业支付宝账户点击【自助限额】申请,若限额申请失败请点击【联系客服】咨询:账户额度提升申请" },
+ new AlipayErrorCode { Code="PERM_PAY_USER_DAILY_QUOTA_ORG_BALANCE_LIMIT", Message="该账户今日余额付款额度已达上限。", Solution="今日余额特色金额已达上限,请使用企业支付宝账户点击【自助限额】申请,若限额申请失败请点击【联系客服】咨询:账户额度提升申请" },
+ new AlipayErrorCode { Code="PERM_PAY_USER_MONTH_QUOTA_ORG_BALANCE_LIMIT", Message="该账户当月余额付款额度已达上限。", Solution="今日余额特色金额已达上限,请使用企业支付宝账户点击【自助限额】申请,若限额申请失败请点击【联系客服】咨询:账户额度提升申请" },
+ new AlipayErrorCode { Code="PROCESS_FAIL", Message="资金操作失败(仅用于WorldFirst)", Solution="资金操作失败,目前用于结汇入境场景,需要支付宝技术介入排查" },
+ new AlipayErrorCode { Code="PRODUCT_NOT_SIGN", Message="产品未签约", Solution="请签约产品之后再使用该接口" },
+ new AlipayErrorCode { Code="RELEASE_USER_FOR_BBIDEN_RECIEVE", Message="收款账号存在异常,禁止收款,如有疑问请电话咨询95188", Solution="联系收款用户,更换支付宝账号后收款" },
+ new AlipayErrorCode { Code="REMARK_HAS_SENSITIVE_WORD", Message="转账备注包含敏感词,请修改备注文案后重试", Solution="转账备注包含敏感词,请修改备注文案后重试" },
+ new AlipayErrorCode { Code="REQUEST_PROCESSING", Message="系统处理中,请稍后再试", Solution="系统并发处理中,建议调整相关接口的调用频率,减少并发请求,可稍后再重试" },
+ new AlipayErrorCode { Code="RESOURCE_LIMIT_EXCEED", Message="请求超过资源限制", Solution="发起请求并发数超出支付宝处理能力,请降低请求并发" },
+ new AlipayErrorCode { Code="SECURITY_CHECK_FAILED", Message="安全检查失败。当前操作存在风险,请停止操作,如有疑问请咨询服务热线95188", Solution="安全检查失败。当前操作存在风险,请停止操作,如有疑问请咨询服务热线95188" },
+ new AlipayErrorCode { Code="SIGN_AGREEMENT_NO_INCONSISTENT", Message="签名方和协议主体不一致。请确认payer_info.ext_info.agreement_no和sign_data.ori_app_id是否匹配。", Solution="签名方和协议主体不一致。请确认payer_info.ext_info.agreement_no和sign_data.ori_app_id是否匹配,再重试。" },
+ new AlipayErrorCode { Code="SIGN_INVALID", Message="签名非法,验签不通过。请确认签名信息是否被篡改以及签名方签名格式是否正确。", Solution="签名非法,验签不通过。请确认签名信息是否被篡改以及签名方签名格式是否正确。" },
+ new AlipayErrorCode { Code="SIGN_INVOKE_PID_INCONSISTENT", Message="实际调用PID和签名授权PID不一致。请确认实际调用PID和sign_data.partner_id是否一致。", Solution="请确认实际调用PID和sign_data.partner_id是否一致,一致后再重试。" },
+ new AlipayErrorCode { Code="SIGN_NOT_ALLOW_SKIP", Message="该场景强制验签,不允许跳过。请按要求上报sign_data后重试。", Solution="该场景强制验签,不允许跳过。请按要求上报sign_data后重试。" },
+ new AlipayErrorCode { Code="SIGN_PARAM_INVALID", Message="验签参数非法。请确认sign_data参数是否正确。", Solution="验签参数非法,请确认sign_data参数是否正确。" },
+ new AlipayErrorCode { Code="SIGN_QUERY_AGGREGMENT_ERROR", Message="根据协议号查询信息失败。请确认payer_info.ext_info.agreement_no是否正确。", Solution="请确认上报协议号payer_info.ext_info.agreement_no内容正确后再重试。" },
+ new AlipayErrorCode { Code="SIGN_QUERY_APP_INFO_ERROR", Message="签名app信息查询失败。请确认sign_data.ori_app_id是否正确。", Solution="请确认签名方sign_data.ori_app_id是否正确,信息正确后再重试。" },
+ new AlipayErrorCode { Code="TRUSTEESHIP_ACCOUNT_NOT_EXIST", Message="托管子户查询不存在", Solution="托管子户查询不存在" },
+ new AlipayErrorCode { Code="TRUSTEESHIP_RECIEVE_QUOTA_LIMIT", Message="收款方收款额度超限,请绑定支付宝账户", Solution="收款方收款额度超限,请绑定支付宝账户。" },
+ new AlipayErrorCode { Code="USER_AGREEMENT_VERIFY_FAIL", Message="用户协议校验失败", Solution="确认入参中协议号是否正确" },
+ new AlipayErrorCode { Code="USER_NOT_EXIST", Message="用户不存在(仅用于WorldFirst)", Solution="用户不存在,请检查收付款方信息" },
+ new AlipayErrorCode { Code="USER_RISK_FREEZE", Message="账户异常被冻结,无法付款,请咨询支付宝客服95188", Solution="账户异常被冻结,无法付款,请咨询支付宝客服95188" }
+ };
+
+ ///
+ /// 根据错误码获取错误信息
+ ///
+ ///
+ ///
+ public static AlipayErrorCode Get(string code)
+ {
+ return StatusCodes.FirstOrDefault(u => u.Code.EqualIgnoreCase(code));
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs b/Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs
new file mode 100644
index 00000000..01c06d7e
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs
@@ -0,0 +1,273 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Admin.NET.Core.Service.Alipay;
+using Aop.Api;
+using Aop.Api.Domain;
+using Aop.Api.Request;
+using Aop.Api.Response;
+using Aop.Api.Util;
+using Microsoft.AspNetCore.Hosting;
+
+namespace Admin.NET.Core.Service;
+
+///
+/// 支付宝支付服务 🧩
+///
+[ApiDescriptionSettings(Order = 240)]
+public class AlipayService : IDynamicApiController, ITransient
+{
+ private readonly UserManager _userManager;
+ private readonly SysConfigService _sysConfigService;
+ private readonly AlipayOptions _alipayOptions;
+ private readonly IHttpContextAccessor _httpContext;
+ private readonly IWebHostEnvironment _webHostEnvironment;
+ private readonly IAopClient _alipayClient;
+
+ public AlipayService(UserManager userManager,
+ SysConfigService sysConfigService,
+ IOptions alipayOptions,
+ IHttpContextAccessor httpContext,
+ IWebHostEnvironment webHostEnvironment)
+ {
+ _userManager = userManager;
+ _sysConfigService = sysConfigService;
+ _alipayOptions = alipayOptions.Value;
+ _httpContext = httpContext;
+ _webHostEnvironment = webHostEnvironment;
+
+ // 初始化支付宝客户端
+ string path = App.WebHostEnvironment.ContentRootPath;
+ _alipayClient = new DefaultAopClient(new AlipayConfig
+ {
+ Format = "json",
+ Charset = "UTF-8",
+ AppId = _alipayOptions.AppId,
+ SignType = _alipayOptions.SignType,
+ ServerUrl = _alipayOptions.ServerUrl,
+ PrivateKey = _alipayOptions.PrivateKey,
+ EncryptKey = _alipayOptions.EncryptKey,
+ AppCertPath = Path.Combine(path, _alipayOptions.AppCertPath),
+ RootCertPath = Path.Combine(path, _alipayOptions.RootCertPath),
+ AlipayPublicCertPath = Path.Combine(path, _alipayOptions.AlipayPublicCertPath)
+ });
+ }
+
+ ///
+ /// 获取授权信息 🔖
+ ///
+ ///
+ ///
+ [NonUnify]
+ [AllowAnonymous]
+ [DisplayName("获取授权信息")]
+ [ApiDescriptionSettings(Name = "GetAuthInfo"), HttpGet]
+ public ActionResult GetAuthInfo([FromQuery] AlipayAuthInfoInput input)
+ {
+ var type = input.UserId.Split('-').FirstOrDefault().ToInt();
+ var userId = input.UserId.Split('-').LastOrDefault().ToLong();
+
+ // 当前网页接口地址
+ var currentUrl = $"{_httpContext.HttpContext!.Request.GetOrigin()}{_httpContext.HttpContext!.Request.Path}?userId={input.UserId}";
+ if (string.IsNullOrEmpty(input.AuthCode))
+ {
+ // 重新授权
+ var url = $"{_alipayOptions.AuthUrl}?app_id={_alipayOptions.AppId}&scope=auth_user&redirect_uri={currentUrl}";
+ return new RedirectResult(url);
+ }
+
+ // 组装授权请求参数
+ AlipaySystemOauthTokenRequest request = new()
+ {
+ GrantType = AlipayConst.GrantType,
+ Code = input.AuthCode
+ };
+ AlipaySystemOauthTokenResponse response = _alipayClient.CertificateExecute(request);
+
+ // token换取用户信息
+ AlipayUserInfoShareRequest infoShareRequest = new();
+ AlipayUserInfoShareResponse info = _alipayClient.CertificateExecute(infoShareRequest, response.AccessToken);
+
+ // 循环执行扫码后需要执行的业务逻辑,需要至少一个继承方法返回true,否则抛出异常
+ var pass = false;
+ foreach (var notify in App.GetServices())
+ if (notify.ScanCallback(type, userId, info)) pass = true;
+ if (!pass) throw Oops.Oh("未处理的授权逻辑");
+
+ // 执行完,重定向到指定界面
+ var authPageUrl = _sysConfigService.GetConfigValueByCode(ConfigConst.AlipayAuthPageUrl + type).Result;
+ return new RedirectResult(authPageUrl);
+ }
+
+ ///
+ /// 支付回调 🔖
+ ///
+ ///
+ [AllowAnonymous]
+ [DisplayName("支付回调")]
+ [ApiDescriptionSettings(Name = "Notify"), HttpPost]
+ public string Notify()
+ {
+ SortedDictionary sorted = new();
+ foreach (string key in _httpContext.HttpContext!.Request.Form.Keys)
+ sorted.Add(key, _httpContext.HttpContext.Request.Form[key]);
+
+ string alipayPublicKey = Path.Combine(_webHostEnvironment.ContentRootPath, _alipayOptions.AlipayPublicCertPath!.Replace('/', '\\').TrimStart('\\'));
+ bool signVerified = AlipaySignature.RSACertCheckV1(sorted, alipayPublicKey, "UTF-8", _alipayOptions.SignType); // 调用SDK验证签名
+ if (!signVerified) throw Oops.Oh("交易失败");
+
+ var outTradeNo = sorted.GetValueOrDefault("out_trade_no");
+ try
+ {
+ // 记录回调日志
+ File.AppendAllText($"{_webHostEnvironment.ContentRootPath}\\AlipayLog\\Notify-{DateTime.Today:yyyy-MM-dd}.txt",
+ $"支付宝支付到平台({DateTime.Now:yyyy-MM-dd HH:mm:ss}):{Environment.NewLine} " +
+ $"登录人:{_userManager.UserId}-{_userManager.RealName}{Environment.NewLine} " +
+ $"IP:{App.HttpContext?.GetRemoteIpAddressToIPv4(true)} {Environment.NewLine} " +
+ $"交易号:{outTradeNo}{Environment.NewLine} " +
+ $"参数:{JSON.Serialize(sorted)}{Environment.NewLine} " +
+ $"-----------------------------------------------------------------------------------------------------------------------" +
+ $"{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}");
+ }
+ catch (Exception ex)
+ {
+ Log.Error("支付宝支付回调日志写入失败:", ex);
+ }
+
+ if (sorted.GetValueOrDefault(AlipayConst.TradeStatus) == AlipayConst.TradeSuccess)
+ {
+ // 约定交易码前四位为类型码,后面为订单号
+ var tradeNo = long.Parse(outTradeNo);
+ var type = long.Parse(outTradeNo.Substring(0, 4));
+
+ // 循环执行业务逻辑,若都未处理(回调全部返回false)则交易失败
+ var isError = true;
+ foreach (var notify in App.GetServices())
+ if (notify.TopUpCallback(type, tradeNo)) isError = false;
+ if (isError) throw Oops.Oh("交易失败");
+ }
+ return "success";
+ }
+
+ ///
+ /// 统一收单下单并支付页面接口 🔖
+ ///
+ ///
+ ///
+ [DisplayName("统一收单下单并支付页面接口")]
+ [ApiDescriptionSettings(Name = "AlipayTradePagePay"), HttpPost]
+ public string AlipayTradePagePay(AlipayTradePagePayInput input)
+ {
+ AlipayTradeWapPayRequest request = new();
+
+ // 组装业务参数model
+ AlipayTradeWapPayModel model = new()
+ {
+ Subject = input.Subject,
+ OutTradeNo = input.OutTradeNo,
+ TotalAmount = input.TotalAmount,
+ Body = input.Body,
+ ProductCode = "QUICK_WAP_WAY",
+ TimeExpire = input.TimeoutExpress
+ };
+ request.SetBizModel(model);
+
+ // 设置异步通知接收地址
+ request.SetNotifyUrl(_alipayOptions.NotifyUrl);
+
+ var response = _alipayClient.SdkExecute(request);
+ if (response.IsError) throw Oops.Oh(response.SubMsg);
+ return $"{_alipayOptions.ServerUrl}?{response.Body}";
+ }
+
+ ///
+ /// 交易预创建 🔖
+ ///
+ ///
+ ///
+ [DisplayName("交易预创建")]
+ [ApiDescriptionSettings(Name = "AlipayPreCreate"), HttpPost]
+ public string AlipayPreCreate(AlipayPreCreateInput input)
+ {
+ AlipayTradePrecreateRequest request = new();
+
+ // 设置异步通知接收地址
+ request.SetNotifyUrl(_alipayOptions.NotifyUrl);
+
+ // 组装业务参数model
+ AlipayTradePrecreateModel model = new()
+ {
+ Subject = input.Subject,
+ OutTradeNo = input.OutTradeNo,
+ TotalAmount = input.TotalAmount,
+ TimeoutExpress = input.TimeoutExpress
+ };
+ request.SetBizModel(model);
+
+ var response = _alipayClient.CertificateExecute(request);
+ if (response.IsError) throw Oops.Oh(response.SubMsg);
+ return response.QrCode;
+ }
+
+ ///
+ /// 单笔转账到支付宝账户
+ /// https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer
+ ///
+ [NonAction]
+ public Task Transfer(AlipayFundTransUniTransferInput input)
+ {
+ // 构造请求参数以调用接口
+ AlipayFundTransUniTransferRequest request = new();
+ AlipayFundTransUniTransferModel model = new();
+ model.BizScene = AlipayConst.BizScene;
+ model.ProductCode = AlipayConst.ProductCode;
+
+ // 设置商家侧唯一订单号
+ model.OutBizNo = input.OutBizNo;
+
+ // 设置订单总金额
+ model.TransAmount = input.TransAmount.ToString();
+
+ // 设置转账业务的标题
+ model.OrderTitle = input.OrderTitle;
+
+ // 设置收款方信息
+ Participant payeeInfo = new();
+ payeeInfo.CertType = input.CertType.ToString();
+ payeeInfo.CertNo = input.CertNo;
+ payeeInfo.Identity = input.Identity;
+ payeeInfo.Name = input.Name;
+ payeeInfo.IdentityType = input.IdentityType.ToString();
+ model.PayeeInfo = payeeInfo;
+
+ // 设置业务备注
+ model.Remark = input.Remark;
+
+ // 设置转账业务请求的扩展参数
+ string payerShowNameUseAlias = input.PayerShowNameUseAlias.ToString().ToLower();
+ model.BusinessParams = $"{{\"payer_show_name_use_alias\":\"{payerShowNameUseAlias}\"}}";
+
+ request.SetBizModel(model);
+ var response = _alipayClient.CertificateExecute(request);
+
+ try
+ {
+ File.AppendAllText($"{_webHostEnvironment.ContentRootPath}\\AlipayLog\\{DateTime.Today:yyyy-MM-dd}.txt",
+ $"支付宝付款到账户({DateTime.Now:yyyy-MM-dd HH:mm:ss}):{Environment.NewLine} " +
+ $"登录人:{_userManager.UserId}-{_userManager.RealName}{Environment.NewLine} " +
+ $"IP:{App.HttpContext?.GetRemoteIpAddressToIPv4(true)} {Environment.NewLine} " +
+ $"参数:{JSON.Serialize(model)}{Environment.NewLine} " +
+ $"返回:{JSON.Serialize(response)}{Environment.NewLine}" +
+ $"-----------------------------------------------------------------------------------------------------------------------" +
+ $"{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}");
+ }
+ catch (Exception ex)
+ {
+ Log.Error("单笔转账到支付宝账户日志写入失败:", ex);
+ }
+ return Task.FromResult(response);
+ }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs b/Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs
new file mode 100644
index 00000000..5c8ef4e7
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs
@@ -0,0 +1,169 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Aop.Api.Domain;
+using Newtonsoft.Json;
+
+namespace Admin.NET.Core.Service;
+
+public class AlipayFundTransUniTransferInput
+{
+ ///
+ /// 商家侧唯一订单号
+ ///
+ [Required(ErrorMessage = "订单号不能为空")]
+ public string OutBizNo { get; set; }
+
+ ///
+ /// 转账金额
+ ///
+ [Required(ErrorMessage = "转账金额不能为空")]
+ public decimal? TransAmount { get; set; }
+
+ ///
+ /// 转账业务标题
+ ///
+ [Required(ErrorMessage = "业务标题不能为空")]
+ public string OrderTitle { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Remark { get; set; }
+
+ ///
+ /// 是否展示付款方别名
+ ///
+ public bool PayerShowNameUseAlias { get; set; }
+
+ ///
+ /// 收款方证件类型
+ ///
+ public AlipayCertTypeEnum? CertType { get; set; }
+
+ ///
+ /// 收款方证件号码,条件必填
+ ///
+ [CommonValidation($"{nameof(CertType)} != null && string.IsNullOrWhiteSpace({nameof(CertNo)})", "", ErrorMessage = "证件号码不能为空")]
+ public string CertNo { get; set; }
+
+ ///
+ /// 收款方身份标识
+ ///
+ [Required(ErrorMessage = "身份标识不能为空")]
+ public string Identity { get; set; }
+
+ ///
+ /// 收款方真实姓名
+ ///
+ [Required(ErrorMessage = "真实姓名不能为空")]
+ public string Name { get; set; }
+
+ ///
+ /// 收款方身份标识类型
+ ///
+ [Required(ErrorMessage = "身份标识类型不能为空")]
+ public AlipayIdentityTypeEnum? IdentityType { get; set; }
+}
+
+///
+/// 统一收单下单并支付页面接口输入参数
+///
+public class AlipayTradePagePayInput
+{
+ ///
+ /// 商户订单号
+ ///
+ [Required(ErrorMessage = "商户订单号不能为空")]
+ public string OutTradeNo { get; set; }
+
+ ///
+ /// 订单总金额
+ ///
+ [Required(ErrorMessage = "订单总金额不能为空")]
+ public string TotalAmount { get; set; }
+
+ ///
+ /// 订单标题
+ ///
+ [Required(ErrorMessage = "订单标题不能为空")]
+ public string Subject { get; set; }
+
+ ///
+ ///
+ ///
+ public string Body { get; set; }
+
+ ///
+ /// 超时时间
+ ///
+ public string TimeoutExpress { get; set; }
+
+ ///
+ /// 二维码宽度
+ ///
+ [Required(ErrorMessage = "二维码宽度不能为空")]
+ public int? QrcodeWidth { get; set; }
+
+ ///
+ /// 业务参数
+ ///
+ public ExtendParams ExtendParams { get; set; }
+
+ ///
+ /// 商户业务数据
+ ///
+ public Dictionary BusinessParams { get; set; }
+
+ ///
+ /// 开票信息
+ ///
+ public InvoiceInfo InvoiceInfo { get; set; }
+
+ ///
+ /// 外部买家信息
+ ///
+ public ExtUserInfo ExtUserInfo { get; set; }
+}
+
+public class AlipayPreCreateInput
+{
+ ///
+ /// 商户订单号
+ ///
+ [Required(ErrorMessage = "商户订单号不能为空")]
+ public string OutTradeNo { get; set; }
+
+ ///
+ /// 订单总金额
+ ///
+ [Required(ErrorMessage = "订单总金额不能为空")]
+ public string TotalAmount { get; set; }
+
+ ///
+ /// 订单标题
+ ///
+ [Required(ErrorMessage = "订单标题不能为空")]
+ public string Subject { get; set; }
+
+ ///
+ /// 超时时间
+ ///
+ public string TimeoutExpress { get; set; }
+}
+
+public class AlipayAuthInfoInput
+{
+ ///
+ /// 用户Id
+ ///
+ public string UserId { get; set; }
+
+ ///
+ /// 授权码
+ ///
+ public string AuthCode { get; set; }
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Service/Alipay/IAlipayNotify.cs b/Admin.NET/Admin.NET.Core/Service/Alipay/IAlipayNotify.cs
new file mode 100644
index 00000000..cd1e557a
--- /dev/null
+++ b/Admin.NET/Admin.NET.Core/Service/Alipay/IAlipayNotify.cs
@@ -0,0 +1,31 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+//
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+//
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using Aop.Api.Response;
+
+namespace Admin.NET.Core.Service.Alipay;
+
+///
+/// 支付宝回调接口
+///
+public abstract class IAlipayNotify
+{
+ ///
+ /// 充值回调方法
+ ///
+ /// 交易类型
+ /// 交易id
+ public abstract bool TopUpCallback(long type, long tradeNo);
+
+ ///
+ /// 扫码回调
+ ///
+ ///
+ ///
+ ///
+ ///
+ public abstract bool ScanCallback(long type, long userId, AlipayUserInfoShareResponse response);
+}
\ No newline at end of file
diff --git a/Admin.NET/Admin.NET.Core/Utils/AdminResultProvider.cs b/Admin.NET/Admin.NET.Core/Utils/AdminResultProvider.cs
index 0a555b82..5e6f06a7 100644
--- a/Admin.NET/Admin.NET.Core/Utils/AdminResultProvider.cs
+++ b/Admin.NET/Admin.NET.Core/Utils/AdminResultProvider.cs
@@ -20,7 +20,7 @@ public class AdminResultProvider : IUnifyResultProvider
///
public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata)
{
- return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors), UnifyContext.GetSerializerSettings(context));
+ return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, msg: metadata.Errors), UnifyContext.GetSerializerSettings(context));
}
///
@@ -31,7 +31,7 @@ public class AdminResultProvider : IUnifyResultProvider
///
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
- return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors), UnifyContext.GetSerializerSettings(context));
+ return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, msg: metadata.Errors), UnifyContext.GetSerializerSettings(context));
}
///
@@ -53,7 +53,7 @@ public class AdminResultProvider : IUnifyResultProvider
///
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
- return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult), UnifyContext.GetSerializerSettings(context));
+ return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, msg: metadata.ValidationResult), UnifyContext.GetSerializerSettings(context));
}
///
@@ -76,12 +76,12 @@ public class AdminResultProvider : IUnifyResultProvider
// 若存在身份验证失败消息,则返回消息内容
if (context.Items.TryGetValue(SignatureAuthenticationDefaults.AuthenticateFailMsgKey, out var authFailMsg))
msg = authFailMsg + "";
- await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: msg),
+ await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, msg: msg),
App.GetOptions()?.JsonSerializerOptions);
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
- await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 禁止访问,没有权限"),
+ await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, msg: "403 禁止访问,没有权限"),
App.GetOptions()?.JsonSerializerOptions);
break;
// 处理 302 状态码
@@ -93,7 +93,7 @@ public class AdminResultProvider : IUnifyResultProvider
else
{
var errorMessage = "302 跳转失败,没有提供 Location 头信息";
- await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: errorMessage),
+ await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, msg: errorMessage),
App.GetOptions()?.JsonSerializerOptions);
}
break;
@@ -108,9 +108,9 @@ public class AdminResultProvider : IUnifyResultProvider
///
///
///
- ///
+ ///
///
- private static AdminResult