支付系统-支付微服务落地设计2:扫码支付流程
大家好,我是此林。
前面我们讲述了支付系统的支付渠道设计,包括支付渠道的概要、redis 缓存、工厂+策略模式解耦等,今天我们来继续来设计扫码支付的内容。
1. 扫码支付原理
扫码支付的原理大概可以总结如下:
1. 商户后端(即我们的支付系统) 先调用支付平台(如微信、支付宝)的统一下单接口,生成一个带订单信息、商户号、金额及签名的支付链接,然后将该链接转换成二维码展示给用户。
2. 用户用支付 App 扫描二维码后,App 识别出这是支付链接,自动跳转到支付界面。
3. 支付平台验证链接参数合法性,并根据用户操作完成扣款流程。
4. 支付完成后,平台通过回调通知商户后端确认订单状态。
整个过程具有安全性和不可篡改性,二维码本质上只是跳转到支付平台的专用链接,用户实际支付行为由三方支付平台控制。

2. 交易单实体设计
交易单实体 TradingEntity 对应数据库表 wy_trading。我们直接看实体类设计即可。
它是针对于订单进行支付的记录,其中记录了订单号,支付状态、支付平台、金额、是否有退款等信息。
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("wy_trading")
public class TradingEntity extends BaseEntity {
private static final long serialVersionUID = -3427581867070559590L;
@ApiModelProperty(value = "openId标识")
private String openId;
@ApiModelProperty(value = "业务系统订单号")
private Long productOrderNo;
@ApiModelProperty(value = "交易系统订单号【对于三方来说:商户订单】")
private Long tradingOrderNo;
@ApiModelProperty(value = "支付渠道【支付宝、微信、现金、免单挂账】")
private String tradingChannel;
@ApiModelProperty(value = "交易类型【付款、退款、免单、挂账】")
private String tradingType;
@ApiModelProperty(value = "交易单状态【DFK待付款,FKZ付款中,QXDD取消订单,YJS已结算,MD免单,GZ挂账】")
private TradingStateEnum tradingState;
@ApiModelProperty(value = "收款人姓名")
private String payeeName;
@ApiModelProperty(value = "收款人账户ID")
private Long payeeId;
@ApiModelProperty(value = "付款人姓名")
private String payerName;
@ApiModelProperty(value = "付款人Id")
private Long payerId;
@ApiModelProperty(value = "交易金额")
private BigDecimal tradingAmount;
@ApiModelProperty(value = "退款金额【付款后】")
private BigDecimal refund;
@ApiModelProperty(value = "是否有退款:YES,NO")
private String isRefund;
@ApiModelProperty(value = "第三方交易返回编码【最终确认交易结果】")
private String resultCode;
@ApiModelProperty(value = "第三方交易返回提示消息【最终确认交易信息】")
private String resultMsg;
@ApiModelProperty(value = "第三方交易返回信息json【分析交易最终信息】")
private String resultJson;
@ApiModelProperty(value = "统一下单返回编码")
private String placeOrderCode;
@ApiModelProperty(value = "统一下单返回信息")
private String placeOrderMsg;
@ApiModelProperty(value = "统一下单返回信息json【用于生产二维码、Android ios唤醒支付等】")
private String placeOrderJson;
@ApiModelProperty(value = "商户号")
private Long enterpriseId;
@ApiModelProperty(value = "备注【订单门店,桌台信息】")
private String memo;
@ApiModelProperty(value = "二维码base64数据")
private String qrCode;
@ApiModelProperty(value = "是否有效")
protected String enableFlag;
}
注意这里的几个字段。
openId:买家用户在第三方支付平台(微信/支付宝)的唯一标识,不是我们商户。
productOrderNo:我们业务系统的订单号。
tradingType:交易类型(付款 / 退款 / 免单 / 挂账)。
tradingState:交易状态(待付款 / 付款中 / 取消订单 / 已结算 / 免单 / 挂账)。
qrCode:生成的支付二维码(存储为 Base64 格式)
这里 交易类型 和 交易状态 两个字段看起来有点像,但其实作用不一样:
- 交易状态:描述交易当前所处的 “进度/结果”,相当于生命周期状态,会随着交易推进而变化。
- 交易类型:描述交易本身的 “性质/目的”。
还有一个注意点,就是一个订单号可以对应多个支付交易号,因为用户可能分期支付、退款等操作。
tradingOrderNo:我们业务系统的支付交易号,一个 productOrderNo 可以对应多个 tradingOrderNo,这个 tradingOrderNo 对于三方支付平台来说就是商户订单号。
那这里就有个疑问:为什么不直接用 productOrderNo 订单号作为入参去调用支付平台的支付呢?
原因:
-
这个订单号在支付平台侧是幂等的:同一个订单号,多次请求只会识别为同一笔支付。
-
如果你用相同的订单号再次请求支付,支付平台会认为你是在重复发起支付,而不会重新生成二维码或支付链接。
-
在交易状态是【取消订单】或【挂账】时,我们业务支付系统允许用户再次发起支付。
-
如果此时依旧使用原来的交易号(TradingOrderNo)去调支付平台接口,那么支付平台会直接拒绝,认为这笔交易已结束或无效了。
因此,这里需要重新生成一个新的 TradingOrderNo(交易单号),这样在调用支付平台时就能作为 一个新的支付申请。
所以说,我们最后查询支付状态。
用户前端可以通过 TradingOrderNo 或 productOrderNo 向我们业务支付系统查询信息,
如果用户传来 productOrderNo ,我们间接通过这个订单号关联到 TradingOrderNo ,业务支付系统最终通过 TradingOrderNo 向三方支付平台查询支付状态。
三方支付平台在统一下单成功后,会返回一个支付平台内部的交易流水号,这些信息那我们把它保存到统一下单信息的相关字段中。

