Эх сурвалжийг харах

feat: 😀接入支付宝支付API

喵你个旺呀 1 жил өмнө
parent
commit
653f056c4c

+ 17 - 0
Admin.NET/Admin.NET.Application/Configuration/Alipay.json

@@ -0,0 +1,17 @@
+{
+  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
+
+  "Alipay": { // 支付宝支付配置,文档地址:https://openhome.alipay.com/develop/sandbox/app
+    "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" // 应用公钥证书存放路径
+  }
+}

+ 2 - 0
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -14,6 +14,7 @@
 
   <ItemGroup>
     <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="3.1.0" />
+    <PackageReference Include="AlipaySDKNet" Version="4.9.370" />
     <PackageReference Include="AngleSharp" Version="1.1.2" />
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
     <PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
@@ -46,6 +47,7 @@
     <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1146" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
+    <PackageReference Include="BouncyCastle.Cryptography" Version="2.5.0" Aliases="BouncyCastleV2" />
   </ItemGroup>
 	
   <ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' ">

+ 39 - 0
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;
+
+/// <summary>
+/// 支付宝支付常量
+/// </summary>
+[SuppressSniffer]
+public class AlipayConst
+{
+    /// <summary>
+    /// 单笔无密转账【业务场景】固定值
+    /// </summary>
+    public const string BizScene = "DIRECT_TRANSFER";
+
+    /// <summary>
+    /// 单笔无密转账【销售产品码】固定值
+    /// </summary>
+    public const string ProductCode = "TRANS_ACCOUNT_NO_PWD";
+
+    /// <summary>
+    /// 交易状态参数名
+    /// </summary>
+    public const string TradeStatus = "trade_status";
+
+    /// <summary>
+    /// 交易成功标识
+    /// </summary>
+    public const string TradeSuccess = "TRADE_SUCCESS";
+
+    /// <summary>
+    /// 授权类型
+    /// </summary>
+    public const string GrantType = "authorization_code";
+}

+ 5 - 0
Admin.NET/Admin.NET.Core/Const/ConfigConst.cs

@@ -101,6 +101,11 @@ public class ConfigConst
     /// </summary>
     public const string SysWebConfigGroup = "WebConfig";
 
+    /// <summary>
+    /// 支付宝授权页面地址
+    /// </summary>
+    public const string AlipayAuthPageUrl = "alipay_auth_page_url_";
+
     // /// <summary>
     // /// 系统图标
     // /// </summary>

+ 21 - 0
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;
+
+/// <summary>
+/// 参与方的证件类型枚举
+/// </summary>
+[SuppressSniffer]
+[Description("参与方的证件类型枚举")]
+public enum AlipayCertTypeEnum
+{
+    [Description("身份证")]
+    IDENTITY_CARD,
+
+    [Description("护照")]
+    PASSPORT
+}

+ 21 - 0
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;
+
+/// <summary>
+/// 参与方的标识类型枚举
+/// </summary>
+[SuppressSniffer]
+[Description("参与方的标识类型枚举")]
+public enum AlipayIdentityTypeEnum
+{
+    [Description("支付宝用户UID")]
+    ALIPAY_USER_ID,
+
+    [Description("支付宝登录号")]
+    ALIPAY_LOGON_ID
+}

+ 27 - 0
Admin.NET/Admin.NET.Core/Extension/RequestExtension.cs

@@ -0,0 +1,27 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// 
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+// 
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+namespace Admin.NET.Core;
+
+public static class RequestExtension
+{
+    /// <summary>
+    /// 获取请求地址源
+    /// </summary>
+    /// <param name="request"></param>
+    /// <returns></returns>
+    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;
+    }
+}

