Android Question In-App Purchase

drj

Member
Licensed User
Longtime User
I am in the process of implementing an in-app purchase and have been studying the questions and tutorial quite a bit. There are a few items that are not clear at this time.
1. Routine manager_OwnedProducts(success as Boolean , purchases as map)
I assume that this routine returns Boolean success telling me that it found that I had or had not found that the current users owns an in-app purchase. For example, it there has been only one in-app purchase defined for this app in Google Play then I can assume that the user has purchased it if success is true.
Can you tell me what p.Productid is? is this a string returned from the Billing Manager giving me that name of the in-app purchase?
I assume that p.Purchased = p.STATE_PURCHASED means that the user has purchased this product although I can find no reference to p.STATE_PURCHASED in any documentation.
2. I have implemented the purchasing of the in-app purchase in another activity - I assume it is ok to reference the Main activity in calling the BillManager routines - so my call looks like this
Main.manager.RequestPayment(Main.Productid, Main.Producttype, Main.DeveloperPayload)
I have defined Productid , Productstype and DeveloperPayload as strings in Process Globals in my main routine.
Is this correct? and this leads to my third question
3. Calling Main.manager.RequestPayment(Main.Productid,Main.Producttype, Main.DeveoperPayload)
are the variables Productid , Producttype and DeveloperPayload returned from the BillingManager or do I have to supply them?
What are the definitions of these variables and how do I use them.
How can I tell that the in-app has been successfully purchased by the user? is there anything returned from this call to indicate a successful purchase
or do I have to call manager_OwnProducts again after I have made to call manager.RequestPayment

The interface seems very simple , the problem is that there is no documentation or working example to see.
Can you help me?
Thanks
Jerry
 

drj

Member
Licensed User
Longtime User
Ok , so I am not getting feedback on this question, let's simplify the questions.

1. Using BillingManager3 do we still create a separate module (service module) to put the references to Billing like you did in the earlier tutorial on inAppBilling?

2. How do we modify the Manifest Editor using Version 3 , is it is the same as the earlier version?

3. It appears that you have to upload an APK to Google before you can define an in-app purchase , is that correct. So the procedure appears to be take an existing app you want to add an in-app purchase to and modify it's manifest to enable inApp Billing and then upload the APK to Google and then define your in-app products with their productid's or sku's and then once you have done that then go back and modify the app to handle the in-app purchases and then once tested updated the final APK to google - is this correct?

4. It appears to me that one reason to have the inappbiliing manager in a service module that in the Manifest editor one has to make reference to it. Is that correct?

Finally , has anyone out go in-app codes working and in production using basic4Android? if so then can you post some examples? given the current information out there , I cannot seem to understand it , one good working example would solve this. This in the only thing I have run into using Basic4Android that is not well defined and I have had trouble with.
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
I´m self at the beginning of InApp Billing, but i´ll try answer.

Q:1. Routine manager_OwnedProducts(success as Boolean , purchases as map)

A:All owned products (purchases) are listed here as map.
This routine is called with Billmanager.GetOwnedProducts
p.Productid = your inApp product saved in the developer console
if p.STATE_PURCHASED = True then the current user has buyed this item(Productid)


Q:Main.manager.RequestPayment(Main.Productid, Main.Producttype, Main.DeveloperPayload)
I have defined Productid , Productstype and DeveloperPayload as strings in Process Globals in my main routine.

A:You don´t need to define this in Process Globals.
With manager.RequestPayment(Main.Productid, Main.Producttype, Main.DeveloperPayload) you send a request to play store.


Q:Calling Main.manager.RequestPayment(Main.Productid,Main.Producttype, Main.DeveoperPayload)
are the variables Productid , Producttype and DeveloperPayload returned from the BillingManager or do I have to supply them?

A: Supply


Q:is there anything returned from this call to indicate a successful purchase

A:After purchasing one item, following sub will executed
B4X:
Sub billingmanager_PurchaseCompleted (Success As Boolean, Product As Purchase)
    If Success = True Then
        Select Product.ProductId
            Case "your.productid"
                your handling
        End Select
    Else
        ToastMessageShow("PLAY STORE meldet einen Fehler beim Kauf!!", False)
    End If