最后用户支付完成后,我们再次拿着 TradinOrderNo 去查三方支付平台的 API 或者 支付平台异步回调我们的接口,最终的支付成功与否的信息我们保存于以下字段中。

3. 扫描支付业务代码流程
我们有个 NativePayController,这个 controller 接口会对外暴露为 Feign 接口。
3.1. 传入和返回实体
整体扫描支付的流程可能有点多,我们先来看接口传入什么,以及接口最终返回什么。
接口入参:NativePayDTO
@Data
public class NativePayDTO {
@ApiModelProperty(value = "商户号", required = true)
@NotNull(message = "商户号不能为空")
private Long enterpriseId;
@ApiModelProperty(value = "业务系统订单号", required = true)
@NotNull(message = "业务系统订单号不能为空")
private Long productOrderNo;
@ApiModelProperty(value = "支付渠道", required = true)
@NotNull(message = "支付渠道不能为空")
private PayChannelEnum tradingChannel;
@ApiModelProperty(value = "交易金额,单位:元", required = true)
@NotNull(message = "交易金额不能为空")
private BigDecimal tradingAmount;
@ApiModelProperty(value = "备注,如:运费", required = true)
@NotNull(message = "备注不能为空")
private String memo;
}
外部会传入给我们支付微服务的参数:
- 商户号:后续要根据商户号查询支付渠道的配置信息(appid、支付公钥、私钥等)。
- 订单号:这个是我们业务系统生成的订单号。
- 支付渠道:例如支付宝、微信等。
- 交易金额:用户需要支付的金额,当然这个金额肯定不是前端传过来的(因为前端传金额会有安全风险,前端可以被更改,如果1000元被更改为1元肯定是不行的)。
- 备注:支付备注。
返回实体:NativePayResponseDTO
@Data
public class NativePayResponseDTO {
@ApiModelProperty(value = "二维码base64数据")
private String qrCode;
@ApiModelProperty(value = "业务系统订单号")
private Long productOrderNo;
@ApiModelProperty(value = "交易系统订单号【对于三方:商户订单】")
private Long tradingOrderNo;
@ApiModelProperty(value = "支付渠道【支付宝、微信、现金、免单挂账】")
private String tradingChannel;
}
可以看到,我们会返回 base64 的二维码数据、业务系统订单号、交易单号和支付渠道。
3.2. 参数校验
外部传入的 NativePayDTO 最终会被转为 TradingEntity,最终这个 TradingEntity 也会被转为 NativePayResponseDTO 返回相应。
转为 TradingEntity 后,我们需要做的第一件事就是对参数进行前置校验。
我们需要校验 :TradingEntity 不为空,订单备注、订单号、商户号、交易金额、支付渠道 均不为空,以及金额需要大于 0。
@Override
public Boolean checkCreateTrading(TradingEntity tradingEntity) {
//校验不为为空,订单备注、订单号、企业号、交易金额、支付渠道
boolean flag = ObjectUtil.isAllNotEmpty(tradingEntity,
tradingEntity.getMemo(),
tradingEntity.getProductOrderNo(),
tradingEntity.getEnterpriseId(),
tradingEntity.getTradingAmount(),
tradingEntity.getTradingChannel());
if (!flag) {
return false;
}
//金额不能小于等于0
return !NumberUtil.isLessOrEqual(tradingEntity.getTradingAmount(), BigDecimal.valueOf(0));
}
3.3. 统一下单流程-加分布式锁
当前接口为统一下单接口,其实就是通过支付流水号向支付平台发起预支付请求,此时支付平台返回支付链接或二维码,等待用户支付完成后,支付平台异步回调通知我们、或者我们定时任务轮询查询支付状态。
此时我们需要给交易单 加分布式锁,并做 前置幂等校验。
String key = TradingCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
try {
//获取锁
if (lock.tryLock(TradingCacheConstant.REDIS_WAIT_TIME, TimeUnit.SECONDS)) {
//交易前置处理:幂等性处理
this.beforePayHandler.idempotentCreateTrading(tradingEntity);
// ...
}
} catch (Exception e) {
// ...
} finally {
lock.unlock();
}
这里我们的 TradingCacheConstant.CREATE_PAY 为常量:trading:lock:create_pay。
为什么要加分布式锁呢?
1. 我们先说下为什么要加锁?加锁主要是为了保证对于同一订单号的支付请求串行化,前端可能抖动导致用户支付了两次,两次请求同时到达,此时幂等性校验发现数据库中都没有交易流水,那么就会两次都会创建交易流水导致用户重复支付。
2. 一般线上我们是多机器集群部署 SpringBoot 应用,如果我们加单机模式的 JVM 锁(如:synchronized、ReentrantLock),那么 Nacos 负载均衡每次把请求打到了不同的机器上,每个机器都加锁成功了,那锁就失效了。
3.4. 统一下单流程-幂等性校验
接下来,我们看看幂等性校验流程,这个是最为重要的步骤。
我们需要考虑的异常情况有很多。
这里会先去根据传入的 订单号 查询数据库中的 交易流水表 是否存在交易单,如果不存在就生成新的交易单,如果存在就需要进一步去判断它的支付状态 tradingState。
1. 注意支付系统里,是交易流水表。主要描述支付行为:支付渠道、交易单号、支付状态、支付金额、渠道流水号等。关注点是 支付动作(发起支付 / 等待支付 / 成功 / 失败)。一个订单可以对应多条交易流水(多次支付尝试)。
交易流水号(TradingOrderNo)是支付维度的唯一键。2. 订单系统里,会有专门的订单系统去描述订单商品的相关信息,比如:商品明细、数量、价格、买家信息、配送信息、订单总金额等。关注点是 业务履约(发货 / 开通服务 / 退款)。

