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:

Erel

B4X founder
Staff member
Licensed User
Longtime User
This code is wrong:
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

label1.Visible = True
button2.Visible = True
'do my paid program
End If
End Sub

You should only make the button visible if the user has purchased the item. In your code you always set it to true.

Success means that the request to get the owned products has completed successfully.
 

tcgoh

Active Member
Licensed User
Longtime User
Thanks I will try this
Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
Log(Success)
If Success Then
Log(purchases)
label1.Visible = True
button2.Visible = True
For Each p As Purchase In purchases.Values
Log(p.ProductId & ", Purchased? " & (p.PurchaseState = p.STATE_PURCHASED))
Next
'do my paid program
End If
End Sub
 

tcgoh

Active Member
Licensed User
Longtime User
This code is wrong:


You should only make the button visible if the user has purchased the item. In your code you always set it to true.

Success means that the request to get the owned products has completed successfully.

Hi,

I tried placing the label and button visible after the "If success then", they still appeared on the APP with device that have not made the purchase. Sorry I am at a lost now and don't know how to continue with this module, maybe I'll go back to a Trial App and a Paid App.

Thanks and regards
 

tcgoh

Active Member
Licensed User
Longtime User
This is my code
B4X:
Sub Process_Globals
    Dim manager As BillingManager3
       Private key As String = "MIIBIjANBgkq....."
End Sub

Sub Globals
    Dim button1, button2 As Button
    Dim label1 As Label
    Dim ImageView1 As ImageView
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
      manager.Initialize("manager", key)
      manager.DebugLogging = True
    End If
     
    Activity.LoadLayout("Main")    
    label1.Visible = False
    button2.Visible = False
   
End Sub

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

Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
       Log(Success & "$$$$")
       If Success Then
      Log(purchases)
      label1.Visible = True ' only if inapp purchase done!
      button2.Visible = True ' only if inapp purchase done!
      For Each p As Purchase In purchases.Values
         Log(p.ProductId & ", Purchased? " & (p.PurchaseState = p.STATE_PURCHASED))
      Next
       End If
  
End Sub

Sub button1_click
    manager.RequestPayment("testbill", "inapp", "buyapp")
   
End Sub

Sub button2_click
    ImageView1.Visible = True
End Sub

Thanks for looking into it.
 

tufanv

Expert
Licensed User
Longtime User
hello
I have a problem. After the requestpayment triggered, if the user cancels the payment by clicking anywhere outsideof billing screen , biling screen goes of but also my panel get lost what may be the problem ?

as i understand it trgieers activity_resume ?
 
Last edited:

luke2012

Well-Known Member
Licensed User
Longtime User
Hi Erel,

The code was on a CLASS MODULE, and i changed now to a CODE MODULE and it works. I don´t know why , but it´s working now.

I Use Modules because all methods will be called by many activities, and i want to reuse code.

Thanks so much for your help !!

Rafael

Hi @rafaelbr20,

regarding your issue "Even when the purchase is complete the event " manager_PurchaseCompleted" never raises", I'm facing the same problem using code within a class module.

Do you confirm that porting the code within a code module the problem is resolved ?
 

luke2012

Well-Known Member
Licensed User
Longtime User
There is no IsInitialized method. You will need to track it yourself. Create a boolean variable and set it to true after you initialize the manager object.

@Erel about BillingManager I still have a couple of problems :

1) I don't understand why I got the message "Ignoring event ..." on manager initialize event

LOG :

Billing service connected.
Checking for in-app billing 3 support.
In-app billing version 3 supported for luke2012.android.ristodroid_lite
Subscriptions AVAILABLE.
Ignoring event: inappmanager_billingsupported

2)
I don't understand why the "_PurchaseCompleted" event isn't raised after the requestPayment (see log) within the class where I initialized the Billing Manager.
Note:
-
The BillingManager is declared in Class_Globals (Public InAppManager As BillingManager3) and initialized whitin the Class Initialize (InAppManager.Initialize("InAppManager", AppKey))
- The Class is declared whitin Process_Globals and initialized within the Activity_Create of the Main module
- The requestPayment is called within a method of the Class that is called within a "_PageFinished" event of a WebView
- All the Billing Manager events are defined within the class

LOG :

Starting async operation: launchPurchaseFlow
Constructing buy intent for ft001, item type: inapp
requestCode = 1
** Activity (actorderlist) Pause, UserClosed = false **
** Activity (actorderlist) Resume **
 

luke2012

Well-Known Member
Licensed User
Longtime User
1. Is it possible that the activity is paused when the event is raised?

2. Can you upload your project (without the keys)?

@Erel I wish to upload the project but it's too large more than 4 mb (limit is 500kb).
How I can send you the project?
 

holdemadvantage

Active Member
Licensed User
Longtime User
Hi Erel,

i have a problem with my cose as i have an error on manager.initialize (IAB helper is not set up)
I have made my apk without key then i have uploaded it on store as a draft
I have changed key inside my code
This is the code
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
Dim manager As BillingManager3
   Private key As String = "MIIBIjANBgkqhkiG9w0.."
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
Dim AdView1 As AdView
Dim paneladmob As Panel
Dim prodottoacquistato As Boolean
    Private Button1 As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
Activity.LoadLayout("main")  
  
    If FirstTime = True Then
        manager.Initialize("manager", key)
        manager.DebugLogging = True
    Else
        manager.GetOwnedProducts
    End If
    'manager.DebugLogging = True

Activity.LoadLayout("main")

manager.GetOwnedProducts

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

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)
   Dim result As Int
 
  
    If Success Then
        Log(purchases)
prodottoacquistato=False
For Each pu As Purchase In purchases.Values
         
If pu.ProductId = "removeads" AND pu.PurchaseState = pu.STATE_PURCHASED Then
prodottoacquistato=True
  paneladmob.Visible=False
  AdView1.Initialize("Ad", "ca-app-pub-492941727xxx/647398xx")'
  paneladmob.AddView(AdView1, 0dip, 0, Activity.width, 50dip) '<b>previously the height was 48dip. Now it is 50dip.
  AdView1.LoadAd
Else

  paneladmob.Visible=False
  End If
Next
End If
If prodottoacquistato=False Then Button1.visible=True
'il tasto acuista prodotto compare solo se il prodotto no risulta acquistato

End Sub

Public Sub manager_PurchaseCompleted (Success As Boolean, Product As Purchase)
   If Success=True Then paneladmob.Visible=False
   'il prodotto + stato acquistato e rendo invisibile il panel che contiene il banner
End Sub

Sub manager_ProductConsumed (Success As Boolean, Product As Purchase)

End Sub
Sub Button1_Click
    manager.RequestPayment("removeads","inapp","DeveloperPayload")
End Sub

I install both in an emulator and device and have same error in manager.initialize

B4X:
** Activity (main) Create, isFirst = true **


java.lang.NullPointerException


    at anywheresoftware.b4a.objects.IbHelper.startSetup(IbHelper.java:269)
    at anywheresoftware.b4a.inappbilling3.BillingManager3.Initialize(BillingManager3.java:44)
    at com.holdemadvantage.billingtest.main._activity_create(main.java:329)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at com.holdemadvantage.billingtest.main.afterFirstLayout(main.java:98)
    at com.holdemadvantage.billingtest.main.access$100(main.java:16)
    at com.holdemadvantage.billingtest.main$WaitForLayout.run(main.java:76)
    at android.os.Handler.handleCallback(Handler.java:587)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:130)
    at android.app.ActivityThread.main(ActivityThread.java:3683)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.NullPointerException

What could be the issue?
 
Top