+ 68 - 0
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;
+
+/// <summary>
+/// 支付宝支付配置选项
+/// </summary>
+public sealed class AlipayOptions : IConfigurableOptions
+{
+    /// <summary>
+    /// 支付宝 APPID(必填)
+    /// </summary>
+    public string AppId { get; set; }
+
+    /// <summary>
+    /// 支付宝 websocket 服务地址 (必填)
+    /// </summary>
+    public string AlipayWebsocketUrl { get; set; }
+
+    /// <summary>
+    /// 支付宝网关地址 (必填)
+    /// </summary>
+    public string ServerUrl { get; set; }
+
+    /// <summary>
+    /// 支付宝授权回调地址 (必填)
+    /// </summary>
+    public string AuthUrl { get; set; }
+
+    /// <summary>
+    /// 应用回调地址
+    /// </summary>
+    public string NotifyUrl { get; set; }
+
+    /// <summary>
+    /// 加密算法(必填)
+    /// </summary>
+    public string SignType { get; set; }
+
+    /// <summary>
+    /// 从支付宝获取敏感信息时的加密密钥(可选)
+    /// </summary>
+    public string EncryptKey { get; set; }
+
+    /// <summary>
+    /// 应用私钥 (必填)
+    /// </summary>
+    public string PrivateKey { get; set; }
+
+    /// <summary>
+    /// 支付宝公钥证书存放路径(证书加签方式必填)
+    /// </summary>
+    public string AlipayPublicCertPath { get; set; }
+
+    /// <summary>
+    /// 支付宝根证书存放路径(证书加签方式必填)
+    /// </summary>
+    public string RootCertPath { get; set; }
+
+    /// <summary>
+    /// 应用公钥证书存放路径(证书加签方式必填)
+    /// </summary>
+    public string AppCertPath { get; set; }
+}

+ 143 - 0
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;
+
+/// <summary>
+/// 支付宝支付错误码
+/// </summary>
+public class AlipayErrorCode
+{
+    /// <summary>
+    /// 错误代码
+    /// </summary>
+    public string Code { get; private set; }
+
+    /// <summary>
+    /// 错误消息
+    /// </summary>
+    public string Message { get; private set; }
+
+    /// <summary>
+    /// 解决方案
+    /// </summary>
+    public string Solution { get; private set; }
+
+    /// <summary>
+    /// 错误码集
+    /// </summary>
+    private static readonly List<AlipayErrorCode> 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" }
+    };
+
+    /// <summary>
+    /// 根据错误码获取错误信息
+    /// </summary>
+    /// <param name="code"></param>
+    /// <returns></returns>
+    public static AlipayErrorCode Get(string code)
+    {
+        return StatusCodes.FirstOrDefault(u => u.Code.EqualIgnoreCase(code));
+    }
+}

+ 274 - 0
Admin.NET/Admin.NET.Core/Service/Alipay/AlipayService.cs

