Non-Consumable内购沙盒正常 苹果审核时可免付费解锁全功能怎么办
关于跨App读取Keychain的可能性
不会。iOS系统默认Keychain存储是基于App的团队ID+Bundle ID做隔离的,除非你主动开启了Keychain共享能力,且其他App和你属于同一个开发团队、加入了同一个共享组,否则完全不可能跨App读取同键名的存储数据,这个原因可以优先排除。
可能的故障原因
变量初始值错误
你的purchased变量没有设置默认兜底值,代码里仅当Keychain能读到purchased键值时才会给变量赋值,首次安装App时Keychain无对应值,如果purchased变量的初始默认值为true,就会直接解锁所有付费功能。本地测试时因为你之前的测试操作已经在Keychain写入了值,覆盖了初始值,所以不会触发问题,审核人员首次安装时就会命中该问题。
修复方案:添加默认值兜底,修改读取逻辑:override func viewDidLoad() { super.viewDidLoad() purchased = keychain.getBool("purchased") ?? false }权限校验逻辑漏洞
你的校验判断为if lists.count > 0 && purchased == false才触发购买拦截,也就是说只要满足lists.count == 0(首次添加清单)或者purchased == true两个条件任意一个,就可以直接进入功能。你设置的规则是未购买用户可创建1个清单,很可能审核人员首次使用时添加第一个清单没有触发拦截,就误认为所有功能都已解锁。另外你没有贴单清单添加条目的校验逻辑,也可能存在类似的判断漏洞。内购交易处理逻辑缺陷
你当前的代码在交易处理完成后没有调用SKPaymentQueue.default().finishTransaction(transaction),未完成的交易会一直留在系统交易队列中,每次App启动添加交易观察者时都会重新触发回调。如果审核人员的沙盒测试账号之前购买过同产品ID的内购(比如你App的旧版本、同团队其他测试App),启动时就会收到purchased状态的交易回调,直接将purchased状态设为true,无需本次购买。
修复方案:在交易处理完成后强制结束交易:func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { if transaction.transactionState == .purchased { purchased = true keychain.set(purchased, forKey: "purchased") resetView() // 新增结束交易代码 SKPaymentQueue.default().finishTransaction(transaction) }else if transaction.transactionState == .failed { purchased = false keychain.set(purchased, forKey: "purchased") // 新增结束交易代码 SKPaymentQueue.default().finishTransaction(transaction) } } }首次安装场景测试缺失
本地测试时你可能没有彻底卸载App、清除Keychain残留数据就进行测试,无法覆盖审核人员全新安装的场景。你可以卸载App后重启设备再安装测试,复现首次启动的状态。第三方Keychain库bug
如果你使用的是第三方Keychain封装库,部分旧版本库在特定iOS版本下存在读取不存在的键返回true而非nil的问题,兜底默认值的方案也可以同时规避该问题。
内容的提问来源于stack exchange,提问作者Faisal Alaseeri

