B4A Library [B4X] [B4XLIB] B4X Purchase Manager — In-App Purchases & Subscriptions Made Simple

Like many devs, I kept running into the same problem — monetizing my apps often felt like it took longer than building them.

I had a PHP setup for iOS validation that worked, but every new app meant more servers, databases, hosting costs, ongoing maintenance, and rebuilding purchase flows and subscription screens. For Android, I didn't even have a server-side validation option — I just had to trust the billing library response client-side, with no way to detect refunds or verify anything independently.

So I decided to tackle it properly and built my own monetization system.

I figured others in the B4X community were probably hitting the same walls, so what started as a simple solution for my own apps turned into months of building both the B4X library and the backend infrastructure behind it — into a service any B4X developer can use.

Register your app, grab your API key, add the b4xlib, and you're ready to go in just a few lines of code:

That's it. Seriously.:
' Initialize with your App ID and API key from the dashboard
PurchaseManager.Initialize(Root, BILLING_KEY, APP_ID, API_KEY)

' Add your products
PurchaseManager.AddProduct("premium", "Premium", "Unlock all features")

' Show the purchase screen — that's it
Wait For (PurchaseManager.ShowInAppPurchase) Complete (Success As Boolean)

Check status anytime — instant, from local cache:

Status & Validation:
If PurchaseManager.IsUnlocked Then ...

' Or trigger a live validation at any time
Wait For (PurchaseManager.ValidateNow) Complete (Success As Boolean)

01-in-app-custom.png
02-in-app-dark.png
03-subs-regular.png


Key Features
• Built-in purchase and subscription screens (dark mode, theming, feature cards)
• Server-side validation — receipts verified directly with Apple and Google
• Zero secrets in your app — credentials stored securely server-side
• Smart caching — status checks are instant from local cache, background re-validation only when needed
• Restore purchases flow for both platforms
• Consumable product support
• Auto-generated privacy policy and terms of service (or supply your own URL/HTML)
• Subscription screens include Apple-required disclosures (auto-renewal terms, ToS/Privacy links) — App Store ready out of the box
• Non-purchasing users generate zero API calls

How It Works
1. Create an account at b4xpurchasemanager.com and register your app
2. Upload your store credentials (Apple Shared Secret / Google Play service account)
3. Grab your API key from the dashboard
4. Add the b4xlib to your project and initialize
5. The library handles everything — purchase UI, store interaction, receipt validation, caching, and status management

Your app talks to the BPM validation server over HTTPS. The server verifies receipts directly with Apple/Google and returns the result. No secrets in your app, no server to maintain.

Setup
1. Copy B4XPurchaseManager.b4xlib to your B4X Additional Libraries folder
2. iOS: Your provisioning profile must be based on an explicit App ID (e.g., com.yourcompany.yourapp) — wildcard profiles (com.*) will not compile
3. Android: Add the following to your manifest:
Android Manifest:
CreateResourceFromFile(Macro, GooglePlayBilling.GooglePlayBilling)

Add your constants to Class_Globals:
Constants:
Private Const BILLING_KEY As String = "MIIBIjANBgkq..."  ' Your Google Play license key (ignored on iOS)
Private Const APP_ID As String = "my-app"              ' Your BPM dashboard app identifier
Private Const API_KEY As String = "bpm_your_key_here"  ' Your BPM API key

In-App Purchase Example (One-Time Unlock)
In-App Purchase:
Private PurchaseManager As B4XPurchaseManager

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
 
    PurchaseManager.Initialize(Root, BILLING_KEY, APP_ID, API_KEY)
    PurchaseManager.DisplayAppName = "My App"
    PurchaseManager.AddFeature("🎨", "Premium Themes", "Access all premium themes")
    PurchaseManager.AddFeature("🚀", "Advanced Features", "Unlock powerful tools")
    PurchaseManager.AddProduct("premium_unlock", "Premium", "Unlock all features")
End Sub

Private Sub btnPurchase_Click
    Wait For (PurchaseManager.ShowInAppPurchase) Complete (Success As Boolean)
    If Success Then xui.MsgboxAsync("Purchase successful!", "Success")
End Sub

Private Sub btnRestore_Click
    Wait For (PurchaseManager.RestorePurchases) Complete (Success As Boolean)
End Sub

Private Sub B4XPage_Appear
    PurchaseManager.CheckStatus
    If PurchaseManager.IsUnlocked Then
        ' Show premium features
    End If
End Sub

Sub B4XPage_CloseRequest As ResumableSub
    If PurchaseManager.IsActive Then
        PurchaseManager.HidePurchaseScreen
        Return False
    End If
    Return True
End Sub

