From 681b8adb2fc6cbd6e236a8922353bbc79329e9c8 Mon Sep 17 00:00:00 2001 From: zuohuaijun Date: Sun, 29 Dec 2024 23:53:58 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=8E=E5=A2=9E=E5=8A=A0=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=AE=9D=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Configuration/Alipay.json | 18 + .../Admin.NET.Core/Admin.NET.Core.csproj | 5 +- .../Attribute/CommonValidationAttribute.cs | 92 +++++ Admin.NET/Admin.NET.Core/Const/AlipayConst.cs | 39 ++ Admin.NET/Admin.NET.Core/Const/ConfigConst.cs | 5 + .../Admin.NET.Core/Enum/AlipayCertTypeEnum.cs | 21 + .../Enum/AlipayIdentityTypeEnum.cs | 21 + .../Extension/ObjectExtension.cs | 14 + .../Admin.NET.Core/Option/AlipayOptions.cs | 68 +++ .../Service/Alipay/AlipayErrorCodes.cs | 143 +++++++ .../Service/Alipay/AlipayService.cs | 273 ++++++++++++ .../Service/Alipay/Dto/AlipayInput.cs | 169 ++++++++ .../Service/Alipay/IAlipayNotify.cs | 31 ++ .../Utils/AdminResultProvider.cs | 18 +- Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs | 17 + Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs | 21 +- .../Admin.NET.Web.Core/ProjectOptions.cs | 1 + Web/src/api-services/api.ts | 1 + Web/src/api-services/apis/alipay-api.ts | 389 ++++++++++++++++++ .../models/alipay-pre-create-input.ts | 54 +++ .../models/alipay-trade-page-pay-input.ts | 97 +++++ Web/src/api-services/models/ext-user-info.ts | 70 ++++ Web/src/api-services/models/extend-params.ts | 82 ++++ Web/src/api-services/models/index.ts | 6 + Web/src/api-services/models/invoice-info.ts | 35 ++ .../api-services/models/invoice-key-info.ts | 40 ++ 26 files changed, 1711 insertions(+), 19 deletions(-) create mode 100644 Admin.NET/Admin.NET.Application/Configuration/Alipay.json create mode 100644 Admin.NET/Admin.NET.Core/Attribute/CommonValidationAttribute.cs create mode 100644 Admin.NET/Admin.NET.Core/Const/AlipayConst.cs create mode 100644 Admin.NET/Admin.NET.Core/Enum/AlipayCertTypeEnum.cs create mode 100644 Admin.NET/Admin.NET.Core/Enum/AlipayIdentityTypeEnum.cs create mode 100644 Admin.NET/Admin.NET.Core/Option/AlipayOptions.cs create mode 100644 Admin.NET/Admin.NET.Core/Service/Alipay/AlipayErrorCodes.cs create mode 100644 Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs create mode 100644 Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs create mode 100644 Admin.NET/Admin.NET.Core/Service/Alipay/IAlipayNotify.cs create mode 100644 Web/src/api-services/apis/alipay-api.ts create mode 100644 Web/src/api-services/models/alipay-pre-create-input.ts create mode 100644 Web/src/api-services/models/alipay-trade-page-pay-input.ts create mode 100644 Web/src/api-services/models/ext-user-info.ts create mode 100644 Web/src/api-services/models/extend-params.ts create mode 100644 Web/src/api-services/models/invoice-info.ts create mode 100644 Web/src/api-services/models/invoice-key-info.ts 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 RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default) + private static AdminResult RESTfulResult(int statusCode, bool succeeded = default, object data = default, object msg = default) { //// 统一返回值脱敏处理 //if (data?.GetType() == typeof(String)) @@ -125,7 +125,7 @@ public class AdminResultProvider : IUnifyResultProvider return new AdminResult { Code = statusCode, - Message = errors is null or string ? (errors + "") : JSON.Serialize(errors), + Message = msg is null or string ? (msg + "") : JSON.Serialize(msg), Result = data, Type = succeeded ? "success" : "error", Extras = UnifyContext.Take(), diff --git a/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs b/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs index 24b27f0f..bc5f3dbd 100644 --- a/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs +++ b/Admin.NET/Admin.NET.Core/Utils/CommonUtil.cs @@ -81,6 +81,23 @@ public static class CommonUtil return result; } + /// + /// 获取请求地址源 + /// + /// + /// + public static string GetOrigin(this HttpRequest request) + { + string scheme = request.Scheme; + string host = request.Host.Host; + int port = request.Host.Port ?? (-1); + + string url = $"{scheme}://{host}"; + if (port != 80 && port != 443 && port != -1) url += $":{port}"; + + return url; + } + /// /// 对象序列化XML /// diff --git a/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs b/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs index 6d93ee09..f1f78e40 100644 --- a/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs +++ b/Admin.NET/Admin.NET.Core/Utils/ExcelHelper.cs @@ -21,6 +21,7 @@ public class ExcelHelper try { var result = CommonUtil.ImportExcelDataAsync(file).Result ?? throw Oops.Oh("有效数据为空"); + result.ForEach(u => u.Id = YitIdHelper.NextId()); var tasks = new List(); action.Invoke(result, (storageable, pageItems, rows) => @@ -28,12 +29,14 @@ public class ExcelHelper // 标记校验信息 tasks.Add(Task.Run(() => { - if (storageable.TotalList.Any()) - { - for (int i = 0; i < rows.Count; i++) pageItems[i].Id = rows[i].Id; + if (!storageable.TotalList.Any()) return; - for (int i = 0; i < storageable.TotalList.Count; i++) - pageItems[i].Error ??= storageable.TotalList[i].StorageMessage; + // 通过Id标记校验信息 + var itemMap = pageItems.ToDictionary(u => u.Id, u => u); + foreach (var item in storageable.TotalList) + { + var temp = itemMap.GetValueOrDefault(item.Item.Id); + if (temp != null) temp.Error ??= item.StorageMessage; } })); }); @@ -41,7 +44,9 @@ public class ExcelHelper // 等待所有标记验证信息任务完成 Task.WhenAll(tasks).GetAwaiter().GetResult(); - return ExportData(result); + // 仅导出错误记录 + var errorList = result.Where(u => !string.IsNullOrWhiteSpace(u.Error)); + return ExportData(errorList.Any() ? errorList : new List()); } catch (Exception ex) { @@ -59,7 +64,7 @@ public class ExcelHelper } /// - /// 导出xlsx数据 + /// 导出Xlsx数据 /// /// /// @@ -72,7 +77,7 @@ public class ExcelHelper } /// - /// 根据类型导出xlsx模板 + /// 根据类型导出Xlsx模板 /// /// /// diff --git a/Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs b/Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs index d0fb0b53..0b47ed08 100644 --- a/Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs +++ b/Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs @@ -37,6 +37,7 @@ public static class ProjectOptions services.AddConfigurableOptions(); services.AddConfigurableOptions(); services.AddConfigurableOptions(); + services.AddConfigurableOptions(); services.Configure(App.Configuration.GetSection("IpRateLimiting")); services.Configure(App.Configuration.GetSection("IpRateLimitPolicies")); services.Configure(App.Configuration.GetSection("ClientRateLimiting")); diff --git a/Web/src/api-services/api.ts b/Web/src/api-services/api.ts index cb413362..26793340 100644 --- a/Web/src/api-services/api.ts +++ b/Web/src/api-services/api.ts @@ -12,6 +12,7 @@ * Do not edit the class manually. */ export * from './apis/apijsonapi'; +export * from './apis/alipay-api'; export * from './apis/sys-app-api'; export * from './apis/sys-auth-api'; export * from './apis/sys-cache-api'; diff --git a/Web/src/api-services/apis/alipay-api.ts b/Web/src/api-services/apis/alipay-api.ts new file mode 100644 index 00000000..95be4dbc --- /dev/null +++ b/Web/src/api-services/apis/alipay-api.ts @@ -0,0 +1,389 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import globalAxios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios'; +import { Configuration } from '../configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base'; +import { AdminResultString } from '../models'; +import { AlipayPreCreateInput } from '../models'; +import { AlipayTradePagePayInput } from '../models'; +/** + * AlipayApi - axios parameter creator + * @export + */ +export const AlipayApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary 交易预创建 🔖 + * @param {AlipayPreCreateInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiAlipayAlipayPreCreatePost: async (body?: AlipayPreCreateInput, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/alipay/alipayPreCreate`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + localVarHeaderParameter['Content-Type'] = 'application/json-patch+json'; + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json'; + localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || ""); + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + /** + * + * @summary 统一收单下单并支付页面接口 🔖 + * @param {AlipayTradePagePayInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiAlipayAlipayTradePagePayPost: async (body?: AlipayTradePagePayInput, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/alipay/alipayTradePagePay`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + localVarHeaderParameter['Content-Type'] = 'application/json-patch+json'; + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json'; + localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || ""); + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + /** + * + * @summary 获取授权信息 🔖 + * @param {string} [userId] 用户Id + * @param {string} [authCode] 授权码 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiAlipayGetAuthInfoGet: async (userId?: string, authCode?: string, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/alipay/getAuthInfo`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + if (userId !== undefined) { + localVarQueryParameter['UserId'] = userId; + } + + if (authCode !== undefined) { + localVarQueryParameter['AuthCode'] = authCode; + } + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + /** + * + * @summary 支付回调 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + apiAlipayNotifyPost: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/alipay/notify`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com'); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication Bearer required + // http bearer authentication required + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + localVarHeaderParameter["Authorization"] = "Bearer " + accessToken; + } + + const query = new URLSearchParams(localVarUrlObj.search); + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]); + } + for (const key in options.params) { + query.set(key, options.params[key]); + } + localVarUrlObj.search = (new URLSearchParams(query)).toString(); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * AlipayApi - functional programming interface + * @export + */ +export const AlipayApiFp = function(configuration?: Configuration) { + return { + /** + * + * @summary 交易预创建 🔖 + * @param {AlipayPreCreateInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayAlipayPreCreatePost(body?: AlipayPreCreateInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await AlipayApiAxiosParamCreator(configuration).apiAlipayAlipayPreCreatePost(body, options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + /** + * + * @summary 统一收单下单并支付页面接口 🔖 + * @param {AlipayTradePagePayInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayAlipayTradePagePayPost(body?: AlipayTradePagePayInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await AlipayApiAxiosParamCreator(configuration).apiAlipayAlipayTradePagePayPost(body, options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + /** + * + * @summary 获取授权信息 🔖 + * @param {string} [userId] 用户Id + * @param {string} [authCode] 授权码 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayGetAuthInfoGet(userId?: string, authCode?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await AlipayApiAxiosParamCreator(configuration).apiAlipayGetAuthInfoGet(userId, authCode, options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + /** + * + * @summary 支付回调 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayNotifyPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise>> { + const localVarAxiosArgs = await AlipayApiAxiosParamCreator(configuration).apiAlipayNotifyPost(options); + return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url}; + return axios.request(axiosRequestArgs); + }; + }, + } +}; + +/** + * AlipayApi - factory interface + * @export + */ +export const AlipayApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + return { + /** + * + * @summary 交易预创建 🔖 + * @param {AlipayPreCreateInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayAlipayPreCreatePost(body?: AlipayPreCreateInput, options?: AxiosRequestConfig): Promise> { + return AlipayApiFp(configuration).apiAlipayAlipayPreCreatePost(body, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary 统一收单下单并支付页面接口 🔖 + * @param {AlipayTradePagePayInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayAlipayTradePagePayPost(body?: AlipayTradePagePayInput, options?: AxiosRequestConfig): Promise> { + return AlipayApiFp(configuration).apiAlipayAlipayTradePagePayPost(body, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary 获取授权信息 🔖 + * @param {string} [userId] 用户Id + * @param {string} [authCode] 授权码 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayGetAuthInfoGet(userId?: string, authCode?: string, options?: AxiosRequestConfig): Promise> { + return AlipayApiFp(configuration).apiAlipayGetAuthInfoGet(userId, authCode, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary 支付回调 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async apiAlipayNotifyPost(options?: AxiosRequestConfig): Promise> { + return AlipayApiFp(configuration).apiAlipayNotifyPost(options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * AlipayApi - object-oriented interface + * @export + * @class AlipayApi + * @extends {BaseAPI} + */ +export class AlipayApi extends BaseAPI { + /** + * + * @summary 交易预创建 🔖 + * @param {AlipayPreCreateInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AlipayApi + */ + public async apiAlipayAlipayPreCreatePost(body?: AlipayPreCreateInput, options?: AxiosRequestConfig) : Promise> { + return AlipayApiFp(this.configuration).apiAlipayAlipayPreCreatePost(body, options).then((request) => request(this.axios, this.basePath)); + } + /** + * + * @summary 统一收单下单并支付页面接口 🔖 + * @param {AlipayTradePagePayInput} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AlipayApi + */ + public async apiAlipayAlipayTradePagePayPost(body?: AlipayTradePagePayInput, options?: AxiosRequestConfig) : Promise> { + return AlipayApiFp(this.configuration).apiAlipayAlipayTradePagePayPost(body, options).then((request) => request(this.axios, this.basePath)); + } + /** + * + * @summary 获取授权信息 🔖 + * @param {string} [userId] 用户Id + * @param {string} [authCode] 授权码 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AlipayApi + */ + public async apiAlipayGetAuthInfoGet(userId?: string, authCode?: string, options?: AxiosRequestConfig) : Promise> { + return AlipayApiFp(this.configuration).apiAlipayGetAuthInfoGet(userId, authCode, options).then((request) => request(this.axios, this.basePath)); + } + /** + * + * @summary 支付回调 🔖 + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AlipayApi + */ + public async apiAlipayNotifyPost(options?: AxiosRequestConfig) : Promise> { + return AlipayApiFp(this.configuration).apiAlipayNotifyPost(options).then((request) => request(this.axios, this.basePath)); + } +} diff --git a/Web/src/api-services/models/alipay-pre-create-input.ts b/Web/src/api-services/models/alipay-pre-create-input.ts new file mode 100644 index 00000000..81162168 --- /dev/null +++ b/Web/src/api-services/models/alipay-pre-create-input.ts @@ -0,0 +1,54 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + /** + * + * + * @export + * @interface AlipayPreCreateInput + */ +export interface AlipayPreCreateInput { + + /** + * 商户订单号 + * + * @type {string} + * @memberof AlipayPreCreateInput + */ + outTradeNo: string; + + /** + * 订单总金额 + * + * @type {string} + * @memberof AlipayPreCreateInput + */ + totalAmount: string; + + /** + * 订单标题 + * + * @type {string} + * @memberof AlipayPreCreateInput + */ + subject: string; + + /** + * 超时时间 + * + * @type {string} + * @memberof AlipayPreCreateInput + */ + timeoutExpress?: string | null; +} diff --git a/Web/src/api-services/models/alipay-trade-page-pay-input.ts b/Web/src/api-services/models/alipay-trade-page-pay-input.ts new file mode 100644 index 00000000..c03dab09 --- /dev/null +++ b/Web/src/api-services/models/alipay-trade-page-pay-input.ts @@ -0,0 +1,97 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import { ExtUserInfo } from './ext-user-info'; +import { ExtendParams } from './extend-params'; +import { InvoiceInfo } from './invoice-info'; + /** + * 统一收单下单并支付页面接口输入参数 + * + * @export + * @interface AlipayTradePagePayInput + */ +export interface AlipayTradePagePayInput { + + /** + * 商户订单号 + * + * @type {string} + * @memberof AlipayTradePagePayInput + */ + outTradeNo: string; + + /** + * 订单总金额 + * + * @type {string} + * @memberof AlipayTradePagePayInput + */ + totalAmount: string; + + /** + * 订单标题 + * + * @type {string} + * @memberof AlipayTradePagePayInput + */ + subject: string; + + /** + * @type {string} + * @memberof AlipayTradePagePayInput + */ + body?: string | null; + + /** + * 超时时间 + * + * @type {string} + * @memberof AlipayTradePagePayInput + */ + timeoutExpress?: string | null; + + /** + * 二维码宽度 + * + * @type {number} + * @memberof AlipayTradePagePayInput + */ + qrcodeWidth: number; + + /** + * @type {ExtendParams} + * @memberof AlipayTradePagePayInput + */ + extendParams?: ExtendParams; + + /** + * 商户业务数据 + * + * @type {{ [key: string]: any; }} + * @memberof AlipayTradePagePayInput + */ + businessParams?: { [key: string]: any; } | null; + + /** + * @type {InvoiceInfo} + * @memberof AlipayTradePagePayInput + */ + invoiceInfo?: InvoiceInfo; + + /** + * @type {ExtUserInfo} + * @memberof AlipayTradePagePayInput + */ + extUserInfo?: ExtUserInfo; +} diff --git a/Web/src/api-services/models/ext-user-info.ts b/Web/src/api-services/models/ext-user-info.ts new file mode 100644 index 00000000..61ac1f1f --- /dev/null +++ b/Web/src/api-services/models/ext-user-info.ts @@ -0,0 +1,70 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + /** + * + * + * @export + * @interface ExtUserInfo + */ +export interface ExtUserInfo { + + /** + * @type {string} + * @memberof ExtUserInfo + */ + certNo?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + certType?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + fixBuyer?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + identityHash?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + minAge?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + mobile?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + name?: string | null; + + /** + * @type {string} + * @memberof ExtUserInfo + */ + needCheckInfo?: string | null; +} diff --git a/Web/src/api-services/models/extend-params.ts b/Web/src/api-services/models/extend-params.ts new file mode 100644 index 00000000..4d1d9d2a --- /dev/null +++ b/Web/src/api-services/models/extend-params.ts @@ -0,0 +1,82 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + /** + * + * + * @export + * @interface ExtendParams + */ +export interface ExtendParams { + + /** + * @type {string} + * @memberof ExtendParams + */ + cardType?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + creditExtInfo?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + hbFqNum?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + hbFqSellerPercent?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + industryRefluxInfo?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + royaltyFreeze?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + specifiedSellerName?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + sysServiceProviderId?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + tcInstallmentOrderId?: string | null; + + /** + * @type {string} + * @memberof ExtendParams + */ + tradeComponentOrderId?: string | null; +} diff --git a/Web/src/api-services/models/index.ts b/Web/src/api-services/models/index.ts index 62a9721d..f9677c08 100644 --- a/Web/src/api-services/models/index.ts +++ b/Web/src/api-services/models/index.ts @@ -128,6 +128,8 @@ export * from './admin-result-visual-db-table'; export * from './admin-result-wechat-pay-output'; export * from './admin-result-wx-open-id-output'; export * from './admin-result-wx-phone-output'; +export * from './alipay-pre-create-input'; +export * from './alipay-trade-page-pay-input'; export * from './aliyun-send-sms-template-input'; export * from './amount'; export * from './api-output'; @@ -212,6 +214,8 @@ export * from './event-attributes'; export * from './event-info'; export * from './export-proc-by-tmpinput'; export * from './export-proc-input'; +export * from './ext-user-info'; +export * from './extend-params'; export * from './field-attributes'; export * from './field-info'; export * from './filter'; @@ -238,6 +242,8 @@ export * from './idisposable'; export * from './isite'; export * from './info-save-input'; export * from './int-ptr'; +export * from './invoice-info'; +export * from './invoice-key-info'; export * from './jtoken'; export * from './job-create-type-enum'; export * from './job-detail-input'; diff --git a/Web/src/api-services/models/invoice-info.ts b/Web/src/api-services/models/invoice-info.ts new file mode 100644 index 00000000..be432a06 --- /dev/null +++ b/Web/src/api-services/models/invoice-info.ts @@ -0,0 +1,35 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import { InvoiceKeyInfo } from './invoice-key-info'; + /** + * + * + * @export + * @interface InvoiceInfo + */ +export interface InvoiceInfo { + + /** + * @type {string} + * @memberof InvoiceInfo + */ + details?: string | null; + + /** + * @type {InvoiceKeyInfo} + * @memberof InvoiceInfo + */ + keyInfo?: InvoiceKeyInfo; +} diff --git a/Web/src/api-services/models/invoice-key-info.ts b/Web/src/api-services/models/invoice-key-info.ts new file mode 100644 index 00000000..dc412144 --- /dev/null +++ b/Web/src/api-services/models/invoice-key-info.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Admin.NET 通用权限开发平台 + * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。
👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + /** + * + * + * @export + * @interface InvoiceKeyInfo + */ +export interface InvoiceKeyInfo { + + /** + * @type {string} + * @memberof InvoiceKeyInfo + */ + invoiceMerchantName?: string | null; + + /** + * @type {boolean} + * @memberof InvoiceKeyInfo + */ + isSupportInvoice?: boolean; + + /** + * @type {string} + * @memberof InvoiceKeyInfo + */ + taxNum?: string | null; +}