Skip to content

App Monetization — Ads, In-App Purchases & Subscriptions Strategy Guide

DodaTech Updated 2026-06-22 8 min read

In this tutorial, you'll learn about App Monetization. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

App monetization is the strategy of generating revenue from mobile applications through advertising, in-app purchases, subscriptions, and premium features, with the global app economy exceeding 500 billion dollars in annual consumer spending.

What You'll Learn & Why It Matters

In this tutorial you will learn how to monetize mobile apps using AdMob banner and interstitial ads, consumable and non-consumable in-app purchases, auto-renewable subscriptions with introductory offers, and premium feature paywalls using RevenueCat. Choosing the right monetization model can increase lifetime value (LTV) by 3-5 times compared to a single strategy.

Real-world use: DodaZIP offers a freemium model where basic file compression is free, advanced format support (RAR, 7z) requires a subscription, and the ad-free experience is a one-time purchase — generating 80 percent of revenue from subscriptions.

Prerequisites

  • An Android or iOS app with a user base (or a test app)
  • A Google Play Console and/or App Store Connect account
  • Basic familiarity with Kotlin or Swift

Learning Path

flowchart LR
  A[App Development Overview] --> B[App Monetization]
  B --> C[App Store Optimization]
  B --> D[Mobile Analytics]
  B:::current
  classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px

In-App Advertising with AdMob

AdMob is Google's mobile ad platform supporting banner, interstitial, rewarded, and native ads.

Android Banner Ad

class MainActivity : AppCompatActivity() {
    private lateinit var adView: AdView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        MobileAds.initialize(this) { }

        adView = AdView(this).apply {
            adUnitId = "ca-app-pub-3940256099942544/6300978111" // Test ID
            adSize = AdSize.BANNER
            layoutParams = FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT
            ).apply { gravity = Gravity.BOTTOM }
        }

        val adContainer = findViewById<FrameLayout>(R.id.ad_container)
        adContainer.addView(adView)

        val adRequest = AdRequest.Builder().build()
        adView.loadAd(adRequest)
    }

    override fun onDestroy() {
        adView.destroy()
        super.onDestroy()
    }
}

Expected behavior: A banner ad appears at the bottom of the screen. The test ad unit returns a placeholder ad. Replace with your production ad unit ID before releasing.

iOS Interstitial Ad

import GoogleMobileAds

class InterstitialManager: NSObject, GADFullScreenContentDelegate {
    private var interstitial: GADInterstitialAd?

    func loadAd() {
        let request = GADRequest()
        GADInterstitialAd.load(
            withAdUnitID: "ca-app-pub-3940256099942544/4411468910",
            request: request
        ) { [weak self] ad, error in
            if let error = error {
                print("Failed to load interstitial: \(error)")
                return
            }
            self?.interstitial = ad
            self?.interstitial?.fullScreenContentDelegate = self
        }
    }

    func showAd(from viewController: UIViewController) {
        guard let interstitial = interstitial else {
            print("Ad not ready")
            return
        }
        interstitial.present(fromRootViewController: viewController)
    }

    func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        loadAd() // Preload the next ad
    }
}

Expected behavior: An interstitial ad covers the full screen. The adDidDismissFullScreenContent callback fires when the user closes it, triggering preloading of the next ad.

In-App Purchases (Consumable and Non-Consumable)

In-app purchases let users buy digital goods. Consumables (coins, hints) can be bought repeatedly. Non-consumables (premium unlock, ad removal) are purchased once.

Android Billing Client

class BillingManager(private val context: Context) : PurchasesUpdatedListener {
    private val billingClient = BillingClient.newBuilder(context)
        .setListener(this)
        .enablePendingPurchases()
        .build()

