' ~~~~~~~~~~~~~~~~~~~~[/INDENT]
[INDENT]' MANIFEST[/INDENT]
[INDENT]' ~~~~~~~~~~~~~~~~~~~~[/INDENT]
[INDENT]'This code will be applied to the manifest file during compilation.[/INDENT]
[INDENT]'You do not need to modify it in most cases.[/INDENT]
[INDENT]'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136[/INDENT]
[INDENT]AddManifestText([/INDENT]
[INDENT]<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="28"/>[/INDENT]
[INDENT]<supports-screens android:largeScreens="true"[/INDENT]
[INDENT] android:normalScreens="true"[/INDENT]
[INDENT] android:smallScreens="true"[/INDENT]
[INDENT] android:anyDensity="true"/>)[/INDENT]
[INDENT]SetApplicationAttribute(android:icon, "@drawable/icon")[/INDENT]
[INDENT]SetApplicationAttribute(android:label, "$LABEL$")[/INDENT]
[INDENT]CreateResourceFromFile(Macro, Themes.DarkTheme)[/INDENT]
[INDENT]'End of default text.[/INDENT]
[INDENT]CreateResourceFromFile(Macro, GooglePlayBilling.GooglePlayBilling)[/INDENT]
[INDENT][/INDENT]
[INDENT][/INDENT]
[INDENT][/INDENT]
[INDENT][/INDENT]
[INDENT]' ~~~~~~~~~~~~~~~~~~~~[/INDENT]
[INDENT]' MAIN MODULE[/INDENT]
[INDENT]' ~~~~~~~~~~~~~~~~~~~~[/INDENT]
[INDENT]#Region Project Attributes[/INDENT]
[INDENT] #ApplicationLabel: a B4A Billing Subscriptions Example[/INDENT]
[INDENT] #VersionCode: 12[/INDENT]
[INDENT] #VersionName: v0.1[/INDENT]
[INDENT] 'SupportedOrientations possible values: unspecified, landscape or portrait.[/INDENT]
[INDENT] #SupportedOrientations: unspecified[/INDENT]
[INDENT] #CanInstallToExternalStorage: False[/INDENT]
[INDENT]#End Region[/INDENT]
[INDENT][/INDENT]
[INDENT]' Testers Playstore-Link --> https://play.google.com/apps/testing/b4a.example.biltest001[/INDENT]
[INDENT][/INDENT]
[INDENT]' Google's howto --> https://developer.android.com/google/play/billing/billing_testing[/INDENT]
[INDENT][/INDENT]
[INDENT]' User-test a Google Play Billing app --> https://developer.android.com/google/play/billing/billing_testing#test-purchases-sandbox[/INDENT]
[INDENT][/INDENT]
[INDENT]' Google Test subscriptions-specific features --> https://developer.android.com/google/play/billing/billing_testing.html#testing-subscriptions[/INDENT]
[INDENT]#Region Activity Attributes[/INDENT]
[INDENT] #FullScreen: true [/INDENT]
[INDENT] #IncludeTitle: false[/INDENT]
[INDENT]#End Region[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub Process_Globals[/INDENT]
[INDENT] Dim BillingMapOfAvailableProducts As Map[/INDENT]
[INDENT] Dim BillingActiveProductId As String = ""[/INDENT]
[INDENT] Dim BillingActiveCostLevel As Int = 0[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT]Sub Globals[/INDENT]
[INDENT] Private LabelAskProducts As B4XView[/INDENT]
[INDENT] Private BillingProductsClv As CustomListView[/INDENT]
[INDENT] Private clvitemBackPanel As B4XView[/INDENT]
[INDENT] Private clvitemLabel1 As B4XView[/INDENT]
[INDENT] Private clvitemLabel3 As B4XView[/INDENT]
[INDENT] Private clvitemLabel4 As B4XView[/INDENT]
[INDENT] Private clvitemButton1 As B4XView[/INDENT]
[INDENT] Private StateOnline As B4XView[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT]Sub Activity_Create(FirstTime As Boolean)[/INDENT]
[INDENT] Activity.LoadLayout("Layout1")[/INDENT]
[INDENT] Activity.Color = Colors.White[/INDENT]
[INDENT] StateOnline.Text = ""[/INDENT]
[INDENT] [/INDENT]
[INDENT] BillingMapOfAvailableProducts = GetBillingSubProducts[/INDENT]
[INDENT] [/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT]Sub Activity_Resume[/INDENT]
[INDENT] wait for(BillingAskProductsAndFillClv) complete(nix As Object)[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT]Sub Activity_Pause (UserClosed As Boolean)[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]' -------------------------------------[/INDENT]
[INDENT][/INDENT]
[INDENT]private Sub GetBillingSubProducts As Map[/INDENT]
[INDENT] ' These are the App's Subsciption products as they are implemented in Google Playstore[/INDENT]
[INDENT] ' Path: GooglePlay console/ Store presence/ In-App products/ Subsriptions[/INDENT]
[INDENT] [/INDENT]
[INDENT] ' The items should be ordered from lowest price to highest, so that[/INDENT]
[INDENT] ' an "up/downgrade" option can be implemented[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim MapOfBillingSubscritions As Map[/INDENT]
[INDENT] MapOfBillingSubscritions.Initialize[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim BillingSubProduct As BillingSubProduct[/INDENT]
[INDENT] BillingSubProduct.Initialize[/INDENT]
[INDENT] BillingSubProduct.ProductID = "free"[/INDENT]
[INDENT] BillingSubProduct.OrderId = ""[/INDENT]
[INDENT] BillingSubProduct.Title = "Free Testversion"[/INDENT]
[INDENT] BillingSubProduct.Description = $"Basic functions. ${CRLF}Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa."$[/INDENT]
[INDENT] BillingSubProduct.Price = "0.00"[/INDENT]
[INDENT] BillingSubProduct.CostLevel = 0[/INDENT]
[INDENT] BillingSubProduct.FreeTrialPeriod = 0[/INDENT]
[INDENT] BillingSubProduct.BillingPeriod = "free"[/INDENT]
[INDENT] BillingSubProduct.DeveloperPayload = ""[/INDENT]
[INDENT] MapOfBillingSubscritions.Put(BillingSubProduct.ProductID, BillingSubProduct)[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim BillingSubProduct As BillingSubProduct[/INDENT]
[INDENT] BillingSubProduct.Initialize[/INDENT]
[INDENT] BillingSubProduct.ProductID = "subsprod1"[/INDENT]
[INDENT] BillingSubProduct.OrderId = ""[/INDENT]
[INDENT] BillingSubProduct.Title = "Prod1 SubWeekly"[/INDENT]
[INDENT] BillingSubProduct.Description = $"Testproduct for SUBS "weekly". ${CRLF}Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa."$[/INDENT]
[INDENT] BillingSubProduct.Price = "0.50"[/INDENT]
[INDENT] BillingSubProduct.CostLevel = 1[/INDENT]
[INDENT] BillingSubProduct.FreeTrialPeriod = 3[/INDENT]
[INDENT] BillingSubProduct.BillingPeriod = "week"[/INDENT]
[INDENT] BillingSubProduct.DeveloperPayload = ""[/INDENT]
[INDENT] MapOfBillingSubscritions.Put(BillingSubProduct.ProductID, BillingSubProduct)[/INDENT]
[INDENT][/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim BillingSubProduct As BillingSubProduct[/INDENT]
[INDENT] BillingSubProduct.Initialize[/INDENT]
[INDENT] BillingSubProduct.ProductID = "subsprod2"[/INDENT]
[INDENT] BillingSubProduct.OrderId = ""[/INDENT]
[INDENT] BillingSubProduct.Title = "Prod2 SubMonth"[/INDENT]
[INDENT] BillingSubProduct.Description = $"Testproduct for SUBS "monthly". ${CRLF}Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa."$[/INDENT]
[INDENT] BillingSubProduct.Price = "1.00"[/INDENT]
[INDENT] BillingSubProduct.CostLevel = 2[/INDENT]
[INDENT] BillingSubProduct.FreeTrialPeriod = 3[/INDENT]
[INDENT] BillingSubProduct.BillingPeriod = "month"[/INDENT]
[INDENT] BillingSubProduct.DeveloperPayload = ""[/INDENT]
[INDENT] MapOfBillingSubscritions.Put(BillingSubProduct.ProductID, BillingSubProduct)[/INDENT]
[INDENT] [/INDENT]
[INDENT] Return MapOfBillingSubscritions[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub BillingAskProductsAndFillClv As ResumableSub[/INDENT]
[INDENT] Log("#-Sub main.AskProducts")[/INDENT]
[INDENT] LabelAskProducts.Text = "Working..."[/INDENT]
[INDENT] [/INDENT]
[INDENT] StateOnline.Text = Chr(0xE2C1) ' cloud OFF[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim bspNull As BillingSubProduct[/INDENT]
[INDENT] bspNull.Initialize[/INDENT]
[INDENT] BillingActiveCostLevel = 0[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim sb As StringBuilder[/INDENT]
[INDENT] sb.Initialize[/INDENT]
[INDENT] [/INDENT]
[INDENT] Wait For (Starter.billing.ConnectIfNeeded) Billing_Connected (Result As BillingResult)[/INDENT]
[INDENT] [/INDENT]
[INDENT] If Result.IsSuccess Then[/INDENT]
[INDENT] StateOnline.Text = Chr(0xE2C2) ' cloud ON[/INDENT]
[INDENT] If Starter.billing.SubscriptionsSupported Then[/INDENT]
[INDENT] sb.Append("SUBS supported, ")[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] sb.Append("SUBS not supported, ")[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] [/INDENT]
[INDENT] Wait For (Starter.billing.QueryPurchases("subs")) Billing_PurchasesQueryCompleted (Result As BillingResult, Purchases As List)[/INDENT]
[INDENT] If Result.IsSuccess Then[/INDENT]
[INDENT] Log("#-")[/INDENT]
[INDENT] Log("#- x106, Purchases.size=" & Purchases.Size)[/INDENT]
[INDENT] If Purchases.Size = 0 Then[/INDENT]
[INDENT] Dim bspdefault As BillingSubProduct = BillingMapOfAvailableProducts.Get("free")[/INDENT]
[INDENT] BillingActiveProductId = bspdefault.ProductID[/INDENT]
[INDENT] BillingActiveCostLevel = bspdefault.CostLevel[/INDENT]
[INDENT] Starter.BillingActiveSubsProductTitle = bspdefault.Title[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] [/INDENT]
[INDENT] For Each p As Purchase In Purchases[/INDENT]
[INDENT] Log("#- x134, p.OrderId = " & p.OrderId)[/INDENT]
[INDENT] Log("#- x135, p.PurchaseState = " & p.PurchaseState & " --> " & Starter.BillingPurchState(p.PurchaseState))[/INDENT]
[INDENT] Log("#- x136, p.PurchaseTime = " & p.PurchaseTime & $" --> $DateTime{p.PurchaseTime}"$)[/INDENT]
[INDENT] Log("#- x137, p.Sku = " & p.Sku )[/INDENT]
[INDENT] Log("#- x138, p.IsAcknowledged = " & p.IsAcknowledged)[/INDENT]
[INDENT] Log("#- x139, p.DeveloperPayload = " & p.DeveloperPayload)[/INDENT]
[INDENT] Log("#- x140, p.IsAutoRenewing = " & p.IsAutoRenewing)[/INDENT]
[INDENT] [/INDENT]
[INDENT] sb.Append(p.Sku).Append(", ")[/INDENT]
[INDENT] [/INDENT]
[INDENT] If p.Sku <> Null Then[/INDENT]
[INDENT] Dim bspx As BillingSubProduct = BillingMapOfAvailableProducts.GetDefault(p.Sku, bspNull)[/INDENT]
[INDENT] If bspx.ProductID = p.Sku Then[/INDENT]
[INDENT] BillingActiveProductId = p.Sku [/INDENT]
[INDENT] bspx.DeveloperPayload = p.DeveloperPayload[/INDENT]
[INDENT] BillingMapOfAvailableProducts.put(p.Sku, bspx)[/INDENT]
[INDENT] BillingActiveCostLevel = bspx.CostLevel[/INDENT]
[INDENT] Starter.BillingActiveSubsProductTitle = bspx.Title[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] [/INDENT]
[INDENT] Next[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] LabelAskProducts.Text = sb.ToString[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] LabelAskProducts.Text = "Not connected"[/INDENT]
[INDENT] Log("#- x139, not connected")[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] Log("#-")[/INDENT]
[INDENT] [/INDENT]
[INDENT] FillClv_Products[/INDENT]
[INDENT] [/INDENT]
[INDENT] Return Null[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]private Sub FillClv_Products[/INDENT]
[INDENT] BillingProductsClv.Clear[/INDENT]
[INDENT] Dim i As Int = 0[/INDENT]
[INDENT] For Each k As String In BillingMapOfAvailableProducts.Keys[/INDENT]
[INDENT] Dim bsp As BillingSubProduct = BillingMapOfAvailableProducts.Get(k)[/INDENT]
[INDENT] BillingProductsClv.Add(BillingProductsClv_CreateItem(bsp, i), i) [/INDENT]
[INDENT] i = i +1[/INDENT]
[INDENT] Next[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]private Sub BillingProductsClv_CreateItem(bsp As BillingSubProduct, Index As Int) As B4XView[/INDENT]
[INDENT] 'Log($"#-Sub main.BillingProductsClv_CreateItem, Index=${Index}, bsp.ProductID="${bsp.ProductID}"$ )[/INDENT]
[INDENT] Dim PanelHeight As Int = 216dip[/INDENT]
[INDENT] Dim p As Panel[/INDENT]
[INDENT] p.Initialize("")[/INDENT]
[INDENT] p.SetLayout(0, 0, BillingProductsClv.AsView.Width, PanelHeight)[/INDENT]
[INDENT] p.LoadLayout("billing_clvitem")[/INDENT]
[INDENT] clvitemBackPanel.SetLayoutAnimated(0, 8dip, 0, BillingProductsClv.AsView.Width -16dip, PanelHeight -8dip)[/INDENT]
[INDENT] clvitemLabel1.SetLayoutAnimated(0, 0, 0, BillingProductsClv.AsView.Width -16dip, 40dip)[/INDENT]
[INDENT] clvitemLabel1.Text = bsp.Title[/INDENT]
[INDENT] clvitemLabel3.Text = bsp.Description[/INDENT]
[INDENT] clvitemLabel4.Text = bsp.Price[/INDENT]
[INDENT] clvitemLabel4.SetColorAndBorder(Starter.xui.Color_White, 2dip, Starter.xui.Color_LightGray, 5dip)[/INDENT]
[INDENT] clvitemButton1.Tag = bsp.ProductID[/INDENT]
[INDENT] [/INDENT]
[INDENT] 'Log("#- x205, BillingActiveProductId=" & BillingActiveProductId & ", BillingActiveCostLevel=" & BillingActiveCostLevel)[/INDENT]
[INDENT] If BillingActiveProductId = bsp.ProductID Then[/INDENT]
[INDENT] clvitemLabel4.Text = "Activated"[/INDENT]
[INDENT] clvitemButton1.Text = "Show my Subscriptions"[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] If BillingActiveProductId = "free" Then[/INDENT]
[INDENT] clvitemButton1.Text = "BUY"[/INDENT]
[INDENT] Else If Index > BillingActiveCostLevel Then[/INDENT]
[INDENT] clvitemButton1.Text = "UPgrade"[/INDENT]
[INDENT] else If Index < BillingActiveCostLevel Then[/INDENT]
[INDENT] [/INDENT]
[INDENT] If bsp.ProductID = "free" Then[/INDENT]
[INDENT] clvitemButton1.Text = "Cancel Subscription"[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] clvitemButton1.Text = "DOWNgrade"[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] [/INDENT]
[INDENT] Else[/INDENT]
[INDENT] clvitemButton1.Text = "BUY"[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] [/INDENT]
[INDENT] If bsp.ProductID = "free" Then[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] Return p[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub clvitemButton1_Click As ResumableSub[/INDENT]
[INDENT] Log("#-")[/INDENT]
[INDENT] Dim ButtonX As Button = Sender[/INDENT]
[INDENT] Dim bspSelectedByUser As BillingSubProduct = BillingMapOfAvailableProducts.Get(ButtonX.Tag)[/INDENT]
[INDENT] Log("#-Sub main.clvitemButton1_Click, bspSelectedByUser.ProductID=" & bspSelectedByUser.ProductID )[/INDENT]
[INDENT] If bspSelectedByUser.ProductID = "free" Then[/INDENT]
[INDENT] ' Cancel subscription[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim sf As Object = Msgbox2Async($"The subscription cancellation takes place in the Playstore. All important information about the termination date will be displayed there."$, "Cancel Subscription" , "OK", "", "", Null, True )[/INDENT]
[INDENT] Wait For (sf) Msgbox_Result (intIlResult As Int)[/INDENT]
[INDENT][/INDENT]
[INDENT] StartActivity (Starter.pi.OpenBrowser ("https://play.google.com/store/account/subscriptions") )[/INDENT]
[INDENT] [/INDENT]
[INDENT] else If bspSelectedByUser.ProductID = BillingActiveProductId Then[/INDENT]
[INDENT] StartActivity (Starter.pi.OpenBrowser ("https://play.google.com/store/account/subscriptions") )[/INDENT]
[INDENT] [/INDENT]
[INDENT] Else[/INDENT]
[INDENT] CallSubDelayed2(Me, "BuyProductBySku", bspSelectedByUser)[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] Return Null[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub BuyProductBySku(bsp As BillingSubProduct) As ResumableSub[/INDENT]
[INDENT] Dim ProductToBuy As String = bsp.ProductID[/INDENT]
[INDENT] Log("#-Sub main.BuyProduct, ProductToBuy=" & ProductToBuy & ", " & $"$DateTime{DateTime.Now}"$)[/INDENT]
[INDENT] LabelAskProducts.Text = $"Buy product "${ProductToBuy}" in progress"$[/INDENT]
[INDENT] Log("#- x239, BillingActiveProductId=" & BillingActiveProductId)[/INDENT]
[INDENT] [/INDENT]
[INDENT] Wait For (Starter.billing.ConnectIfNeeded) Billing_Connected (Result As BillingResult)[/INDENT]
[INDENT] Log("#- x241, Result.DebugMessage=" & Result.DebugMessage)[/INDENT]
[INDENT] If Result.IsSuccess Then[/INDENT]
[INDENT] [/INDENT]
[INDENT] Dim sf As Object = Starter.billing.QuerySkuDetails("subs", Array(ProductToBuy))[/INDENT]
[INDENT] Wait For (sf) Billing_SkuQueryCompleted (Result As BillingResult, SkuDetails As List)[/INDENT]
[INDENT] If Result.IsSuccess And SkuDetails.Size = 1 Then[/INDENT]
[INDENT][/INDENT]
[INDENT] Log("#- x249, BillingActiveProductId=" & BillingActiveProductId)[/INDENT]
[INDENT] If BillingActiveProductId <> "free" Then[/INDENT]
[INDENT] Result = LaunchBillingFlow2(SkuDetails.Get(0), BillingActiveProductId)[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] 'start the billing process. The PurchasesUpdated event will be raised in the starter service[/INDENT]
[INDENT] Result = Starter.billing.LaunchBillingFlow(SkuDetails.Get(0))[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] Log("#- x78, Result.IsSuccess=" & Result.IsSuccess)[/INDENT]
[INDENT][/INDENT]
[INDENT] If Result.IsSuccess Then[/INDENT]
[INDENT] LabelAskProducts.Text = $"Product "${ProductToBuy}" bought ok"$[/INDENT]
[INDENT] Return Null[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] LabelAskProducts.Text = $"Failed to buy product "${ProductToBuy}" "$[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] ToastMessageShow("#- x74, Error starting billing process", True)[/INDENT]
[INDENT] Return Null[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub LaunchBillingFlow2(sku As SkuDetails, OldSku As String) As BillingResult[/INDENT]
[INDENT] ' Google --> https://developer.android.com/google/play/billing/billing_subscriptions.html#Allow-upgrade[/INDENT]
[INDENT] ' Erel --> https://www.b4x.com/android/forum/threads/howto-googleplaybilling-subscription-up-downgrade.111090/#post-693109[/INDENT]
[INDENT] ' should be called from the activity[/INDENT]
[INDENT] Dim jo As JavaObject = Starter.billing[/INDENT]
[INDENT] Dim BillingClient As JavaObject = jo.GetField("client")[/INDENT]
[INDENT] Dim context As JavaObject[/INDENT]
[INDENT] context.InitializeContext[/INDENT]
[INDENT] Dim BillingFlowParams As JavaObject[/INDENT]
[INDENT] BillingFlowParams = BillingFlowParams.InitializeStatic("com.android.billingclient.api.BillingFlowParams") _[/INDENT]
[INDENT] .RunMethodJO("newBuilder", Null).RunMethodJO("setSkuDetails", Array(sku)) _[/INDENT]
[INDENT] .RunMethodJO("setOldSku", Array(OldSku)).RunMethod("build", Null)[/INDENT]
[INDENT] Return BillingClient.RunMethod("launchBillingFlow", Array(context, BillingFlowParams))[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub AskPurchState_Click[/INDENT]
[INDENT] CallSubDelayed(Me, "BillingAskProductsAndFillClv")[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]' -------------------------------------[/INDENT]
[INDENT][/INDENT]
[INDENT][/INDENT]
[INDENT]' ~~~~~~~~~~~~~~~~~~~~[/INDENT]
[INDENT]' STARTER SERVICE MODULE[/INDENT]
[INDENT]' ~~~~~~~~~~~~~~~~~~~~[/INDENT]
[INDENT]#Region Service Attributes[/INDENT]
[INDENT] #StartAtBoot: False[/INDENT]
[INDENT] #ExcludeFromLibrary: True[/INDENT]
[INDENT]#End Region[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub Process_Globals[/INDENT]
[INDENT] Type BillingSubProduct(ProductID As String, Title As String, Description As String, Price As String, BillingPeriod As String, FreeTrialPeriod As Int, OrderId As String, CostLevel As Int, DeveloperPayload As String)[/INDENT]
[INDENT] Public xui As XUI[/INDENT]
[INDENT] Public billing As BillingClient[/INDENT]
[INDENT] Public PurchSubsOk As Boolean[/INDENT]
[INDENT] ' ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~[/INDENT]
[INDENT] [/INDENT]
[INDENT] [/INDENT]
[INDENT] Public const BILLING_KEY As String = "MIIBI..." ' <-- YOUR "licence key for this application" in GooglePlaystore developer console/ thisapp/ Development tools/ Services & APIs[/INDENT]
[INDENT] [/INDENT]
[INDENT] [/INDENT]
[INDENT] ' ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~ ~~~~[/INDENT]
[INDENT] Public const BILLINGPROD1 As String = "subsprod1"[/INDENT]
[INDENT] Public const BILLINGPROD2 As String = "subsprod2"[/INDENT]
[INDENT] Public BillingPurchState(3) As String[/INDENT]
[INDENT] Public BillingActiveSubsProductTitle As String = ""[/INDENT]
[INDENT] Public pi As PhoneIntents[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub Service_Create[/INDENT]
[INDENT] billing.Initialize("billing")[/INDENT]
[INDENT] BillingPurchState(0) = "STATE_UNSPECIFIED"[/INDENT]
[INDENT] BillingPurchState(1) = "STATE_PURCHASED"[/INDENT]
[INDENT] BillingPurchState(2) = "STATE_PENDING"[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]' --- --- --- --- --- --- --- --- --- --- --- --- ---[/INDENT]
[INDENT]Sub billing_PurchasesUpdated (Result As BillingResult, Purchases As List)[/INDENT]
[INDENT] Log("#-Sub starter.Sub billing_PurchasesUpdated, Result.IsSuccess=" & Result.IsSuccess)[/INDENT]
[INDENT] 'This event will be raised when the status of one or more of the purchases has changed.[/INDENT]
[INDENT] 'It will usually happen as a result of calling LaunchBillingFlow however it can be called in other cases as well.[/INDENT]
[INDENT] If Result.IsSuccess Then[/INDENT]
[INDENT] For Each p As Purchase In Purchases[/INDENT]
[INDENT] Log("#- x39, p.Sku = " & p.Sku)[/INDENT]
[INDENT] If p.Sku.StartsWith("subsprod") Then[/INDENT]
[INDENT] HandleSubscriptionPurchase(p)[/INDENT]
[INDENT] Else[/INDENT]
[INDENT] Log("#- x43, Unexpected product...")[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] Next[/INDENT]
[INDENT] End If[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Private Sub HandleSubscriptionPurchase (p As Purchase) As ResumableSub[/INDENT]
[INDENT] Log("#-Sub starter.Sub HandleSubscriptionPurchase, p.sku=" & p.Sku)[/INDENT]
[INDENT] If p.PurchaseState <> p.STATE_PURCHASED Then Return Null[/INDENT]
[INDENT] [/INDENT]
[INDENT] ' --> https://developer.android.com/google/play/billing/billing_testing.html#testing-renewals[/INDENT]
[INDENT] [/INDENT]
[INDENT] 'Verify the purchase signature.[/INDENT]
[INDENT] 'This cannot be done with the test id.[/INDENT]
[INDENT] If Not(p.Sku.StartsWith("subsprod")) And billing.VerifyPurchase(p, BILLING_KEY) = False Then [/INDENT]
[INDENT] Log("#- x55, Invalid purchase")[/INDENT]
[INDENT] Return Null[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] If p.IsAcknowledged = False Then[/INDENT]
[INDENT] 'we either acknowledge the product or consume it.[/INDENT]
[INDENT] Dim Intval As Int = 3 ' Testdays[/INDENT]
[INDENT] Select Case p.Sku[/INDENT]
[INDENT] Case "subsprod1" ' Weekly subscription[/INDENT]
[INDENT] Intval= 7[/INDENT]
[INDENT] Case "subsprod2" ' Monthly subscription[/INDENT]
[INDENT] Intval= 30[/INDENT]
[INDENT] End Select[/INDENT]
[INDENT] Dim SubsEndTime As Long = p.PurchaseTime + (DateTime.TicksPerDay *Intval)[/INDENT]
[INDENT] Dim mypayload As String = "mp~" & p.Sku & "~" & p.PurchaseTime & "~" & Intval & "~" & SubsEndTime[/INDENT]
[INDENT] Wait For (billing.AcknowledgePurchase(p.PurchaseToken, mypayload)) Billing_AcknowledgeCompleted (Result As BillingResult)[/INDENT]
[INDENT] Log("#- x61, Acknowledged: " & Result.IsSuccess & ", p.OrderId=" & p.OrderId)[/INDENT]
[INDENT] End If[/INDENT]
[INDENT] PurchSubsOk = True[/INDENT]
[INDENT] Log("#- x64, Purchase OK!")[/INDENT]
[INDENT] CallSub(Main, "BillingAskProductsAndFillClv")[/INDENT]
[INDENT] Return Null[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub Service_Start (StartingIntent As Intent)[/INDENT]
[INDENT] Service.StopAutomaticForeground 'Starter service can start in the foreground state in some edge cases.[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]'Return true to allow the OS default exceptions handler to handle the uncaught exception.[/INDENT]
[INDENT]Sub Application_Error (Error As Exception, StackTrace As String) As Boolean[/INDENT]
[INDENT] Return True[/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]Sub Service_Destroy[/INDENT]
[INDENT][/INDENT]
[INDENT]End Sub[/INDENT]
[INDENT][/INDENT]
[INDENT]