22

I'm developing an app using Unity (for Android and iOS). I'm using the SOOMLA plugin to allow users to purchase Gems (virtual currency) with In App Purchase.

Users and Gems and all other game logic go through my server on Azure.

I want the following procedure to take place as a single transaction in some way:

  1. User buys Gems with IAP
  2. App notifies server
  3. Server validates the purchase and updates data

But if the internet connection breaks down between step 1 and step 2 - the user payed for Gems that he did not receive (not good!)

So my current approach is this:

  1. User initiates a purchase
  2. App notifies the server
  3. Server blindly updates data accordingly
  4. User buys Gems with IAP
  5. If the purchase is cancelled, notify server to undo it

That way, the user is guaranteed to get his purchased Gems, but I am not guaranteed to get paid (not great...)

Note: I don't want to manage user Gems in the store itself. I want everything on my own server. So the SOOMLA's balance is meaningless to me. I don't care for it.

I was thinking maybe the app can store the purchase data in persistent storage until it manages to notify the server about it, and then delete it. But I was also thinking that this might be a bad solution. Hence this question.


I imagine the best solution as something that will properly handle this scenario:

  1. User buys Gems with IAP
  2. IAP succeeds
  3. Internet breaks down
  4. My own server isn't notified
  5. User uninstalls app from his device
  6. User may then install the app on other devices:

    • Either he was charged and he got the gems by some magic
    • Or he was refunded automatically, since the gems were not received

So far it seems like this is impossible by any means, which makes me disappointed with the technology of IAP's. Hoping for answers that will prove me wrong.


Seems like all I'd ever need is the ability get a user's purchase history from my server, with a secured request to Google Play or Apple Store. But that's just not part of the framework.


So what are others doing? What is the best approach?

