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:

Dieter Baumgartner

Member
Licensed User
Longtime User
Purchasecompleted Event

Tried the new IN-APP API, because with the old one where problems mostly on Samsung SIII. Does anyone know how is the definition of the purchasecompleted - Event that is raised after the purchase.

Sub manager_Purchasecompleted(gekauftedatei As String)

says "signature does not match expected signature" ??
 

Dieter Baumgartner

Member
Licensed User
Longtime User
Thank you

Thank you Erel, until now i really didn't know the trick to write
Sub followed by a blank and then press the TAB- key.

This is perfect support for event-definitions, great help
 

Firpas

Active Member
Licensed User
Longtime User
InAppBilling 3 - error

Hi Erel:

I am testing the InAppBilling 3 lib and i have the attached error.

Do I need to specify any permission in the manifest as in the previous version?

Thanks in advance
 

Attachments

  • InAppBilling3 - Error.png
    InAppBilling3 - Error.png
    15.4 KB · Views: 1,629

Firpas

Active Member
Licensed User
Longtime User
Yes, App is published and also the integrated products Monthly subscription (andestest1) and anual subscription (andestest3).

See the attacched image
 

Attachments

  • Andestas.jpg
    Andestas.jpg
    85.6 KB · Views: 1,303

Firpas

Active Member
Licensed User
Longtime User
It is solved.
The problem was that the published application worked with the previous version of InAppBilling. Once updated, the problem was solved.

Thanks for your cooperation.
 

holdemadvantage

Active Member
Licensed User
Longtime User
Super noob question :sign0104::sign0013:!!
I have to make an app for a client with in-app subscription (annual fee) and obviously cash must go to him.
Do i have to open a new developer account on google playstore registered to the client with his own payment info and publish app with his developer account?
 

zjhi

New Member
Licensed User
Longtime User
java.lang.NoSuchMethodError

Hi!

For some reason i can't get the InApp Billing to work. I got BillingManager3 set up OK, was able to query for owned products and show the checkout window for 'android.test.purchased' item. However, after the app resumed from the checkout window, manager_PurchaseComplete function was not executed, but instead the app crashed. Any idea what i'm doing wrong?

Code:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   'Activity.LoadLayout("Layout1")
   If FirstTime Then
      manager.Initialize("manager", key)
   End If
   manager.DebugLogging = True
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)
   
   manager.GetOwnedProducts
End Sub

Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
   Dim result As Int
   Dim strFree As String
    Log("rivi 840: manager_OwnedProducts, "&Success)
    If Success Then
        Log(purchases)
        For Each pu As Purchase In purchases.Values
            Log(pu.ProductId & ", Purchased? " & (pu.PurchaseState = pu.STATE_PURCHASED))
         ' Jos maksettu
         If pu.ProductId = "vuosimaksu" AND pu.PurchaseState = pu.STATE_PURCHASED Then
            strFree = "0"
         End If
        Next
    End If
   ' Poistaa kaikki faket
   If purchases.ContainsKey("android.test.purchased") Then
      Log("poistetaan tuote 'android.test.purchased'")
      'manager.ConsumeProduct(purchases.Get("android.test.purchased"))
   End If
   
   ' Ei maksettu
   If strFree <> "0" Then
      result = Msgbox2("Sinulla on ohjelman ilmaisversio. Haluatko siirtyä ostamaan täysversion?"  , "Maksullinen versio" ,"Kyllä", "", "En" ,Null)
      If result = DialogResponse.Positive Then
         'vuosimaksu, subs
         
         manager.RequestPayment("android.test.purchased","inapp","")
      End If
   End If
End Sub
Public Sub manager_PurchaseCompleted (Success As Boolean, Product As Purchase)
   
End Sub

Sub manager_ProductConsumed (Success As Boolean, Product As Purchase)
   Log("tuote kulutettu: "&Product.ProductId)
End Sub

Logcat output:

