StoreKit 2 Sandbox use can't buy the same subscription again

My StoreKit 2 tests flow looks like this:

  1. Buy subscription lvl 1
  2. Buy subscription lvl 2
  3. Buy subscription lvl 1

The problem is that when trying to buy the same subscription again with Sandbox user - the iOS dialogs to confirm Payment are not shown. Product.PurchaseResult in .success but if checking in Settings - the subscription is not changed.

What's also interesting: it works if changes directly from System Settings.

Any ideas of what is going on would be appreciated.

Replies

I believe what you are observing is the “downgrade“ behavior, which don’t go into effect immediately. Subscriber keeps their existing plan and on their renewal date they are downgraded to the other plan. The opposite is the case for upgrades, where we do upgrade you immediately (charged, new period begins, and receive pro-rated refund for previous service unused). What plan changes are an upgrade or downgrade is determined by how you setup your products in your subscription group in App Store Connect..

Think of this scenario: If I’m on the monthly plan and want to move to annual, most devs and customers would want that to be instant (upgrade) as I could lock in the annual plan cost savings right away. So the dev must set the annual plan at a higher level than the monthly. With this setup, In the inverse, if move from annual to the monthly plan, that plan change goes into effect at the end of my annual plans period.

See this description of plan “rankings” and how the levels in App Store Connect determine this behavior: https://developer.apple.com/app-store/subscriptions/#ranking and here for setup: https://help.apple.com/app-store-connect/#/dev75708c031

  • But in the case of an upgrade/downgrade subscription, everything works as expected. The problem pops up when trying to buy a subscription again that was bought, and upgraded to another one. So the sandbox user can't buy the same subscription again even when it is not the actual one or the expected one (will be turned on after downgrade).

  • Also, it is possible to complete such payment from IOS Settings -> Sandbox -> Manage (sometimes have to provide sandbox user credentials two times). But not possible by Storekit Product.purchase().

    Transaction.finish() call or ClearPurchaseHistory option in AppStoreConnect have no effect.

  • Thank you for answering but the upgrade/downgrade flow works fine. The problem occurs when the Sandbox user tries to buy the subscription again after it was upgraded/ downgraded to another one.

    It is possible to do it from Settings->Snbdbox account -> Manage. But it doesn't work from Product. purchase().Extra call of Transaction.finish() or Clear Purchase History for sandbox user AppstoreConnect has no effect.

Add a Comment

The upgrade/downgrade flow works fine. The problem occurs when the Sandbox user tries to buy the subscription again after it was upgraded/ downgraded to another one. Success transaction received but with the old id and no subscription change effect.

By the way, it is possible to do it from Settings -> Sandbox account -> Manage. But it doesn't work from Storekit2 Product. purchase().
Extra call of Transaction.finish() or Clear Purchase History for sandbox user AppstoreConnect has no effect.

If the plan change was an upgrade, there will be a new transaction as they were upgraded and charged immediately. If their plan change was a downgrade or crossgrade, while it was successful there is no new transactions as these plan changes mean they continue their current period and at the time of renewal their plan downgrades/crossgrade to their new choice. So while no transaction is there correctly you will see this change reflected in the renewalInfo object and the property autoRenewPreference will indicate the new product they are downgrade/crossgrading to. If using original storekit, those respective values are pending_renewal_Info and auto_renew_product_id

Not OP but I believe it is this issue: https://developer.apple.com/forums/thread/723126

There is no transaction within Transaction.currentEntitlements after this happens which is completely broken.