asp.net core 3.1开发交通银行支付接口笔记一底层代码
asp.net core 3.1开发交通银行支付接口笔记二支付
退款
由于业务需要。这里要做退款功能。也好。以前我做的接口都不做退款功能。所以,我都是用自己的钱包测试的。当然。我也会找客户的帐号用,让客户左手转右手。虽然不多。但也是钱是不?这次有退款功能,而且用的是微信,绑定太多,所以就没法用客户帐号了。
开始
发送请求
按照文档的要求在第一章的基础类里面拼接XML然后用第一章的服务器soket类POST发送到文档说的指定接口(不是页面接口是接口这个注意)
响应
这是同步响应,阻塞的,最好在页面点击退款有个读条遮罩防止二次退款,得到响应用第一章的代码解签后本地业务处理结果即可。
查询
按照文档的要求在第一章的基础类里面拼接XML然后用第一章的服务器soket类POST发送到文档说的指定接口(不是页面接口是接口这个注意)
响应
这个也是同步的。即时结果,同样也是用第一章的解签出来后自己本地处理逻辑
收尾
这里交行技术人员对接有个要求,支付必须扣款成功,有退款必须到账。在没收到回调需要轮询。要求是:原则上,查询不能太频繁,如查询间隔不能低于10s。无效查询不能太多,如下单五分钟后仍未终态(success 或 failure )也停止查询。
如果没有达到要求拒绝上线的。
退款和查询很简单上面一笔带过。下面说轮询。轮询我用后台任务+redis队列做的
redis
WINDOWS64下载:https://pan.baidu.com/s/1fslLAYpWX8kE2-RWon2Ebw 提取码:3z9q
安装
配置,我这里由于有软件洁癖(其实是怕装太多拖慢我的宝贝I7新机)。
私人开发的数据库和其它辅助都是装自己的服务器上的,自己的机子只装开发工具和游戏。所以用远程部署。
设置远程访问地址。在(redis.windows-service.conf)
################################ GENERAL #####################################
下的
# bind 127.0.0.1下面加一句
bind 0.0.0.0
设置密码。这个网上资料说(redis.windows-service.conf里面设置是错误的。没效果。只有在redis.windows.conf设置才有用)
在################################## SECURITY ###################################下面的
# requirepass foobared的下面加一句
requirepass 你的密码
安装命令:redis-server.exe --service-install redis.windows.conf --loglevel verbose
启动服务命令:redis-server.exe --service-start
关闭服务命令:redis-server.exe --service-stop
使用:我把订单ID加入队列,我用的是左进右出
private readonly IDatabase _redis;
public CourseMajorBLL(WXOfficeContext _db, ILogProvider _log, IServiceScopeFactory serviceScopeFactory, RedisHelper client)
{
db = _db;
log = _log;
_redis = client.GetDatabase();
_serviceScopeFactory = serviceScopeFactory;
}
/// <summary>
/// 加入队列
/// </summary>
/// <param name="id"></param>
private void pushRedis(string id)
{
using (var context = (WXOfficeContext)_serviceScopeFactory.CreateScope().ServiceProvider.CreateScope().ServiceProvider.GetService(typeof(WXOfficeContext)))
{
log.WriteOperateLog(context, "自动加入队列", id, new UsersInfoModel() { NickName = "系统", UserName = "交行报文", IP = "" });
context.SaveChanges();
}
_redis.ListLeftPush("lnr", id);
}
小技巧:由于在线程里面用dbcontext而一个线程只允许用一个dbcontext所以解决方案是即时生成使用即时销毁。所以上面的日志我用了using
订单ID取出队列
_redis.ListRightPop("lnr", 0);
后台定时任务
public class QueryBackgroundService : BackgroundService
{
private IBCMHandleLogic _iBCMHandleLogic;
public QueryBackgroundService(IBCMHandleLogic iBCMHandleLogic)
{
_iBCMHandleLogic = iBCMHandleLogic;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("查询服务开启");
Console.WriteLine("MyServiceA 开始执行");
while (!stoppingToken.IsCancellationRequested)
{
await _iBCMHandleLogic.StartQueryOrderTask();
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
}
Console.WriteLine("查询服务停止");
}
public override void Dispose()
{
base.Dispose();
}
}
配置
public void ConfigureServices(IServiceCollection services)
{
//..其它配置
services.AddHostedService<QueryBackgroundService>();
}
public async Task StartQueryOrderTask()
{
if (ThreadPool.ThreadCount < 20)
{
//Thread.Sleep(3000);
string orderNo = _redis.ListRightPop("lnr", 0);
if (!string.IsNullOrWhiteSpace(orderNo))
{
WriteOperateLog("出队列", orderNo, new UsersInfoModel() { NickName = "系统", UserName = "交行报文", IP = "" });
await Task.Run(async () =>
{
for (int i = 0; i < 20; i++)
{
using (var db = (WXOfficeContext)_serviceScopeFactory.CreateScope().ServiceProvider.CreateScope().ServiceProvider.GetService(typeof(WXOfficeContext)))
{
TotalOrder order = null;
for (int j = 0; j < 4; j++)
{
order = db.TotalOrder.Where(r => r.OrderNo == orderNo).FirstOrDefault();
if (j == 3 && order == null)
{
WriteOperateLog(orderNo, "订单还没保存到数据库", new UsersInfoModel() { NickName = "系统", UserName = "交行报文", IP = "" });
break;
}
if (order == null)
{
Thread.Sleep(10000);
}
else
{
break;
}
}
if (order == null||!(order.Status==(int)PayStateEnum.Payments||order.Status==(int)PayStateEnum.Refunding))
{
break;
}
if (order.Status == (int)PayStateEnum.Payments)//支付
{
TimeSpan ts = DateTime.Now - order.AddTime;
if (ts.Seconds < 15)
{
Thread.Sleep((15 - ts.Seconds) * 1000);
}
}
else if (order.Status == (int)PayStateEnum.Refunding)//退款
{
Thread.Sleep(15000);
}
}
WriteOperateLog(orderNo, "开始查询", new UsersInfoModel() { NickName = "系统", UserName = "交行报文", IP = "" });
string rs = await runQuery(orderNo);//本地自己的业务逻辑
WriteOperateLog(orderNo, "开始查询"+i+"次,结果:"+rs, new UsersInfoModel() { NickName = "系统", UserName = "交行报文", IP = "" });
if (!string.IsNullOrWhiteSpace(rs))
{
Thread.Sleep(15000);
}
else
{
break;
}
}
});
}
}
}
上面是具体逻辑。这里有个问题。会阻塞订单。也就是说,如果有2个订单A、B进入队列B会在A完成后才会执行取出B,原因我怀疑在那个线程休眠,但是时间紧还没头绪处理。
至此。整个接口的笔记完成。撒花