支持多种支付方式的支付平台架构,示例使用 Python Tornado 框架。
用到了工厂模式和模板方法模式。
工厂模式:子类的某个方法要根据情况来决定用什么类去实例化对象。
模板方法模式:用来定义算法的各个步骤,并将某些步骤交由子类实现。
流程
流程为:下单、用户付款、确认用户付款、发货。
- 下单:客户端调用该接口,上传必要信息,如商品 ID、收货人,服务器创建订单,返回一个唯一的订单号。
- 用户付款:付款方式由支付渠道提供。
- 确认用户付款:一般有两种方式:回调与主动验单。
- 回调:用户支付成功后,充值渠道发起请求到我方服务器。比如支付宝。
- 主动验单:充值渠道将充值数据返回给客户端,客户端将其提交到服务器,服务器拿着数据去充值渠道验单。比如苹果支付、谷歌支付。
- 通知发货。
设计
- 充值渠道可以共用创建订单、发货的方法
- 由于回调一般为 POST 请求,所以只能通过 URL 来区分不同的充值渠道,而不能使用相同的 URL 然后再通过参数来区分。
- 在基类中定义需要实现的模板方法,由子类(具体的支付渠道)去具体实现。
所以整个基类的结构如下:
class AbstractPay(tornado.web.RequestHandler):
def create_order(self):
"""创建订单"""
pass
def respond_callback(self):
"""响应支付方回调"""
raise NotImplementedError()
def verify_order(self):
"""向支付方验证订单"""
raise NotImplementedError()
def notify_delivery(self):
"""通知发货"""
pass
raise NotImplementedError()
是需要子类去实现的方法,pass
的表示由基类实现,子类共用的的方法。
URL 如下
handlers = [
('/' + API_VERSION + 'create_order/', CreateOrderHandler),
('/' + API_VERSION + 'callback/' + '(?P<channel>[^\/]+)', RespondCallbackHandler),
('/' + API_VERSION + 'verify/' + '(?P<channel>[^\/]+)', VerifyHandler),
]
根据 channel
实例化对应的类,然后调用 respond_callback
或 verify_order
方法,验证成功后再通知发货。
响应回调的代码如下:
def post(self, channel):
pay_channel = globals()[channel.title()]()
pay_channel.respond_callback()
确保成功
需要确保我们的主动请求 —— 验证订单和通知发货一定成功。
- 在第一次请求失败后,隔一定时间再次发起请求,如果还是失败,增大等待间隔,再次发起请求,以此类推,直到一定次数之后,抛出异常。
- 增加定时任务,定期去验证订单和通知发货。
异常处理
所有支付渠道可以共用一个异常处理函数。异常处理顺序由具体到抽象,由自定义到内置。
这里以 Tornado 为例,在 AbstractPay
中重写 write_error() 即可。
使用时只需要在子类中抛出对应异常。
安全
流程如下:
- 回调订单号应该存在并且为
等待支付
状态 - 支付金额和创建订单时的金额一致
- 商品 ID 一致
- 验证签名
以上任何一个验证失败,则忽略该回调/验证数据。
- 第一步为了防止多次发货
- 第二三步确保购买的商品、付款的金额一致
- 验证签名确保信息来源正确
由于各种渠道回传的数据不一致,所以这个由子类自己实现。