B4X:
Starting async operation: launchPurchaseFlow
Constructing buy intent for android.test.purchased, item type: inapp
requestCode = 1
** Activity (main) Pause, UserClosed = false **
sending message to waiting queue (OnActivityResult)
running waiting messages (1)
Arrived: 1, 1
Ending async operation: launchPurchaseFlow
Successful resultcode from purchase activity.
Purchase data: {"packageName":"b4a.example","orderId":"transactionId.android.test.purchased","productId":"android.test.purchased","developerPayload":"","purchaseTime":0,"purchaseState":0,"purchaseToken":"inapp:b4a.example:android.test.purchased"}
Data signature: 
Extras: Bundle[{INAPP_PURCHASE_DATA={"packageName":"b4a.example","orderId":"transactionId.android.test.purchased","productId":"android.test.purchased","developerPayload":"","purchaseTime":0,"purchaseState":0,"purchaseToken":"inapp:b4a.example:android.test.purchased"}, INAPP_DATA_SIGNATURE=, RESPONSE_CODE=0}]
Expected item type: inapp
main$ResumeMessagerun (B4A line: 43)
End Sub
GC_FOR_ALLOC freed 240K, 7% free 9169K/9768K, paused 21ms, total 21ms
java.lang.NoSuchMethodError: anywheresoftware.b4a.BA.LogError
   at anywheresoftware.b4a.objects.IbHelper.logError(IbHelper.java:974)
   at anywheresoftware.b4a.objects.IbHelper.handleActivityResult(IbHelper.java:478)
   at anywheresoftware.b4a.objects.IbHelper$2.ResultArrived(IbHelper.java:379)
   at anywheresoftware.b4a.BA$4.run(BA.java:461)
   at anywheresoftware.b4a.BA.setActivityPaused(BA.java:373)
   at b4a.example.main$ResumeMessage.run(main.java:215)
   at android.os.Handler.handleCallback(Handler.java:725)
   at android.os.Handler.dispatchMessage(Handler.java:92)
   at android.os.Looper.loop(Looper.java:137)
   at android.app.ActivityThread.main(ActivityThread.java:5039)
   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:793)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
   at dalvik.system.NativeStart.main(Native Method)
 

JOANORSKY

Member
Licensed User
Longtime User
Ok.. this is getting a little confusing for my head.. (big head here.. but most of it is hard skull :BangHead:)

So... this is what i've done :

  • I've uploaded a draft apk
  • Made a inapp billing item
  • Attached it to the apk
  • etc etc
  • ...

My app is a very simple application with just one design view. Basically it sells license codes for a specific hardware device. The application is supposed to request the costumer 3 fields of data.. and in order to process his request he must pay a one time fee (this means that if he want the license code.. he must pay for it). The license is then sent by email and "attached-for-internal-licenses-purposes" also to his email (this means.. that a user can have multiple licenses as long as each license is request to a different email - which is one of the requested data fields). So.. this means that i must be able to sell the item multiple times..

The problem comes.. due to my lack of understanding on how i can get the purchased items in a variable or array of strings.. or map.. etc.. :BangHead:

I understand that this will request it...
PHP:
If ownedProducts.ContainsKey("test2") Then 
    manager.ConsumeProduct(ownedProducts.Get("test2"))
End If

but.. what is "ownedProducts" ? Is this an object... ? Where is it declared? How can i get (manipulate) the items within this so called "ownedProducts"?

:sign0148:

These are mostly noob questions i suppose.. but.. something that would really.. realllyyy... reallly help.. would be a ready made example... a working one. Learning by example is still my favorite kinda way to do it.. :eek:

I do manage to make my tablet ask for the license fee with the inapp billing... but i seen to be unable to go further.

Any good-willing-charitable-fellow-soul is willing to post a full working example here?

puss-in-boots.jpg


... please?
(eheh... really hope the puss-in-the-boots look works out.. eheheh)
 

JOANORSKY

Member
Licensed User
Longtime User
Thanks Erel.. i guess that i am not familiar with it. I check that and get back to you after I take a look on it.

(eheheh... that photo reminded my own cat today. It's so hot here these days that he finds the most strange positions to take a nap.. :D)
 

JOANORSKY

Member
Licensed User
Longtime User
Humm... some of the tips i already knew.. and some i didn't. Still the billing issue is still not very clear to me. So if an example could be posted.. i would be thankful.

As per my understanding.. the OwnedProducts is Map type variable that is filled with the manager.GetOwnedProducts calling code.. right? (this means that i must declare the OwnedProducts as a Map on the main application so that it can be filled in with the data coming from the manager.GetOwnedProducts.. correct?)

I am receiving this error when i am testing the purchase with a testing account : Error in obtaining informations from th server. [RPC:S-7:AEC-0]

Why is this error appearing?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
It is difficult to provide a full working example, as for an example to work it must be connected to a real Google account.

the OwnedProducts is Map type variable that is filled with the manager.GetOwnedProducts calling code.. right?
In the first post there is an example of handling the OwnedProducts event. You can store the Map received in this event in a global variable and use it later.

Though if you want to consume the purchase immediately then you should handle the PurchaseCompleted event.

See this discussion about this error message: Google Groups
 

JOANORSKY

Member
Licensed User
Longtime User
Ohh.. ok... i understand now. Thanks for your patience and willingness to help. I will give some feedback in a while after i done this successfully.

About the RPC:S-7:AEC-0 Error Code.. this comes from the CC company which is unable to correctly authenticate with PlayStore. Inputing the card directly on the wallet or using another "standard" card will solve the issue.. (here is the info.. just in case someone else is having this issue as well)
 
Top