Android Question BillingManager3 and Google

Paul Leischow

Member
Licensed User
Longtime User
I'm trying to get in-app subscription purchases working with the BillingManager library and I'm having some issues.
Just making sure... if you have an app that only has in-app purchases for subscriptions, do you mark it as a "free app" in the Google Console?

Does anyone have advice when doing in-app purchases for subscriptions in B4A? All the tutorial only seem to talk about "inapp" consumables, not "subs"
Does Google look after the subscription period or do I have to remember the purchase date and expire date?

Thanks.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
do you mark it as a "free app" in the Google Console?
Yes. It will show a message under the app name that says that the app offers in app purchases.

Does Google look after the subscription period or do I have to remember the purchase date and expire date?
Google manages it. You should call GetOwnedProducts to get the valid products.
 
Upvote 0

Paul Leischow

Member
Licensed User
Longtime User
Ok, thanks Erel.
I set my app to Free and set up some Product ID's for different Subscriptions and now when I call manager.RequestPayment() it pops up a message to purchase the Subscription with all the relevant details... so I must be doing something right :)
Just have to work on how to handle what comes next.
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
Here's some code from one of my apps that has subscriptions, as well as a normal in-app purchase (lifetime_license). Even though Play Store keeps track of subscriptions, I also keep track of them in the app through the cOpts class you see in the code (not expiry dates, etc... but just whether there is a valid subscription or not) in case there's an issue connecting to Play Store to get the owned products list. In my Starter service I get the owned products list every time the app starts & update my local record according to that. If for any reason I can't connect to Play Store I just use my local record.

It might be useful to help you figure it out (or not). If you have questions, post them in this thread & I'll do my best to answer:

B4X:
Private Sub manager_OwnedProducts (Success As Boolean, purchases As Map)
    Starter.monthlySub = False
    Starter.sixMonthlySub = False
    Starter.yearlySub = False
    Starter.lifeTimeSub = False
   
    Log($"ownedProducts (purchase): ${Success}"$)
    If Success Then
        Log("PURCHASES: " & purchases) 'ignore
        For Each p As Purchase In purchases.Values
            Log(p.ProductId & ", Purchased? " & (p.PurchaseState = p.STATE_PURCHASED))
            If p.ProductId = "monthly_sub" Then
                If p.PurchaseState = p.STATE_PURCHASED Then Starter.monthlySub = True
            Else If p.ProductId = "six_monthly_sub" Then
                If p.PurchaseState = p.STATE_PURCHASED Then    Starter.sixMonthlySub = True
            Else If p.ProductId = "annual_sub" Then
                If p.PurchaseState = p.STATE_PURCHASED Then Starter.yearlySub = True
            Else If p.ProductId = "lifetime_license" Then
                If p.PurchaseState = p.STATE_PURCHASED Then Starter.lifeTimeSub = True
            End If
        Next
    End If
    Starter.cOpts.updateSub("Monthly", Starter.monthlySub)
    Starter.cOpts.updateSub("SixMonthly", Starter.sixMonthlySub)
    Starter.cOpts.updateSub("Yearly", Starter.yearlySub)
    Starter.cOpts.updateSub("Lifetime", Starter.lifeTimeSub)
   
    If Not(Starter.monthlySub) And Not(Starter.sixMonthlySub) And Not(Starter.yearlySub) And Not(Starter.lifeTimeSub) Then 
        Log("No Remote Subscription")
        updateButtons
    End If
       
    Private lst As List
   
    lst.Initialize
    lst.AddAll(Array As String("monthly_sub", "six_monthly_sub", "annual_sub", "lifetime_license"))
    Starter.manager.GetInventoryInformation(lst)
   
End Sub

Private Sub manager_InventoryCompleted (Success As Boolean, Products As List)
   
    Log(Products) 'ignore
    If Success Then
        For Each sk As SkuDetails In Products
            Select Case sk.Sku
                Case "monthly_sub"
                    lblMonthly.Text = $"Monthly (${sk.Price})"$
                Case "six_monthly_sub"
                    lblSixMonthly.Text = $"Six Monthly (${sk.Price})"$
                Case "annual_sub"
                    lblYearly.Text = $"Yearly (${sk.Price})"$
                Case "lifetime_license"
                    lblLifeTime.Text = $"Lifetime License (${sk.Price})"$
            End Select
        Next
    End If
   
End Sub

Sub manager_PurchaseCompleted(Success As Boolean, Product As Purchase)
    If Success Then
        Select Case Product.ProductId
            Case "monthly_sub" 
                Starter.monthlySub = True
                Starter.cOpts.updateSub("Monthly", Starter.monthlySub)
            Case "six_monthly_sub" 
                Starter.sixMonthlySub = True
                Starter.cOpts.updateSub("SixMonthly", Starter.sixMonthlySub)
            Case "annual_sub" 
                Starter.yearlySub = True
                Starter.cOpts.updateSub("Yearly", Starter.yearlySub)
            Case "lifetime_license"
                Starter.lifeTimeSub = True
                Starter.cOpts.updateSub("Lifetime", Starter.lifeTimeSub)
        End Select        
        ToastMessageShow("Thanks for purchasing!", False)
        updateButtons
        Main.newTheme = True
    Else
        ToastMessageShow("Purchase canceled", False)
    End If
End Sub
'
Sub buy_Click
    Private btn As Button = Sender
   
    Msgbox2Async("Purchase the selected subscription?", "Subscription Purchase", "Yes", "", "No", Null, False)
    Wait for MsgBox_Result(iRes As Int)
    If iRes = DialogResponse.POSITIVE Then 
        Private tag As String = btn.Tag
        If tag.Contains("sub") Then Starter.manager.RequestPayment(btn.tag, "subs", "myPets") Else Starter.manager.RequestPayment(btn.tag, "inapp", "myPets")
    End If
   
   
End Sub

- Colin.
 
Upvote 0

Paul Leischow

Member
Licensed User
Longtime User
Thank you Colin for the tips.
So if you are also keeping track of the subscriptions in the event a user is unable to connect to the Play Store, does that mean under normal conditions if the user cannot connect to the Play Store, the app would not function in Subscription mode?

It would be interesting to see your Starter service code to see how you manage things.
Also wondering what calls "manager_InventoryCompleted" in your posted code?
 
Upvote 0

Computersmith64

Well-Known Member
Licensed User
Longtime User
Thank you Colin for the tips.
So if you are also keeping track of the subscriptions in the event a user is unable to connect to the Play Store, does that mean under normal conditions if the user cannot connect to the Play Store, the app would not function in Subscription mode?

It would be interesting to see your Starter service code to see how you manage things.
Also wondering what calls "manager_InventoryCompleted" in your posted code?

If for whatever reason the app can't get the current owned products it will fall back to whatever its local record is - so if there was a valid subscription last time it was able to complete an owned products request, it will run in subscription mode. If there wasn't, then it won't. There are obviously potentially ways a user could exploit this, however they don't know that this is how I manage the subscriptions - so I'd say it's pretty low risk.

manager_InventoryCompleted is a callback as a result of the call to Starter.manager.GetInventoryInformation(lst).

The billing related code in Starter just consists of initializing the manager object, which results in the manager_BillingSupported callback, which in turn makes a call to manager.GetOwnedProducts to check the remote subscriptions.

- Colin.
 
Upvote 0
Top