如果根据订单号查询交易单数据,如果不存在说明新交易单,生成交易单号后直接返回,这里的交易单号也是使用雪花id。
tradingState == 已结算/免单:抛出重复支付异常
tradingState == 付款失败/取消订单/挂账:重新生成交易号 TradingOrderNo,在这里就会出现 id 与 TradingOrderNo 数据不同的情况。将id设置为原交易号,交易号重新生成,这样做的目的是既保留了原订单的交易号,又可以生成新的交易号(不重新生成的话,没有办法在支付平台进行支付申请),与之前不会有影响。
tradingState == 付款中:此时有两种情况:
- 如果支付渠道相同(此前使用支付宝付款,本次也是使用支付宝付款),幂等返回已有支付信息。
- 如果支付渠道不同,我们是允许在生成二维码后更换支付渠道,此时需要重新生成交易单号,此时交易单号与id将不同。
当前我们只有一张交易流水表,而且每次交支付尝试都会覆盖前一次的,这样还是有缺陷,不方便后续对账,改进方法有两种。
依旧单表模式:
字段一样包含:
id(雪花 id)
productOrderNo(业务订单号)
tradingOrderNo(支付流水号)
channel(支付渠道)
state(状态:init / pending / success …)其他扩展字段(回调参数、支付链接、失败原因 …)
特点:
productOrderNo不是唯一约束;一笔业务订单可能对应多条流水。每次发起支付,就插入一条新记录。
最终支付结果可通过
productOrderNo聚合查询 得到。双表模式(主单 + 流水):
交易主单表 (TradingOrder):一笔业务订单只对应一条记录。保存汇总状态(比如“最终支付成功的流水号”)。
交易流水表 (TradingTransaction):保存每一次支付尝试。
特点:
主单和业务订单 1:1;
主单和流水 1:N;
好处是对「最终支付状态」的查询非常快(直接查主单即可),而不需要每次去扫流水表。
扩展性更强(比如做账务系统、清算系统)。
更远一步来讲,我们目前的系统暂时不支持分账和组合支付,如果需要支持,我们在系统架构上还要分出一个交易层,当前系统是(订单层+支付层)。

也就是一个订单对应多个交易单,每个交易单对应多个支付单。当然这个就不是我们今天要探讨的范围了。
3.5. 二维码生成
支付宝或微信的扫码支付返回是一个链接,并不是二维码,所以我们需要根据链接生成二维码,生成二维码的库使用的是:zxing
https://github.com/zxing/zxing
最终生成的二维码图片使用的base64字符串返回给前端。
{
"qrCode": "data:image/png;base64,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",
"productOrderNo": "11112241",
"tradingOrderNo": "1559096271641808897",
"tradingChannel": "ALI_PAY"
}
在生成二维码时,我们采用的是服务端生成二维码方式,这种方式会比较消耗服务器的CPU、内存资源,比较好的做法是生成二维码的动作交由客户端(前端)来生成。
实际上,前端应该做兼容处理,在返回的【qrCode】字段中,如果内容以【data:image/png;】开头,直接展现,否则就将返回的数据(支付宝或微信返回的原始数据,例如:https://qr.alipay.com/bax034140c7lyawo6oiy55f2)生成二维码。
今天的介绍就到这里了,之前第一节已经说过工厂模式自动选择不同的支付渠道了,那么下一节我们继续看支付宝和微信支付的不同实现细节。
我是此林,关注我吧!
带你看不一样的世界!
更多推荐



所有评论(0)