@@ -0,0 +1,274 @@
+// 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;
+// ReSharper disable All
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统登录授权服务 🧩
+/// </summary>
+[ApiDescriptionSettings(Order = 500)]
+public class AlipayService : IDynamicApiController, ITransient
+{
+    private readonly IWebHostEnvironment _webHostEnvironment;
+    private readonly SysConfigService _sysConfigService;
+    private readonly IHttpContextAccessor _httpContext;
+    private readonly AlipayOptions _alipayOptions;
+    private readonly IAopClient _alipayClient;
+    private readonly UserManager _userManager;
+
+    public AlipayService(
+        UserManager userManager,
+        SysConfigService sysConfigService,
+        IHttpContextAccessor httpContext,
+        IWebHostEnvironment webHostEnvironment,
+        IOptions<AlipayOptions> alipayOptions)
+    {
+        _userManager = userManager;
+        _httpContext = httpContext;
+        _sysConfigService = sysConfigService;
+        _alipayOptions = alipayOptions.Value;
+        _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)
+        });
+    }
+
+    /// <summary>
+    /// 获取授权信息
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [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<IAlipayNotify>())
+            if (notify.ScanCallback(type, userId, info)) pass = true;
+        if (!pass) throw Oops.Oh("未处理的授权逻辑");
+
+        // 执行完,重定向到指定界面
+        var authPageUrl = _sysConfigService.GetConfigValue<string>(ConfigConst.AlipayAuthPageUrl + type).Result;
+        return new RedirectResult(authPageUrl);
+    }
+
+    /// <summary>
+    /// 支付回调
+    /// </summary>
+    /// <returns></returns>
+    [AllowAnonymous]
+    [DisplayName("支付回调")]
+    [ApiDescriptionSettings(Name = "Notify"), HttpPost]
+    public string Notify()
+    {
+        SortedDictionary<string, string> 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<IAlipayNotify>())
+                if (notify.TopUpCallback(type, tradeNo)) isError = false;
+            if (isError) throw Oops.Oh("交易失败");
+        }
+        return "success";
+    }
+
+    /// <summary>
+    ///  统一收单下单并支付页面接口
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [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}";
+    }
+
+    /// <summary>
+    ///  交易预创建
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [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;
+    }
+
+    /// <summary>
+    /// 单笔转账到支付宝账户
+    ///  https://opendocs.alipay.com/open/62987723_alipay.fund.trans.uni.transfer
+    /// </summary>
+    public Task<AlipayFundTransUniTransferResponse> 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);
+    }
+}

+ 170 - 0
Admin.NET/Admin.NET.Core/Service/Alipay/Dto/AlipayInput.cs

