其他分享
首页 > 其他分享> > 支付功能设计及实现思路

支付功能设计及实现思路

作者:互联网

支付功能设计

主要包括:订单表,订单日志表,订单队列,定时任务。

主要考虑:事务性、幂等性、安全性。

表结构设计

订单表,最主要的就是订单号、支付状态。

CREATE TABLE `t_order` (
  `fid` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键,自增id',
  `forder_id` varchar(35) NOT NULL COMMENT '订单号,唯一',
  `fpay_status` varchar(15) DEFAULT '00' COMMENT '00:未支付,01:支付成功,10:订单关闭,02:支付失败,03:已下单,04:申请退款,05:退款成功,06:退款失败 ',
  `fuser_id` int(11) DEFAULT NULL COMMENT '用户id',
  `ftotal_price` decimal(25,2) NOT NULL COMMENT '总价',
  `fcreate_time` datetime DEFAULT NULL COMMENT '购买时间',
  PRIMARY KEY (`fid`),
  UNIQUE KEY `idx_order` (`forder_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='订单表';

订单日志表,最主要的就是订单号,支付状态,操作记录,支付渠道。

 CREATE TABLE `t_order_log` (
  `fid` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键,自增id',
  `forder_id` varchar(35) NOT NULL COMMENT '订单号',
  `fuser_id` int(11) DEFAULT NULL COMMENT '用户id',
  `fcreate_time` datetime DEFAULT NULL COMMENT '操作时间',
  `fpay_status` varchar(15) DEFAULT '00' COMMENT '00:未支付,01:支付成功,10:订单关闭,02:支付失败,03:已下单,04:申请退款,05:退款成功,06:退款失败 ',
  `faction` tinyint(2) unsigned DEFAULT NULL COMMENT '操作记录:1,提交;2,关闭;3,第三方回调;4.前端轮询;5.后台查询第三方;6.定时任务查询',
  `fresult` text COMMENT '订单的回调结果',
  `ftotal_price` decimal(25,2) NOT NULL COMMENT '总价',
  `fpay_channel` varchar(25) DEFAULT NULL COMMENT '支付渠道。1,微信支付;2,支付宝;3,银联支付;',
  PRIMARY KEY (`fid`),
  UNIQUE KEY `idx_order` (`forder_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='订单日志表';

此文主要涉及到订单表,以及订单日志表。

略去用户表,商品表,商品详情表,商品订单关系表。

支付流程及时序图

商品系统:指的是购物网站等系统。

如果要进行"去库存"的处理,可以在第10步中进行。

支付系统:指的是提供聚合支付服务的系统,同时提供微信支付,支付宝,银联等多种支付方式。
如果项目不需要这种聚合支付系统,也可以直接对接微信支付等。

第1步,用户点击"购买";

第2步,"商品系统"展示商品信息,生成订单id;

第3步,用户选择商品信息,提交订单。

订单入Redis队列,方便定时任务主动去取订单进行支付结果查询;

第4步,访问"支付系统",并提供appId,订单id,支付回调url等;

第5步,"支付系统"返回支付的url;

第6步,"商品系统"展示支付页面,提供多种支付方式;

第7步,用户选择支付方式,进行支付;

第8步,"支付系统"通知支付结果;

第9步,"支付系统"调用支付回调url,告知支付结果详情。

支付回调,每隔一段时间就会不断地回调,比如 1/1/4/8/16/32/.... 直到接收到回复为止。

第10步,根据支付状态,决定是否执行商品业务逻辑。

第11步,通知支付结果。

支付回调

第9步和第10步是整个支付模块中最重要的部分。

支付回调的接口,需要保证幂等性、事务性、安全性。

幂等性

数据库排它锁。Select for update。性能太差,应付不了并发。

乐观锁的版本机制。添加version字段。在这种场景下太过冗余。

支付场景下,更好的做法是:使用状态机制,直接利用订单表已有的支付状态。

当回调结果为支付成功,而且数据库的支付状态不是支付成功时,才将支付状态改为支付成功,并执行业务功能。

sql如下:

UPDATE t_order SET fpay_status='01' WHERE forder_id='xxxxx' AND fpay_status!='01'

事务性

执行业务功能和修改支付状态,要做事务处理,保证事务性。

如果支付成功,但是后续的业务功能执行失败,就会回滚。

安全性

数据要加密,包括商户号等信息。

支付回调接口,一定要校验商品信息/商品价格是否正确。

订单日志表,记录下所有的操作,包括生成订单,提交订单,支付回调,支付状态等。

定时任务

定时任务:为了避免支付回调不成功,出现用户付款成功,却没有执行功能服务的情况。

可以使用定时任务,主动去"支付系统"中查询订单的支付状态,这是一种补偿机制。

引入定时任务后,会有很多值得思考又有趣的问题。

支付失败,订单会重新入Redis队列,进行重试。当重试次数达到限度,给用户支付失败的提醒。

同上"怎么保证支付回调接口的幂等性"

假如有很多笔订单,进入Redis队列后,又一直都没有支付,那就可能会变成脏数据。

可以用另一个新的Redis队列,当失败达到一次的次数后,就用新队列来存放这些未支付或者支付失败的订单。

开多线程Redis队列里面取数据。

可选方案

Redis实现的方式就是将订单id作为Key,支付状态作为value,并设置一个 key 的过期时间。

如果发现缓存中已经有了同样的订单id,就视为重复,不会进行支付请求。

拓展

并发更新。事务。

标签:COMMENT,回调,订单,支付,思路,NULL,id,功能设计
来源: https://www.cnblogs.com/expiator/p/12867242.html