tikhop / TPInAppReceipt

Reading and Validating In App Purchase Receipt Locally.
MIT License
635 stars 95 forks source link

Recipe invalidation after app updates #107

Closed zizlak closed 1 year ago

zizlak commented 1 year ago

Hello, we are facing some unexpected behavior after app updates: the recipe will be invalidated. Bundle version verification leads to the problem, that after the app updates via Appsotre and the new app version has another bundle version - this error: ARError.validationFailed(reason: .bundleVersionVerification) is getting thrown and users don't have access to the paid content in the app until they initiate the restore subscription process actively.

Is it necessary to perform bundle version verification in addition to bundle identifier verification?

tikhop commented 1 year ago

Hi @zizlak, based on Apple documentation we have to verify bundle the version identifier as well:

...

  1. Verify that the bundle identifier, identified as ASN.1 Field Type 2, matches your app’s bundle identifier.
  2. Verify that the version identifier string, identified as ASN.1 Field Type 3, matches the version string in your app’s bundle. ...

Could you please provide the version identifier you have in your bundle and what comes from the receipt?

Thank you!

zizlak commented 1 year ago

Hi @tikhop, when launching on device via XCode: after increasing build number: on the next launch we receive an error because the recipe's bundle number comes from the previous launch and does not match with the current one. Do you know at what time point should the existing recipe being updated with the new build number, so that we could perform validation after that, becouse currently validating the recipe just after launch and it returns an error if the bundle number has been changed.

Screenshot 2023-05-29 at 09 20 32 Screenshot 2023-05-29 at 09 20 07
tikhop commented 1 year ago

@zizlak As I know, the receipt is automatically updated by the system when certain events occur, such as a purchase, subscription renewal or restoring. However, there is a way to manually update the receipt:

InAppReceipt.refresh { (error) in
  if let err = error
  {
    print(err)
  } else {
    initializeReceipt()
  }
}

NOTE: If I'm not mistaken and remember correctly, using this method may bring the auth alert and user will be prompted to enter his appleId+password

When the validation fails, I would suggest to refresh the receipt first and try to validate it again.

Another way to deal with this issue is avoiding bundle version validation at all:

do {
  try? receipt.verifyHash()
  try? receipt.verifyBundleIdentifier()
  try? receipt.verifySignature()
} catch IARError.validationFailed(reason: .hashValidation) {
  // Do smth
} catch IARError.validationFailed(reason: .bundleIdentifierVerification) {
  // Do smth
} catch IARError.validationFailed(reason: .signatureValidation) {
  // Do smth
} catch {
  // Do smth
}
zizlak commented 1 year ago

Thank you @tikhop it really helped.