7.1 支付框架
Limax提供完整的支付框架,可以接入任意第三方支付系统。同时该支付框架隔离了第三方支付系统与应用服务器实现,便于应用服务器的开发调试。
-
A类支付流程
1. Provider向Endpoint发布商品信息(可选步骤)
2. Endpoint首先向Auany下订单,获得订单id,使用该订单id向第三方支付系统发起支付
3. 第三方支付系统向Auany反馈支付结果,如果支付失败,流程结束。
4. Auany向应用服务器投递支付数据
-
B类支付流程
1. Endpoint向支付服务器购买,获得发票
2. Endpoint向Auany提交发票直到Auany接受发票
3. Auany向服务器验证发票,如果失败,流程结束
4. Auany向应用服务器投递支付数据
-
客户端
limax.Endpoint.AuanyService.pay(int gateway, int payid, int product, int price, int quantity, String receipt, long timeout,Result onresult);
该方法向auany发起支付
gateway:接下来使用的第三方支付网关在系统内分配的id
payid:接受支付的ProviderId,第三方支付网关成功支付以后,Auany使用该ProviderId进行投递。
product:商品号
price:单价
quantity:数量
recepit:发票
timeout:请求超时,单位毫秒
onresult:接收返回结果。
public interface ResultPay { void accept(int errorSource, int errorCode, String result); }
注意, 对于A类支付流程, 首先调用pay, 验证errorCode == 0, 获得result, 随即使用result进行第三方支付;对于B类支付流程, 获得发票后, 本地存储发票, 使用发票调用pay, 返回errorCode==0后可删除本地存储的发票,否则应该持续重试。
-
Provider
-
实现
public interface PaySupport { void onPay(long serial, long sessionid, int product, int price, int quantity, Runnable ack); void onPayConfirm(long serial, Runnable ack); }
Provider实现ProviderListener的同时,与之并列实现PaySupport接口。
首先需要提供一张表格记录serial.
onPay的实现
1. 检查表格中是否存在serial,如果存在转4
2. 对于A类支付检查product与price是否与系统定义的商品信息匹配,如果不匹配说明Endpoint向Auany发起支付时作假;对于B类支付price=-1,仅需要检查product。发生了作假的情况可以根据应用设计要求处理这笔收入然后转4。
3. 提供quantity个prodcut给sessionid对应用户,记录serial。这两个操作建议在同一事务中完成。
4. 调用ack();
onPayConfirm的实现
1. 删除表格记录的serial
2. 调用ack();
注意,这两个方法必须在操作的最后调用ack,如果没有调用,Auany将周期性向Provider发起请求。记录支付日志时,通过转换Long.toString(serial, Character.MAX_RADIX)获得订单号,该订单号与Auany的支付日志相对应,便于在必要的时候进行对账。
-
配置
Provider无需任何配置
-
-
Auany上的配置
-
Auany配置节点属性
payEnable:开启运营环境的支付支持,默认为true
payLoggerClass:支付日志实现类,默认为limax.auany.PayLoggerSimpleFile,可以定义运营环境自己的支付日志类,该类必须实现limax.auany.PayLogger接口。
payLoggerSimpleFileHome:PayLoggerSimpleFile记录支付日志使用的目录,默认为paylogs。
orderExpire:订单过期时间,默认3600000ms即1小时。
orderQueueConcurrencyBits:订单处理队列并发参数,默认为3,建立1<<3 == 8个处理队列。
orderQueueHome:订单处理队列目录,默认为queue。
deliveryExpire:订单投递超时,默认为604800000ms,即7天,投递超时可能有几种原因。其一,下订单时提供的payid错误,其二,超时周期内payid对应的Provider从来没有启动过,其三,应用实现limax.Provider.PaySupport时,忘记调用ack()。订单投递超时以后PayLogger.logDead被调用。
deliveryQueueCheckPeriod:投递队列检查周期,默认为60000ms,即1分钟。
deliveryQueueBackoffMax:投递失败退避参数,默认为5,定义了退避周期序列1,2,3,4,5,5,5…,这里的1,2,3,4,5用来乘以deliveryQueueCheckPeriod即为下一次检查延迟。
deliveryQueueConcurrencyBits:投递队列并发参数,默认为3,建立1<<3 == 8个处理队列。
deliveryQueueHome:投递队列处理目录,默认为queue。
-
pay配置节点属性
gateway:运营系统为第三方支付网关分配的id
className:第三方支付网关消息处理类,必须实现limax.auany.PayGateway接口。
-
-
第三方支付网关扩展
public interface PayGateway { void initialize(Element e, Map<String, HttpHandler> httphandlers) throws Exception; void unInitialize(); void onPay(long sessionid, int gateway, int payid, int product, int price, int quantity, String receipt,Result onresult) throws Exception; }
initialize初始化第三方支付网关配置,一般来说第三方支付网关使用http投递支付消息,可以在httphandlers中设置自己的httpContext与httpHandler的关联。
onPay实现具体支付操作。
-
A类支付示例
系统提供了limax.auany.paygws.Simulation模拟支付网关,可以仿照该实现扩展更多的第三方支付网关支持,该模拟支付网关也可以用来进行应用调试,默认配置如下
<pay className="limax.auany.paygws.Simulation" gateway="0" httpContext="/pay/simulation" maxAmount="999999" maxDeliveryRandomDelay="30000"/>
有两种调试方式:
1. 通过http,访问http://auanyserver:8181/pay/simulation?<order> ,其中order为客户pay操作返回的订单号,这种方式只支持成功支付。
2. maxDeliveryRandomDelay > 0的情况下,系统在maxDeliveryRandomDelay规定值的范围内随机一个延迟,然后进行结果投递。如果price * quantity 在{0, maxAmount}范围内支付成功, 否则失败。maxDeliveryRandomDelay == 0,则只支持http方式。
-
B类支付示例
系统提供了limax.auany.paygws.AppStore,支持AppStore的支付,默认配置如下。
<appstore connectTimeout="15000" home="appstore" readTimeout="15000" receiptExpire="604800000" receiptReplayProtectorConcurrentBits="3" receiptVerifyScheduler="4" retryDelay="300000"/> <pay className="limax.auany.paygws.AppStore" gateway="1" productPattern="[^\d]+([\d]+)$" url="https://buy.itunes.apple.com/verifyReceipt"/>
其中appstore节点为appstore服务的全局配置。
connectTimeout:连接发票验证服务超时,默认15000ms
readTimeout:发票验证服务器返回结果超时,默认15000ms
retryDelay:访问发票验证服务器失败以后,下一次调度延迟,默认300000ms,即5分钟
receiptVerifyScheduler:发票验证调度器线程数,默认为4
home:appstore相关文件的根目录,默认为当前目录下的appstore目录
receiptExpire:发票超时,系统拒绝接受超出该限制的旧发票,从发票的支付之间算起。默认604800000ms,即7天
receiptReplayProtectorConcurrentBits:发票重放保护器并发参数,默认为3,即平均允许1<<3==8个线程同时验证重放
注意,所有B类支付方式都存在发票重放的问题,尽管实现要求客户端在验证发票前进行本地存储,服务器发货完成,通知客户端之后,删除本地存储的发票,但是这种解决方案本身并不可靠,不能避免某些恶意程序在删除本地发票时进行欺骗,从而进行发票重放。为了解决重放问题,发票超时是必要的,这个时间之内进行重放保护,这个时间之外立刻失败,这样重放保护的存储数量才是有界的。
pay节点中,除了gateway, className标准属性外:
productPattern:指定一个正则表达式匹配发票中的product-id,如果不能匹配系统直接拒绝,解析出匹配结果的group(1)对应的数字,作为本框架中的product参数,进行投递。
url:发票验证服务器url
-