来论坛已久,第一次发帖回馈社会😀
看到有些同学对于支付的测试比较迷茫,把我之前的经验总结了一下
希望能有些帮助

背景

支付(内购)的测试一直是一个比较棘手的问题,数据加密、第三方平台都是其中的难点。即便使用了沙盒等测试方式,各种中间环节的数据依然是不可控。测试人员大多只能做一些最基本的流程测试,比如正常走通流程、流程未完成等,有一些安全意识的测试人员还会尝试修改一些请求参数。

原理

要想进行全面深入的测试,首先要了解支付系统的实现原理。典型的实现如下:

无服务端

比如一个单机游戏,由 APP 直接向支付平台(以 APPLE 为例)发起支付请求,用户完成 APPLE 的支付流程后,APP 会收到 APPLE 返回的支付结果,通常包含一个返回码和一个票据,返回码用来标识支付是否成功。APP 根据返回码来决定下一步流程,比如发放物品给用户。

那么这里我们可以看到,只要在 APP 前拦截到支付响应并更改状态码为成功,就可以不花钱获得商品了。所以呢,这种实现方式是没有安全可言的,只要有些网络基础就可以对其破解。

不过大家应该不会遇到上面的这种实现(只有没有任何经验的开发者才会写出这种实现吧),因为通常支付平台都会提供一种本地验证数据有效性的方式。上面不是说到响应结果中包含一种票据么?这个票据是经过重重加密的,其中包含了很多信息,比如支付结果、购买的商品信息等等,只有经过正确的解密才可以看到。解密算法是平台提供给开发者的,于是一个合理的流程是,APP 拿到响应结果,解密票据,判断其中的信息是否正确,然后再决定是否发放物品。

这样,我们就无法像之前那样轻易的免费获取物品了。然而这样子还不能叫做安全,因为只要你了解了加密解密的算法,同样可以构造出合法的票据,只不过技术门槛变高了。

更进一步的说,任何客户端数据都可以进行破解。要想安全,一定要有服务端,接下来才是我们的重点。

有服务端

标准的实现方式是这样的

客户端的支付步骤和之前是一样的,但是 APP 得到支付结果后需要回调服务端,把支付结果告诉服务端。服务端再用票据去 APPLE 进行验证,根据验证结果来执行相应的流程。

这样,所有的数据处理都是放在服务端,客户端层面的任何数据篡改都会导致服务端的验证失败。

好好理解这个流程,然后再来想一想这样的一个系统有哪些测试点或者安全隐患,作为测试我们有哪些能做的?

注:

  1. 实际客户端向平台发送支付请求之前,一般需要先向服务端下订单,服务端返回商品信息并开启一个新的购买流程,这里为了简洁省略掉了。
  2. 回调逻辑可能与大家预想的不一样,我之前也以为回调应该是由支付平台发起。但 APPLE, GOOGLE 均为图中逻辑,PAYPAL 则为平台回调,所以要去看各平台的开发者文档。

传统测试

客户端层面的测试,一定是只能验证基本流程的,完成一次支付流程看看是否正确获得物品、中途中断、或者再继续完成支付等几个场景。

即使是通过一些接口测试的手段,因为核心的数据交互在服务端和支付平台之间(上图 4 和 5),也是无能为力的。只能发现更改了回调数据以后,服务端返回支付失败。

那么这样是否测试充足了呢?

测试场景

先来想象一些典型的测试场景,再考虑通过何种方式测试。这也是我经常对新人说的,最重要的是知道自己想做什么,然后再想怎么做。

下面这些场景都是传统测试无法实现的:

支付验证结果有多少种状态?每种是否触发不同的服务端逻辑?
异常情况如何处理?比如验证超时、无响应等
更复杂的商品如订阅(自行了解),如何操纵过期时间?
如何实现自动化?客户端层面去做也不是不可能,但是实在牵连太多

新的测试(mock)

考虑一下上面的问题就会发现,核心的核心就是我们需要控制支付平台到服务端的数据交互,这样也就间接控制了服务端的各种逻辑。

自动化测试同理,我们想要达到的效果是,不通过真实的购买就可以走完服务端处理流程。比如自己构造一个收据,服务端就可以验证成功。

要做到这种控制,只能通过 mock 来实现,将系统结构变为如下。

这时,已经没有外部支付平台的地位了,所有的数据流都在我们的控制之内。如何实现?很简单,只要把服务端代码的外部验证地址放到配置中,测试时修改配置就可以了。

mock server 主要实现两块逻辑,一是根据不同的地址返回不同平台的数据格式;二是实现控制逻辑,可以设定某个单据成功或是失败,或者是模拟各种异常状态。

那么,一个典型的测试用例就可以是这样的:

成功支付

  1. 客户端(测试代码)向服务端下一个订单,获取到商品信息
  2. 调用 mock server 的控制接口,声明某个票据或订单需要返回成功
  3. 直接构造支付成功的票据发往服务端
  4. 验证服务端返回的结果是否成功,并检查物品发放情况

再来个复杂一些的:
订阅过期

  1. 客户端完成一个商品订阅(比如有效期一个月,并验证成功)
  2. 调用 mock server 控制接口,设置某个订单的订阅状态为已过期
  3. 客户端使用此订阅商品的功能(一定是与服务端交互的)
  4. 验证服务端是否返回失败的状态

典型用例

到此,测试可以通过代码来实现,自动化就要发挥威力了。下面列一些典型的测试用例,都是发现过真实问题的哦,可以想一想如何实现用例。

关于安全

所有的一切,我们都是基于两个假设:
支付平台是安全的。即查询某个订单返回支付成功,那这个订单就一定是已经支付的。
服务端本身是安全的。即用户无法拦截篡改服务端的数据,否则服务端也成了 “客户端”。

所以这里也可以稍微引申一些安全测试的层次,通常可按如下分:

我们上面所涉及的种种测试只可能属于业务安全。而像黑客登录了服务器(假设 2),篡改了验证结果,这种属于主机安全,是完全不同的领域。

业务安全上,也有太多的问题不是通过代码逻辑就可以简单解决的。最知名的就是刷单,也就是黑产,正常购买商品后再通过平台的渠道退款,商品是不会自动退回的。如果你们的业务很火,那么会有大量的人做这件事,需要花心思去避免这种损失。(我公司的一款产品每天被退款的商品超过 1W 刀)

一些建议

读支付平台的开发文档!!!
支付平台的开发文档,一般都会提供最佳实现方式。平台比个人更在意安全性。

看服务端源码
看得懂逻辑,才更可能了解逻辑的漏洞
比如服务端代码中有验证失败时重试的逻辑,如果不看代码是如何也想不到这里的测试点的。


↙↙↙阅读原文可查看相关链接,并与作者交流