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


上一页 下一页