I use BillingmanagerV3 in the main module, so i´ve no experience with do it in Service Module

Q: How do we modify the Manifest Editor using Version 3 , is it is the same as the earlier version?
A: Nothing to do, after checking the lib, all inserts are automatically inserted


You must upload an apk with support for inApp Billing. But you don´t need to publish it. Save it as Draft, save some inApp Products in the Developer Console thats all to test this from an emulator.
Don´t forget to put the email adress from test account in the developer console settings, other way, your testaccount will pay for the products.
 
Upvote 0

drj

Member
Licensed User
Longtime User
Thanks a million Eumel , I have modified my code based on your feedback , I have several other minor questions below. I am pasting all the changes I made to convert this app to use inappbilling , is this correct in your opinion?

Code changes I have made to do the inapp

1. Downloaded the inAppBilling3 library

2. In my Process Globals
Dim manager As BillingManager3
Private key As String = "M......." ' get this from the developer console for this app

3. In my Main Create
manager.Initialize("manager",key)
manager.DebugLogging = True
4. In my startup routine New in Main I make this call I have a Question here????

manager.GetOwnedProducts ' note it does not appear that I have to define calling parameters in this call why?

5. I have this routine in the Main

Sub manager_OwnedProducts (Success AsBoolean, purchases AsMap)
' The OwnedProducts event includes a Map that holds the user current purchased.
' The keys In the Map are the products ids (OR Skus) AND the values are objects of Type Purchase.
Log(Success)
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 = "myid" AND p.PurchaseState = p.STATE_PURCHASED Then
Currentlicensemode = 2
Label5.Visible=False
ImageView1.Visible=False
Return
End If
Next
Else
Currentlicensemode = 0
End If
End Sub
5. I have created a separate Purchase activity module where I display to the user the price and what they are getting in the inapp purchase and in this activity there is a Buy button. Note that I have to reference back to the main where the inappbilling is defined , I believe that many users will have this design , that is going to a new display to purchase , note that in the call to billingmanager_purchaseComplete I had to supply parameters – is that correct?
And also on the call to Request payment I had to send it the type of Purchase “inapp” and the product id “from the consoile” and I am not sure why I had to send it developerpayload which I made up ?????

Sub Button1_Click

Dim Productid As String
Dim Producttype As String
Dim DeveloperPayload As String
DeveloperPayload="Name of my App " ' this is input
Producttype="inapp" ' this is an input
Productid="inapp name I defined in store" ' this is an input I it is the product id i defined in the store
Main.manager.RequestPayment(Productid,Producttype,DeveloperPayload)
Main.Currentlicensemode = 0
Dim Success As Boolean
Dim Product As Purchase
' note that I had to define calling parameters to the call to this routine
billingmanager_PurchaseCompleted (Success, Product)
End Sub
Sub billingmanager_PurchaseCompleted (Success As Boolean, Product As Purchase)
If Success = True Then
Select Product.ProductId
Case"inapp name I defined in store"
' your handling I save in the data base that this inapp has been purchased for this user
Main.Currentlicensemode = 2
Main.LicenseStatus = 2
SaveSelectedCaseNumber
End Select
Else
ToastMessageShow("This Inapp Purchase is not in the store", False)
End If
End Sub
 
Upvote 0

drj

Member
Licensed User
Longtime User
Another question - I am getting series of errors in the log - do I need to worry about them

the errors are saying iAB helper is not set up

but it does later give me a message that in-billing is supported
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
"the errors are saying iAB helper is not set up"

At the beginning i had this error too.
I changed following from the tutorial:

From:
B4X:
  If FirstTime Then
      manager.Initialize("manager", key)
  End If
  manager.DebugLogging = True

To:
B4X:
    If FirstTime = True Then
        manager.Initialize("manager", key)
    Else
        manager.GetOwnedProducts
    End If
    manager.DebugLogging = True

Don´t know why, but it works

i put my inapp billing routines in main, on an extra panel and show it the user with panel.visible = true , because as i wrote, i have no experience with putting inApp in an code module.

In your button1_click event you only need:

