Android Tutorial Android In-App Billing v3 Tutorial

New version: GooglePlayBilling - In App Purchases

This tutorial covers the new API for in-app billing provided by Google.

The main differences between v3 and the previous version are:
- (v3) Is easier to implement.
- Supports managed products and subscriptions. Unmanaged products are not supported.
- Includes a method to retrieve all purchased items. This means that you do not need to manage the items yourself.
- Allows you to "consume" managed products. For example if the user has bought a game add-on and then used it, the app consumes the product allowing the user to purchase the add-on again.

The official documentation is available here: In-app Billing Version 3 | Android Developers

Implementing in-app billing in your application

The first step is to upload a draft APK to Google developer console. Note that you should use a private signing key to sign the app.

Under Services & APIs you will find your license key. This key is also required for in-app billing:

SS-2013-06-06_17.21.31.png


You should also add at least one item to the "In-app Products" list. The item's type should be Managed Product or Subscription.

Basic4android code

The first step is to initialize a BillingManager3 object:
B4X:
Sub Process_Globals
   Dim manager As BillingManager3
   Private key As String = "MIIBIjANBgkqhkiG9w0BAQEFAA..."
End Sub

Sub Globals


End Sub

Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
      manager.Initialize("manager", key)
      manager.DebugLogging = True
   End If
   ...
End Sub

Sub Manager_BillingSupported (Supported As Boolean, Message As String)
   Log(Supported & ", " & Message)
   Log("Subscriptions supported: " & manager.SubscriptionsSupported)
End Sub

The BillingSupported event will be raised with the result. Note that all of the billing related actions happen in the background and raise events when the action is completed.

Calling manager.GetOwnedProducts will raise the OwnedProducts event. The OwnedProducts event includes a Map that holds the user current purchases. The keys in the map are the products ids (or Skus) and the values are objects of type Purchase. These objects include more information about the purchase and are required for other actions, such as consuming a purchase.

B4X:
Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
   Log(Success)
   If Success Then
      Log(purchases)
      For Each p As Purchase In purchases.Values
         Log(p.ProductId & ", Purchased? " & (p.PurchaseState = p.STATE_PURCHASED))
      Next
   End If
End Sub

Purchasing a product is done by calling: manager.RequestPayment. The user will be asked to approve the payment. The PurchaseCompleted event will be raised when the operation completes.

Note that managed products can only be purchased once. Only after you call ConsumeProduct will the user be able to purchase the same item again.

Consuming purchased products is done by calling manager.ConsumeProduct.
For example:
B4X:
If ownedProducts.ContainsKey("test2") Then
   manager.ConsumeProduct(ownedProducts.Get("test2"))
End If

The ProductConsumed event will be raised.

Tips
- See this tutorial for more information about the possible testing options: Testing In-app Billing | Android Developers
- If you get a "signature verification error" when trying to make a purchase then you should make sure that the licensing key is correct. If it is correct then try to upload a new APK.
- It is recommended to use a process global variable to hold the key. This way it will be obfuscated when you compile in obfuscated mode.
- Only one background request can run at a time.

The library is available here:
http://www.b4x.com/forum/additional.../29998-app-billing-v3-library.html#post174139
 
Last edited:

touchsquid

Active Member
Licensed User
Longtime User
We have in-app purchase working since December. We refunded an order on one of our test accounts Dec 23 2013. As off today we still get back STATE_PURCHASED (0) when querying own products on that account.

Is there any possibility of a bug in the library?
 

touchsquid

Active Member
Licensed User
Longtime User
Bought and refunded another order today. Like the first one, it still returns STATE_PURCHASED (0) when querying owned products on that account.
 

touchsquid

Active Member
Licensed User
Longtime User
Thanks Erel. It does seem odd that an item I refunded almost 3 months ago still shows as purchased. The other posts you refer to mention trying to purchase it again and rebooting the device as ways to trigger an update in the cache. I doubt reboot will help as these are test devices, they get rebooted a lot! Anyway I will try again.
 

touchsquid

