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:

JonPM

Well-Known Member
Licensed 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
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.
 

Devv

Active Member
Licensed User
i'am always getting this error, any ideas ?

g8FVk.png


B4X:
Sub Activity_Create(FirstTime As Boolean)
     If FirstTime Then
         Activity.LoadLayout("main") 'draw the screen GUI
        manager.Initialize("manager", key)    'intialize the in-app billing library
        manager.DebugLogging = True  
     End If
End Sub


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

    If Supported = True Then    'check your licence key if it support billing
        ToastMessageShow("Ready",False)
      
        ' add the menu items
        Activity.AddMenuItem("5$","five")
        Activity.OpenMenu
    Else
        ToastMessageShow("Billing is not supported !",True)
    End If
End Sub

Sub five_click
   manager.RequestPayment("testsword","inapp","test") 'buy the item (you can manage items from your devlopers account "In-app Products" page)
End Sub
 

Devv

Active Member
Licensed User
This message means that the user is not logged in to the Google account.
i was signed in with google account, that problem was solved when i published myapp

anyway now i'm getting another eror
"the item you requested is not available for puchase"

i have the same item id in the app and the developer console (very wired)
 

JonPM

Well-Known Member
Licensed User
I'm still confused as to the best way to use this library when needing to make purchases (or check what's been purchased) across multiple activities. Can someone shed some light on this?
 

Erel

Administrator
Staff member
Licensed User
Put a single BillingManager in the StarterService.

The BillingSupported event will be raised in the service.

The other events will be raised in the activity that called the relevant method.

For example if you call BillingManager.GetOwnedProducts in the Main activity then the OwnedProducts will be raised on that activity.
 

tufanv

Expert
Licensed User
I am also getting "Authentication is required" message altough i am using a real device and i am already loggedin to google account and a draft uploaded to google play ( i have activated the product )

edit: Publishing the app in beta mode fixed the problem


This message means that the user is not logged in to the Google account.
 
Last edited:

JonPM

Well-Known Member
Licensed User
Is there a min API needed for this library? I'm getting a number of users reporting their purchased IAP content gets locked when the device is offline. I'm not having the issue on any of my devices.

Is it unsafe to store a boolean in a map file in DirInternal which saves the purchase state (ie unlocked content)?
 

Robert Valentino

Well-Known Member
Licensed User
Hello:

I am just setting up a Subscriptions In-App billing.

Is there a way I can request what Subscriptions are available?
Not what ones have been purchased but what ones are available to purchase?

When I set these up in Google I had to give a Title, Description and amount for each Subscription how can I retrieve this list?

BobVal

I read somewhere that Google does not provide a complete list of what is available.

But this will get me all the information on a product if I know the productID

But I do not see in BillingManager any call to get this information

This is what I need to do:

The getSkuDetails() method
This method returns product details for a list of product IDs. In the response Bundle sent by Google Play, the query results are stored in a String ArrayList mapped to the DETAILS_LIST key. Each String in the details list contains product details for a single product in JSON format. The fields in the JSON string with the product details are summarized in table 2.

Table 2. Description of JSON fields with product item details returned from a getSkuDetails request.

Key Description
productId The product ID for the product.
type Value must be “inapp” for an in-app product or "subs" for subscriptions.
price Formatted price of the item, including its currency sign. The price does not include tax.
price_amount_micros Price in micro-units, where 1,000,000 micro-units equal one unit of the currency. For example, if price is "€7.99", price_amount_micros is "7990000".
price_currency_code ISO 4217 currency code for price. For example, if price is specified in British pounds sterling, price_currency_code is "GBP".
title Title of the product.
description Description of the product.
 
Last edited:

sorex

Expert
Licensed User
I'm reading this thread but what's not clear is to me is if this library is writing data to the app folder or not.

what will happen if you disable wifi/mobile and then open the app?

will it read the purchases from 'disk' or pretend that nothing has been purchased since it can't connect to google play?

or do we need to write purchase info ourselfs when a payment succeeded and read it back in to disable ads for example.
 

Bill Kantz

Member
Licensed User
Billing manager does keep a local cache of purchase information. It does refresh the cache it if and when an internet connection is available.
 

sorex

Expert
Licensed User
you're right, Bill.

Even when disconnected from the mighty net it still pulls in the purshased item from the cache.
 

luke2012

Well-Known Member
Licensed User
TARGET: Implement in-app subscription within the Starter Service.

I did the following test:

1) I run the following code within the Main activity. RESULT = IT WORKS
2) I run the following code within the Starter Service*. RESULT = APP CRASH (see attached log)

*All the code was copied into the Starter Service (as it is). Code within "Sub Activity_Create" was copied within the "Sub Service_Create".

@Erel this is an expected behaviour ?
There is a way to run this code within the Starter Service ?

Thanks in advance for any help!

B4X:
Sub Process_Globals
    'in-app purchase (Annual Subscription)
    Public key_InApp_Billing As String = "XXX" 'The test was made using a valid key
    Public InAppManager As BillingManager3
    Public SubName As String
    Public SubPaid As Boolean
    Public SubSupported As Boolean
End Sub

Sub Activity_Create(FirstTime As Boolean)

    'in-app billing (see in-app subscription "sb001")
    InAppManager.Initialize("InAppManager", key_InApp_Billing)
    InAppManager.DebugLogging = True
    SubName = "sb001" 'subscription ID

End Sub

Sub InAppManager_OwnedProducts (Success As Boolean, purchases As Map)

   If Success Then

      Log(purchases)

     SubPaid = False
      For Each p As Purchase In purchases.Values
         Log(p.ProductId & ", Purchased? " & (p.PurchaseState = p.STATE_PURCHASED))
    
        If p.ProductId = SubName Then
            SubPaid = True
        End If
    
      Next

     Log("SubPaid = " & SubPaid)
     ToastMessageShow("SubPaid = " & SubPaid, True)

     If SubPaid = False Then InAppManager.requestPayment (SubName,"subs","")

   End If

End Sub

Sub InAppManager_BillingSupported (Supported As Boolean, Message As String)

   ToastMessageShow("Subscriptions supported: " & InAppManager.SubscriptionsSupported, True)

   If InAppManager.SubscriptionsSupported Then

        InAppManager.GetOwnedProducts
        'SubSupported = True
  
    Else
  
        'SubSupported = False
        ToastMessageShow ("Subscriptions NOT supported!", True)
  
   End If

End Sub

Sub InAppManager_PurchaseCompleted (Success As Boolean, Product As Purchase)

  If Success Then

      Log("Pagamento riuscito")
    ToastMessageShow ("Pagamento OK", False)

    InAppManager.GetOwnedProducts

  Else
    Log("Pagamento fallito!")
    ToastMessageShow ("Pagamento FALLITO", False)

    SubPaid = False
  
  End If

End Sub
 

Attachments

  • log.PNG
    log.PNG
    47.6 KB · Views: 225
Last edited:
Top