SimpleVar
  • 14,044
  • 4
  • 38
  • 60
  • My understanding is that SOOMLA does a check on game start to see if any items were purchased but not processed yet. And if it finds some then it runs the code and you will have a OnPurchase event fire. answers.soom.la is the dedicated forum for SOOMLA related questions. You might get a quicker answer there. – Andrei Bazanov Feb 04 '16 at 13:06
  • @cocojiambo Yes, as a part of the SOOMLA's purchase verification process. The problem is when SOOMLA has completely processed the purchase and the OnPurchased event has already fired. This is when I would logically notify my own server of the purchase and it's verification detailed provided by SOOMLA, but what if the connection breaks just before this step? – SimpleVar Feb 04 '16 at 15:51
  • I see. Ok so here is some speculation... All purchases from Google have a unique token against them. It you were to hook onto the OnGameStarted event and as SOOMLA does its own checks between its records against the Google ones, you do your own at that moment, by checking all tokens against your server. Should be possible. This is me just thinking aloud here. – Andrei Bazanov Feb 04 '16 at 20:27
  • Looking for a solution that would handle the case where user uses device A to make a purchase, internet breaks down after purchase before notifying my own server, and then user uninstalls from device A never to be seen again. User may then install on new device, and either he wasn't charged and didn't get gems in the end, or he did. That would be the perfect solution I'm looking for, and that I expect this technology to have. – SimpleVar Feb 14 '16 at 13:28
  • Refer to [How does Apple notify iOS apps of refunds of in-app purchases (IAP)?](https://stackoverflow.com/q/6439482/6521116) – LF00 Aug 20 '19 at 07:30

5 Answers5

6

In general, you seem to suffer from the Two Generals' Problem which was

the first computer communication problem to be proved to be unsolvable.

Since everywhere in your communication protocol a message can be lost (even the acknowledgement or the acknowledgement`s acknowledgement or the ...) you cannot be 100% sure that both communication parties (the user device and your server) have agreed upon the same state. You can only say that upon a certain probability the state information has been interchanged successfully.

I would send a couple of ACKs back-and-forth and store the purchase if a sufficient number got trough. Quote from Wikipedia:

Also, the first general can send a marking on each message saying it is message 1, 2, 3 ... of n. This method will allow the second general to know how reliable the channel is and send an appropriate number of messages back to ensure a high probability of at least one message being received

For customer satisfaction I would take the odds in their favor - 1% not delivered goods will get you in a lot of trouble but 1% loss on your side is acceptable.

PhilLab
  • 4,777
  • 1
  • 25
  • 77
  • So in short, there is no perfect solution. You will have the losses on your end and be fine with it, and do your best to minimize those possible losses. Is this correct? – SimpleVar Feb 17 '16 at 20:45
  • exactly - it is surprising how many seemingly trivial problems have been proven to be unsolvable – PhilLab Feb 19 '16 at 08:04
5

Considering your Gems are a virtual currency, then the natural in-app product type should be consumable, i.e. they are not restorable purchases.

To consume a purchase with a Google Play purchase you will call consumePurchase. On iOS you will call finishTransaction on the SKPaymentQueue. In both marketplaces the consumable purchase will remain in an active state until they have been consumed. If the user deletes the app off their device before the purchase is consumed, they will be able to re-install, and restore their previous unconsumed purchases.

In-between the initial purchase and consumption is where you want to put your server-side validation. When a purchase is made, send the receipt or token to your server, perform the validation and respond accordingly. The app should wait for a valid response from the server before consuming the purchase.

(Note that consumed purchases will not appear in the in_app collection on an iTunes receipt. They are only present if the purchase has not been consumed yet).

If the server is timing-out or network connectivity is lost the purchases will remain in an active state and the app should continue trying to send the details periodically until it receives a response it is expecting.

Purchases for both Google Play and iOS are stored locally so you will just need to run a process that looks for unconsumed purchases once network connectivity is re-established.

You can treat the provisioning of Gems in the same way a bank handles deposits of cheques; the new balance will be immediately updated but the amount spendable will not match until the cheque (or in your case a validation) is cleared.

Some pseudo code to make the process clear:

Purchase product or Restore purchases
While consumable purchases > 0
  Send purchase receipt to API
  If response is ok
    If purchase is valid
      Consume product
      Allocate gems
      Break
    Else
      Remove retroactive gem allocation
      Discipline the naughty user
      Break
  Else
    Retroactively allocate un-spendable gems
    Pause process until network is re-established
      Re-send receipt to API
Marc Greenstock
  • 11,278
  • 4
  • 30
  • 54
  • Sounds brilliant. Wondering if I can use SOOMLA without changing too much, if I can pretend its a non-consumable product, maybe it'll just skip the consumption part? I'll dig into the source code and find out. Seems like a good answer so far! – SimpleVar Feb 20 '16 at 21:01
  • @SimpleVar I'm not familiar with SOOMLA but if you were to use non consumable products, you will need to ensure that when restoring purchases they do not allocate Gems that have already been allocated. This will need to be done on your server, where you keep a registrar of transaction/order ID's. Consumable products have the benefit of holding state of their consumption; thereby indicating if the server knows of the transaction and that the Gems have been allocated or not. – Marc Greenstock Feb 21 '16 at 09:51
0

Some advice:

  1. User buys Gems with IAP

  2. IAP succeeds

  3. Internet breaks down

  4. My own server isn't notified

  5. User uninstalls app from his device

  6. User may then install the app on other devices:

    Either he was charged and he got the gems by some magic, or he was refunded automatically, since the gems were not received.


  1. At step 3, receipt information is stored on the user's device. If the user uninstalls and reinstalls your app, the receipt information will be lost. But you can restore it; Apple talk about that here. You can resend a restored receipt to your server. At your server, you verify that plus Gems for the user so he can receive what he should be.

  2. he was refunded automatically, since the gems were not received

    This seem impossible with IAP because Apple does not allow a user to cancel their purchase. (With Google IAB, refund are allowed, more about that here)

Community
  • 1
  • 1
Nhat Dinh
  • 3,378
  • 4
  • 35
  • 51
  • So there is no cross-platform solution? I'd imagine if only I could simply access the purchase history of each user from my secured server with some store API, it'd be all very easy. But it seems that this is impossible. – SimpleVar Feb 18 '16 at 12:21
0

I don't have much knowledge about android but after reading your post I was really interested to search keenly and more over how game like clash of clans in app purchase logic work and prevent freedom fake purchase hacks.

After doing some research I would like to share my thoughts over your problem, You can implement it by following approach:

Make your in app purchase verification completely online. For e.g., you can consider the game clash of clans.

How it works:

1)Game loads, synced with server. Network connection required, as network connection breaks game reloads from server.

2)User have 10 gems, server also have 10 gems.

3)User purchased gems, server verify purchase separately for the purchased consumables, gems credited to the users account.

4)If in can case network fails, then also server can verify purchase and later on it update it in account of user, whether it is on any device.

5)This will also help you to bypass many fake in app purchase hacks like freedom (prevention) and lucky patcher.

Google provide api for server side to verify or get purchase detail and when purchase from application side and server side match then only you credit gems or consumable item into users account.

For more information about in app purchases and their hack preventions you can visit these links:

1)Server side verification of in app purchase part 1 and part 2.

2)How would you verify in app purchase billing server side.

3)Verify purchase via PHP.

4)Secure in app purchase scenario on web server.

Hope this might lead you in direction which you want to go, also I would love to hear your thoughts over the same.

Community
  • 1
  • 1
Akshay Sunderwani
  • 12,428
  • 8
  • 29
  • 52
0

You can try restoring the In App purchases made with a particular account.

The feature is given just for this case when either the payment was made and user didn't receive the items he was promised or when he switched device.

Upon restoring the purchase you'll receive the purchased product again from the iTunes server and then you can accordingly notify your server.

Vikas Dadheech
  • 1,672
  • 12
  • 23