Active Member
Licensed User
Longtime User
OK here is what I found so far:
Reboot does not help.
Trying to purchase the app again, then backing out, causes an update, then the app shows as not purchased. Not too helpful.

It seems Google caches the data to save traffic on their servers.
 

tufanv

Expert
Licensed User
Longtime User
I am using the lib without any problem but with emulator devices i cant get any of my apps running . it gives lastexception javalang nullpointer error.
here :

If FirstTime Then
manager.Initialize("manager", key)
manager.DebugLogging = True
End If

can anyone help ?
 

tufanv

Expert
Licensed User
Longtime User
is there anyway to get the price of an item ? for example when user click on $1.000.000 in game money the manager wil check for its price in his own currency and print it ?
 

luke2012

Well-Known Member
Licensed User
Longtime User
@Erel, very interesting library!
Is it possible to manage a Try & Buy approach ?

I mean that the user can try the functionality and than he can decide to buy it with in-app purchase.
 

rafaelbr20

Member
Licensed User
Longtime User
Hi Erel,

I have a question.

I´m using this code on Manifest.

B4X:
'InAppBilling declarations - Start
AddReceiverText(InAppBillingService,  <intent-filter>
                <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
                <action android:name="com.android.vending.billing.RESPONSE_CODE" />
                <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
            </intent-filter>)
'InAppBilling declarations - End

This version 3 i need to create a service module like the previous version or not ?

Thanks so Much.

Rafael
 

rafaelbr20

Member
Licensed User
Longtime User
All of these is not required. Only the steps in the tutorial are required.

Hi Erel,

Thanks.

I´m trying but i´m getting this error.

Do you Have any Idea ?

LOG ERROR

B4X:
** Activity (main) Create, isFirst = true **
Illegal state for operation (queryInventory): IAB helper is not set up.
java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: queryInventory
    at anywheresoftware.b4a.objects.IbHelper.checkSetupDone(IbHelper.java:776)
    at anywheresoftware.b4a.objects.IbHelper.queryInventoryAsync(IbHelper.java:609)
    at anywheresoftware.b4a.objects.IbHelper.queryInventoryAsync(IbHelper.java:636)
    at anywheresoftware.b4a.inappbilling3.BillingManager3.GetOwnedProducts(BillingManager3.java:82)
    at com.mobileforfunapps.vestindominhaboneca.appbilling._vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3(appbilling.java:41)
    at com.mobileforfunapps.vestindominhaboneca.main._vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv4(main.java:368)
    at com.mobileforfunapps.vestindominhaboneca.main._activity_create(main.java:291)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:174)
    at com.mobileforfunapps.vestindominhaboneca.main.afterFirstLayout(main.java:98)
    at com.mobileforfunapps.vestindominhaboneca.main.access$100(main.java:16)
    at com.mobileforfunapps.vestindominhaboneca.main$WaitForLayout.run(main.java:76)
    at android.os.Handler.handleCallback(Handler.java:725)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:176)
    at android.app.ActivityThread.main(ActivityThread.java:5279)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: queryInventory
Billing service connected.

Checking for in-app billing 3 support.
In-app billing version 3 supported for com.mobileforfunapps.vestindominhaboneca
Subscriptions AVAILABLE.


MY CLASS "appBilling.bas" CODE

B4X:
'Class module
Sub Class_Globals
    Dim manager As BillingManager3
      Private key As String = "MIIBIjANB..."
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
     manager.Initialize("manager", key)
      manager.DebugLogging = True
End Sub

Sub Manager_BillingSupported (Supported As Boolean, Message As String)
   Log(Supported & ", " & Message)
   Log("Subscriptions supported: " & manager.SubscriptionsSupported)
End Sub

Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
    Log("Atualizou BD")
   baseMod.atualizaBdComprasUsuario(Success,purchases)
End Sub

Sub buscarProdutosComprados
    Log("Chamou Busca")
    manager.GetOwnedProducts ' Ao Executar este método, o resultado será o evento "manager_OwnedProducts(Success As Boolean, purchases As Map)"
End Sub

