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.basic4ppc.com/forum/addi.../29998-app-billing-v3-library.html#post174139
 
Last edited:

achtrade

Active Member
Licensed User
Longtime User
if an user purchase an item with this lib, will google take a percentage?
 

Bill Kantz

Member
Licensed User
Longtime User
Go to the google play developer console and select account details. There is a field called "Gmail accounts with testing access" you can add email addresses of testers that will do through the billing procedure but will not do the final charge.
 

Alberto Michelis

Well-Known Member
Licensed User
Longtime User
I cant make the PurchaseCompleted event to be raized.

In the Main activity I Initialize the manager
Manager_BillingSupported is raized ok
manager_OwnedProducts succeded ok
and I can set my app licence

In the main activity I also have a Manager_PurchaseCompleted that logically is never rized because the buying proc is in another activity

In the buy activity I have a button wich fires the proces...

B4X:
Sub bTrader_Click
      Main.manager.RequestPayment(Main.Productid2, Main.Producttype, Main.DeveloperPayload)
End Sub

Sub Manager_PurchaseCompleted(Success As Boolean, Product As Purchase)
    If Success Then
      'Main.manager.ConsumeProduct(Product)  ' I do not consume because its a suscription
      If Product.Productid = Main.Productid1 Then
            Licence=1
      Else If Product.Productid = Main.Productid2 Then
            Licence=2
      End If
    End If
End Sub

I can buy the products but, Manager_PurchaseCompleted is never fired.

What am I doing wrong?
 

Alberto Michelis

Well-Known Member
Licensed User
Longtime User
Ok, I copy the
-manager Process_Globals from main to the other activity
-manager initialize
-and modify the code to not to use the main. but the local manager
If I do this I allways get an error saying the app is not suitable for billing.
See the log image please.
 

Attachments

  • addinerror.png
    addinerror.png
    14.6 KB · Views: 228

Alberto Michelis

Well-Known Member
Licensed User
Longtime User
Ok, thanks but I dont know how to do it.
You mean:

In the buy activity button:
B4X:
Sub bButtonBuy_Click
    CallSubDelayed(Main,"RequestPayment")
End Sub

In the main activity:
B4X:
Sub RequestPayment
     manager.RequestPayment(Productid1, Producttype, DeveloperPayload)
End Sub

How to "later return to the original activity" ?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Create an activity that will be used to manage the purchases. Not the main activity.

B4X:
Sub RequestPayment (Callback As Object)
 mCallBack = Callback 'mCallBack is a process global variable
End Sub
CallSubDelayed2 (PurchaseActivity, "RequestPayment", Me)

In the PurchaseCompleted event you can return to the previous activity with Activity.Finish or CallSubDelayed (mCallBack, "SomeSub")
 

Alberto Michelis

Well-Known Member
Licensed User
Longtime User
Sory, but now I understand nothing
I define the manager in the main activity to test which product the user has and set the licence level.
You mean to delete all the manager code from Main and copy all this code to a new activity?
I will need to call this new activity,
-first to know the products the user has.
-later to buy some product

The code of the new activity will be like:
B4X:
Sub Process_Globals
   ...manager and variables

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

EndSub

Sub Manager_BillingSupported (Supported As Boolean, Message As String)
    'Fired by manager.Initialize
End Sub

Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
    'Fired by BillingSupported
    'Here I know which products the user owns !!!!!!!!!!!!! ok
End Sub

Sub RequestPaymentInversor
     manager.RequestPayment(Productid1, Producttype, DeveloperPayload)
End Sub

Sub RequestPaymentTrader
     manager.RequestPayment(Productid2, Producttype, DeveloperPayload)
End Sub

Sub Manager_PurchaseCompleted(Success As Boolean, Product As Purchase)
    'Fired by RequestPaymentInversor or RequestPaymentTrader
End Sub

If that code is what you mean:
- How do I call it first time from the Main Activity?
- How do I call RequestPaymentInversor and RequestPaymentTrader later from buttons of the buy activity

Thanks
 

Alberto Michelis

Well-Known Member
Licensed User
Longtime User
I've done what I type in the last post

Calling the Billing activity first time with
B4X:
    CallSubDelayed(mBilling,"IniManager")

It works as expected, only problem I get a blank activity shown while its working, any way to avoid it?

Calling the Subs to buy from buttons of another activity with
B4X:
    CallSubDelayed(mBilling,"RequestPaymentTrader")

