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:

holdemadvantage

Active Member
Licensed User
Longtime User
After some hours i have faced message like:

"This version of the application is not configured for billing through Google Play"

and other error message that now disappear permitting to test/purchase but not with my account (i am the editor so i cannot purchase)

App Version must be the same on device and on store, different version number can cause many issue and error message.
 

Bill Kantz

Member
Licensed User
Longtime User
Is there anything special to know when running inapp billing in a service instead of an activity?
 

hillzx

Member
Licensed User
Longtime User
How to do RestoreTransaction ? I can't find any sample code of that, i tried code like this manager.restoretransaction but it error
 

hillzx

Member
Licensed User
Longtime User
There is no such method. You need to call GetOwnedProducts.
First, in other thread you said that google would not recommend for us to call restore transaction each starting app, as i already heard that we have to call it just once after user reinstall app, so when is the correct timing implementation to call GetOwnedProducts ?

Second question is , actually i am trying to create Managed Product, if we need to redo checking owned product everytime app starts, so is it means the app really depends on internet connection?
 

Bill Kantz

Member
Licensed User
Longtime User
Billing manager 3 is started from a service called "ws" in Sub Service_Create

The log is as follows:

B4X:
PackageAdded: package:com.sailawayapps.bestwx.inapp
** Service (ws) Create **
** Service (ws) Start **
Billing service connected.
Checking for in-app billing 3 support.
In-app billing version 3 supported for com.sailawayapps.bestwx.inapp
Subscriptions AVAILABLE.
** Service (ws) Start **
true, Setup successful. (response: 0:OK)
Subscriptions supported: true
Starting async operation: refresh inventory
** Service (ws) Start **
Querying owned items, item type: inapp
Package name: com.sailawayapps.bestwx.inapp
Calling getPurchases with continuation token: null
Owned items response: 0
Continuation token: null
Querying SKU details.
queryPrices: nothing to do because there are no SKUs.
Querying owned items, item type: subs
Package name: com.sailawayapps.bestwx.inapp
Calling getPurchases with continuation token: null
Owned items response: 0
Continuation token: null
Querying SKU details.
queryPrices: nothing to do because there are no SKUs.
Ending async operation: refresh inventory
start of Purchases
Owned   true
(MyMap) {}


I then create an acclivity and try to purchase WS.BM.RequestPayment("Purchase App"," "," ")

My service ws does continue to run. It is on a 1 second timer StartServiceAt( "" , DateTime.Now + 1 * 1000, False)

the log I get is:

B4X:
** Activity (purchase) Create, isFirst = true **
** Activity (purchase) Resume **
Starting async operation: launchPurchaseFlow
Constructing buy intent for Purchase App, item type: 
Unable to buy item, Error response: 3:Billing Unavailable
Unable to buy item (response: 3:Billing Unavailable)
Ending async operation: launchPurchaseFlow
** Service (ws) Start **
** Service (ws) Start **

Not sure what is causing the error? Could it be I started the billing manager from a service?

This being used to purchase a widget


Thanks Bill
 

Bill Kantz

Member
Licensed User
Longtime User
When I call Request Payment this is what I get the msg box below . I believe my gmail account is fine. It is listed as a test account. I can make purchases in Google play no problem. Any Idea. The gmail account I am using is not my developer account. I have tried this with diffrent devices and diffrent accounts. For the GetBUYIntent I looked up result code "2" and it seem to be "network not available". I changed the product ID to something that does not exist to see if the error would change but it did not.

The only thing I am doing different from the norm is I am starting the billing from a service and all events are located in the service. (The app is a widget)

I do call the request payment for within the "purchase" activity.

B4X:
' WS service
Sub Process_Globals
     Dim BM As BillingManager3
     Private key As String = "MIIBIjANBgkqhkiG..............."
end sub

Sub Service_Create

    rv = ConfigureHomeWidget("l1", "rv", 0, "Best Weather 4 X 2")
    tracker.Initialize
    GPS1.Initialize("GPS")
    parser.Initialize
    BM.Initialize("BM", key)
        BM.DebugLogging =True

     
End Sub

Sub BM_BillingSupported (Supported As Boolean, Message As String)
     Log(Supported & ", " & Message)
       Log("Subscriptions supported: " & BM.SubscriptionsSupported)
     BM.GetOwnedProducts
End Sub

