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: 641
Last edited:

Filippo

Expert
Licensed User
Hi ilan,

thank you for this tutorial.

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"?
 

ilan

Expert
Licensed 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"?

use "subscription".

after user buy the "subscription" product you can create a file with the purchased date and check on start if 1 month (or year) is over
and then inform the user he need to purchase that product again for another year...
 

Filippo

Expert
Licensed User
Thanks ilan. :)
 

Filippo

Expert
Licensed User
Hi ilan,

where has the buyer the price before buying? Do I have the price integrate into my app? If yes, how do I do when the price changed? Do I need to change my app? :(
 

ilan

Expert
Licensed User
Hi ilan,

where has the buyer the price before buying? Do I have the price integrate into my app? If yes, how do I do when the price changed? Do I need to change my app? :(

no you set the price in your dev console.

1.jpg

you can change the price whenever you want. the user will get the updated price every time he tries to purchase the product.
 

Filippo

Expert
Licensed User
no you set the price in your dev console.

View attachment 41733

you can change the price whenever you want. the user will get the updated price every time he tries to purchase the product.
I know that.
See this picture on, the buyer should know before buying what the one or the other costs, right? Or it will not work as think?
inappbilling.png
 

ilan

Expert
Licensed User
i dont think the lib can retrieve product details like: price, description,..

but you can do it with mysql db.
when you change the price in your dev console you also change the price in your online db and when user want to purchase you show him the price
from your mysql db.
 

ilan

Expert
Licensed User
btw: you should write "der einzige Unterschied..." and not "die einzige..." "der" bezieht sich auf Unterschied ;)

EDIT: ...besteht daran "dass"... (doppel S)

EDIT: Funk(t)ionalitaten.. (t - is missing)
 
Last edited:

Filippo

Expert
Licensed User
btw: you should write "der einzige Unterschied..." and not "die einzige..." "der" bezieht sich auf Unterschied ;)

EDIT: ...besteht daran "dass"... (doppel S)

EDIT: Funk(t)ionalitaten.. (t - is missing)
:(
Thanks for the correction. :)


but you can do it with mysql db.
when you change the price in your dev console you also change the price in your online db and when user want to purchase you show him the price
from your mysql db.
I think that is a bit complicated.
The question is: should I ever show the price?
 

ilan

Expert
Licensed User
I think that is a bit complicated.

no it is not. you can also upload a txt file to your server and in that file write the price.
then just download with http job and read the text in that txt file (without any db)

The question is: should I ever show the price?

i dont show the price. its up to you if you want to show it. anyway he will see it after he choose between month/year product. (before he purchase the product)
 

Filippo

Expert
Licensed User
i dont show the price. its up to you if you want to show it. anyway he will see it after he choose between month/year product. (before he purchase the product)
Thank you, I wished to read. ;)
 

sorex

Expert
Licensed User
Ilan,

Why is the init of the manager only done when firstTime=true ?

I have some problems with it when exiting the app.


When I trap the back key and let it exit with false the app closes, at restart it doesn't refresh the purchased info. (firsttime=false)

When I trap the back key and let it exit with application finish the app closes, at restart it doesn't refresh the purchased info. (firsttime=false)

When I trap the back key and let it exit with exitapplication the app closes, at restart the refresh works right. (firsttime=true)
 

ilan

Expert
Licensed User
Why is the init of the manager only done when firstTime=true ?

this is how @Erel did it in his tutorial, so you should ask him why. (https://b4x.com/android/forum/threads/android-in-app-billing-v3-tutorial.29997/)

When I trap the back key and let it exit with application finish the app closes, at restart it doesn't refresh the purchased info. (firsttime=false)

this is the reason i create the file because the "getownedproduct" is been called only if firsttime = true.
 

sorex

Expert
Licensed User
yeah, nice workaround for that problem.

pulling the init out of the if/then solves it too.

I never really understood that firsttime stuff. apps seem to resume but everything starts from scratch.
if I put it in the if/then nothing gets created. Will read that lifecycle stuff again ;)
 

ilan

Expert
Licensed User
I never really understood that firsttime stuff

let say you create a map in Process_Globals.
then you fill that map with lots of informations. now if you exit your app and return (someone called you and you exit the app or you pressed the homebutton)

if you intialize the map only if firsttime = true it will still hold all informations in it but if you intialize it on activity_start also if firstime = false it will clear everything
as soon you return to your app.

this is 1 example but there are many.
 

sorex

Expert
Licensed User
I get the home button stuff. but not when you press back to exit it.

it's like it always starts from the beginning again with 2 cases giving firsttime false and one true (using the real exitapplication command)
 

ilan

Expert
Licensed User
I get the home button stuff. but not when you press back to exit it.

it's like it always starts from the beginning again with 2 cases giving firsttime false and one true

sorry i have not understood what you wrote in your last post

exitaaplication will kill your app. you should not use that way to exit your app. better use Activity.Finish

firsttime false means that your activity was still running in the background so less data to load when you return to it.

on every start activity_create will be called (even if firsttime = false) then activity_resume will be called.

so if you for exanple load a layout in activity create it will load it on every app start so you should never try to load
your layout file in "firsttimer = true" condition because you will get a blank screen when you return to your app and firsttime was false
 

ilan

Expert
Licensed User
anyway i think it is maybe better to start a new thread about this question.
it could help others too but it will be hard to find something about it in a different topic.

try this: https://www.b4x.com/android/forum/threads/android-process-and-activities-life-cycle.6487/

i remember erel has created a quiz about that topic and there it is very good explained but i cant find it :(

the search function in this forum is not good, i almost never find threads that i remember i have read before
 
Top