Android Tutorial Android In-app Billing tutorial

For new projects it is recommended to use the more recent API (v3): http://www.b4x.com/forum/basic4andr...ls/29997-android-app-billing-v3-tutorial.html

Android market provides an in-app billing service which you can use to accept payments from your own application.

The code required to use this service is quite simple. However the actual process that is done in the background is pretty complicated. To better understand it you should read the official documentation: In-app Billing | Android Developers

This tutorial will cover the main concepts and the steps required to integrate in-app billing in your Basic4android project.

The billing service allows the user to purchase "products". Products are declared in the Android market developer console.
There are two types of products: managed and unmanaged products.

Differences between managed and unmanaged products:
- The user can only purchase a managed products once.
- You can later restore all the managed transactions.

For example, access to premium features should be a managed product.
Coins, potions and other "temporary" assets should be unmanaged products.

Developer console:

SS-2012-02-02_10.32.07.png


SS-2012-02-02_10.31.41.png


Basic4android code
The InAppBilling library includes one type of object which is BillingManager.
The BillingManager should belong to a Service module.

Typical code should look like:
B4X:
'Service module
Sub Process_Globals
   Dim Manager As BillingManager
   Dim PublicKey As String
   PublicKey = "xxxx" 'From the developer console
End Sub

Sub Service_Create
   Manager.Initialize("manager", PublicKey)
End Sub

Sub Service_Start (StartingIntent As Intent)
   Manager.HandleIntent(StartingIntent)
End Sub

Sub Manager_BillingSupported (Supported As Boolean)
   Log("BillingSupported: " & Supported)
End Sub

Sub Manager_PurchaseStateChange (PurchaseState As Int, ProductIdAs String, OrderId As String, PurchaseTime As Long, ExtraData As String)
   Log("PurchaseStateChange: " & PurchaseState & ", " & ProductId& ", " & OrderId & ", " & DateTime.Time(PurchaseTime) & ", " & ExtraData)
   If PurchaseState = Manager.PURCHASE_STATE_PURCHASED Then
      'Here you should store the purchase information
      'and then notify the activity about the purchase.
      'In most cases the activity will be running at this state. However it is possible that it will be paused.
      'In that case the following call will be ignored.
      CallSub2(Main, "ThankYouForPurchasing", ProductId)
   End If
End Sub

Sub Service_Destroy
   Manager.UnbindMarketService
End Sub
PublicKey is your developer public key as appears in the developer console under Edit Profile.
This string should be set in Process_Globals sub to allow it to be obfuscated.

The BillingManager is initialized in Service_Create. BillingManager processes the intents that are sent to the service (through the internal receiver).

BillingManager provides two events:
BillingSupported - This event is raised after initializing the manager. The parameter will tell you whether billing is supported. Note that the emulator doesn't support billing.

PurchaseStateChange - This event is raised when a product is purchased, refunded or cancelled.
PurchaseState will be one of the following values: PURCHASE_STATE_PURCHASED, PURCHASE_STATE_CANCELED, PURCHASE_STATE_REFUNDED or PURCHASE_STATE_NODATA.
ExtraData is an arbitrary string that you can send together with the purchase request.

When the service is destroyed you should call UnbindMarketService to free resources.

Activity code
B4X:
Sub Activity_Create(FirstTime As Boolean)
   If IsPaused(InAppBillingService) Then StartService(InAppBillingService)
   Activity.LoadLayout("1")
End Sub

Sub Button1_Click
   InAppBillingService.Manager.RequestPayment(Activity, "unmanaged_1", "extra data")
End Sub

Sub ThankYouForPurchasing (Product As String)
   Msgbox("Thank you for purchasing: " & Product, "")
End Sub

When the activity is created we start the service if necessary.
The following code sends a purchase request to the market for a product:
B4X:
'InAppBillingService is the name of the Service.
InAppBillingService.Manager.RequestPayment(Activity, "unmanaged_1", "extra data")

RestoreTransactions
Managed products are tracked by the market. It is possible to retrieve previous purchases of such products.
For example if the user has uninstalled the application and now installs it again, you can call RestoreTransactions to retrieve the previous products.

