androidiosreceipt-validationandroid-inapp-purchase

In app purchase revocation mechanism for Android and iOS


I am supposed to implement a generic revocation policy for purchases both for android and iOS.

Here is what I mean by revocation policy:
A user is presented with the option to buy some virtual currency (VC) in an app. When the user successfully completes the transaction the user's balance will be increased with the amount of VC that he has purchased. If later on he refunds/cancels the transaction then the user's balance needs to get deducted (reverting the deposit).

I also want to clear out any questions that might pop up.
Both the Android and iOS app have a common back-end which will be used for the following:

One possible flow that I can think of is:

User opens the app (iOS or Android it does not matter) and starts the purchase flow. Enters his payment details and clicks submit. This creates a transaction in the corresponding App Store. The app then sends the transaction details to the server. The server is responsible for validating the transaction and upon successful validation depositing the VC to the users balance.

Possible problems:

  1. A few days later the user refunds his transaction. How would the server get notified?

    1.1. According to Google's docs they provide a Void API and Server Push Notifications but only for subscriptions and one time purchases not for regular purchases. The same applies for Apple's Server APIs which also have Server Push Notifications that are for subscriptions only and Receipt Validation. Those APIs don't seem suitable for implementing a revocation mechanism for purchases. Also keep in mind that there are hundreds of thousands purchases per day and the API needs to be suited for such volume.

  2. What if for some reason the server performs successfully the purchase validation but cannot grant the VC because of some business rules? In this case the user's money will be taken by the App Store and he won't receive the items that he payed for.

    2.1. I understand that after validating the purchase on the back-end the app needs to finish/confirm the transaction to the App Store. But in this case the transaction should not be confirmed as it cannot be performed based on some business rules. The user should not get charged in this case. The docs both for iOS and Android are not clear how this can be achieved.

Help on the following will be highly appreciated:


Solution

  • TL;DR

    The back-end server can poll a Google specialized API for purchase status changes and act accordingly. Apple however do not have a specialized API for bulk status retrieval.

    Problem 1 - Handling refunds

    Before implementing any solution it is important to understand that both Google and Apple do their best to reduce the number of refunds and detect fraudulent activity. However it is still possible to end up in a scenario where a refund has happened and the application owner would like to revoke the granted goods.

    Also the amount of Google (Android) refunds is not the same as Apple (iOS) refunds. The Android platform tends to have more refunds on average then the iOS one. This might be important when deciding to implement a revocation policy for each system separately as it seems there is no common solution.

    Possible Solution

    The centralized back-end server needs to be aware when a refund happens. This would allow it to revoke the goods (in this case revert the Virtual Currency transaction). The server must not rely on acquiring this knowledge from the mobile devices as they are not a controlled source of truth. For this reason the back-end must utilize APIs exposed by Google and Apple to handle Android refunds and iOS refunds respectively.

    Google (Android)

    Google provides a Voided Purchases API. This API can be used to retrieve bulk information like if a purchase has been refunded or even charged-back. The API can be polled periodically from the back-end and use the returned purchase token to link it with the purchase made by the user and execute the required revocation policy.

    Apple (iOS)

    Unlike Google, Apple do not provide a specialized bulk API for retrieving purchase information. It however provides AppStore Receipt Verification API which can be used to retrieve the status of each separate purchase. The API is intended to be used for server side validation of a purchase when the purchase actually happens. It does not seem to be suited for periodically checking purchase statuses. This does not mean it can't be polled but I will not be surprised if a certain request quota gets reached and the requests get throttled (just guessing here). Apple also provide AppStore Server Notifications. Which can be used as a callback to notify your back-end when a status of a subscription changes. Unfortunately according to the docs this API is ONLY FOR SUBSCRIPTIONS and not for one time purchases. Thus leaving the AppStore Receipt Verification API the only possible solution.

    Problem 2 - Unfinished/Unconsumed Transactions

    I could not find any official docs explaining what happens if a purchase is not consumed/finished in Android/iOS. All docs state that a purchase MUST be consumed/finished.

    Android

    After experimenting with Android I have come to notice that an automatic refund is issued from Google Play when a purchase is not consumed. For test purchases the time needed to issue the refund is < 30 min, however for real transactions it can take a day. So in the case if a server fails to grant the goods to the user after the user has been charged one can rely on a refund being issued a day or two later. However note that this is not documented in any official source.

    iOS

    I have not tested with iOS purchases. The docs state that after a purchase is made it needs to be finished against the AppStore. Until it is finished it will remain in the iOS purchase Q. It is unclear for how long will this unfinished transaction remain in the Q. And it is unclear if a refund will be made in similar fashion as unconsumed Google purchases.