Android Question BillingManager3 and Google

Discussion in 'Android Questions' started by Paul Leischow, Mar 29, 2019.

  1. Paul Leischow

    Paul Leischow Member Licensed 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.
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    Yes. It will show a message under the app name that says that the app offers in app purchases.

    Google manages it. You should call GetOwnedProducts to get the valid products.
     
  3. Paul Leischow

    Paul Leischow Member Licensed 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.
     
  4. Computersmith64

    Computersmith64 Well-Known Member Licensed 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:

    Code:
    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"NullFalse)
        
    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.
     
    asales and rboeck like this.
  5. Paul Leischow

    Paul Leischow Member Licensed 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?
     
  6. Computersmith64

    Computersmith64 Well-Known Member Licensed User

    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.
     
  7. Paul Leischow

    Paul Leischow Member Licensed User

    Is there a reason to put the initialization stuff in the Starter service as opposed to Activity_Create ?
     
  8. asales

    asales Well-Known Member Licensed User

    +1, @Computersmith64
    Can you give more information about the cOpts class?
    Thanks!
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice