1. 前言
支付模块不管在哪个系统都是重要的(跟钱有关系的能不重要嘛)。课程中使用的是yansongda/pay
的支付库,集成了支付宝、微信支付,使用方便。支付模块主要是按照支付通道的支付规则编码实现,其实质就是接口请求。课程中侧重开发实现,介绍了支付拓展包的使用。因此,支付集成的部分,本章节暂且跳过,只记录学习点。
课程传送门-支付集成
此外,课程中还实现了电商行业大热分期付款。本章节主要梳理课程中对分期付款的实现方案。
课程传送门-分期付款
2. 功能分析
2.1 支付集成
2.1.1 编码学习
支付模块单拎出来就很抗打,涉及到加解密、字符编码的处理,代码封装。
- 实现过程
支付模块比较有意思的是代码实现部分,不同支付通道支持的支付方式不同,传递的参数、验证方式不同。怎么样把不同支付通道的代码封装起来,方便统一处理调用值得去研究。 - 容器的使用
2.2 分期付款
分期付款可以看作是新的一种支付网关,对于一个订单来说,它跟银行卡支付、支付宝支付一样,都可以作为支付方式,只是它每一期分期付款还是依赖于真实的支付方式支付。
2.2.1 需求分析
分期付款的业务逻辑如下:
- 只有当商品订单总金额高于某个数值时才可以使用分期付款;
- 用户使用分期付款时,需要选择还款期限,通常为 3 个月的倍数;
- 使用分期付款需要支付手续费,不同的还款期限手续费费率不同,还款期限越久费率越高;
- 分期付款的手续费与银行贷款的利息不同,银行贷款的利息会随着还款而逐渐降低,而分期付款的手续费则是固定的;
- 使用分期付款后,用户需要立即支付第一期的费用,当第一期费用支付成功后,对应的商品订单状态即变为已支付;
- 用户需每 30 天还款一次,如果在还款截止日期之后仍未还款,需支付逾期费,逾期费按天计算;
- 逾期之后产生的逾期费用最多不超过当期的本金 + 手续费;
- 每一期还款金额计算公式:(本金 + 手续费) / 还款期数 + 当期逾期费;
- 使用了分期付款的商品订单如果发生退款,则退回所有已支付的本金,手续费与逾期费不退回。
2.2.2 实现逻辑
- 选择分期付款下单:
- 在用户选择分期付款时,按照分期期数和金额计算出每一期需要还款的本金、手续费、还款时间等还款信息;
- 再用一个定时脚本去计算当期的逾期费;
- 分期还款支付:(请求支付和支付回调与普通商品的不一样,需要单独实现)
- 请求支付,使用当期的流水号、当期支付金额、分期的回调地址和返回地址去请求第三方支付;
- 按照分期逻辑实现支付回调,更新当期支付信息,若是第一期需更新订单的状态和支付信息,以及分期状态;
-
分期退款:
分期的每一期批量退款,修改订单退款状态;
2.2.3 表设计
分期分款可以选择还款期数,需要计算每一期还款的本金、手续费、还款时间,以及需要保存每一期还款的具体支付方式和支付单号。因此,我们需要用两张表来分别保存分期信息和每期分期的详细信息。
具体表结构如下:
# 分期表
CREATE TABLE `installments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, # 自增ID
`no` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, # 分期单号
`user_id` int(10) unsigned NOT NULL, # 所属用户ID
`order_id` int(10) unsigned NOT NULL, # 对应订单ID
`total_amount` decimal(8,2) NOT NULL, # 总本金
`count` int(10) unsigned NOT NULL, # 还款期数
`fee_rate` double(8,2) NOT NULL, # 手续费率
`fine_rate` double(8,2) NOT NULL, # 逾期费率
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending', # 还款状态,默认未执行
`created_at` timestamp NULL DEFAULT NULL, # 创建时间
`updated_at` timestamp NULL DEFAULT NULL, # 更新时间
PRIMARY KEY (`id`),
UNIQUE KEY `installments_no_unique` (`no`),
KEY `installments_user_id_foreign` (`user_id`),
KEY `installments_order_id_foreign` (`order_id`),
CONSTRAINT `installments_order_id_foreign` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE,
CONSTRAINT `installments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
# 分期项表
CREATE TABLE `installment_items` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, # 自增ID
`installment_id` int(10) unsigned NOT NULL, # 分期ID
`sequence` int(10) unsigned NOT NULL, # 期数
`base` decimal(8,2) NOT NULL, # 当期本金
`fee` decimal(8,2) NOT NULL, # 当期手续费
`fine` decimal(8,2) DEFAULT NULL, # 当期逾期费
`due_date` datetime NOT NULL, # 还款截止日期
`paid_at` datetime DEFAULT NULL, # 还款日期
`payment_method` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, # 还款支付方式
`payment_no` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, # 换的支付单号
`refund_status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending', # 退款状态 默认未退款
`created_at` timestamp NULL DEFAULT NULL, # 创建时间
`updated_at` timestamp NULL DEFAULT NULL, # 更新时间
PRIMARY KEY (`id`),
KEY `installment_items_installment_id_foreign` (`installment_id`),
CONSTRAINT `installment_items_installment_id_foreign` FOREIGN KEY (`installment_id`) REFERENCES `installments` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
2.3.4 代码借鉴
本章节业务逻辑有点复杂,实现方案不算复杂,学习到了以下几点:
- 浮点数的计算方式(使用 bcmath)
- 在处理大数据量的数据库操作时,使用chunkById减少内存占用;