| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
- //
- // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
- //
- // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
- using Aop.Api;
- using Aop.Api.Domain;
- using Aop.Api.Request;
- using Aop.Api.Response;
- using Aop.Api.Util;
- using Microsoft.AspNetCore.Hosting;
- using NewLife.Reflection;
- namespace Admin.NET.Core.Service;
- /// <summary>
- /// 支付宝支付服务 🧩
- /// </summary>
- [ApiDescriptionSettings(Order = 240)]
- public class SysAlipayService : IDynamicApiController, ITransient
- {
- private readonly IWebHostEnvironment _webHostEnvironment;
- private readonly SysConfigService _sysConfigService;
- private readonly List<IAopClient> _alipayClientList;
- private readonly IHttpContextAccessor _httpContext;
- private readonly AlipayOptions _option;
- private readonly ISqlSugarClient _db;
- public SysAlipayService(
- ISqlSugarClient db,
- IHttpContextAccessor httpContext,
- SysConfigService sysConfigService,
- IWebHostEnvironment webHostEnvironment,
- IOptions<AlipayOptions> alipayOptions)
- {
- _db = db;
- _httpContext = httpContext;
- _sysConfigService = sysConfigService;
- _option = alipayOptions.Value;
- _webHostEnvironment = webHostEnvironment;
- // 初始化支付宝客户端列表
- _alipayClientList = [];
- foreach (var account in _option.AccountList) _alipayClientList.Add(_option.GetClient(account));
- }
- /// <summary>
- /// 获取授权信息 🔖
- /// </summary>
- /// <param name="input"></param>
- /// <returns></returns>
- [NonUnify]
- [AllowAnonymous]
- [DisplayName("获取授权信息")]
- [ApiDescriptionSettings(Name = "AuthInfo"), HttpGet]
- public ActionResult GetAuthInfo([FromQuery] AlipayAuthInfoInput input)
- {
- var type = input.UserId?.Split('-').FirstOrDefault().ToInt();
- var userId = input.UserId?.Split('-').LastOrDefault().ToLong();
- var account = _option.AccountList.FirstOrDefault();
- var alipayClient = _alipayClientList.First();
- // 当前网页接口地址
- var currentUrl = $"{_option.AppAuthUrl}{_httpContext.HttpContext!.Request.Path}?userId={input.UserId}";
- if (string.IsNullOrEmpty(input.AuthCode))
- {
- // 重新授权
- var url = $"{_option.AuthUrl}?app_id={account!.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);
- // 记录授权信息
- var entity = _db.Queryable<SysAlipayAuthInfo>().First(u =>
- (!string.IsNullOrWhiteSpace(u.UserId) && u.UserId == info.UserId) ||
- (!string.IsNullOrWhiteSpace(u.OpenId) && u.OpenId == info.OpenId)) ?? new();
- entity.Copy(info, excludes: [nameof(SysAlipayAuthInfo.Gender), nameof(SysAlipayAuthInfo.Age)]);
- entity.Age = int.Parse(info.Age);
- entity.Gender = info.Gender switch
- {
- "m" => GenderEnum.Male,
- "f" => GenderEnum.Female,
- _ => GenderEnum.Unknown
- };
- entity.AppId = account!.AppId;
- if (entity.Id <= 0) _db.Insertable(entity).ExecuteCommand();
- else _db.Updateable(entity).ExecuteCommand();
- // 执行完,重定向到指定界面
- //var authPageUrl = _sysConfigService.GetConfigValueByCode<string>(ConfigConst.AlipayAuthPageUrl + type).Result;
- //return new RedirectResult(authPageUrl);
- return new RedirectResult(_option.AppAuthUrl + "/index.html");
- }
- /// <summary>
- /// 支付回调 🔖
- /// </summary>
- /// <returns></returns>
- [AllowAnonymous]
- [DisplayName("支付回调")]
- [ApiDescriptionSettings(Name = "Notify"), HttpPost]
- public string Notify()
- {
- SortedDictionary<string, string> sorted = [];
- foreach (string key in _httpContext.HttpContext!.Request.Form.Keys)
- sorted.Add(key, _httpContext.HttpContext.Request.Form[key]);
- var account = _option.AccountList.FirstOrDefault();
- string alipayPublicKey = Path.Combine(_webHostEnvironment.ContentRootPath, account!.AlipayPublicCertPath!.Replace('/', '\\').TrimStart('\\'));
- bool signVerified = AlipaySignature.RSACertCheckV1(sorted, alipayPublicKey, "UTF-8", account.SignType); // 调用SDK验证签名
- if (!signVerified) throw Oops.Oh("交易失败");
- // 更新交易记录
- var outTradeNo = sorted.GetValueOrDefault("out_trade_no");
- var transaction = _db.Queryable<SysAlipayTransaction>().First(x => x.OutTradeNo == outTradeNo) ?? throw Oops.Oh("交易记录不存在");
- transaction.TradeNo = sorted.GetValueOrDefault("trade_no");
- transaction.TradeStatus = sorted.GetValueOrDefault("trade_status");
- transaction.FinishTime = sorted.ContainsKey("gmt_payment") ? DateTime.Parse(sorted.GetValueOrDefault("gmt_payment")) : null;
- transaction.BuyerLogonId = sorted.GetValueOrDefault("buyer_logon_id");
- transaction.BuyerUserId = sorted.GetValueOrDefault("buyer_user_id");
- transaction.SellerUserId = sorted.GetValueOrDefault("seller_id");
- transaction.Remark = sorted.GetValueOrDefault("remark");
- _db.Updateable(transaction).ExecuteCommand();
- return "success";
- }
- /// <summary>
- /// 统一收单下单并支付页面接口 🔖
- /// </summary>
- /// <param name="input"></param>
- /// <returns></returns>
- [DisplayName("统一收单下单并支付页面接口")]
- [ApiDescriptionSettings(Name = "AlipayTradePagePay"), HttpPost]
- public string AlipayTradePagePay(AlipayTradePagePayInput input)
- {
- // 创建交易记录,状态为等待支付
- var transactionRecord = new SysAlipayTransaction
- {
- AppId = _option.AccountList.First().AppId,
- OutTradeNo = input.OutTradeNo,
- TotalAmount = input.TotalAmount.ToDecimal(),
- TradeStatus = "WAIT_PAY", // 等待支付
- CreateTime = DateTime.Now,
- Subject = input.Subject,
- Body = input.Body,
- Remark = "等待用户支付"
- };
- _db.Insertable(transactionRecord).ExecuteCommand();
- // 设置支付页面请求,并组装业务参数model,设置异步通知接收地址
- AlipayTradeWapPayRequest request = new();
- request.SetBizModel(new AlipayTradeWapPayModel()
- {
- Subject = input.Subject,
- OutTradeNo = input.OutTradeNo,
- TotalAmount = input.TotalAmount,
- Body = input.Body,
- ProductCode = "QUICK_WAP_WAY",
- TimeExpire = input.TimeoutExpress
- });
- request.SetNotifyUrl(_option.NotifyUrl);
- var alipayClient = _alipayClientList.First();
- var response = alipayClient.SdkExecute(request);
- if (response.IsError) throw Oops.Oh(response.SubMsg);
- return $"{_option.ServerUrl}?{response.Body}";
- }
- /// <summary>
- /// 交易预创建 🔖
- /// </summary>
- /// <param name="input"></param>
- /// <returns></returns>
- [DisplayName("交易预创建")]
- [ApiDescriptionSettings(Name = "AlipayPreCreate"), HttpPost]
- public string AlipayPreCreate(AlipayPreCreateInput input)
- {
- // 创建交易记录,状态为等待支付
- var transactionRecord = new SysAlipayTransaction
- {
- AppId = _option.AccountList.First().AppId,
- OutTradeNo = input.OutTradeNo,
- TotalAmount = input.TotalAmount.ToDecimal(),
- TradeStatus = "WAIT_PAY", // 等待支付
- CreateTime = DateTime.Now,
- Subject = input.Subject,
- Remark = "等待用户支付"
- };
- _db.Insertable(transactionRecord).ExecuteCommand();
- // 设置异步通知接收地址,并组装业务参数model
- AlipayTradePrecreateRequest request = new();
- request.SetNotifyUrl(_option.NotifyUrl);
- request.SetBizModel(new AlipayTradePrecreateModel()
- {
- Subject = input.Subject,
- OutTradeNo = input.OutTradeNo,
- TotalAmount = input.TotalAmount,
- TimeoutExpress = input.TimeoutExpress
- });
- var alipayClient = _alipayClientList.First();
- 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>
- [NonAction]
- public async Task<AlipayFundTransUniTransferResponse> Transfer(AlipayFundTransUniTransferInput input)
- {
- var account = _option.AccountList.FirstOrDefault(u => u.AppId == input.AppId) ?? throw Oops.Oh("未找到商户支付宝账号");
- var alipayClient = _option.GetClient(account);
- // 构造请求参数以调用接口
- AlipayFundTransUniTransferRequest request = new();
- AlipayFundTransUniTransferModel model = new()
- {
- BizScene = AlipayConst.BizScene,
- ProductCode = AlipayConst.ProductCode,
- OutBizNo = input.OutBizNo, // 商家订单
- TransAmount = $"{input.TransAmount}:F2", // 订单总金额
- OrderTitle = input.OrderTitle, // 业务标题
- Remark = input.Remark, // 业务备注
- PayeeInfo = new() // 收款方信息
- {
- CertType = input.CertType?.ToString(),
- CertNo = input.CertNo,
- Identity = input.Identity,
- Name = input.Name,
- IdentityType = input.IdentityType.ToString()
- },
- BusinessParams = input.PayerShowNameUseAlias ? "{\"payer_show_name_use_alias\":\"true\"}" : null
- };
- request.SetBizModel(model);
- var response = alipayClient.CertificateExecute(request);
- // 保存转账记录
- await _db.Insertable(new SysAlipayTransaction
- {
- UserId = input.UserId,
- AppId = input.AppId,
- TradeNo = response.OrderId,
- OutTradeNo = input.OutBizNo,
- TotalAmount = response.Amount.ToDecimal(),
- TradeStatus = response.Code == "10000" ? "SUCCESS" : "FAILED",
- Subject = input.OrderTitle,
- ErrorInfo = response.SubMsg,
- Remark = input.Remark
- }).ExecuteCommandAsync();
- return response;
- }
- }
|