According to Google documentation you should not call RestoreTransactions each time your application starts.

When you call RestoreTransactions the PurchaseStateChange event will be raised for each purchase. If there are no previous transaction then PurchaseStateChange will be raised and PurchaseState parameter value will be PURCHASE_STATE_NODATA.

Manifest editor
The following code should be added to the manifest editor code (change InAppBillingService to your service name):
B4X:
'InAppBilling declarations - Start
AddReceiverText(InAppBillingService,  <intent-filter>
                <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
                <action android:name="com.android.vending.billing.RESPONSE_CODE" />
                <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
            </intent-filter>)
'InAppBilling declarations - End

Testing your application

It is not so simple to test the payment process. A device set to the same account as the merchant account cannot be used, as you are not allowed to purchase from yourself.

From my experience this is the easiest way to test your application:
- Upload a draft application to the market. You do not need to publish it.
- Set some low cost ($0.01) products.
- Use a device with an account other than the merchant account.
- Add this account as a test account. This is done under Edit Profile page.
The account must be a gmail.com account.

You will now be able to develop and debug with this device. If you like you can also cancel the orders from the merchant console.

You may need to first publish the application and then unpublish it.
The application must be signed with the same signature and have the same version number as the uploaded one.

Tips
- Track the logs.
- When you cancel an order the purchase state will be PURCHASE_STATE_CANCEL. When you partially refund it the state will be PURCHASE_STATE_REFUND.

The library is available here: http://www.b4x.com/forum/additional...oid-market-app-billing-service.html#post82746
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
1) It is better to start a new thread for this question as it is not really related to this one.

2) -1 is the value of BillingManager.PURCHASE_STATE_NODATA. As written in the tutorial this means that there are no previous managed transactions.

The error that you see is because you tried to show a Msgbox from a service. This is not possible. Services cannot show any UI elements except of a toast message.
 

Firpas

Active Member
Licensed User
Longtime User
Android In-app Billing

This tutorial is ok when you want to sale your app. But ...
How to sale some sevices like suscriptions?

Thanks in advance
 

sorex

Expert
Licensed User
Longtime User
Is this somethine that is being worked on?

I'm also busy with an app that required monthly or quarterly billing,
we first thought to send a mail after subscription that contained
a link to the billing web page.

But now the customer wants it to happen inside the application :cool:
 

xfernal

Member
Licensed User
Longtime User
Erel,
I know you are a busy man. Is there was a way to "donate" to have subscriptions added sooner than later? I have already purchased the Enterprise version, but would be glad to "donate" some $ to make this be a higher priority. I am sure others would agree to donate as well.
 

sorex

Expert
Licensed User
Longtime User
I could do a donation aswell, since a project that's 90+% finished will get cancelled if I can't add the subscription stuff (they requested it when it was already at this stage tho, so lesson learned... next time they can put it all on paper and if stuff change I can still decide if I want to add it or not and for what price)
 

nad

Active Member
Licensed User
Longtime User
Hello Erel,

One question I don't understand very well.

To make the managed orders available after reinstalling I am verifying if it is the first time the user uses the application on this device and only this time I execute a RestoreTransactions. That should initiate a PurchaseStateChange event where I store if purchased that the user is licensed correctly (or not if he has canceled the order for example).

My question is, what happens when the user has finished using the app and after some hours or days he requests a cancel or refund. There should not be any way for the app to know that the refund has been done and my stored flag is still (licensed) until the next RestoreTransactions execution right? Or maybe I should execute a rnd(1,11)=1 then RestoreTransactions every app start to at least avoid that he is using (statistically) the app (without rights to it) more than 10 times?
If i don't do that, won't any user that refunds be able to use the app freely until the next uninstall/install again?

Thanks,
 

nad

Active Member
Licensed User
Longtime User
Thanks,

And if the user has the mobile switched off when issuing the refund, cancel (from a tablet for example)? Won't he be able to continue using the managed item (no ads for example) freely from his mobile after turning it on?

I will try adding a RestoreTransactions every number of startups just in case.
 
Last edited:

Prosg

Active Member
Licensed User
Longtime User
Hello,

Question 1:
errorAp.jpg

