When is ProcessPurchase called?

I’m trying to control when our app refreshes the receipts from the app stores for autorenewing subscriptions. From the testing I’ve done, it seems that InitiatePurchase itself does not refresh Apple receipts, and the receipts you have access to after OnInitialized are not the most current recents. However, InitiatePurchasing does seem to be calling ProcessPurchase automatically, which does refresh the receipts.

I’ve never seen it documented anywhere that InitiatePurchasing calls ProcessPurchase, but it’s clear from testing that it is. Moreover, sometimes it is called multiple times. In the case where the app is reinstalled after being removed from the device, it calls ProcessPurchase once for every receipt in the IPurchaseReceipt[ ] array (so if your app has autorenewed over 42 cycles, you have 42 past individual receipts, and ProcessPurchase gets called 42 times!). I’d like to have better control over this, or at least better understanding and I haven’t found any documentation on it. Can anyone tell me when exactly ProcessPurchase is called (aside from the obvious case of after you buy a product)?

InitiatePurchase starts the process of reaching out to the app stores. There is no new receipt until ProcessPurchase is called. ProcessPurchase is a callback that is used after there is a successful transaction. So after calling InitiatePurchase, Unity IAP reaches out to the store system on the device and waits for a successful transaction, after which it calls ProcessPurchase. (If the transaction fails for any reason, then OnPurchaseFailed is called instead.)

https://docs.unity3d.com/Manual/UnityIAPProcessingPurchases.html

The two reasons ProcessPurchase can be called:

  • A purchase was initiated with controller.InitiatePurchase
  • Products were restored after a reinstall.

Yes, Apple uses a unified receipt that contains all of the previous receipts. Unity IAP leaves it to the developer to determine the best way to handle those receipts for their app.

From the Manual page above:

2 Likes

I’m still confused about when it is called. The two reasons you have given when ProcessPurchase is called make sense, but they don’t seem to cover all the cases. I’m running Debugs, and it is very clear that process purchase is being called without any Purchase being initiated or any products being restored (if by “restore” you mean the outcome of the RestoreTransactions method). Moreover, ProcessPurchase is often times being called multiple times --usually three times in succession for a single product, sometimes for each receipt in the IPurchaseReceipt[ ] array, and sometimes not at all.

I need to have control or understanding over when it is being called, because ProcessPurchase seems to be the only place where the IAP system is retrieving the most recent app receipts after a autorenew subscription cycle.

Is there specific functionality that changes when the product is an autorenewing subscription?

1 Like

@acr1378 ,

You are right; there is an additional reason ProcessPurchase is called that I neglected to mention.

ProcessPurchase will be called anytime there is pending transactions. During the normal purchase flow, you initiate the purchase, Unity IAP reaches out to the store and calls ProcessPurchase after. At that point, within ProcessPurchase, you must return PurchaseProcessingResult.Complete to finish the transaction. If instead, PurchaseProcessingResult.Pending is returned, then the Purchasing system will call ProcessPurchase the next time the app starts.

Currently, Unity IAP does not treat auto-subscriptions any differently. However, improved subscription support is in the works.

What is the result when you Refresh the receipt?
https://docs.unity3d.com/Manual/UnityIAPiOSMAS.html

I am having the same issue, the subscription receipt seems to be only updated after process purchase is called.
currently i call “RefreshAppReceipt” whenever subscription expires but it does not update the receipt.
But soon after this when i multitask and come back ProcessPurchase is called with updated receipt( which shows subscription is auto renewed)

Is this behavior expected or am i missing something

Can you elaborate “i call “RefreshAppReceipt” whenever subscription expires” , how do you know it is expired? Can you describe what yo mean by multitask and come back". Basically please provide exact steps to reproduce, and what you are trying to achieve.

When i purchase a subscription class, SubscriptionInfo class of SubscriptionManager will be filled with current cycle of subscription data.
SubscriptionInfo.getExpireDate() based on this method, i will be running a timer, when the timer reaches zero, i call
IAppleExtensions.RefreshAppReceipt(), after this i expect that that when i call SubscriptionInfo.getExpireDate() &
SubscriptionInfo.getPurchaseDate () again i will get data from the new cycle of subscription.
But this data doesn’t get updated until ProcessPurchase() method gets called.

So what i am expecting is that as soon as i call RefreshAppReceipt() method, ProcessPurchase() also will get called.
But currently after calling RefreshAppReceipt(), i have to do at least one multitask ( that is nothing but going out of focus from app, based on which OnApplicationPause() gets called), after coming into focus again ProcessPurchase() method will get called.

Note that ProcessPurchase() method doesn’t get called if i have not called RefreshAppReceipt() on the previous multitask, if ProcessPurchase() method was called as soon as receipt expired, it would have been the ideal situation.

We don’t call ProcessPurchase, the stores do via their API. It only occurs during a purchase or restore or subscription renew. This timing is from the stores, not Unity code, we have no control over the refresh timing. But I would not expect ProcessPurchase to be fired when calling RefreshAppReceipt. What are you trying to accomplish? Are you trying to see if a product expires during gameplay, then immediately remove the product, or perhaps provide a warning about to expire? If you are testing in Google, subscriptions expire and renew quickly within minutes, so you might see several ProcessPurchase events at once at app start. I would not recommend to use a timer to track subscriptions, only use the SubscriptionManager

My issue is as soon the timer expires(set using the getExpireDate()), i fetch the
SubscriptionInfo class again, this time it gives
SubscriptionInfo.isSubscribed () = Result.False && SubscriptionInfo.isExpired () = Result.True
This is the problem, actually it is not expired, receipt has just not been updated, after i call RefreshAppReceipt() and multitask, ProcessPurchase() is called, after this when i again access SubscriptionInfo class it correctly gives
SubscriptionInfo.isSubscribed () = Result.true && info.isExpired () = Result.False