I get the same Developer error than in my first post
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
+1 for any way to obtain the list of available products for purchase. Didn't see it in google's api though.
Actually, there is a way to do it in the API:

  1. Usually, you'll want to inform the user of the products that are available for purchase. To query the details of the in-app products that you defined in Google Play, your application can send a getSkuDetails request. You must specify a list of product IDs in the query request. If the request is successful, Google Play returns a Bundle containing product details including the product’s price, title, description, and the purchase type.

Unfortunately, this doesn't seem to have been implemented in the library.
 

JonPM

Well-Known Member
Licensed User
Longtime User
So when user purchases a managed product (nonconsumable) to say unlock pro version, should the app check GetOwnedProducts with each launch and through code unlock pro items?
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
So when user purchases a managed product (nonconsumable) to say unlock pro version, should the app check GetOwnedProducts with each launch and through code unlock pro items?
That would seem to be the most intuitive way to handle it. Another way would be to keep a local record of purchases in a file on the device, but if the user uninstalled & re-installed the app, this record would be lost. For cases such as this & other unforeseen losses of the purchase record, you'd have to provide a way for the user to manually query purchases made - so I think checking whenever the app starts is probably the best way to go.

- Colin.
 

JonPM

Well-Known Member
Licensed User
Longtime User
Great thanks Colin. So is this the correct way to check if the managed product has been purchased and to unlock the app?

B4X:
Dim unlocked as Boolean = False   'in Process_Globals

...
Sub IAP_OwnedProducts (Success As Boolean, purchases As Map)
   Log(Success)
   If Success Then
      Log(purchases)
      For Each Pur As Purchase In purchases.Values
         Log(Pur.ProductId & ", Purchased? " & (Pur.PurchaseState = Pur.STATE_PURCHASED))
         If Pur.ProductId = "com.my.app.unlock" And Pur.PurchaseState = Pur.STATE_PURCHASED Then
            Log("Purchased!")
            unlocked = True
        End If
      Next
   End If
End Sub
 
Last edited:

Computersmith64

Well-Known Member
Licensed User
Longtime User
Great thanks Colin. So is this the correct way to check if the managed product has been purchased?

B4X:
Dim unlocked as Boolean 'in Process_Globals

...
Sub IAP_OwnedProducts (Success As Boolean, purchases As Map)
   Log(Success)
   If Success Then
      Log(purchases)
      For Each Pur As Purchase In purchases.Values
         Log(Pur.ProductId & ", Purchased? " & (Pur.PurchaseState = Pur.STATE_PURCHASED))
         If Pur.ProductId = "com.my.app.unlock" And Pur.PurchaseState = Pur.STATE_PURCHASED Then
            Log("Purchased!")
            unlocked = True
        End If
      Next
   End If
End Sub
Yeah - that looks about right. You can also use that code to disable any buttons / links / whatever to purchases already made. I have consumable purchases of bonus bubble packs in my Bubble Tap ad, so this is what I do to stop users trying to purchase more bonus bubble packs before they have used the previously purchased one up:

B4X:
        If Success Then
            For Each p As Purchase In purchases.Value
                If p.ProductId = "bonus_1" Then
                    If p.PurchaseState = p.STATE_PURCHASED Then
                        If cOpts.Bonus1Balance = 0 Then
                            consumeProducts.Add(p)
                        Else
                            bonus_1.Enabled = False
                            bonus_1.Text = cOpts.Bonus1Balance & " left"
                        End If
                    Else If p.PurchaseState = p.STATE_CANCELED Or p.PurchaseState = p.STATE_REFUNDED Then
                        cOpts.Bonus1Balance = cOpts.Bonus1Balance - 10
                        If cOpts.Bonus1Balance <= 0 Then
                            cOpts.Bonus1Balance = 0
                            consumeProducts.Add(p)
                        End If
                    End If
                End If
            Next
        End If

The "bonus_1.Enabled = False" disables the purchase button if they haven't used the previously purchased pack & the next line shows them how many bonus bubbles are remaining from that purchase.

The "STATE_CANCELED Or STATE_REFUNDED" part is supposed to take back the bonus bubbles purchased if the purchaser requests a refund or I cancel the order via the console, however there is a huge delay in this status getting updated, so they could probably consume the entire purchase before I could take it back...

- Colin.
 
Top