Android SaaS App with Subscriptions: Complete 2025 Guide
Source: Dev.to
Market Context 2025
The global SaaS market size is estimated at USD 408.21 billion in 2025, projected to reach USD 1,251.35 billion by 2034.
The subscription billing management market is expected to reach $17.95 billion by 2030, growing at a CAGR of 16.9%.
1. Google Play Billing Library
Current Version
- Play Billing Library 7.x (Required by August 2025)
- Important Deadline: By August 31 2025, all new apps and updates must use Billing Library 7 or newer. Extensions are available until November 1 2025.
Key Features in Version 7.0+
- Installment Subscriptions – Users pay in smaller, manageable installments (available in Brazil, France, Italy, Spain)
- Pending Transactions for Subscriptions – Handle
SUBSCRIPTION_STATE_PENDINGbefore activation - ReplacementMode API – Replaces deprecated
ProrationModefor upgrades/downgrades - User Choice Billing –
enableUserChoiceBilling()for alternative payment options
Implementation
// build.gradle.kts
implementation("com.android.billingclient:billing-ktx:7.1.1")
// Initialize BillingClient
val billingClient = BillingClient.newBuilder(context)
.setListener { billingResult, purchases ->
// Handle purchase updates
purchases?.forEach { purchase ->
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
// Verify on backend, then acknowledge
verifyAndAcknowledge(purchase)
}
}
}
.enablePendingPurchases(
PendingPurchasesParams.newBuilder()
.enablePrepaidPlans()
.build()
)
.build()
// Connect to Google Play
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
querySubscriptions()
}
}
override fun onBillingServiceDisconnected() {
// Implement retry logic
}
})
// Query available subscriptions
fun querySubscriptions() {
val params = QueryProductDetailsParams.newBuilder()
.setProductList(
listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_monthly")
.setProductType(BillingClient.ProductType.SUBS)
.build(),
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_yearly")
.setProductType(BillingClient.ProductType.SUBS)
.build()
)
)
.build()
billingClient.queryProductDetailsAsync(params) { billingResult, productDetailsList ->
// Check for installment plans (PBL 7+)
productDetailsList.forEach { product ->
product.subscriptionOfferDetails?.forEach { offer ->
offer.installmentPlanDetails?.let { installment ->
// Handle installment subscription display
val commitments = installment.installmentPlanCommitment
}
}
}
}
}
// Launch purchase flow
fun launchPurchase(activity: Activity, productDetails: ProductDetails, offerToken: String) {
val flowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setOfferToken(offerToken)
.build()
)
)
.build()
billingClient.launchBillingFlow(activity, flowParams)
}
Google Play Commission Rates 2025
| Scenario | Rate |
|---|---|
| First $1 M annual revenue | 15 % |
| Subscriptions after first year | 15 % |
| First‑year subscriptions (standard) | 30 % |
2. 2025 Policy Update: Alternative Billing in the US
Major Change (October 2025)
Following the Epic Games ruling, Google now allows US developers to offer alternative payment methods:
- No requirement to use Google Play Billing exclusively
- Developers may communicate with users about alternative payment options
- No price restrictions based on the payment method used
- Payments can be processed through Stripe, PayPal, or custom solutions
External Offers Program (EEA)
For the European Economic Area, developers can lead users outside the app to promote offers, subject to Google Play’s Payments policy.
// User Choice Billing implementation
val billingClient = BillingClient.newBuilder(context)
.enableUserChoiceBilling(UserChoiceBillingListener { userChoiceDetails ->
// User selected alternative billing
val externalTransactionToken = userChoiceDetails.externalTransactionToken
// Process with your payment provider
processAlternativePayment(externalTransactionToken)
})
.build()
3. Third‑Party Subscription Platforms
RevenueCat
The most popular cross‑platform subscription management SDK.
// Setup
Purchases.configure(
PurchasesConfiguration.Builder(context, "your_api_key")
.appUserID(userId)
.build()
)
// Fetch offerings
Purchases.sharedInstance.getOfferingsWith(
onError = { error -> /* Handle error */ },
onSuccess = { offerings ->
offerings.current?.availablePackages?.let { packages ->
// Display subscription options
}
}
)
// Make purchase
Purchases.sharedInstance.purchase(
PurchaseParams.Builder(activity, packageToPurchase).build(),
onError = { error, userCancelled -> },
onSuccess = { storeTransaction, customerInfo ->
if (customerInfo.entitlements["premium"]?.isActive == true) {
// Unlock premium features
}
}
)
// Check subscription status
Purchases.sharedInstance.getCustomerInfo(
onError = { /* Handle */ },
onSuccess = { customerInfo ->
val isPremium = customerInfo.entitlements["premium"]?.isActive == true
}
)
Pricing: Free up to $2,500 MTR, then 1 % of MTR.
Adapty
Strong alternative with superior A/B testing capabilities.
Advantages over RevenueCat
- More sophisticated multivariate paywall testing
- More analytics metrics on free plan
- Real‑time dashboard data
- Faster customer‑support response times
- No‑code paywall builder with granular control
Pricing: Starts at $99 per month.
Comparison Table
| Feature | RevenueCat | Adapty |
|---|---|---|
| SDK Platforms | iOS, Android, Flutter, RN, Unity, Cordova, Ionic | iOS, Android, Flutter, RN, Unity |
| Free Tier Analytics | 6 metrics | 10+ metrics |
| Real‑time Data | No | Yes |
| A/B Testing | Basic | Advanced multivariate |
| Paywall Builder | Native (Paywalls v2) | No‑code drag‑drop |
| Starting Price | Free / $8 mo | $99 /mo |
4. Backend Architecture with Supabase
Supabase provides an excellent open‑source backend for SaaS apps with PostgreSQL, authentication, and real‑time capabilities.
Database Schema for Subscriptions
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Users table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
-- Subscriptions table
CREATE TABLE subscriptions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
product_id TEXT NOT NULL,
purchase_token TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('active','canceled','past_due','expired')),
period_start TIMESTAMP WITH TIME ZONE NOT NULL,
period_end TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
-- Indexes for fast lookup
CREATE INDEX idx_subscriptions_user_id ON subscriptions(user_id);
CREATE INDEX idx_subscriptions_status ON subscriptions(status);
Use Supabase’s Row‑Level Security (RLS) policies to ensure that users can only read/write their own subscription records, and integrate webhook listeners to handle Google Play purchase notifications for real‑time status updates.