B4X:
Sub Button1_Click
    manager.RequestPayment("ProductID","inapp","DeveloperPayload")
End Sub

After that, the routine
B4X:
Sub billingmanager_PurchaseCompleted (Success As Boolean, Product As Purchase)

will be executed
 
Upvote 0

drj

Member
Licensed User
Longtime User
So you don't have to call billingmanager_PurchaseCompleted - it is called automatically

also I assume if my product id is "Math"

then my call to RequestPayment is

manager.RequestPayment("Math","inapp","DeveloperPayload")

Also I did not understand your comment about the email , you made before , where does an email come into play , I did not see an email specification in Google Play?
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
To test your InApp Billing, you can define testusers. This can be done in the settings Tab from developer console.
These accounts can buy your products without paying. That´s all
 
Upvote 0

drj

Member
Licensed User
Longtime User
I am still working on it. I defined some gmail accounts in the settings tab as you suggested.

Can this be tested in the emulator?

I hooked a device up to my computer and built the product on the device and tried it. When I select the Buy button it tells me that

"The item you requested is not available for purchase"

or do you have to upload the product and go into production and download the product to the device and then test it using a test account?

I don't see how it can be tested in the emulator?

Thanks for all your help. I am almost there.

Jerry
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
It works on an emulator, if the email you defined is registered in the emulator.
you have to upload your apk, but not to publish. save it as a draft.
important is the version nummer of your apk (not the version string). in emulator or phone it must the same as in the developer console.
 
Upvote 0

drj

Member
Licensed User
Longtime User
I am down to two problems. I have been testing on a 14" tablet and a 7" tablet both Samsung.

1. On the 14" everything works except when I go to purchase it tries to change it to my main developer account instead of the test emails I put on the Settings tab. I am not sure how to fix that. Is there a way to log into the device with one of my test emails?

2. On the 7" device , it gives me a message that the inapp billing is not available from the
Manager_BillingSupported routine. I am not sure what to do. Maybe I have to exit th

I decided to upgrade the software in both devices as a precaution.
 
Upvote 0

drj

Member
Licensed User
Longtime User
I found the problems.

1. 14" device , you have to add the test email account to Google Play accounts and set it to that test account. It will pick up that account in the emulator. I have figured out about how to handle the charge card in the test account yet.

2. 7" device , I did not have any account set up on at all in Google Play at all so the software detects that Billing is not available. I am changing the code to advise the user that they must set up a Google Play account. This could happen when someone just purchases a new device.
 
Upvote 0

drj

Member
Licensed User
Longtime User
I found a design problem , maybe with your logic too. I am not sure.

You suggested.

If FirstTime = True Then
manager.Initialize("manager", key)
Else
manager.GetOwnedProducts
End If
manager.DebugLogging = True

I put this code in the Activity_Create. The problem is that this sub is only executed once so it never catches the manager.getownedproducts call and you need to do the getownedcall to find out where the user has bought a particular inapp purchase.

The problem is that you cannot put the call for getownedproducts after the call to manager.initialize directly because the initialize must complete before you call getownedproducts and since it runs in the background one cannot know when it is complete

I changed my code to


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

Then I put the call to getownedproducts on a button and it worked. it worked because the manager.initialize was completed. I did this just to test it , we cannot have it this way since it has to automatically test which apps have been bought on start up

I am not sure how to fix this. the objective is to

1. initialize the manager the first time

2. And when we know manager.initiaze is complete then we call manager.getownedproducts to determine which products have been bought - could it be put in Manager_BillingSupported?


I am having a problems in the billingmanager_PurchaseCompleted - it does not seem to get called automatically , it appears that one has to explicitly call it


I was able to buy the inapp successfully with a test email , it did not detect in my code because billingmanager_PurchaseCompleted was not called and I am trouble finding the right time and sequence of calling manager.getownedProducts

Once these items are resolved I am done.

Thanks again for all your help.

Jerry
 
Upvote 0

drj

Member
Licensed User
Longtime User
I made this change then getownedproducts worked

Sub Manager_BillingSupported (Supported AsBoolean, Message AsString)
Log(Supported & ", " & Message)
Log("Subscriptions supported: Jerry " & manager.SubscriptionsSupported)