Sub BM_OwnedProducts (Success As Boolean, Purchases As Map)
    Log("start of Purchases")
    Log("Owned   " & 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

Sub BM_PurchaseCompleted (Success As Boolean, Product As Purchase)
    Log("Purchase    " & Success)
End Sub

' Purchase Activity

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Purchase")
End Sub


Sub Purchase_Click
    WS.BM.RequestPayment("Purchase App1","inapp"," ")
End Sub


2015-01-08%2020.46.53.png


Here is the log

Thanks Bill

B4X:
** Service (ws) Create **
** Service (ws) Start **
Billing service connected.
Checking for in-app billing 3 support.
In-app billing version 3 supported for com.sailawayapps.bestwx.inapp
Subscriptions AVAILABLE.
true, Setup successful. (response: 0:OK)
Subscriptions supported: true
Starting async operation: refresh inventory
Querying owned items, item type: inapp
Package name: com.sailawayapps.bestwx.inapp
Calling getPurchases with continuation token: null
Owned items response: 0
Continuation token: null
Querying SKU details.
queryPrices: nothing to do because there are no SKUs.
Querying owned items, item type: subs
Package name: com.sailawayapps.bestwx.inapp
Calling getPurchases with continuation token: null
Owned items response: 0
Continuation token: null
Querying SKU details.
queryPrices: nothing to do because there are no SKUs.
Ending async operation: refresh inventory
start of Purchases
Owned   true
(MyMap) {}

** Activity (purchase) Resume **
** Service (ws) Start **
Starting async operation: launchPurchaseFlow
Constructing buy intent for Purchase App, item type: inapp
requestCode = 3

sending message to waiting queue (OnActivityResult)
running waiting messages (1)
Arrived: 3, 3
Ending async operation: launchPurchaseFlow
Purchase canceled - Response: 2:Unknown
User canceled. (response: -1005:User cancelled)
** Activity (purchase) Resume **

** Activity (purchase) Pause, UserClosed = false **
 
Last edited:

dbergusa

Member
Licensed User
Longtime User
Erel, Sir you are a Saint to provide this library and the level of support/feedback you have been doing. Thanks for your patience and effort.
 

Peter van Hoof

Member
Licensed User
Longtime User
I can see the image fine. It states in a Google play window
"Authentication is required. You need to sign into your Google Account."

I have exactly the same problem, I believe it has to do with the new requirement that testing for in app billing can only be done on a published app..... it has to be alpha , beta or release.

Peter

When I call Request Payment this is what I get the msg box below


2015-01-08%2020.46.53.png
 

Bill Kantz

Member
Licensed User
Longtime User
Yes that is true it has to be published alpha or beta. The mistake I made I was using the Name no the ID for purchase. At first I didn't realize the ID is in Parentheses next to the Name in the Google Dev Console.

Bill
 

luke2012

Well-Known Member
Licensed User
Longtime User
Hi to all,
which is the most convenient way to manage montly or annual app use/user support license ? Implementing Managed In-app Products or Subscriptions ?
 

Bill Kantz

Member
Licensed User
Longtime User
If you want the license to expire after a certain amount of time you have to use subscription.
 

trueboss323

Active Member
Licensed User
Longtime User
Hey Erel,

I am using the In-App billing for removing advertisements. I got some questions.
On the application startup, I would like to check and see if the user already bought the item, so that way it removes ads immediately. I know that it's something to do with manager.GetOwnedProducts , but I don't know how to identify the product, is there some sort of 'key' associated with the purchased item?
And where should I place the code for detecting if the user removed ads?

Another question, also with manager.GetOwnedProducts, I also have a button that let's the user restore their purchase if they log in from another device. In case it doesn't happen automatically, this would do it manually. What would be the correct code for doing that?

Thanks Erel.
 

Bill Kantz

Member
Licensed User
Longtime User
Read the inapp billing tutorial then this code will makes sense. This code assumes only one in app item is available for purchase.

B4X:
Sub BM_OwnedProducts (Success As Boolean, Purchases As Map)

    Log("start of Purchases")
    Log("Owned   " & Success)
       If Success Then
        If Purchases.Size > 0 Then
            Dim p As Purchase
            p = Purchases.Get("purchase")
            If p.PurchaseState = p.STATE_PURCHASED Then
                licensed = True
            Else
                license = False
            End If
        Else
            Ads = True
        End If
 End Sub
 

Bill Kantz

Member
Licensed User
Longtime User
I also have a button that let's the user restore their purchase if they log in from another device. In case it doesn't happen automatically, this would do it manually. What would be the correct code for doing that?

The In App billing manager will handle checking the license automatically and will retrieve the purchase status based on the user ID. It does not matter what device is used.

you do not need the restore button.
 
Top