Coding Guidelines

Initialization

After you finish Google Play and Plugin Setup, you can proceed to scripting and implement InApps into your project. You may fill InApps setting programmatically in case  you do not want to use plugin editor settings by any reason. The following steps are not required if you have already filled plugins settings as was described in the previous article. 

Note:  The following steps are not required, you may use plugin editor settings instead.

Setting Base64 key

AndroidNativeSettings.Instance.base64EncodedPublicKey = "YOUR_BASE64_KEY_HERE";

Registering the in-app products

You may add InApp products programmatically using the  

string MyProductSKU = "my.prod.id";
AndroidInAppPurchaseManager.Client.AddProduct(MyProductSKU);

//or
GoogleProductTemplate tpl = new GoogleProductTemplate();
tpl.SKU = MyProductSKU;
AndroidInAppPurchaseManager.Client.AddProduct(tpl);

Connecting to billing service

Before connecting to the billing services I would recommend to subscribe to the billing events, to make sure you will not loose any event, since some event may be fired right after the connection.  To connect to the billing service, all you need to do is to call Connect method of the billing client. See the code snippet below:

//listening for Purchase and consume events
AndroidInAppPurchaseManager.ActionProductPurchased += OnProductPurchased;  
AndroidInAppPurchaseManager.ActionProductConsumed  += OnProductConsumed;

//listening for store initialising finish
AndroidInAppPurchaseManager.ActionBillingSetupFinished += OnBillingConnected;

//you may use loadStore function without parameter if you have filled base64EncodedPublicKey in plugin settings
AndroidInAppPurchaseManager.Client.Connect();


private static void OnBillingConnected(BillingResult result) {
	AndroidInAppPurchaseManager.ActionBillingSetupFinished -= OnBillingConnected;

	if(result.IsSuccess) {
		Debug.Log("Connected");
	} else {
		Debug.Log("Connection failed");
	}
}

Getting Inventory

After initialization is done (ActionBillingSetupFinished action fired), we can retrieve current user inventory with the following method:

AndroidInAppPurchaseManager.Client.RetrieveProducDetails();

Here is an example of parsing ActionBillingSetupFinished action and requesting product details.

private static void OnBillingConnected(BillingResult result) {
	AndroidInAppPurchaseManager.ActionBillingSetupFinished -= OnBillingConnected;

	if(result.IsSuccess) {
        AndroidInAppPurchaseManager.ActionRetrieveProducsFinished += OnRetrieveProductsFinised;

//Store connection is Successful. Next we loading product and customer purchasing details
		AndroidInAppPurchaseManager.Client.RetrieveProducDetails();		
	} 

	AndroidMessage.Create("Response", result.Response.ToString() + " " + result.Message);
	Debug.Log ("Response: " + result.Response.ToString() + " " + result.Message);
}

After the ActionRetrieveProducsFinished is fired, we will get our inventory filled.

private static void OnRetrieveProductsFinised(BillingResult result) {
	AndroidInAppPurchaseManager.ActionRetrieveProducsFinished -= OnRetrieveProductsFinised;


	if(result.IsSuccess) {
		_isInited = true;
		AndroidMessage.Create("Success", "Billing init complete inventory contains: " + AndroidInAppPurchaseManager.Client.Inventory.Purchases.Count + " products");

		foreach(GoogleProductTemplate tpl in AndroidInAppPurchaseManager.Client.Inventory.Products) {
			Debug.Log(tpl.Title);
			Debug.Log(tpl.OriginalJson);
		}
	} else {
		 AndroidMessage.Create("Connection Response", result.Response.ToString() + " " + result.Message);
	}

	Debug.Log ("Connection Response: " + result.Response.ToString() + " " + result.Message);

}

Requesting inventory doen't require the step. But, as for me,  it's very important. User android inventory contains information about user purchases, even if it has been made on another devices. That's why when you are doing In-Apps implementation for android, you do not need "Restore Purchases" implementation, like for IOS. You will get all user purchases information with the android inventory.

As soon as  inventory is ready, I would recommend going over products it contains and check if we need to unlock something.

Parsing Inventory

Let's say we have two products for our game. BONUS_TRACK - which is a managed product for unlocking bonus tracks in your game. And CONIS_PACK - which is the unmanaged product for purchasing 100 in-game coins.