    fun startConnection() {
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(result: BillingResult) {
                if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                    queryProducts()
                }
            }
            override fun onBillingServiceDisconnected() {
                // Retry connection
            }
        })
    }

    private fun queryProducts() {
        val params = QueryProductDetailsParams.newBuilder()
            .setProductList(
                listOf(
                    QueryProductDetailsParams.Product.newBuilder()
                        .setProductId("remove_ads")
                        .setProductType(ProductType.INAPP)
                        .build(),
                    QueryProductDetailsParams.Product.newBuilder()
                        .setProductId("premium_monthly")
                        .setProductType(ProductType.SUBS)
                        .build()
                )
            )
            .build()

        billingClient.queryProductDetailsAsync(params) { result, productList ->
            // Store product details for display
        }
    }

    fun launchPurchase(activity: Activity, product: QueryProductDetailsParams.Product) {
        val flowParams = BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(
                listOf(
                    BillingFlowParams.ProductDetailsParams.newBuilder()
                        .setProductDetails(product)
                        .build()
                )
            )
            .build()
        billingClient.launchBillingFlow(activity, flowParams)
    }

    override fun onPurchasesUpdated(result: BillingResult, purchases: List<Purchase>?) {
        if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
            for (purchase in purchases) {
                handlePurchase(purchase)
            }
        }
    }

    private fun handlePurchase(purchase: Purchase) {
        if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
            // Acknowledge the purchase
            val acknowledgeParams = AcknowledgePurchaseParams.newBuilder()
                .setPurchaseToken(purchase.purchaseToken)
                .build()
            billingClient.acknowledgePurchase(acknowledgeParams) { /* done */ }

            // Unlock the feature
            when (purchase.products[0]) {
                "remove_ads" -> Preferences.setAdsRemoved(true)
                "premium_monthly" -> Preferences.setPremium(true)
            }
        }
    }
}

Expected behavior: The Google Play billing dialog appears. After successful payment, the purchase must be acknowledged within 3 days or it is refunded. The feature unlocks immediately after acknowledgment.

iOS StoreKit 2 (Swift)

import StoreKit

@MainActor
class PurchaseManager: ObservableObject {
    @Published var products: [Product] = []
    @Published var purchasedProductIDs: Set<String> = []

    func loadProducts() async {
        do {
            let products = try await Product.products(for: [
                "com.dodatech.remove_ads",
                "com.dodatech.premium_monthly",
                "com.dodatech.premium_yearly",
            ])
            self.products = products.sorted(by: { $0.price < $1.price })
        } catch {
            print("Failed to load products: \(error)")
        }
    }

    func purchase(_ product: Product) async throws {
        let result = try await product.purchase()
        switch result {
        case .success(let verification):
            let transaction = try checkVerified(verification)
            await transaction.finish()
            purchasedProductIDs.insert(product.id)
        case .userCancelled:
            break
        case .pending:
            break
        @unknown default:
            break
        }
    }

    func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .unverified:
            throw StoreError.failedVerification
        case .verified(let safe):
            return safe
        }
    }
}

enum StoreError: Error {
    case failedVerification
}

Expected behavior: Products load from App Store Connect. The purchase() method presents the system payment sheet. After successful verification, the Transaction finishes and the product is marked as purchased.

Subscription Management with RevenueCat

RevenueCat simplifies subscription management across Android and iOS with a unified API.

class RevenueCatManager {
    fun configure() {
        Purchases.configure(PurchaseConfiguration.Builder(context)
            .apiKey("your_revenuecat_api_key")
            .build()
        )
    }

    fun checkSubscriptionStatus() {
        Purchases.sharedInstance.getCustomerInfo { customerInfo, error ->
            if (customerInfo?.entitlements?.get("pro")?.isActive == true) {
                // User has active Pro subscription
                unlockProFeatures()
            }
        }
    }

    fun purchasePackage(activity: Activity, package: Package) {
        Purchases.sharedInstance.purchasePackage(activity, package) { customerInfo, _, error ->
            if (customerInfo?.entitlements?.get("pro")?.isActive == true) {
                unlockProFeatures()
            }
        }
    }

    fun restorePurchases() {
        Purchases.sharedInstance.restorePurchases { customerInfo, error ->
            if (customerInfo?.entitlements?.get("pro")?.isActive == true) {
                unlockProFeatures()
            }
        }
    }