since isSubscribed flag is set to false, i reset all the benefits, but actually they are still subscribed.
Another requirement is i need to track how many days continuously they were subscribed, based on which they will get more benefits, to do this i save the first time purchase date in the profile, and reset when isSubscribed returns false, so because of delay in Receipt updating, i end up reseting this field, and players lose their benefit.
How to solve this issue.

Does it give SubscriptionInfo.isSubscribed () = Result.true && info.isExpired () = Result.False when accessing SubscriptionInfo Class when ProcessPurchase () is called? If so, maybe you can consider resetting benefits in ProcessPurchase ().

Do not use a timer.

Hi,
I have the same issue. How can I retrieve the updated receipt after a renewal? Subscription info returns the expired product only and server side validation is not possible with the old receipt.

Here it says “Each renewal will generate a ProcessPurchase event”

That doesn’t seem to be the case.

It just takes some time (Google time, not Unity time!) . And keep in mind that production behavior for Google is quite different than their testing environment. We are working with them to suggest improvements.

Google is working fine. The problem is on iOS.

1 Like

Got it. Does it resolve itself over time? (If over a day, then likely an issue)

yes isSubscribed = true is returned only if process purchase is called, but if its actually not renewing that is say if user cancelled or in test mode it only renews 6 times, after the 6th time even if i call RefreshAppReceipt and do multitask, process purchase will not be called, because its actually expired.
so “If so, maybe you can consider resetting benefits in ProcessPurchase ()” this i cant do.

I wanted to confirm is this late renewals expected, that is the receipts not getting updated until Process purchase is called. and Process purchase call itself being delayed till multitask.
Will this not cause a problem for when apple reviews the app for subscription, since immediate renewals are not happening.

ProcessPurchase is not called when a subscription expires. Don’t call RefreshAppReceipt, just read the receipts at app launch. If a subscription expires, you will know within a day or so. We have no control over this. There is generally no need to check during game play. Store subscriptions behave differently in test than production.

Another issue i am getting without sandbox, when i purchased a 1 week subscription and 3 days trial.
after about 2 days and few hours automatically next purchase cycle is being returned from subscription info class.
means since trial was running and 3 days was not completed i should have been getting trial cycle information.
but subscription info class has started returning already the next cycle information.
i save the values returned from SubscriptionInfo.getPurchaseDate & SubscriptionInfo.getExpireDate () in the profile. when i convert to the saved purchase date to EPOCH time, it shows in 7 hrs, means purchase date is yet to come, still unity has returned me this data early.
why is it like this?

I’m having an issue aswell in Android.

  1. Once Google play purchase window is open.
  2. Pause the game.
  3. Close the game.
    => The next game startup ProcessPurchase is called, and the receipt we get seems valid.
  • The purchase is not registered in Google play, but since the receipt is VALID, our server adds the game currency(gems) to the player…

How can we fix that? This bug is in our live game :(…
(Deleted signature on purpose, but it is a valid hash).

“{"Store":"GooglePlay","TransactionID":"pacahfmlfacipnbblihnfbhl.AO-J1Owz_RFQXd3fAmZZt2Yz3Lu3LJ3mvKxfWAJpOkcLLRrrKw3h_h9COI55RoGCJ3d4fBsbnk6PbQ3-gBKn7LSvSY4_abQ8qwQHjw-I0uwFoSEZb6G1yOHw7xXToAwdUic8wsxo3umdu2dJGrj4bzU9VrD9jCKo4A","Payload":"{\"json\":\"{\\\"productId\\\":\\\"com.sandforge.warfronts.package1\\\",\\\"purchaseToken\\\":\\\"pacahfmlfacipnbblihnfbhl.AO-J1Owz_RFQXd3fAmZZt2Yz3Lu3LJ3mvKxfWAJpOkcLLRrrKw3h_h9COI55RoGCJ3d4fBsbnk6PbQ3-gBKn7LSvSY4_abQ8qwQHjw-I0uwFoSEZb6G1yOHw7xXToAwdUic8wsxo3umdu2dJGrj4bzU9VrD9jCKo4A\\\",\\\"purchaseTime\\\":1596044948471,\\\"developerPayload\\\":\\\"{\\\\\\\"developerPayload\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"is_free_trial\\\\\\\":false,\\\\\\\"has_introductory_price_trial\\\\\\\":false,\\\\\\\"is_updated\\\\\\\":false,\\\\\\\"accountId\\\\\\\":\\\\\\\"\\\\\\\"}\\\"}\",\"signature\":\"==\",\"skuDetails\":\"{\\\"skuDetailsToken\\\":\\\"AEuhp4LpKoHPB_p9LiQh9ZPYoSuAUSh9ZVahzHh5CDC8euTeZkbzqBVwwWga9148QPzP\\\",\\\"productId\\\":\\\"com.sandforge.warfronts.package1\\\",\\\"type\\\":\\\"inapp\\\",\\\"price\\\":\\\"€5.49\\\",\\\"price_amount_micros\\\":5490000,\\\"price_currency_code\\\":\\\"EUR\\\",\\\"title\\\":\\\"Handful of Gems (Warfronts: Battle for Toria!)\\\",\\\"description\\\":\\\"Contains a Handful of Magical Gems.\\\"}\",\"isPurchaseHistorySupported\":true,\"isOwned\":true}"}”

What you mentioned may be related to this: https://forum.unity.com/threads/processpurchase-called-even-if-purchase-was-not-completed.932016/#post-6204708

Please try to update to the latest version.