Sub manager_PurchaseCompleted (Success As Boolean, Product As Purchase)
    'Após concluir a compra, atualizar o banco de dados e o MAP com as compras do usuário
    If Success  = True Then
    manager.ConsumeProduct(Product)
    End If
End Sub

Sub efetuarCompraProduto(skuProduto As String)
manager.RequestPayment(skuProduto,"inapp",skuProduto)
End Sub


On Main Activity i Call This Code

B4X:
baseMod.objCompras.Initialize
baseMod.objCompras.buscarProdutosComprados 'manager.GetOwnedProducts

OBS: Dim objCompras As appBilling
OBS: This variable "baseMod.objCompras" is a Process_Globals variable in baseMod ( "Module" ), because i want to access it from other activities.


The Problem occurs when i call, from MAIN ACTIVITY, my sub buscarProdutosComprados , and it calls "manager.GetOwnedProducts"

Thanks so Much !
 

rafaelbr20

Member
Licensed User
Longtime User
Perfect Erel !!! You Got it !!

Now i´m callin sub "manager.GetOwnedProducts" inside the sub "Manager_BillingSupported". It´s working now !

Thanks again for your quickly reply !!!

I have another question.

I´m getting this error when i call "manager.RequestPayment()" , then the billing window open and then i close it, and click on another product and "manager.RequestPayment()" is called again , but the first operation is still in progress.

How do i resolve it ? There is some Event that i have to wait before call "manager.RequestPayment()" again ?

I tried the sub "manager_PurchaseCompleted" but didn´t work, when the billing window closes, this event did not fires !!


B4X:
Error occurred on line: 35 (appbilling)
java.lang.IllegalStateException: Can't start async operation (launchPurchaseFlow) because another async operation(launchPurchaseFlow) is in progress.

    at anywheresoftware.b4a.objects.IbHelper.flagStartAsync(IbHelper.java:814)
    at anywheresoftware.b4a.objects.IbHelper.launchPurchaseFlow(IbHelper.java:350)
    at anywheresoftware.b4a.inappbilling3.BillingManager3.RequestPayment(BillingManager3.java:91)
    at com.mobileforfunapps.vestindominhaboneca.appbilling._efetuarcompraproduto(appbilling.java:66)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:636)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:302)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:238)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:121)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:162)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:158)
    at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:66)
    at android.view.View.performClick(View.java:4383)
    at android.view.View$PerformClick.run(View.java:18097)
    at android.os.Handler.handleCallback(Handler.java:725)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:176)
    at android.app.ActivityThread.main(ActivityThread.java:5279)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
    at dalvik.system.NativeStart.main(Native Method)

Thanks Again

Rafael
 

rafaelbr20

Member
Licensed User
Longtime User
You need to wait for PurchaseCompleted event. Only then you can call RequestPayment again.

Hi Erel.

This event is not firing if i close the billing window. I close the billing window clicking on any part of activity outside the window.

This is the code i have in my class and i put it also on my activity , just to try because this event is not firing.

B4X:
Sub manager_PurchaseCompleted(Success As Boolean, Product As Purchase)
    'Após concluir a compra, atualizar o banco de dados e o MAP com as compras do usuário
    If Success  = True Then
    manager.ConsumeProduct(Product)
    End If
End Sub

I was reading this on Google and i see this table.
http://developer.android.com/google/play/billing/billing_reference.html

I Need to identify BILLING_RESPONSE_RESULT_USER_CANCELED.

How can i do it ?

BILLING_RESPONSE_RESULT_OK 0 Success
BILLING_RESPONSE_RESULT_USER_CANCELED 1 User pressed back or canceled a dialog
...
...
...

Thanks Again

Rafael
 
Last edited:

rafaelbr20

Member
Licensed User
Longtime User
Hi Erel,

I have another question.

The order is CANCELED on google wallet but the API says it is ok ("PurchaseState = 0" ) , pelase see the screens attached !

There is some time delay to check it ?
 

Attachments

  • tela2.png
    tela2.png
    205.4 KB · Views: 385
  • tela1.png
    tela1.png
    84 KB · Views: 370
Top