i use the code from here with my service name, and my managed product activate in Google play. Why this error ?

my manifest editor

B4X:
AddManifestText(
<uses-sdk android:minSdkVersion="4"/>
<supports-screens android:largeScreens="true" 
    android:normalScreens="true" 
    android:smallScreens="true" 
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
AddApplicationText(<activity android:name="ice.zxing.CaptureActivity"
         android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden"
         android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
         android:windowSoftInputMode="stateAlwaysHidden">
      </activity>)
'End of default text.
'InAppBilling declarations - Start
AddReceiverText(InProsgApp1Billing,  <intent-filter>
                <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
                <action android:name="com.android.vending.billing.RESPONSE_CODE" />
                <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
            </intent-filter>)
'InAppBilling declarations - End

Question 2:
How can i check if someone have a purchase a managed product ?

like

If checkPurchasse("Name") then do this


Question 3:
is subscription is now available with 2.5 ?


Thanks for help
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
1) Try to publish your app and then unpublish it.

2) The proper way is to save the purchased products whenever a product is purchased. You can call RestoreTransactions to receive all the managed transactions again.

3) Subscriptions are not supported. Google has released a new version of this API. However the new version doesn't support subscriptions. Hopefully it will be added soon.
 

Prosg

Active Member
Licensed User
Longtime User
thx a lot

I have an error sometimes when my app start :

Error: java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
do you know what this could be ?

and i buy a unmanaged product with my app, and this doesn't work :

B4X:
CallSub2(Main, "ThankYouForPurchasing", ProductId)


is it normal that all in my sub work exept this call ? i must be sure that my licence.txt will work at 100% for each paiement


B4X:
Sub Manager_PurchaseStateChange (PurchaseState As Int, ProductId As String, OrderId As String, PurchaseTime As Long, ExtraData As String)
   ' Log("PurchaseStateChange: " & PurchaseState & ", " & ProductId& ", " & OrderId & ", " & DateTime.Time(PurchaseTime) & ", " & ExtraData)
    If PurchaseState = Manager.PURCHASE_STATE_PURCHASED Then
      Select Case ProductId
         Case "more_25"
            'nouveau nombre
            Main.gNbMaxClient = Main.gNbMaxClient + 25   
         Case "more_50"
            'nouveau nombre
            Main.gNbMaxClient = Main.gNbMaxClient + 50
         Case "more_100"
            'nouveau nombre
            Main.gNbMaxClient = Main.gNbMaxClient + 100
         
      End Select
      
      'Détruit le fichier
       File.Delete(File.DirInternal, "licence.txt")
      
      'Recré la licence avec le nouveau nombre
      Dim TextWriter1 As TextWriter
      TextWriter1.Initialize(File.OpenOutput(File.DirInternal, "licence.txt", False))
      'Format : licence & " " & nombre max client
      TextWriter1.WriteLine(Encrypt(GetDeviceId & " " & Main.gNbMaxClient))
      TextWriter1.Close
      
         
        'Here you should store the purchase information
        'and then notify the activity about the purchase.
        'In most cases the activity will be running at this state. However it is possible that it will be paused.
        'In that case the following call will be ignored.
        CallSub2(Main, "ThankYouForPurchasing", ExtraData)
    End If
End Sub


Could you confirm me that only the call could not work ?


thx erel
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Error: java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
If you are not doing any network communication in your program then it is probably related to B4A-Bridge (so you can ignore it).

In which way does the call do not work? If the target activity is not running then the call will do nothing. You should also save the information in a file and then when the activity is created you should check the file and handle the unhandled transactions. There are other possible ways to handle the transactions.
 

peacemaker

Expert
Licensed User
Longtime User
For ref - seems, something is changed in the in-app billing procedures, and now more errors are returned by the service: In app purchase displays message "you already own this item." in android - Stack Overflow

My apps with old Erel's code cound not restore the payment state, seems, several latest months (starting 2013), if to save the previous try result, and if to try the restore only if no saved attempt (fully re-installed app state).
I have made the restoring try at every opening the payment activity - and users reported that this problem (with error "You already own this item") is solved and the prevous payment is restored well.
 
Last edited:
Top