TerraMours:Net7对接支付宝当面付

TerraMours:Net7对接支付宝当面付

使用场景:

TerraMours开源项目之一:基于GPT与stable diffusion webui的开源项目:希望能够加入充值入口,并使用tokens数来扣费。

后台源码地址:https://github.com/TerraMours/TerraMours_Gpt_Api

一:先想清楚自己系统支付的逻辑。

最开始是准备想着根据不同类别的会员来设置。后面感觉很难定价以及市场的波动,同时汇率的起伏。算了,还是类似于充值QB,然后基于金额来消费。这样类似于定好了单位一,也就是1 token多少钱,当前这个并没有解决汇率的问题。这个以7来计算的。

简单的画个图,不一定严谨,方便理解即可

图片

根据这个逻辑,基本上需要用到的技术也定了,由于后台需要推送,那么选择SignalR.

二:查看文档

官方地址:https://opendocs.alipay.com/common/02kkv3

由于官方demo,net的很旧,同时我也希望如果我要对接微信支付,我不需要在引入一个新的库,我希望是一个通用的,但是由于微信支付需要有商家资质,我是开源项目,同时是个人开发。所以只支持支付宝。

选择的开源库是:https://github.com/essensoft/paylink

基本上很简单,同时,项目也有示例代码。

三:对接支付

1.当面付预下单的二维码code接口
请求参数:

out_trade_no这个是自己内部系统的交易号,需要保证唯一。

subject: 主题,与英语邮件类似,英文邮件的主题就是subject,正文是body。可以理解为概要等等

body:具体描述,订单详细说明等

total_amount:这个订单的总金额.支付宝规定:订单总金额,单位为元,精确到小数点后两位,取值范围为 [0.01,100000000],金额不能为 0。

参数说明官方地址:https://opendocs.alipay.com/open/f540afd8_alipay.trade.precreate?pathHash=d3c84596&ref=api&scene=19

         [Required]         [Display(Name = "out_trade_no")]         public string OutTradeNo { get; set; }          [Required]         [Display(Name = "subject")]         public string Subject { get; set; }          [Display(Name = "body")]         public string Body { get; set; }          [Required]         [Display(Name = "total_amount")]         public string TotalAmount { get; set; }          [Display(Name = "notify_url")]         public string NotifyUrl { get; set; }
MinimalApi
         /// <summary>         /// 当面付-扫码支付         /// </summary>         [HttpPost]         public async Task<IResult> PreCreate(AlipayTradePreCreateReq viewModel)         {             if (viewModel.UserId == null) {                 viewModel.UserId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.UserData);             }             var res = await _payService.PreCreate(viewModel);             return Results.Ok(res);         }
PayService
 /// <summary>         ///  当面付-扫码支付         /// </summary>         /// <param name="req"></param>         /// <returns></returns>         public async Task<ApiResponse<AlipayTradePrecreateResponse>> PreCreate(AlipayTradePreCreateReq req)         {             //生成系统内唯一交易号             var tradeNo = $"TerraMours-{Guid.NewGuid()}";             // 支付宝规定:订单总金额,单位为元,精确到小数点后两位,取值范围为 [0.01,100000000],金额不能为 0。             //但是我们系统是decimal 保留六位小数,这里由于充值我们是整数,所以我们只需要传给支付宝的钱处理下,保留两位小数即可             var model = new AlipayTradePrecreateModel             {                 //系统内部的唯一交易号  $"TerraMours-{Guid.NewGuid()}"                 OutTradeNo = tradeNo,                 //类似于邮件主题                 Subject = req.Name,                 //金额总数: 将商品价格转换为保留 2 位小数的 decimal 再转换为字符串                 TotalAmount = Math.Round(req.Price, 2).ToString(),                 Body = req.Description             };             var request = new AlipayTradePrecreateRequest();             request.SetBizModel(model);             request.SetNotifyUrl(req.NotifyUrl);              //此时应该先在自己的order表里面创建一个待支付的订单              var order = new Order(req.ProductId, req.Name, req.Description, req.Price, req.UserId, tradeNo);             await _dbContext.Orders.AddAsync(order);             await _dbContext.SaveChangesAsync();              var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);              if (!response.IsError)             {                 return ApiResponse<AlipayTradePrecreateResponse>.Success(response);             }              return ApiResponse<AlipayTradePrecreateResponse>.Fail("支付宝当面付二维码生成失败");         }