Subscription Example (Monthly + Yearly)
Subscriptions:
Private PurchaseManager As B4XPurchaseManager

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
 
    PurchaseManager.Initialize(Root, BILLING_KEY, APP_ID, API_KEY)
    PurchaseManager.DisplayAppName = "My App"
    PurchaseManager.AddFeature("⭐", "Premium Access", "Full access to all features")
    PurchaseManager.AddFeature("🔄", "Cross-Platform", "Works on Android and iOS")
 
    ' Required for subscriptions
    PurchaseManager.PolicyCompanyName = "Your Company"
    PurchaseManager.PolicyEmailAddress = "support@yourcompany.com"
    PurchaseManager.PolicyEffectiveDate = "January 1, 2025"
 
    ' BasePlanId must match Google Play Console (ignored on iOS)
    PurchaseManager.AddSubscription("premium_monthly", "monthly-plan", _
        "Monthly Premium", "Full access, billed monthly", "Monthly")
    PurchaseManager.AddSubscription("premium_yearly", "yearly-plan", _
        "Yearly Premium", "Full access, billed yearly — save 40%", "Yearly")
End Sub

Private Sub btnSubscribe_Click
    Wait For (PurchaseManager.ShowSubscriptionPurchase) Complete (Success As Boolean)
    If Success Then xui.MsgboxAsync("Subscription activated!", "Success")
End Sub

Private Sub btnRestore_Click
    Wait For (PurchaseManager.RestoreSubscriptions) Complete (Success As Boolean)
End Sub

Private Sub B4XPage_Appear
    PurchaseManager.CheckStatus
    If PurchaseManager.IsSubscriptionActive Then
        ' Show premium features
    End If
End Sub

Sub B4XPage_CloseRequest As ResumableSub
    If PurchaseManager.IsActive Then
        PurchaseManager.HidePurchaseScreen
        Return False
    End If
    Return True
End Sub

Additional Features
Direct Purchase (Custom UI), Theming, Consumables, Debug:
' ── Direct Purchase (use your own UI instead of built-in screens) ──
Wait For (PurchaseManager.PurchaseProduct("premium_unlock")) Complete (Success As Boolean)
Wait For (PurchaseManager.PurchaseSubscription("premium_monthly", "monthly-plan")) Complete (Success As Boolean)

' ── Theming ──
PurchaseManager.DarkMode = True
PurchaseManager.SetThemeColors(0xFF6366F1, 0xFF8B5CF6, 0xFFEC4899, 0xFFFFFFFF, 0xFFF5F5F7)

' ── Consumables ──
PurchaseManager.AddConsumableProduct("power_boost", "Power Boost", "Boost your power", 10)
If PurchaseManager.UseConsumables(1) Then Log("Consumed 1 item")
Dim count As Int = PurchaseManager.GetConsumableCount

' ── Debug & Testing ──
PurchaseManager.DebugValidation = True  ' Faster cache intervals for development
Wait For (PurchaseManager.ResetPurchaseForTesting("premium_unlock")) Complete (Success As Boolean)
Wait For (PurchaseManager.ResetSubscriptionForTesting("premium_monthly")) Complete (Success As Boolean)

' ── Privacy Policy & Terms Export (debug only) ──
PurchaseManager.ExportPrivacyPolicyInapp       ' Logs full HTML to IDE log
PurchaseManager.ExportPrivacyPolicySubscription
PurchaseManager.ExportTermsOfService
' Right-click HTML line in IDE log → Copy Line → paste into editor or LLM

' ── Custom Privacy Policy & Terms ──
PurchaseManager.CustomPrivacyPolicyInapp = "https://myapp.com/privacy.html"  ' URL or HTML
PurchaseManager.CustomTermsOfService = "https://myapp.com/terms.html"

Caching & Validation
The library uses smart caching so status checks are always instant. In-app purchases and subscriptions are cached independently — only the type whose cache has expired gets re-validated:

• New in-app purchases (first 14 days / refund window): re-validated every 24 hours
• Established in-app purchases (after 14 days): re-validated every 14 days (~2 credits/month)
• Active subscriptions: re-validated every 24 hours (~30 credits/month)
• Expired subscriptions: go dormant after 7-day grace period (zero cost)
• Refunded purchases: go dormant immediately (zero cost)
• Users who never purchased: zero API calls, zero credits consumed

CheckStatus reads from local cache instantly. Background validation only triggers when cache expires. Blended average: ~15 validations per paying user per month.

Pricing
1 credit = 1 validation. Free tier: 500 credits/month (no credit card required). Paid tiers start at $5/month.
You only pay for active paying users — non-purchasers, expired subs, and refunded users all cost zero.

Validation typically costs less than 1% of what your paying users generate in revenue.