@@ -0,0 +1,170 @@
+// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
+// 
+// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
+// 
+// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+
+using System.Text.Json.Serialization;
+using Aop.Api.Domain;
+using Newtonsoft.Json;
+
+namespace Admin.NET.Core.Service;
+
+public class AlipayFundTransUniTransferInput
+{
+    /// <summary>
+    /// 商家侧唯一订单号
+    /// </summary>
+    [Required(ErrorMessage = "订单号不能为空")]
+    public string OutBizNo { get; set; }
+
+    /// <summary>
+    /// 转账金额
+    /// </summary>
+    [Required(ErrorMessage = "转账金额不能为空")]
+    public decimal? TransAmount { get; set; }
+
+    /// <summary>
+    /// 转账业务标题
+    /// </summary>
+    [Required(ErrorMessage = "业务标题不能为空")]
+    public string OrderTitle { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
+
+    /// <summary>
+    /// 是否展示付款方别名
+    /// </summary>
+    public bool PayerShowNameUseAlias { get; set; }
+
+    /// <summary>
+    /// 收款方证件类型
+    /// </summary>
+    public AlipayCertTypeEnum? CertType { get; set; }
+
+    /// <summary>
+    /// 收款方证件号码,条件必填
+    /// </summary>
+    [CommonValidation($"{nameof(CertType)} != null && string.IsNullOrWhiteSpace({nameof(CertNo)})", "", ErrorMessage = "证件号码不能为空")]
+    public string CertNo { get; set; }
+
+    /// <summary>
+    /// 收款方身份标识
+    /// </summary>
+    [Required(ErrorMessage = "身份标识不能为空")]
+    public string Identity { get; set; }
+
+    /// <summary>
+    /// 收款方真实姓名
+    /// </summary>
+    [Required(ErrorMessage = "真实姓名不能为空")]
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 收款方身份标识类型
+    /// </summary>
+    [Required(ErrorMessage = "身份标识类型不能为空")]
+    public AlipayIdentityTypeEnum? IdentityType { get; set; }
+}
+
+/// <summary>
+///  统一收单下单并支付页面接口输入参数
+/// </summary>
+public class AlipayTradePagePayInput
+{
+    /// <summary>
+    /// 商户订单号
+    /// </summary>
+    [Required(ErrorMessage = "商户订单号不能为空")]
+    public string OutTradeNo { get; set; }
+
+    /// <summary>
+    /// 订单总金额
+    /// </summary>
+    [Required(ErrorMessage = "订单总金额不能为空")]
+    public string TotalAmount { get; set; }
+
+    /// <summary>
+    /// 订单标题
+    /// </summary>
+    [Required(ErrorMessage = "订单标题不能为空")]
+    public string Subject { get; set; }
+
+    /// <summary>
+    ///
+    /// </summary>
+    public string Body { get; set; }
+
+    /// <summary>
+    /// 超时时间
+    /// </summary>
+    public string TimeoutExpress { get; set; }
+
+    /// <summary>
+    /// 二维码宽度
+    /// </summary>
+    [Required(ErrorMessage = "二维码宽度不能为空")]
+    public int? QrcodeWidth { get; set; }
+
+    /// <summary>
+    /// 业务参数
+    /// </summary>
+    public ExtendParams ExtendParams { get; set; }
+
+    /// <summary>
+    /// 商户业务数据
+    /// </summary>
+    public Dictionary<string, object> BusinessParams { get; set; }
+
+    /// <summary>
+    /// 开票信息
+    /// </summary>
+    public InvoiceInfo InvoiceInfo { get; set; }
+
+    /// <summary>
+    /// 外部买家信息
+    /// </summary>
+    public ExtUserInfo ExtUserInfo { get; set; }
+}
+
+public class AlipayPreCreateInput
+{
+    /// <summary>
+    /// 商户订单号
+    /// </summary>
+    [Required(ErrorMessage = "商户订单号不能为空")]
+    public string OutTradeNo { get; set; }
+
+    /// <summary>
+    /// 订单总金额
+    /// </summary>
+    [Required(ErrorMessage = "订单总金额不能为空")]
+    public string TotalAmount { get; set; }
+
+    /// <summary>
+    /// 订单标题
+    /// </summary>
+    [Required(ErrorMessage = "订单标题不能为空")]
+    public string Subject { get; set; }
+
+    /// <summary>
+    /// 超时时间
+    /// </summary>
+    public string TimeoutExpress { get; set; }
+}
+
+public class AlipayAuthInfoInput
+{
+    /// <summary>
+    /// 用户Id
+    /// </summary>
+    public string UserId { get; set; }
+
+    /// <summary>
+    /// 授权码
+    /// </summary>
+    public string AuthCode { get; set; }
+}

+ 31 - 0
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;
+
+/// <summary>
+/// 支付宝回调接口
+/// </summary>
+public abstract class IAlipayNotify
+{
+    /// <summary>
+    /// 充值回调方法
+    /// </summary>
+    /// <param name="type">交易类型</param>
+    /// <param name="tradeNo">交易id</param>
+    public abstract bool TopUpCallback(long type, long tradeNo);
+
+    /// <summary>
+    /// 扫码回调
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="userId"></param>
+    /// <param name="response"></param>
+    /// <returns></returns>
+    public abstract bool ScanCallback(long type, long userId, AlipayUserInfoShareResponse response);
+}

+ 1 - 0
Admin.NET/Admin.NET.Web.Core/ProjectOptions.cs

@@ -37,6 +37,7 @@ public static class ProjectOptions
         services.AddConfigurableOptions<CryptogramOptions>();
         services.AddConfigurableOptions<SMSOptions>();
         services.AddConfigurableOptions<EventBusOptions>();
+        services.AddConfigurableOptions<AlipayOptions>();
         services.AddConfigurableOptions<CDConfigOptions>();
         services.Configure<IpRateLimitOptions>(App.Configuration.GetSection("IpRateLimiting"));
         services.Configure<IpRateLimitPolicies>(App.Configuration.GetSection("IpRateLimitPolicies"));

+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/AlipayCrt/alipayPublicCert.crt


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/AlipayCrt/alipayRootCert.crt


+ 0 - 0
Admin.NET/Admin.NET.Web.Entry/AlipayCrt/appPublicCert.crt