    private fun unlockProFeatures() {
        // Enable premium features, remove ads, etc.
    }
}

Expected behavior: RevenueCat creates a single source of truth for subscription status across platforms. Restore purchases works across devices. The SDK handles receipt validation and expiration tracking.

Monetization Architecture

flowchart TD
  A[User opens app] --> B{Free or Premium?}
  B -->|Free| C[Show banner ads]
  B -->|Premium| D[Hide all ads]
  C --> E{Engaged user?}
  E -->|High intent| F[Show interstitial ad]
  E -->|Needs feature| G[Show paywall]
  F --> H[Reward: continue]
  G --> I{User purchases?}
  I -->|Subscription| J[Unlock all premium]
  I -->|One-time| K[Unlock specific feature]
  I -->|Decline| L[Continue with ads]
  J --> M[Receipt validation]
  K --> M

Common Errors & Mistakes

1. Not Handling Purchase Cancellation Properly

Mistake: Only checking subscription status at app launch, not listening for real-time changes via the purchase listener.

Fix: Register a purchase update listener and revoke access immediately when a subscription expires or is cancelled.

2. Silencing AdMob Errors

Mistake: Logging ad load failures with Log.e but not showing a fallback UI, leaving an empty ad container.

Fix: Add an AdListener with onAdFailedToLoad callback that hides the ad container when no ad is available.

3. Offering Consumables Without Proper Inventory Management

Mistake: Letting users buy consumables (coins, gems) without persisting the purchase to a server, enabling offline cheating.

Fix: Track consumable balances on your backend. Validate every purchase server-side before granting the item.

4. Not Testing Sandbox Purchases

Mistake: Releasing subscription code without testing the full lifecycle (purchase, renewal, cancellation, refund) in the sandbox environment.

Fix: Use App Store Connect sandbox testers and Google Play license testers. Test all edge cases before production.

5. Ignoring Introductory Pricing

Mistake: Setting up subscriptions without free trials or introductory offers, reducing conversion by 40-60 percent.

Fix: Use RevenueCat or platform APIs to offer a 3-day free trial or first-month discount. Display the trial prominently on the paywall.

Practice Questions

Question 1

What is the difference between a consumable and a non-consumable purchase?

Show answer Consumables (coins, hints) can be purchased multiple times and are used up. Non-consumables (premium unlock, ad removal) are purchased once and permanently associated with the user's account.

Question 2

Why must purchases be acknowledged on Android?

Show answer Google Play requires purchase acknowledgment within 3 days or the payment is refunded. Acknowledgment confirms the app has delivered the purchased item and prevents automatic refunds.

Question 3

What is RevenueCat and why use it?

Show answer RevenueCat is a subscription management platform that abstracts Android and iOS billing APIs into a unified SDK. It handles receipt validation, status tracking, and cross-platform synchronization, reducing integration time by weeks.

Question 4

How does an auto-renewable subscription work?

Show answer An auto-renewable subscription bills the user at regular intervals (monthly, yearly) until the user cancels. The app store handles billing, receipt delivery, and renewal notifications. The app checks the subscription status on launch and via webhooks.

Challenge

Implement a complete freemium model: a free tier with banner ads and limited features, a one-time purchase to remove ads, and a monthly subscription for all premium features. Use RevenueCat for cross-platform management. Write server-side receipt validation for the subscription. Implement a paywall screen that shows trial offer and pricing tiers.

Mini Project: Subscription Analytics Dashboard

Build a dashboard that displays subscription metrics using RevenueCat webhooks: current MRR, active subscribers, trial conversion rate, churn rate, and LTV by cohort. Use a backend webhook endpoint to receive RevenueCat events and store them in PostgreSQL. Display charts with a 7-day and 30-day trend using Chart.js.


Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Author: DodaTech | Last updated: June 22, 2026

DodaTech tutorials are built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro — security tools used by millions worldwide.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro