Testing & Troubleshooting

This guide describes how to test in-app purchases in your app and provides useful tips.

Android

Testing purchases in Android is the same as real purchases. However, you will need to add your Google account to application license testing in order to make purchases for free. For more information about the testing purchases, please read the official documentation.

Test a Fresh Install

To test a fresh install you will need to:

  • Delete user in Apphud
  • Delete the app

Android SDK doesn't save the User ID / Device ID to the device keychain. This means that deleting the app and user in Apphud will generate a new User ID / Device ID pair. When launching the app after re-install, you will get a fresh user without purchases.

However, if restore purchases are called, Apphud will merge an existing user with a new user. As a result, the User ID will change to the original one and the user will have two devices, with the old and new Device IDs. The original purchase history will be restored.

Clear Purchases History

Google Play doesn't fully clear purchase history. The best way is to change your Google account on the device, but if this is not an option, you can refund/cancel transactions in Google Play console > Order management.

Troubleshooting

Common steps

In case you experience any issues with in-app purchases in your Android app, please check the following:

  • Make sure you have correctly set up Google Play Service Credentials .
  • View debug logs in Android Studio. To enable debug logs call: Apphud.enableDebugLogs() before SDK initialization. If you have any issues with in-app purchases, the error will populate in console.
  • Make sure you have entered the correct Android Package Name in Apphud app settings.
  • If you don't use Apphud billing client, make sure Apphud.syncPurchases() is called after a successful purchase or restoration.

iOS

Testing Environments

There are two ways to test purchases in iOS: using Local StoreKit Testing (Xcode builds only) or Sandbox Testing (Xcode or TestFlight builds).

You can read more about differences and limitations between Sandbox and StoreKit Testing in this documentation.

Local StoreKit Testing

Apphud supports receipts made using StoreKit Configuration File. No need to update SDK and no additional code is required. With StoreKit Configuration File enabled in your scheme, you can make purchases in iOS Simulator (or real device) as usual by calling Apphud.purchase(product){...} method. You can read more about StoreKit Configuration File set up in our blog post.

Note, that receipt generated by Local StoreKit is limited, and doesn't contain many fields that are available when verifying receipts through Apple. For example, trial period field is missing, as well as pending_renewal_info, which is a special sub-json containing important fields about the state of subscription, like auto-renew, in billing retry, etc.

Limitations

  • Unable to detect trial status of the subscription. That means after purchasing subscription with trial period, you will get ApphudSubscripton model with regular status in the response. But don't worry, that is only local receipt issue, everything will work fine in production!
  • ApphudSubscripton models generated from local receipts will always have isInRetryBilling = false and isAutorenewEnabled = true, unless expired.
  • Simplified renewals logic. Local receipt is just a snapshot and we are unable to detect renewals of such receipts, until a fresh local receipt is sent to our backend. Which means that subscription will move to expired state after the first transaction is expired. When launching the app again in simulator, a new receipt will be automatically sent to Apphud, and subscriptions data will be updated. No actions required on developer side. Again, everything will work just fine in production.
  • Apphud doesn't create transactions and events of local receipts. In Apphud User page, subscription will only have status and expiration date.
  • Testing promotional offers using Xcode generated Subscription Offers key will be supported soon.
  • Testing eligibilities is not supported.
  • Testing Apphud Rules is not supported.

📘

Note

We don't validate local receipts with the StoreKit certificate at the moment (you can test without them, everything would work fine). The certificates required for testing automation scenarios. Will be added later.

Sandbox Testing

Sandbox testing requires creating Sandbox User if testing in Xcode build. TestFlight builds use real Apple ID. You can read more about differences and limitations between Sandbox and StoreKit Testing in this documentation.

Create Sandbox User

Creating a sandbox test user is required for making purchases in Xcode build. You can create as many sandbox users as you want in App Store Connect. In most cases one sandbox user is enough, but you may want to create a new sandbox user in order to clear purchases history.

👍

Note

You can read more about testing and setting up auto-renewable subscriptions in our blog.

To test in app purchases you need to create sandbox user. Go to App Store Connect and open "Users and Access", then – "Sandbox Testers":

1317

You can sign in to Sandbox account separately from your App Store account since iOS 12. Sign in to sandbox here:

375

📘

Note

You can read more about creating sandbox testers here.

Sandbox Purchasing

Purchasing process is the same as real in-app purchases. The only difference is accelerated time and impossibility to cancel subscription manually. Subscriptions in sandbox renew 6 times per day maximum.

Real durationDuration in sandbox
1 week3 minutes
1 month5 minutes
2 months10 minutes
3 months15 minutes
6 months30 minutes
1 year1 hour

Which Events Can Be Tested?

You can test following all events in sandbox except REFUNDED events.

However, REFUNDED events can still be tested using Local StoreKit Testing.

📘

Note

You can read more about events here.

Eligibilities

❗️

Important Note

Testing Eligibilities are not supported in Local StoreKit Testing mode. Use Sandbox testing instead.

Apphud checks whether the given user is eligible for purchasing introductory or promotional offers by searching for transactions in ANY of the current user's subscriptions. This means that if your Apphud user has several app store receipts from previous sandbox Apple IDs, you may get unexpected results.

Apphud User may have several subscriptions with the same product from a different Apple ID account if you were changing sandbox Apple ID without deleting Apphud User. In this case, Apphud will merge multiple subscriptions under the same user. That is why it is recommended to delete your Apphud User from Apphud Dashboard when testing the app from another Apple ID.

Testing Eligibility for Trial / Introductory Offer

To correctly use the checkEligibilityForIntroductoryOffer method please do the following:

  1. Please make sure that your Apphud User doesn't contain multiple App Store receipts from previous test Apple IDs. If so, delete your user.
  2. Note that the "Reset Eligibility" option (Settings > App Store > Sandbox Account > Manage > Your App) is not supported. Apple doesn't delete old trial transactions from the receipt, so there is no technical way to correctly determine introductory eligibility after you pressed the "Reset Eligibility" button.
  3. Test on a clean Apple ID account. Create a fresh sandbox Apple ID account.
  4. When running the app for the first time after deleting (i.e. fresh install), keep in mind that you would need to submit App Store Receipt to Apphud either by calling Apphud.migratePurchasesIfNeeded{} or by tapping the restore purchases button in your App's UI. This is required only in Sandbox because apps running from Xcode/TestFlight initially don't have App Store Receipts and Apphud doesn't automatically refresh missing App Store receipts to avoid Apple ID password prompt. At production, however, everything will work okay, because App Store receipts always exist in apps downloaded from the App Store.
  5. Now you can test your eligibility method: the method will return true because you didn't yet use the introductory offer. After you have used the introductory offer, the method will return false.

Testing Eligibility for Promotional Offer

To correctly use checkEligibilityForPromotionalOffer method please do the following:

  1. Please make sure that your Apphud User doesn't contain multiple App Store receipts from previous test Apple IDs. If so, delete your user.
  2. When running the app for the first time after deleting (i.e. fresh install), keep in mind that you would need to submit App Store Receipt to Apphud either by calling Apphud.migratePurchasesIfNeeded{} or by tapping the restore purchases button in your App's UI. This is required only in Sandbox because apps running from Xcode/TestFlight initially don't have App Store Receipts and Apphud doesn't automatically refresh missing App Store receipts to avoid Apple ID password prompt. At production, however, everything will work okay, because App Store receipts always exist in apps downloaded from the App Store.
  3. Now you can test your promo eligibility method: the method will return true if Apphud User has any subscription and false if the user doesn't have any subscriptions.

Test a Fresh Install

In order to test a fresh install in Apphud you need to:

  • Delete your user in Apphud.
  • Delete the app.
  • Optionally clear purchase history.

Clear Purchases History

If you make purchases in Local StoreKit Testing, deleting the app and Apphud User will be enough since it will reset the receipt.

However, when making purchases using Sandbox / TestFlight mode, you will also need to change sandbox Apple ID.

Troubleshooting

Payment cancelled but Apphud.hasActiveSubscription method still returns true

Please check your Apphud user's purchase history. Probably, you made purchases earlier for this user. Try testing with a fresh install.

Payment completed but it does not appear in Apphud / Apphud.hasActiveSubscription returns false

In case you experience any issues with in-app purchases in Apphud, please check the following steps:

  • Make sure you have entered correctly Shared Secret .
  • View debug logs in Xcode. To enable debug logs call: Apphud.enableDebugLogs() before SDK initialization. If you have any issues with in-app purchases, the error will populate in the console.
  • Make sure you have entered the correct iOS Bundle ID.

Errors during the purchase

When you make purchases using Apphud, in response you get ApphudPurchaseResult object. If the purchase failed, you get an error that may be of three classes:

  • SKError from StoreKit with SKErrorDomain codes. This is a system error when purchasing transactions. Apphud is not responsible for SKError domain codes, because they are returned directly from App Store.
  • NSError from HTTP Client with NSURLErrorDomain codes. This is a network/server issue when uploading receipts to Apphud.
  • Custom ApphudError without codes. For example, if couldn't sign a promo offer or couldn't get an App Store receipt. This is an SDK level error.

"Cannot connect to iTunes Store" / SKError.code = unknown

If you experience issues like "Cannot connect to iTunes Store" or your SKPaymentTransaction fails with SKError.code = unknown, it is a generic error message that App Store generates. This may be one of the:

  • Transaction is interrupted by the Ask to Buy feature
  • Transaction is interrupted due to Strong Customer Authentication
  • There was another problem with the user's Apple ID account.

