prefácio
As compras no aplicativo, relacionadas à receita real, geralmente são a principal prioridade de todas as empresas e também a principal prioridade de nossos desenvolvedores. Portanto, hoje discutiremos o conteúdo das compras no aplicativo dos mais importantes. O simples processo de compra no aplicativo é concluído passo a passo através do processamento de cada cena especial. Se você acha que é muito longo, assista a parte final
Compra básica no aplicativo
Logo no início do artigo, simulamos um cenário mais básico de processamento de compras in-app, ou seja, para garantir que o processo normal seja creditado na conta e, em seguida, fizemos otimizações subsequentes de acordo com diferentes cenários.
O processo básico de compra no aplicativo é o seguinte:
Processo do cliente:
graph LR
用户充值 --> 下单传递userId --> 下单成功 --> 充值 --> 苹果返回成功 --> 上报凭证传递userId --> 收到成功回调 --> 关闭交易事务
Processo do lado do servidor:
Para fazer um pedido:
graph LR
接收客户端下单信息 --> 订单信息入库 --> 返回订单号
Documentos de relatório:
graph LR
接收客户端上报凭证 --> 找苹果校验成功 --> 返回客户端成功 --> 客户端关闭交易事务
找苹果校验成功 --> 通过userId查找对应的订单 --> 充值到账并写入凭证的transactionId
Diagrama de interação bem-sucedida:
Devido ao conteúdo excessivo do diagrama de interação, apenas o processo de interação do processo bem-sucedido é exibido temporariamente.
sequenceDiagram
用户->>客户端: 点击充值
客户端->>服务端: 下单(带userId)
服务端->>服务端: 订单入库
服务端-->>客户端: 下单成功
客户端->>苹果: 调用充值接口
苹果-->>用户: 充值操作
用户->>苹果: 充值完成
苹果-->>客户端: 回调充值成功
客户端->>服务端: 上报凭证(带userId)
服务端->>苹果: 调用凭证验证接口
苹果-->>服务端: 凭证验证合法
服务端-->>客户端: 上报成功
客户端-->>用户: 提示充值成功
服务端->>服务端: 通过userId查询订单信息
服务端->>服务端: 充值到账并写入凭证的transactionId
problema e analise
No processo de uso de compras básicas no aplicativo, o problema mais comum é o problema anormal causado pela troca de conta.
Cenário 1: alternar contas entre recargas
Ambiente da cena:
Um dispositivo, duas contas de login A e B.
Processo de exceção:
A conta A faz um pedido de recarga e, quando a recarga é concluída e a Apple não retorna, ela muda para a conta B e o certificado passa a ser informado por B.
situação real:
No jogo, os gerentes do sindicato precisam trocar de conta com frequência, ou trocar a conta do trompete para realizar operações diferentes. Após a recarga normal, se o usuário não perceber o status da conta e trocar de conta diretamente, essa situação pode ocorrer .
Desempenho anormal:
- Se verificar o registro do pedido neste momento. Ou seja, ele consultará o pedido anterior feito pelo userId antes e descobrirá que o pedido não foi feito antes e não pode ser recebido, resultando em pedido perdido.
- Se o registro do pedido não for verificado neste momento e a conta B for verificada diretamente após a verificação direta, haverá uma sequência de pedidos.
solução:
Usando o esquema de bloqueio do ID do dispositivo (IDFV), apenas um pedido pode existir para cada dispositivo e cada ID de mercadoria no processo completo. Ao relatar o voucher, o pedido é recuperado de acordo com o ID do dispositivo e creditado na conta.
Desbloqueie a atualização da cena:
Desbloqueie a cena |
---|
Apple retorna sucesso e relata sucesso do certificado |
Apple retorna falha de recarga |
Atualização do processo:
Processo do cliente:
graph LR
用户充值 --> 下单传递IDFV --> 下单成功 --> 充值 --> 苹果返回成功 --> 上报凭证传递IDFV --> 收到成功回调 --> 关闭交易事务
充值 --> 苹果返回失败 --> 上报失败信息(传递IDFV) --> 收到接收回调 --> 关闭交易事务
Processo do lado do servidor:
Para fazer um pedido:
graph LR
接收客户端下单信息 --> 查找订单列表中对应设备id是否存在加锁的订单
查找订单列表中对应设备id是否存在加锁的订单 --> 存在加锁订单 --> 返回锁单中
查找订单列表中对应设备id是否存在加锁的订单 --> 不存在加锁订单 --> 创建订单并入库 --> 订单置为锁单状态 --> 返回订单号
Documentos de relatório:
graph LR
接收客户端上报凭证 --> 找苹果校验成功 --> 返回客户端成功 --> 客户端关闭交易事务
找苹果校验成功 --> 通过设备id查找对应的订单 --> 充值到账
通过设备id查找对应的订单 --> 订单解锁并写入凭证的transactionId
Falha ao relatar:
graph LR
接收客户端上报失败信息 --> 通过设备id查找对应的订单 --> 订单解锁 --> 返回接受成功
接收客户端上报失败信息 --> 失败信息写入日志
Diagrama interativo de recarga bem-sucedida:
sequenceDiagram
用户->>客户端: 点击充值
客户端->>服务端: 下单(带IDFV)
服务端->>服务端: 订单入库并加锁
服务端-->>客户端: 下单成功
客户端->>苹果: 调用充值接口
苹果-->>用户: 充值操作
用户->>苹果: 充值完成
苹果-->>客户端: 回调充值成功
客户端->>服务端: 上报凭证(带IDFV)
服务端->>苹果: 调用凭证验证接口
苹果-->>服务端: 凭证验证合法
服务端-->>客户端: 上报成功
客户端-->>用户: 提示充值成功
服务端->>服务端: 通过IDFV查询订单信息
服务端->>服务端: 订单解锁并写入凭证的transactionId
服务端->>服务端: 充值到账
Cenário 2: desbloqueie a exceção
Ambiente da cena:
Durante o processo de recarga, feche o aplicativo e cancele o pagamento
Processo de exceção:
Depois que o usuário fizer o pedido e o processo de recarga, feche o aplicativo e cancele o pagamento. Neste ponto, a Apple não retorna mais nenhuma informação de pagamento.
Desempenho anormal:
Como a Apple não retornou nenhuma informação, o servidor não desbloqueou o pedido, portanto, não pode ser recarregado antes de desinstalar e reinstalar.
situação real:
O usuário pode simplesmente clicar para recarregar por engano. Para evitar a recarga bem sucedida, o aplicativo pode ser fechado diretamente e a recarga será cancelada. Esta situação ocorrerá neste momento.
solução:
在唤起充值之后,如果未返回过充值成功,则在下次服务端返回充值失败时,可以假定用户充值失败,则直接进行解锁操作。
解锁场景更新:
解锁场景 |
---|
苹果返回成功并上报凭证成功 |
苹果返回充值失败 |
重启应用后的首次下单返回锁定,且苹果未返回充值信息 |
流程更新:
客户端流程:
graph LR
用户充值 --> 下单传递IDFV --> 下单成功 --> 充值 --> 苹果返回成功 --> 上报凭证传递IDFV --> 收到成功回调 --> 关闭交易事务
充值 --> 苹果返回失败 --> 上报失败信息(传递IDFV) --> 收到接收回调 --> 关闭交易事务
下单传递IDFV --> 返回锁单中 --> 判断为打开应用后首次下单 --> 上报苹果未返回 --> 收到上报回调 --> 下单传递IDFV
其余流程无需更新
场景三:卸载重装无法到账
场景环境:
一台设备,充值过程中卸载重装应用
异常流程:
账号下单充值,充值完成且在苹果未返回的情况下,卸载重装应用,此时凭证上报时候的设备id发生了变化,导致无法找到设备ID对应的订单。(如果使用保存在 keychain 中的 idfv,则恢复出厂设置时也将无法获取到之前保存的 idfv)
现实场景:
用户在充值之后,可能由于网络问题,暂时无法到账,瞬间弃坑删除应用,再过一段时间的冷静期后,又重新继续下载应用,此时就会出现该情况
解决方案:
上报凭证时候有两种地方获取,一个是旧的直接通过 transactionData 获取, 一种是官方推荐的 NSBundle 中获取。我们需要优化的点在于两处内容同时上报,解析 transactionData 时,可以获取到 UniqueVendorIdentifier ,这个与发起充值时候的 IDFV 的值是相同的,我们就可以针对这点来做订单的关联,并使用bundle中的凭证信息做安全相关的检验。(实际上,直接使用 transactionData 中的数据也是可以的,目前通过苹果验证后的暂未发现异常情况)
流程更新:
服务端流程:
上报凭证:
graph LR
接收客户端上报凭证 --> 找苹果校验成功 --> 返回客户端成功 --> 客户端关闭交易事务
找苹果校验成功 --> 提取凭证中的UniqueVendorIdentifier --> 查找对应的订单 --> 充值到账并写入凭证的transactionId
查找对应的订单 --> 订单解锁
其余流程无需更新
场景四:充值成功但是到账慢
情况有多种,但是表现类似,就放一起讨论 环境场景:
- 设备本身网络较慢
- 服务器与苹果链接慢
- 服务端异常导致入库失败
异常流程:
- 玩家本身网络慢,导致苹果返回充值成功之后,上报凭证服务端超时,按照正常流程则需要苹果再次下发充值成功才会再次上报,间隔时间长。
- 服务器找苹果校验凭证信息,接口调用时间过长导致超时,返回客户端失败,从而等待下次上报
- 服务器本身出现异常(内存满了、发布中等)导致入库异常,返回客户端失败等待重新上报
现实情况:
电梯中或者高铁上玩手机进行充值,时常会有信号不好的时候,此时即为该情况
解决方案:
- 客户端收到苹果下发的充值成功信息后,保存凭证到内存中再进行上报凭证到服务端的操作,上报成功后移除对应凭证信息,上报失败则进行延迟重试处理
- 服务端收到上报的凭证时,入库成功后则直接返回成功给客户端,然后再调用苹果接口校验凭证是否合法,如果发生超时情况,则延后进行重试。
更新的流程放在结尾处
结尾
以上为旧版 storekit 所使用的方案,在目前已知情况下除了苹果自身多次扣款,其余情况都能保证充值的正常到账。新版的 storeKit 可以直接使用 appAccountToken 进行关联。如果有其他情况,欢迎一起讨论
完整解锁场景
解锁场景 |
---|
苹果返回成功并上报凭证成功 |
苹果返回充值失败 |
重启应用后的首次下单返回锁定,且苹果未返回充值信息 |
完整流程
客户端流程:
graph LR
用户充值 --> 下单传递IDFV --> 下单成功 --> 充值 --> 苹果返回成功 --> 保存凭证信息 --> 上报凭证 --> 收到成功回调 --> 关闭交易事务 --> 移除保存的凭证
上报凭证 --> 收到异常回调 --> 延时30s --> 上报凭证
充值 --> 苹果返回失败 --> 上报失败信息(传递IDFV) --> 收到接收回调 --> 关闭交易事务
下单传递IDFV --> 返回锁单中 --> 判断为打开应用后首次下单 --> 上报苹果未返回 --> 收到上报回调 --> 下单传递IDFV
服务端流程:
下单:
graph LR
接收客户端下单信息 --> 查找订单列表中对应设备id是否存在加锁的订单
查找订单列表中对应设备id是否存在加锁的订单 --> 存在加锁订单 --> 返回锁单中
查找订单列表中对应设备id是否存在加锁的订单 --> 不存在加锁订单 --> 创建订单并入库 --> 订单置为锁单状态 --> 返回订单号
上报凭证:
graph LR
接收客户端上报凭证 --> 请求苹果凭证校验接口 --> 苹果校验返回成功 --> 返回客户端成功 --> 客户端关闭交易事务
苹果校验返回成功 --> 提取凭证中的UniqueVendorIdentifier --> 查找对应的订单 --> 充值到账并写入凭证的transactionId
查找对应的订单 --> 订单解锁
请求苹果凭证校验接口 --> 找苹果校验超时 --> 延时30s --> 请求苹果凭证校验接口
上报失败:
graph LR
接收客户端上报失败信息 --> 通过设备id查找对应的订单 --> 订单解锁 --> 返回接受成功
接收客户端上报失败信息 --> 失败信息写入日志
充值成功交互图:
sequenceDiagram
用户->>客户端: 点击充值
客户端->>服务端: 下单(带 IDFV)
服务端->>服务端: 订单入库并加锁
服务端-->>客户端: 下单成功
客户端->>苹果: 调用充值接口
苹果-->>用户: 充值操作
用户->>苹果: 充值完成
苹果-->>客户端: 回调充值成功
客户端->>服务端: 上报凭证
服务端->>苹果: 调用凭证验证接口
苹果-->>服务端: 凭证验证合法
服务端-->>客户端: 上报成功
客户端-->>用户: 提示充值成功
服务端->>服务端: 通过凭证获取的UniqueVendorIdentifier查询订单信息
服务端->>服务端: 订单解锁并写入凭证的transactionId
服务端->>服务端: 充值到账
FAQ
- 多次扣款时如何补发给用户?
这种情况由于是极少数情况,只能定位到多次扣款的那个订单信息,并定位确认最近的订单,并跟用户确认后手动补发
- 如何验证重复上报凭证
上报凭证时候,先判断凭证的 transactionId 是否已存在,如果已存在则说明已上报过,直接返回成功即可
- 是否要做其他的限制
凭证验证成功后,查询订单时,查询凭证充值时间前的订单即可,并确认解析后的包名是否正确,如果需要的话,可以考虑查询到过早的订单时,不进行到账处理并预警该订单
- 我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。