There are a number of ways to monetize an app depending on genre, target market and audience. One of the first decisions to be made is whether to make the app paid or free. Offering the app with a price tag can significantly reduce the potential customer base—most people are hard to convince to pay upfront for a product they have not yet tried. Latest market reports suggest that paid apps account for less than 10% of the generated app store revenue with the lion share of sales coming from the free apps.
Free apps are usually monetized either by placing an advertisement within the product or by implementing micro-transactions (in-app purchases). In-app purchases (IAPs), allow developers to charge users for specific functionality or content while using an app like unlocking premium levels or purchasing a rare virtual item in a game. Implementing IAPs is particularly compelling for several reasons and this blog post is inspired from the experience shared by one of our clients to help other App42 developers maximize sales in free apps.
In this post, you will learn the basics of securing in-app purchases through server-side custom cloud code validation. Piracy rates on app stores are high, especially in emerging markets, and to stay in business developers must verify if the IAP that was made by the player is valid. Only after validation is confirmed, virtual goods should be delivered to the player.
Also Read: Best Practices to Writing Cloud Code
The validation process for Android Play Store billing system consists of the following steps:
1. Verify the signed response that is sent by Google to the app when an in-app purchase is made
By performing signature verification you can detect responses that have been tampered. Ideally, you should perform signature verification on a remote server and not on a device. Implementing the verification process on a server makes it difficult for attackers to break the process by reverse engineering your .apk file.
With App42 you can implement Android Play Store server-side signature verification using Custom Cloud Code and a simple Java class:
[code java]public class SecurityGoogle
{
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
public boolean VerifyPurchase(String base64PublicKey, String signedData, String signature) throws Exception
{
if(null==base64PublicKey || null==signedData || null==signature)
{
return false;
}
if(base64PublicKey.length()==0 || signedData.length()==0 || signature.length()==0)
{
return false;
}
PublicKey key = this.GeneratePublicKey(base64PublicKey);
return this.Verify(key, signedData, signature);
}
public PublicKey GeneratePublicKey(String encodedPublicKey) throws Exception
{
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
}
public boolean Verify(PublicKey publicKey, String signedData, String signature) throws Exception
{
Signature sig;
try
{
sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (false==sig.verify(Base64.decode(signature)))
{
return false;
}
return true;
}
catch (SignatureException e)
{
throw e;
}
}
}[/code]
2. Once the signature is verified, it is safe to assume that the in-app purchase response is authentic and has not been altered. However, there is a possibility that someone took that response and made it available to multiple people and they are all able to bypass the previous check using the same authentic data.
To handle this scenario, you will need to save all IAP orders in a collection (table) and make a query using the new order id to make sure that such an IAP was not already processed before. Again you can do this with Custom Cloud Code using this simple Java snippet:
[code java] public boolean IsOrderProcessed(StorageService storageService, String orderId) throws App42Exception
{
try
{
Storage storage = storageService.findDocumentByKeyValue("DBNAME", "IAPORDERS", "order_id", orderId);
ArrayList jsonDocList = storage.getJsonDocList();
if(jsonDocList.size() > 0)
{
return true;
}
}
catch(App42Exception e)
{
//skip the "no documents" in collection exception
if(e.getAppErrorCode()!=2602 & & e.getAppErrorCode()!=2601)
{
throw e;
}
}
return false;
}[/code]
Also, make sure to save the order in the collection (table) once the previous check is completed.
[code java]public void SaveOrder(StorageService storageService, String orderId) throws JSONException
{
JSONObject jsonOrder=new JSONObject();
jsonOrder.put("order_id", orderId);
storageService.insertJSONDocument("DBNAME", "IAPORDERS", jsonOrder);
}[/code]
We would like to thank Magicindie the developer behind Cyberline Racing and Shadow Quest for sharing his experience and helping other developers to safeguard in-app purchases in their apps.
Also Read: How to Write and Deploy Custom Cloud Code For iOS
In case you need any help while using any of our Cloud APIs then please write to us at support@shephertz.com. We will be happy to assist.
Leave A Reply