Apphud SDK is unable to fix the issue and the developer is responsible to handle these types of errors. The best practice is to check the error code, and if it is SKError.code = paymentCancelled, then the payment was cancelled by the user, do nothing in this case. In other cases, you may want to display the error message to the user.

Another case of a high percentage of failed transactions is Strong Customer Authentication (SCA) for European Economic Area, which requires all payments to be verified. In this case, the current transaction goes to the failed state, and once payment is approved, a new transaction with the purchased state is created. Apphud SDK automatically handles interrupted purchases for this case as well, you have nothing to do about it.

Failed to get App Store Receipt

This error means that the App Store receipt is missing on the device even after a successful purchase. Apphud SDK automatically tries to recover the receipt with SKReceiptRefreshRequest , but if the receipt is still missing "Failed to get App Store Receipt" error will be thrown. Apphud SDK is unable to recover this issue due to an iOS / Sandbox bug. Unfortunately, this often happens to Apple Reviewers.

App was rejected due to purchase problems

If your app got rejected and worked fine in Xcode / TestFlight builds during the test, don't worry – your app probably works fine, but reviewers are having issues with their devices. They may experience the following issues:

  • Transaction failed with SKError.code = unknown
  • Transaction complete, but Failed to get App Store Receipt error is thrown in case App Store receipt is missing on the device even after purchase.
  • Transaction complete, but Sandbox server is down. In this case, Apphud SDK will throw an error.

The first thing you need to do is to determine, whether SKProducts were actually loaded in the reviewer's paywall screen. In most cases, the reviewer attaches a screenshot of their error/paywall screen.

If products did load correctly and prices were displayed, then it is probably a payment issue. The next thing is to try to find the reviewer's error in your product analytics, if available.

Workaround:

  • Just re-submit the same build and send the reviewer a message about the Sandbox issue.
  • Add a code workaround for Sandbox only by letting users go through the paywall screen if the transaction's state is purchased. Here is an example:
extension ApphudPurchaseResult {
    var success: Bool {
        subscription?.isActive() ?? false ||
            nonRenewingPurchase?.isActive() ?? false ||
            (Apphud.isSandbox() && transaction?.transactionState == .purchased)
    }
}

Apple reviewer provided tips regarding validating receipt in a sandbox environment

Sometimes, the reviewer provides a tip with the following message:

When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code "Sandbox receipt used in production," you should validate against the test environment instead.

This tip is useless since Apphud automatically handles sandbox and production environments.

App was rejected due to the use of ASIdentifierManager (IDFA)

Since Apphud SDK 2.1.0 ASIdentifierManager is no longer used as well as AdSupport framework:

pod update 'ApphudSDK'

"Failed to load SKProducts from the App Store" error

Failed to load SKProducts from the App Store, because product identifiers are invalid error means that Product IDs provided in Apphud Product Hub are invalid. Please check the following:

  • Check that the Product ID is correct and doesn't contain hidden spaces, tabs, etc.
  • You agreed to the latest Apple Developer Program License Agreement.
  • You completed all the financial agreements as described in the Agreements, Tax, and Banking.
  • You use the correct bundle ID and signing credentials.

For more information please read the following.

"Attempted to decode store response" error while fetching products

If you encounter the decoding error Failed to load SKProducts from the App Store, error with reason Attempted to decode store response **** , that probably means that you are running the app in iOS Simulator which doesn't support App Store features. Try running on a physical device instead.
On iOS Simulator you can test App Store features using Local StoreKit configuration file.

Products are not appearing in Paywall screen

Keep in mind, that StoreKit products are not appearing immediately after the first app launch. The network request is made to App Store to fetch the products. And if your app displays a paywall screen immediately after launch, be ready to handle case, when products are fetched after the screen is shown.
You can do any of the following:

  • Add observer for Apphud.didFetchProductsNotification()
  • Implement func productsDidFetchCallback(_ callback: @escaping ([SKProduct]) -> Void)
  • Implement func apphudDidFetchStoreKitProducts(_ products: [SKProduct]) delegate method.

Once products are fetched, update your UI.

Products Loading Tip (Advanced)

📘

Note

As you may know, at the launch of the app Apphud SDK fetches product identifiers from the server and then fetches SKProducts from App Store. If you wish to improve the product's loading speed, you can skip request to Apphud by hardcoding product identifiers, using func apphudProductIdentifiers() -> [String] delegate method. See ApphudDelegate for details.

Purchases are ready for sale, but not available in the app

When your app update is approved with new in-app purchases, they may not be available for purchase for the first few hours after approval. This is an App Store cache issue and not related to Apphud SDK (or any other SDKs). In most cases your in-app purchases are ready immediately after approval, however, it may take up to 24 hours.