METHODS REFERENCE:
MethodReturnsDescription
Initialize(Parent, BillingKey, AppId, ApiKey)—Initialize the library.
Pass Root for Parent.
Pass "" for BillingKey on iOS.
AppId is your BPM dashboard app identifier.
ApiKey is required (starts with "bpm_").
IsInitializedBooleanTrue if Initialize has been called successfully
AddProduct(ProductId, Title, Description)—Add a non-consumable product
AddConsumableProduct(ProductId, Title, Description, Quantity)—Add a consumable product. Quantity defines how many consumables are automatically awarded per purchase.
ClearProducts—Remove all configured products
AddSubscription(ProductId, BasePlanId, Title, Description, Period)—Add a subscription
ClearSubscriptions—Remove all configured subscriptions
ShowInAppPurchaseBooleanShow purchase screen. True = success
ShowSubscriptionPurchaseBooleanShow subscription screen. True = success
PurchaseProduct(ProductId)BooleanPurchase directly without built-in screen
PurchaseSubscription(ProductId, BasePlanId)BooleanSubscribe directly without built-in screen
RestorePurchasesBooleanRestore in-app purchases from store
RestoreSubscriptionsBooleanRestore subscriptions from store
CheckStatus—Load cached status, trigger background validation if expired
ValidateNowBooleanForce immediate live validation (costs 1 credit per configured type)
IsUnlockedBooleanTrue if user owns a non-consumable product
IsSubscriptionActiveBooleanTrue if user has valid subscription
GetActiveSubscriptionIdStringActive subscription product ID, or "" if none
GetSubscriptionExpiryDateLongSubscription expiry date in ticks, or 0 if not available
IsActiveBooleanTrue if purchase/subscription screen is showing
ValidationInProgressBooleanTrue if live backend validation is currently running
HidePurchaseScreen—Close any active purchase/subscription screen
AddConsumables(Count)—Manually add consumables (for custom UI purchases, ad rewards, referral bonuses, etc.)
UseConsumables(Count)BooleanDeduct consumables. True if enough available
GetConsumableCountIntCurrent consumable count
SetConsumableCount(Count)—Set count directly (for server sync)
SetThemeColors(Primary, Secondary, Accent, Bg1, Bg2)—Set theme colors. Pass 0 for defaults
AddFeature(Emoji, Title, Description)—Add feature card to purchase screens
ClearFeatures—Remove all feature cards
ShowPrivacyPolicy—Show privacy policy overlay
ShowTermsOfService—Show terms of service overlay
ExportPrivacyPolicyInappBooleanExport in-app privacy policy HTML to IDE log. Debug only.
ExportPrivacyPolicySubscriptionBooleanExport subscription privacy policy HTML to IDE log. Debug only.
ExportTermsOfServiceBooleanExport terms of service HTML to IDE log. Debug only.
ResetPurchaseForTesting(ProductId)BooleanReset a purchase for re-testing. Android: consumes token. iOS: clears cache.
ResetSubscriptionForTesting(ProductId)BooleanReset a subscription for re-testing.

PROPERTIES REFERENCE:
PropertyTypeDescription
DisplayAppNameStringApp name on purchase screens (default: your app's name)
DisplayTaglineStringTagline below app name (default: "Unlock the Full Experience")
DarkModeBooleanDark theme (default: False)
PolicyCompanyNameStringCompany name for legal templates
PolicyEmailAddressStringContact email for legal templates
PolicyEffectiveDateStringEffective date for legal documents
CustomPrivacyPolicyInappStringCustom privacy policy — URL or HTML
CustomPrivacyPolicySubscriptionStringCustom subscription privacy policy — URL or HTML
CustomTermsOfServiceStringCustom terms — URL or HTML
DebugValidationBooleanFaster revalidation intervals for testing (default: False)
AdvancedDebugModeBooleanLogs exactly what the store returned when products don't show up — the IDs requested, what came back, and what didn't. (default: False)
AutoInappValidationBooleanBy default, in-app purchases only re-validate for the first 21 days (covering the refund window).
Turn on to keep auto-validating every 14 days indefinitely for extra coverage, or use ValidateNow when/if required. (default: False)

Links
• Website & Docs: https://b4xpurchasemanager.com
• Dashboard: https://app.b4xpurchasemanager.com

Step-by-step guides for Android setup, iOS setup, Google Play service account, Apple Shared Secret, and more are available on the website — along with a detailed FAQ.

v1.01 Added auto-restore: automatically checks store for existing purchases on first launch (new device, reinstall, migration)
v1.02 Added AdvancedDebugMode: logs exactly what the store returned when products don't show up — the IDs requested, what came back, and what didn't.
Added AutoInappValidation: by default, in-app purchases only re-validate for the first 21 days (covering the refund window), then stop — you re-validate later via ValidateNow. Turn this on to keep auto-validating every 14 days indefinitely for extra coverage.
 

Attachments

  • B4XPurchaseManager.b4xlib
    58.8 KB · Views: 43
Last edited:

Segga

Member
Licensed User
Longtime User
I've added a demo b4x in-app purchase project.
You will of course have to change the package name to suit, and create non-wild card provision files for iOS, and create the in-app product.
Complete guides (including the Play/App store monetisation set up) are available from the website.
If anyone would like a subscription demo too, just let me know.
 

Attachments

  • purchase-manager-in-app-demo.zip
    232.2 KB · Views: 156
Top