appsettings:支付宝

注意:密钥格式:请选择 PKCS1(非JAVA适用),切记 切记 切记!!!

记得密钥转化成PKCS1格式的

AppId、AlipayPublicKey、AppPrivateKey 三个参数数据如何获取,自行百度。

 // 支付宝   // 更多配置,请查看AlipayOptions类   "Alipay": {      // 注意:      // 若涉及资金类支出接口(如转账、红包等)接入,必须使用“公钥证书”方式。不涉及到资金类接口,也可以使用“普通公钥”方式进行加签。     // 本示例默认的加签方式为“公钥证书”方式,并调用 CertificateExecuteAsync 方法 执行API。     // 若使用“普通公钥”方式,除了遵守下方注释的规则外,调用 CertificateExecuteAsync 也需改成 ExecuteAsync。     // 支付宝后台密钥/证书官方配置教程:https://opendocs.alipay.com/open/291/105971     // 密钥格式:请选择 PKCS1(非JAVA适用),切记 切记 切记      // 应用Id     // 为支付宝开放平台-APPID     "AppId": "",      // 支付宝公钥 RSA公钥     // 为支付宝开放平台-支付宝公钥     // “公钥证书”方式时,留空     // “普通公钥”方式时,必填     "AlipayPublicKey": "",      // 应用私钥 RSA私钥     // 为“支付宝开放平台开发助手”所生成的应用私钥     "AppPrivateKey": "",      // 服务网关地址     // 默认为正式环境地址     "ServerUrl": "https://openapi.alipay.com/gateway.do",      // 签名类型     // 支持:RSA2(SHA256WithRSA)、RSA1(SHA1WithRSA)     // 默认为RSA2     "SignType": "RSA2",      // 应用公钥证书     // 可为证书文件路径 / 证书文件的base64字符串     // “公钥证书”方式时,必填     // “普通公钥”方式时,留空     "AppPublicCert": "",      // 支付宝公钥证书     // 可为证书文件路径 / 证书文件的base64字符串     // “公钥证书”方式时,必填     // “普通公钥”方式时,留空     "AlipayPublicCert": "",      // 支付宝根证书     // 可为证书文件路径 / 证书文件的base64字符串     // “公钥证书”方式时,必填     // “普通公钥”方式时,留空     "AlipayRootCert": ""   }
2.查询订单支付状态
一:单个只做查询,没有逻辑,这个可以自己后台或者提供给用户自己查询订单的信息的接口
MinimalApi
 /// <summary>         /// 支付宝交易查询         /// </summary>         [HttpPost]         public async Task<IResult> Query(AlipayTradeQueryReq viewMode)         {             var res = await _payService.Query(viewMode);             return Results.Ok(res);         }
PayService

OutTradeNo:是自己系统生成的唯一交易号。

TradeNo:是支付宝那边的交易号,如果查询这个交易详细信息,传这两个的任意一个都可以查询。但是一开始没支付成功是没有这个的,需要后续自己查询之后再自己保存这个交易号到自己数据库。

         /// <summary>         /// 交易查询         /// </summary>         /// <param name="req"></param>         /// <returns></returns>          public async Task<ApiResponse<AlipayTradeQueryResponse>> Query(AlipayTradeQueryReq req)         {             var model = new AlipayTradeQueryModel             {                 OutTradeNo = req.OutTradeNo,                 //可以不传                 TradeNo = req.TradeNo             };              var request = new AlipayTradeQueryRequest();             request.SetBizModel(model);              var res = await _client.ExecuteAsync(request, _optionsAccessor.Value);             return ApiResponse<AlipayTradeQueryResponse>.Success(res);         }
