支付模块后端设计

现在很多 App 都有应用内购买项目,因此这些 App 都需要一个支付模块。

一般地,我们都是接入第三方支付,如支付宝、微信、网银等。支付的通用流程如下:

  1. 用户在 App 内发起支付请求;
  2. App 通过自己的服务端创建订单;
  3. App 将订单号和应付金额等信息,传递给第三方支付的 SDK,随即显示支付界面;
  4. 用户在第三方支付界面完成支付;
  5. 返回 App 界面,查看支付结果。

下面是一个使用支付宝支付的流程图
支付宝支付流程

App 这边主要维护一个订单表,当然需要自己的服务器,订单数据保存在服务器上。
订单表:

  • 订单号:需要生成一个全局唯一的订单号
  • 用户ID:标明该订单是哪个用户创建的
  • 商品名称
  • 商品描述
  • 商品价格:这是打折前的价格
  • 应付金额:用户实付金额,传递给第三方支付 SDK
  • 创建时间
  • 更新时间:该条记录更新的时间
  • 支付状态:NEW 新创建,PAID 支付成功,FAILURE 支付失败,DONE 订单完成

核心问题

1. 创建订单

关键是生成全局唯一的订单号。最简单的办法是直接使用数据库的自增字段。

一般我们会设计一个有意义的订单号:订单标识 + 时间标识(YYYYMMDDHHMMSS 或者时间戳) + 序号。
订单标识,是预定义好的,作为订单号的前缀,可用于区别不同的业务。
序号,可以是递增的序号,也可以是无意义的随机值,但要保证在同一秒内不重复。递增的序号,可以使用NoSQL 数据库生成,例如 redis 的 incr 操作。

2. 支付回调

上面的通用流程中,显示的支付结果,是同步结果(由第三方支付 SDK 返回的结果)。这可以明确告诉用户,支付已经成功。但是对订单的发货操作,需要等待服务端订单的更新。这就是服务端的异步回调,由第三方支付的服务器向 App 的服务器发送支付结果通知(即上图中的第 13 步),然后 App 的服务端更新订单,执行发货操作。

一般回调请求的参数是带签名或者加密的,App 服务端需要验证签名,以确保通知的合法性。

这里关键是并发的问题,如果同一个订单有多个支付通知同时发过来,而更新订单的逻辑里包括发货操作,且不是原子性的。这就是导致重复发货。对于虚拟商品,如游戏中的金币,就会多次给用户发放金币。所以更新订单的逻辑必须加锁处理,或者采用队列的方式。

标签: none

添加新评论