Android Tutorial Android InApp Purchase Tutorial + Source Code

Hi,

in this tutorial i will show you how to add an in app product to your android app.

There is an official tutorial by @Erel so you should also check that out: https://b4x.com/android/forum/threads/android-in-app-billing-v3-tutorial.29997/

Step 1:

First we need to create an in_app product in our developer console (google play)
but to do that we need to upload an .apk that include InAppBilling3 lib.
so just create an app and check that lib and upload it to the dev console (without adding any code)
after your dev console will recognize you have uploaded an apk that include the in_app library it will allow you to create an in_app product.

Step2:

when you add a new in_app product you can choose between a "Managed Product" and "Subscription"

a "Managed Product" is a product that the user purchase only once like: remove ads, skip level, coins,..

a "Subscription" is a product that will charge the user every x period like: news letter subscription,...

in this tutorial we will create a "Managed Product" (no_Ads).

So create your Product and then copy your In_App ID that you can find here (write also the Product ID down, you will need it later):

inapp.jpg


Step 3: (coding)

Ok now we are ready for some code but first you need to know that your app will probably crash on an emulator so you should test it on a real device.

i will explain how i do it in my apps (but it doesn't mean that this is the only (or the right :rolleyes:) way)

in android on every app start (if firsttime = true) we check if user has purchased one of our products.

First we will intialize the "manager" and this will automaticly call the sub "manager_BillingSupported" and check if billing is supported.
if billing is supported we will ask for a list of all owned products by that user and this is done in sub "manager_OwnedProducts".

then we will check if the "no_ads" product is included in that list (map).

if "no_ads" product has been purchased then we should turn off ads, right?

but what happens when the user purchase the "no_ads" product and the "BillingSupported" request fail to run on app create?
(lets say the user had no internet connection at that moment)

if adtimer will run on every app start that means that ads will be shown even if he has purchased the "no_ads" product.

so the user could be very mad if he see ads after he has purchased the "no_ads" product, so what should we do?

the timer (that request ad's) is in default enabled on every app_create (start on every app start).so how can we avoid a situation where the user see ads even if he has purchased the "no_ads: product?

what i do is, i create a txt file called "ad.txt" after user has purchased the "no_ads" product and then on every app start i will check for that file. if it exists then i disable the adtimer until i am able to check again if user has purchased the "no_ads" product. (on next app start)

you will probably ask now why should i check on every app start if the user has purchased the "no_ads" product? lets create that file on "app purchase" and if file.exist ("ad.txt") then skip the "Get_ownedproducts" process, right?

well that is wrong, because if he purchase the product you create that file and then if he cancel his purchase that file will still exists and no ads will be shown. so the "ad.txt" file just change the default settings of the adtimer from "enable/disable on every activity_create"

ok enough talking, everything should be clear enough from the code bellow. if you have questions don't hesitate to ask and if you have a better solution you are welcome to post it here.

regards, ilan :)

Activity Code:

B4X:
#Region  Project Attributes
    #ApplicationLabel: InApp Example
    #VersionCode: 1
    #VersionName: 1.0
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: portrait
    #CanInstallToExternalStorage: False
    #AdditionalRes: C:\Android SDK\extras\google\google_play_services\libproject\google-play-services_lib\res, com.google.android.gms
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: false
#End Region

Sub Process_Globals
    Dim adtimer As Timer
    Dim manager As BillingManager3
    Private inappkey As String = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiIEJEB/JNzDSn8YoWUE/dq7/q8HDUjUL5np0tSmk3pdauYO2rRMvDNxXO4e+FqgYhYjswBKdFeIlzLVF8+y2J49G85eaznP8ZYbt0vexH6lpk0iJxrfOxeGo8BY3IvUaCm0PXwdM9gos+XwZo14lr+pFSfpR/qd7PzXBqtdZ9NxvAyAnlLGrpzHuTApRCp8EIMc1kYZHd/pGtTcRyLuCGxFW0cbsrpKUIMHzDe1S/pp7bRqs3tMjagySWx0Kzic0ufxxa6v/XzbBDQPeSpyIcZLFxNuDBrZElu83cC39JZz1kKiGSfcMmsz/eegJo/AdII8CVkJjY5ov6CG64i+cPQIDAQAB"
End Sub

Sub Globals
    Dim b As Button
    Dim mwAdInterstitial As mwAdmobInterstitial 'interstitial ad (admob)
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.Color = Colors.White

    adtimer.Initialize("adtimer",25000) '25sec to show ad

    b.Initialize("Button")
    b.Text = "No Ads"
    b.Color = Colors.DarkGray
    b.TextColor = Colors.White

    Activity.AddView(b,20dip,20dip,150dip,50dip)

    mwAdInterstitial.Initialize("mwadi","pub-8081096xxxxxxxxxxxxxxxxxxx")
    mwAdInterstitial.LoadAd

    If FirstTime Then
        manager.Initialize("manager", inappkey)
        manager.DebugLogging = True
    End If

    If File.Exists(File.DirInternal,"ad.txt") Then adtimer.Enabled = False 'change default ad support to off
End Sub

Sub Button_Click
    manager.RequestPayment("remove_ads","inapp","remove_ads") 'request purchase with ID "remove_ads"
End Sub

Sub manager_BillingSupported (Supported As Boolean, Message As String) 'First we check if Billing is supported
   Log(Supported & ", " & Message)
   Log("Subscriptions supported: " & manager.SubscriptionsSupported)
   If Supported Then manager.GetOwnedProducts 'if support true then get owend product by this user
End Sub

Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
   Log(Success)
   Dim purchased_noAds_from_store As Boolean = False  'set a boolean to false and if item with specific ID was purchased change to true

   If Success Then
      Log(purchases)
      For Each p As Purchase In purchases.Values
         Log(p.ProductId & ", Purchased? " & (p.PurchaseState = p.STATE_PURCHASED))
         If p.ProductId = "remove_ads" And p.PurchaseState = p.STATE_PURCHASED Then 'check if item NoAds was purchased
            purchased_noAds_from_store = True 'set boolean to true
            Purchased_NoAds(True)
        End If
      Next

      If purchased_noAds_from_store = False Then Purchased_NoAds(False) 'delete file and start timer
   End If
End Sub

Sub manager_PurchaseCompleted (Success As Boolean, Product As Purchase)
    Log(Product)
    Log(Success)

    If Success Then
        If Product.ProductId = "remove_ads" Then Purchased_NoAds(True)
    End If
End Sub

Sub Purchased_NoAds(purchased As Boolean)
    If purchased Then
        adtimer.Enabled = False 'stop timer if was on
        If File.Exists(File.DirInternal,"ad.txt") = False Then  'create no ad file
           Dim writer1 As TextWriter
           writer1.Initialize(File.OpenOutput(File.DirInternal,"ad.txt", False))
           writer1.WriteLine("1")
           writer1.Close                
        End If
    Else
        If File.Exists(File.DirInternal,"ad.txt") Then File.Delete(File.DirInternal,"ad.txt") 'delete file
        adtimer.Enabled = True  'start timer
    End If
End Sub

Sub adtimer_tick
    If mwAdInterstitial.Status=mwAdInterstitial.Status_AdReadyToShow Then
         adtimer.Interval = 240000 'change the time between ads (so people will also see your app and not only ads ;))
         mwAdInterstitial.Show
    Else
        mwAdInterstitial.LoadAd
        adtimer.Interval = 15000 'if fail - load again and show in 15 sec
    End If
End Sub

Sub mwadi_AdLoaded
    Log("ad loaded")
End Sub

Sub mwadi_AdFailedToLoad (ErrorMessage As String)
    Log("failed to load ad: " & ErrorMessage)
End Sub

Sub mwadi_AdClosed '*******************
    Log("ad closed")
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub
 

Attachments

  • InApp.zip
    3.2 KB · Views: 701
Last edited:

ilan

Expert
Licensed User
Longtime User
A question: how do I do if my app a month or a year to enable the full version?
Should I use the "Managed Product" or "subscription"?

you can use also a "Managed" product (maybe it is the better solution)

and as soon as you call manager.ConsumeProduct the user will be able to purchase that product again.
like this you can avoid mistakes by the user so he will not purchase the same product twice.

so after he purchase the "Managed" product you store the date of purchase (in any way you like, example create a file with purchased date)
then after the purchased period is over (like a month or year) you consume that product with manager.ConsumeProduct and the user will be able to purchase it again.
 

ilan

Expert
Licensed User
Longtime User
Thanks for the correction.

i also think "ab dem Kauftag.." and not "...den..." but our german friends here could correct me if i am wrong.
 

ilan

Expert
Licensed User
Longtime User

sorex

Expert
Licensed User
Longtime User
Ilan,

I think this line

B4X:
 If p.ProductId = "remove_ads" Then 'check if item NoAds was purchased

needs to be

B4X:
 If p.ProductId = "remove_ads" And p.PurchaseState=p.STATE_PURCHASED Then 'check if item NoAds was purchased

otherwise it will remove ads when it returns purchases that are cancelled or refunded
(according to the dox they will still be listed as purchase but with code 1 (canceled) or 2 (refunded) instead of 0 (purchased))
 

ilan

Expert
Licensed User
Longtime User
otherwise it will remove ads when it returns purchases that are cancelled or refunded

Purchases that were refunded or cancelled should not appear in purchases.values. Only purchases that has purchase.state = true should appear in purchases so you ask the same twice.
 

sorex

Expert
Licensed User
Longtime User

ilan

Expert
Licensed User
Longtime User
I understand but what i mean is only purchased item with state 0 should be included it purchases.values (map)

Otherwise you are right. @Erel should tell us if all purchases including canceled and refunded are included in purchases.values or only purchases with state 0.
 

ilan

Expert
Licensed User
Longtime User

Scantech

Well-Known Member
Licensed User
Longtime User
Why is the File.Exists(File.DirInternal,"ad.txt") = False and allows the ad to be disabled and removes button?
B4X:
    If FirstTime Then
        manager.Initialize("manager", inappkey)
        manager.DebugLogging = True
    Else
        If File.Exists(File.DirInternal,"ad.txt") = False Then 'manager will be called only on firsttime true, if firstime is false we will check if ad.txt exists what means that the app was purchased. now we should hide the "buy me" button and disable ads until we will be able to check again if the "no_ads" item was purchased!
            'hide purchase button
            'disable ads
        End If   
    End If

    If File.Exists(File.DirInternal,"ad.txt") Then adtimer.Enabled = False 'change default ad support to off

Why can't it be like this. If the app is paid and i open the app for first time or not, i want to disable and remove purchase button. I did not see anywhere in the example the button is hidden when first time is false? I think your Example file is not updated?
B4X:
    If FirstTime Then
        manager.Initialize("manager", inappkey)
        manager.DebugLogging = True
    End If

    If File.Exists(File.DirInternal,"ad.txt") Then
        'hide purchase button
        adtimer.Enabled = False 'change default ad support to off
    End If
 
Last edited:

ilan

Expert
Licensed User
Longtime User
Why can't it be like this. If the app is paid and i open the app for first time or not, i want to disable and remove purchase button. I did not see anywhere in the example the button is hidden when first time is false? I think your Example file is not updated?

only when a purchase was done i create the ad.txt file.

the manager will be intialized only if FIrsttime = true, and firsttime = true wont be always the case like if you exit your app via home button and then return only "Activity_resume" will be called (firsttime = false) and if you exit your app via back key and return activity_create will be called and then activity_resume (firsstime = false) will be called.

so because we only intialize the manager when firsstime = true we need to store the fact that the user has purchased our product some how.
what you could do is to use the starter service and intialize manager in it and store the purchase data on a booleon and on every activity_resume ask if that variable in the starter service is true or you can do it by creating a file and ask if that file exists.

i am sure that there are more ways to do it, it is up to you what you prefer.

about the purchase button, in this example i have not hided the purchase button but you can hide it easily when you ask if the ad.txt file exists.
 

nhz

Member
Licensed User
Longtime User
Hi, do we need to Publish the empty app first or just upload it will do?
 

aeric

Expert
Licensed User
Longtime User
the search function in this forum is not good, i almost never find threads that i remember i have read before
+1 After I spent some time read the tutorial by Erel, after some trial and error, successfully publish the demo app to Play store, now I found this thread. o_O

Edit: I think because your title is "Android InApp Purchase" but I searched for "In app billing" :D
 
Last edited:
Top