The IAP system has the purpose of letting users perform “real money purchases” from within the client app. The complexity lies in the fact that it is desired to have a unified interface for all supported client platforms and also from the inherent security risks when dealing with money transactions.
A transaction within the IAP system consists of the following steps:
- The user initiates the transaction on the client
- The user completes the transaction, money is actually spent
- The user receives a receipt as proof of his purchase (we use the term receipt even though every platform uses its own name and format)
- The receipt is validated and the purchased item or virtual currency amount is added to the user account
The validation step (4) is the one that needs to be implemented with great care because it involves actually granting the user the item he/she desired to purchase. It also needs to handle cheating attempts. Given the complexity and the security risks delegating this step to a 3rd party seems the best option.
On the client we want a single platform independent system to perform all IAP requests. Unity has its own IAP support (simply called “Unity IAP”), documented here: https://docs.unity3d.com/Manual/UnityIAP.html. This system handles transactions on a few different platforms, including Windows Store and Facebook Gameroom. It does not support Steam. That means that to add Steam support a custom solution using the Steamworks API needs to be implemented. A .NET wrapper of the Steamworks API already exists in the project, currently used for Steam login, it is called “Steamworks.NET”.
Once a user completes a purchase the client will get back a receipt. This receipt is a piece of data usually in XML or JSON format, but it could be anything. In any case a receipt represents the response to a successful purchase request. This receipt should be validated before the user can actually get something in return, this is to prevent cheating. Two 3rd party back-end systems were investigated for this purpose:
- GameSparks: has support for Steam and Windows Store validation
- PlayFab: has support for Windows Store validation
At the moment both seem to lack support for Facebook transactions. To account for that the validation can be performed by the game server using the Facebook API as detailed here: https://developers.facebook.com/docs/games_payments/fulfillment. Another, maybe superior choice could be to perform validation without using a 3rd party system, by letting the game server directly invoke the APIs provided by the various platforms (that is Facebook, Steam and Windows Store).
The biggest challenge in case a “mixed” solution is implemented will be keeping the currency amounts in sync. This is because the 3rd party will store its own amount of player’s currency. If some purchases are validated through the 3rd party and some others internally the two entities will need to communicate. Specifically the game server will need to query the 3rd party to retrieve the amount of currency associated with a player’s account.
Querying Virtual Currency
If a 3rd party solution is implemented the game server will need to be able to contact that entity and retrieve the amount of virtual currency associated with a given player. Assuming that is possible (currently only PlayFab was investigated and it’s known to expose a Server to Server API) the game server will need to merge the retrieved amount with the one stored internally that comes from platforms not supported by the 3rd party. In theory the server could use the following system:
- Internally store currency purchased through platforms unsupported by the 3rd party
- Whenever the credits balance needs to be read or updated query the 3rd party to get its own currency amount
- Add the two amounts to obtain the final one, and store it
- Zero out the stored currency amount on the 3rd party so that the next query doesn’t add currency already accounted for
The advantage of the above solution is that the game server will only need to implement validation on a subset of the platforms (currently only Facebook). The disadvantage is the complexity of the solution itself.