二:后台查询三分钟,通过SignalR向前端推送订单状态。
PaymentHub
 using Essensoft.Paylink.Alipay; using Essensoft.Paylink.Alipay.Domain; using Essensoft.Paylink.Alipay.Request; using Essensoft.Paylink.Alipay.Response; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using TerraMours.Framework.Infrastructure.EFCore; using TerraMours_Gpt.Domains.PayDomain.Contracts.Req; using ILogger = Serilog.ILogger; using TerraMours_Gpt.Framework.Infrastructure.Contracts.Commons.Enums;  namespace TerraMours_Gpt.Domains.PayDomain.Hubs {     /// <summary>     /// 支付相关的长连接     /// </summary>     [EnableCors("MyPolicy")]     public class PaymentHub : Hub     {         private readonly IAlipayClient _client;         private readonly IOptions<AlipayOptions> _optionsAccessor;         private readonly FrameworkDbContext _dbContext;         private readonly Serilog.ILogger _logger;          public PaymentHub(IAlipayClient client, IOptions<AlipayOptions> optionsAccessor, FrameworkDbContext dbContext, ILogger logger)         {             _client = client;             _optionsAccessor = optionsAccessor;             _dbContext = dbContext;             _logger = logger;         }          /// <summary>         /// 即时查询状态         /// </summary>         /// <param name="req"></param>         /// <returns></returns>         public async Task QueryPaymentStatus(AlipayTradeQueryReq req)         {             _logger.Warning($"即时查询状态,订单号:{req.OutTradeNo}");             //获取当前连接id             var connectionId = this.Context.ConnectionId;              //先查询系统里面是否有此账单             // todo  支付成功 则修改用户vip信息             var order = await _dbContext.Orders.FirstOrDefaultAsync(x => x.OrderId == req.OutTradeNo) ?? throw new Exception("此订单不存在");             var model = new AlipayTradeQueryModel             {                 OutTradeNo = req.OutTradeNo,                 //可以不传                 TradeNo = req.TradeNo             };              var request = new AlipayTradeQueryRequest();             request.SetBizModel(model);             //循环查询3分钟这个账单,超时或者状态位已支付则停止             using var httpClient = new HttpClient();             var startTime = DateTime.Now;             //先查一次 以免使用未赋值的变量             AlipayTradeQueryResponse queryPayRes = await _client.ExecuteAsync(request, _optionsAccessor.Value);             _logger.Warning($"第一次查询返回:{req.OutTradeNo},交易状态:{queryPayRes.TradeStatus}");             while ((DateTime.Now - startTime).TotalMinutes <= 3)             {                 queryPayRes = await _client.ExecuteAsync(request, _optionsAccessor.Value);                  if (queryPayRes.TradeStatus != "WAIT_BUYER_PAY")                 {                     _logger.Warning($"订单号状态改变:{req.OutTradeNo},交易状态:{queryPayRes.TradeStatus}");                     //交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)                     break;                 }                 // 间隔3秒进行下一次查询                 await Task.Delay(3000);             }             // 如果超过3分钟仍未支付,发送订单状态信息              order.PayOrder(queryPayRes.TradeNo, queryPayRes.TradeStatus);             _dbContext.Orders.Update(order);             await _dbContext.SaveChangesAsync();             //如果支付成功,则把user的余额加上新充值的钱             var user = await _dbContext.SysUsers.FirstOrDefaultAsync(x => x.UserEmail == order.UserId) ?? throw new Exception("此用户不存在");             user.Balance = user.Balance + order.Price;             _dbContext.SysUsers.Update(user);             await _dbContext.SaveChangesAsync();              var res = false;             //交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)             if (queryPayRes.TradeStatus == AlipayTradeStatusEnum.TRADE_SUCCESS.ToString())             {                 res = true;             }             //queryPayRes,前端只需要传是否成功即可             await Clients.Client(connectionId).SendAsync("QueryPaymentStatus", res);         }      } } 
跳出循环之后,建议再查询一次。这样保证Delay的时间如果在三分钟边界上,可能会有支付成功,但是返回的是没有支付这种情况

总结

整体上来说,采用这个开源库,由于demo示例代码都有,基本上不难,需要注意的是,非Java需要先把密钥格式转化为PKCS1的密钥。当然这个系统对金钱以及安全要求都不高,只是单纯记录TerraMours相关项目的开发过程而已。详细代码可以看GitHub源码。前端源码也是开源,都在组织项目里面。

后台源码地址:https://github.com/TerraMours/TerraMours_Gpt_Api

前端源码地址:https://github.com/TerraMours/TerraMours_Gpt_Web

后台管理前端源码地址:https://github.com/TerraMours/TerraMours_Admin_Web

posted @ 2023-08-20 16:12  忽如一夜娇妹来  阅读(32)  评论(0编辑  收藏  举报