When inventory is loaded, we can get states fo those products. For example, our BONUS_TRACK can be purchased on another device, so we should unlock the track on the current device, or make sure that a track is unlocked if the BONUS_TRACK product is purchased. Here is an example on how we can do this:

if(AndroidInAppPurchaseManager.Client.Inventory.IsProductPurchased(BONUS_TRACK)) {
    if(!GameData.IsBonusTrackUnlocked) {
        GameData.UnlockBonusTrack();
    }
}

 

And we also need to check the CONIS_PACK product, in case it has been purchased but has not been consumed. For example, an app was closed when we were trying to consume the product. It means that a user has paid for the product but he hasn't got coins yet. So, we will check if CONIS_PACK is purchased after inventory is loaded and we will do the consume request if it is necessary. Here is an example:

if(AndroidInAppPurchaseManager.Client.Inventory.IsProductPurchased(CONIS_PACK)) {
    AndroidInAppPurchaseManager.Client.Consume(CONIS_PACK);
}

See the AndroidInventory, GoogleProductTemplate, GooglePurchaseTemplate API Reference to find out more.

So, for example, If I need to know localized price string of the CONIS_PACK product after inventory is loaded, I can go this way:

Debug.Log(AndroidInAppPurchaseManager.Client.Inventory.GetProductDetails(CONIS_PACK).Price)

Purchase And Consume

This is the easiest part. As you remember, we have already subscribed ActionProductPurchased and ActionProductConsumed in the init part, so now we can only run the purchase / consume flow for the products.

So, here is how we will do purchase logic for our BONUS_TRACK(managed) and CONIS_PACK(unmanaged) products. No matter what kind of product we need to purchase, for purchase flow start we should use the following function:

AndroidInAppPurchaseManager.Client.Purchase (SKU);

After the purchase flow is done, the ActionProductPurchased, and here is an action parsing example:

private static void OnProductPurchased(BillingResult result) {
	if(result.IsSuccess) {
		AndroidMessage.Create ("Product Purchased", result.Purchase.SKU+ "\n Full Response: " + result.Purchase.OriginalJson);
		OnProcessingPurchasedProduct (result.Purchase);
	} else {
		AndroidMessage.Create("Product Purchase Failed", result.Response.ToString() + " " + result.Message);
	}

	Debug.Log ("Purchased Response: " + result.Response.ToString() + " " + result.Message);
}

The action contains BillingResult as parameter.

This flag will tell you if the purchase is available

result.IsSuccess

information about purchase is stored here

result.Purchase

here is how, for example, you can get product SKU

result.purchase.SKU

See the BillingResult and GooglePurchaseTemplate to find out more.

So, if the purchase is successful, we need to act depending on what product is purchased. If a product is managed as BONUS_TRACK, we can simply unlock the track and finish with our purchase flow.  But due to the unmanaged product as CONIS_PACK,  we need to consume the product and provide our player with coins only after successfully finished consuming. Here is how our OnProcessingPurchasedProduct looks like.

private static void OnProcessingPurchasedProduct(GooglePurchaseTemplate purchase) {

    switch(purchase.SKU) {
    case COINS_ITEM:
        AndroidInAppPurchaseManager.Client.Consume(CONIS_PACK);
        break;
    case BONUS_TRACK:
         GameData.UnlockBonusTrack();
        break;
    }
}


private static void OnProcessingConsumeProduct(GooglePurchaseTemplate purchase) {
	switch(purchase.SKU) {
	case CONIS_PACK:
		GameData.AddCoins(100);
		break;
	}
}

If you implement subscription purchase in your application, you have to use 

AndroidInAppPurchaseManager.Client.Subscribe (SKU);

instead of 

AndroidInAppPurchaseManager.Instance.Purchase (SKU);

Result callbacks are the same as in general purchases.

If you want to implement the additional protecting level using Developer Payload,  you may specify it as the additional parameter for purchase.

string DeveloperPayload  = GeneratePayloadID();
AndroidInAppPurchaseManager.instance.purchase ("my_product_sku", DeveloperPayload);

And as was mentioned above, you can find out purchase payload using GooglePurchaseTemplate  property.

Read more Security and Design.

Note: This step is not required.

More

You can find more API use examples under the plugin billing example scenes - BillingImplementation and BillingExample by path:

Plugins -> StansAssets -> Modules -> AndroidNative -> xExample -> Scenes -> Billing

You also can use Playmaker Actions for the game billing implementation.