If Supported Then
manager.GetOwnedProducts

EndIf

End Sub
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
You´re right.

As descripted HERE i put manager.GetOwnedProducts in Sub Manager_BillingSupported too.

But this Sub will only called if FirstTime = True.
That´s why i changed the entry at FirstTime

i made a mistake in an earlier post.

Sub billingmanager_PurchaseCompleted --- thats mine
Sub manager_PurchaseCompleted --- that´s your

Can this solve the issue ?
 
Upvote 0

drj

Member
Licensed User
Longtime User
Yes this is solved and works now , everything seems to work but I am doing some testing.

Do you know if there is an easy way to reinitialize a test email that I have defined to not have a license so I can use the email over again? Would be nice otherwise for each email you have to enter a valid charge card eventhough it does not charge your account.

The other thing I noticed is that the app really does not have to maintain the status of a purchase in its own database when one can call getownedproducts to find out the status of inapp purchases. Do you maintain the purchase status in your database?

I just implemented inapp purchases in ten Windows 8 apps and there I had to maintain the purchase status in the database.

Thanks again, now for more testing to make sure it is solid.

Jerry
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
Look at your developer console at the bottom.
You´ll find a link to "Google Wallet Merchant Center"
There you´ll find all buys, testing and real.
Click the testbuys and cancel the order.
Otherwise, after 14 days, Google will cancel itself.
So you can use your testaccounts again and again.

After cancelling an order, your App will register this not directly, it takes up to 2 hours.

I store my inApp purchases not in a database, because it´s localy cached by the phone.

Wolfgang
 
Upvote 0

drj

Member
Licensed User
Longtime User
Thank you very much Wolfgang.

With regard to storing information about the license in a local data base:

If you do not store it in a local data base then if the device cannot connect to internet the user won't be able to use the inapp feature. Isn't that correct?

It seems to me that if the internet is available then on start up the app needs to check the license from the store and use that information but if internet is not available then the app should get the license information from the data base.

What do you think? what happens when internet is not available.

Also , one wonders what happens to the calls to inappbilling if the internet is not available - are there error codes generated - what happens. Seems to me we have to check for the presence of internet before calling these routines.

Jerry
 
Upvote 0

Eumel

Active Member
Licensed User
Longtime User
Hi Jerry,

if an user purchased some inApp, then the information is stored locally by the phone.
So the user is able to use this feature, if no connection is available.
As the information is cached locally, i don´t try how long this information is available.
It can be, that after some time, the information is too old, if never an internet connection is available.
So it can be better, if you store this in a database. But then, you have to sync the information by yourself.

If a connection is available, manager.getownedproducts will sync automatically with the cache, not your database.

If no connection is available, the user will not able to buy some new products. So if he/she click the button, an error side of google play will be visible.


Wolfgang
 
Upvote 0

drj

Member
Licensed User
Longtime User
Hi,

I have been doing some testing - internet vs no internet with various scenerios.

What I found.

1. It seems that once you have installed the app the routine
Sub Manager_BillingSupported (Supported AsBoolean, Message AsString) does not get called and in that routine I had the call to GetOwnedProducts
So If you want to use GetOwnedProducts to query whether an inapp has been purchased or cancelled each time the app is loaded then one has to call it in another location also. On the other hand I found that errors occur if one calls GetOwnedProducts just after manager.initialize.

2. The app should maintain whether the inapp is licensed or not in a local app data base (sqlite3) in case the internet is not available and you cannot use GetOwnedProducts to query whether the license exists for this inapp I tested it with the internet on and off.

3. There is a potential design problem that requires that the rountine GetOwnedProducts has to be excecute. If one assumes that the inapp is bought and that fact is stored in a local data base. If the user sunsequently cancels the purchase then your local data base will be out of sync.

I am investigating how to call getOwnedProducts each time to assess license status so I can keep my local data base updated.


Things are almost always more complicated that they appear at first. Hopefully when I am done I can carry these changes to my ten other apps that I want to convert to free with inapp purchases.

Thanks for all you help. I appreciate your comments.
 
Upvote 0
Top