iOS-IAP掉单及处理办法
什么是掉单
用户选定商品支付完成后,服务器不能正确及时的获取支付状态,导致这笔已支付的订单未能发货。
掉单可能会出现在哪些环节
手机网络情况复杂多变不稳定。
充值过程中 App 进程因为某种原因被 kill 了,其实支付行为还在系统后台进行着,苹果自己做的,很有可能扣款成功。但是这时候没法为用户充值虚拟货币。
苹果服务器在境外连通性不稳定。
以上三个客观原因,我们无法改变,会导致如下情况:
1. 用户支付完成之后,苹果服务器将支付票据返回给app端,app端发送票据到server。(可能会断网,未能提交到server服务器)
2. server拿到支付票据,请求苹果服务器票据验证。(server连接苹果服务器超时,未能验证票据)。
目前的iOS APP端充值订单流程
名词解释
productID
虚拟产品id, 例如用户选择的6元充值,该6元虚拟商品在app store注册时的productID为ai.ling.zh.iap.1
collectionID
每个充值梯度都有对应的 collectionID,该id的用处为帮助server端判断当前交易的优惠力度
eg:
4.1日 6元梯度 充值六元,得 6000 虚拟币,此时的collectionID 为6040581269478a013a508711
4.7日 6元梯度 充值六元,得 6000 虚拟币的同时,额外赠送 1000 虚拟币, 此时collectionID为6040581269478a013a508722
若这两单交易都出现了掉单的情况,补单时,server会根据不同的collectionID所对应的虚拟币进行补单,补4.1日的6000虚拟币 以及4.7日的6000+1000虚拟币
Apple服务器环境
沙盒环境sandbox:https://sandbox.itunes.apple.com/verifyReceipt
沙盒环境sandbox是提供给开发者进行模拟充值使用的服务器,在该环境下进行的所有充值操作均不会产生真实的现金交易
线上环境production: https://buy.itunes.apple.com/verifyReceipt
只有app上线到App Store,才能使用线上环境production,此时用户充值使用的是真实的现金,进行的真实的充值流程
Receipt-data
向Appstore请求购买产品(假设产品信息已经取得),Appstore验证产品成功后,从用户的Apple账户余额中扣费的同时,向客户端返回一段receipt-data,里面记录了本次交易的证书和签名信息,app端拿到receipt-data后,会进行一次base64编码,生成对应的字符串,为方便理解以及文章后续的流程解释,该字符串命名为base64EncodedReceiptDataString
transactionIdentifier
IAP充值后会给每一个购买的产品生成一个交易ID transactionIdentifier, 包含在receipt-data中返回
userId
这里特指当前登录的用户手机号
KeyChain
根据苹果的介绍,iOS设备中的Keychain是一个安全的存储容器,可以用来为不同应用保存敏感信息比如用户名,密码,网络密码,认证令牌。苹果自己用keychain来保存Wi-Fi网络密码,VPN凭证等等。它是一个sqlite数据库,位于/private/var/Keychains/keychain-2.db,其保存的所有数据都是加密过的。
开发者通常会希望能够利用操作系统提供的功能来保存凭证(credentials)而不是把它们(凭证)保存到NSUserDefaults,plist文件等地方。保存这些数据的原因是开发者不想用户每次都要登录,因此会把认证信息保存到设备上的某个地方并且在用户再次打开应用的时候用这些数据自动登录。Keychain的信息是存在于每个应用(app)的沙盒之外的。
这个数据是存在系统的,所以就算卸载软件,也照样存在机器中,除非恢复系统
充值接口流程
eg: 从用户点击某一个梯度(6元)进行充值开始,到用户6000虚拟币入账结束,app端及server端+APPLE server端会进行如下四步
-
1 app端调用 server 接口
CreateAddLukaCoinCreditOrderMutation
, 生成一个订单号 orderID 以及该订单的服务器环境为沙盒还是线上正式环境。 -
2 app端拿到订单的APPLE服务器环境以及productID,向Appstore请求购买产品,Appstore验证产品成功后,从用户的 Apple 账户余额中扣费,同时向客户端返回一段receipt-data,里面记录了本次交易的证书和签名信息。此时,为防止掉单的情况,app端同时在 keyChain 中存储该笔交易的 productID + orderID + transactionIdentifier + userId。
-
3 app端拿到第二步中内购结果返回 receipt-data 信息, 使用server接口
CheckPaymentResultMutation
, 将orderID + base64EncodedReceiptDataString + transactionIdentifier 传给server,进行二次校验,若校验成功,server端会将该笔交易对应的luka币充值到用户的账号余额中。 -
4 server二次校验成功结果返回后,app端标记该交易结束,同时从keyChain中删除该笔交易。
掉单问题处理
- 针对可能出现的掉单环节,app在一启动就设置监听,如果有未完成标记的支付(即KeyChain中存储未标记完成的交易订单),则会回调
SwiftyStoreKit.completeTransactions
进行充值重试的操作。
充值重试操作的触发场景
- 每次在用户账号不用重新登陆的情况下,重启,(满足能在启动时拿到userID, 通过userID筛选KeyChain中当前用户的丢单数据)会查询当前有无掉单数据,如果有的话,将在后台默默重试充值,直到KeyChain保存的未完成标记的交易数据为空。
掉单投诉
客服同学如果遇到丢单的问题时:
-
1 首先引导用户kill掉app,进行重启操作(触发丢单重试操作,注意事项请看👆👆👆充值重试操作的触发场景👆👆👆),若重启金额仍无变化,可再收集用户信息进行补单。
-
2 收集尽可能多的手机信息,拿到手机型号,系统版本,以及用户在充值过程中,有无异常情况发生,比如低电量提示,电话接入,弱网断网等等情况
-
3 需要用户提供支付证明
3.1 首选方案:在用户绑定APPLE ID的邮箱中,会收到"APPLE"发来的"APPLE提供的收据"为主题的邮件,需要用户提供邮件截图,作为补单凭证
3.2 备选方案:如果用户使用了微信支付宝等三方app支付的话,尽量引导用户截图APPLE邮箱的收据,若用户无法提供邮件证明,也可提供支付交易单号截图。