δΈ­

Google Play Billing Setup

This guide covers setting up Google Play Billing for in-app purchases and subscriptions in your Android application.

Prerequisites

  • Google Play Developer account ($25 one-time fee)
  • Published or draft app in Google Play Console
  • Access to Google Play Console
  • Google Cloud project for API access

Step 1: Set Up Google Play Console

  1. Go to Google Play Console
  2. Select your app or create a new one
  3. Complete app listing and content rating
  4. Upload at least an internal testing APK

Step 2: Create In-App Products

One-Time Products (Managed Products)

  1. Go to Monetize > Products > In-app products
  2. Click Create product
  3. Fill in product details:
Field Description Example
Product ID Unique identifier premium_upgrade
Name Display name Premium Upgrade
Description Product description Unlock all features
Default price Base price $9.99
  1. Click Save then Activate

Subscriptions

  1. Go to Monetize > Products > Subscriptions
  2. Click Create subscription
  3. Fill in subscription details:
Field Description Example
Product ID Unique identifier pro_monthly
Name Subscription name Pro Monthly
Description What's included Monthly pro access
  1. Add a base plan:
  • Plan ID: monthly-plan
  • Billing period: Monthly
  • Price: $9.99/month
  • Free trial: Optional
  • Grace period: 3 days recommended
  1. Click Save then Activate

Pricing Templates

Set up consistent pricing across regions:

  1. Go to Monetize > Pricing templates
  2. Create a template for your pricing strategy
  3. Apply to products for automatic currency conversion

Step 3: Set Up Real-time Developer Notifications (RTDN)

RTDN sends purchase updates to your server.

  1. Go to Monetize > Monetization setup
  2. Find Real-time developer notifications
  3. Enter your Cloud Pub/Sub topic:
projects/your-project/topics/play-billing-notifications

Create Pub/Sub Topic

  1. Go to Google Cloud Console
  2. Navigate to Pub/Sub > Topics
  3. Click Create Topic
  4. Topic name: play-billing-notifications
  5. Add subscription for your webhook endpoint

Step 4: Configure Service Account

For server-side verification:

  1. Go to Google Cloud Console
  2. Navigate to IAM & Admin > Service Accounts
  3. Click Create Service Account
  4. Name: play-billing-verifier
  5. Click Create and Continue

Grant Permissions

In Google Play Console:

  1. Go to Users and permissions > Invite new users
  2. Add your service account email
  3. Grant permissions:
  • Financial data, orders, and cancellation survey responses (View)
  • Manage orders and subscriptions

Download Key File

  1. In Google Cloud Console, go to your service account
  2. Click Keys > Add Key > Create new key
  3. Select JSON format
  4. Download and secure the key file

Step 5: Configure in OpenDev

  1. Log in to OpenDev Platform
  2. Go to your application's Payment Configuration
  3. Add Google Play configuration:
{
  "platform": "google_play",
  "enabled": true,
  "config": {
    "packageName": "com.yourcompany.myapp",
    "serviceAccountKey": {
      "type": "service_account",
      "project_id": "your-project",
      "private_key_id": "xxx",
      "private_key": "-----BEGIN PRIVATE KEY-----\n...",
      "client_email": "xxx@xxx.iam.gserviceaccount.com"
    },
    "pubsubTopic": "projects/your-project/topics/play-billing-notifications"
  }
}

Configuration Fields

Field Required Description
Package Name Yes Your app's package name
Service Account Key Yes JSON key content
Pub/Sub Topic Yes RTDN notification topic

Step 6: Configure Product Tiers

Link Google Play Product IDs:

  1. Go to Product Tiers in OpenDev
  2. For each tier, add Google Play product ID:
{
  "productId": "pro_monthly",
  "name": "Pro Monthly",
  "platformProductIds": {
    "google_play": "pro_monthly_subscription"
  }
}

Step 7: Implement Billing in Android

Add Billing Library

dependencies {
    implementation 'com.android.billingclient:billing:6.0.1'
}

Initialize Billing Client

class BillingManager(private val context: Context) {
    private lateinit var billingClient: BillingClient
    
    fun initialize() {
        billingClient = BillingClient.newBuilder(context)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build()
            
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(result: BillingResult) {
                if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                    // Ready to query purchases
                }
            }
            
            override fun onBillingServiceDisconnected() {
                // Reconnect
            }
        })
    }
}

Query Products

suspend fun queryProducts() {
    val productList = listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("pro_monthly")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )
    
    val params = QueryProductDetailsParams.newBuilder()
        .setProductList(productList)
        .build()
        
    val result = billingClient.queryProductDetails(params)
    // Handle product details
}

Launch Purchase Flow

fun launchPurchase(productDetails: ProductDetails) {
    val offerToken = productDetails.subscriptionOfferDetails?.get(0)?.offerToken
    
    val productDetailsParams = BillingFlowParams.ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setOfferToken(offerToken!!)
        .build()
        
    val flowParams = BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(listOf(productDetailsParams))
        .build()
        
    billingClient.launchBillingFlow(activity, flowParams)
}

Handle Purchase Updates

private val purchasesUpdatedListener = PurchasesUpdatedListener { result, purchases ->
    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) {
        // Send to server for verification
        verifyPurchaseOnServer(purchase.purchaseToken)
        
        // Acknowledge the purchase
        if (!purchase.isAcknowledged) {
            val params = AcknowledgePurchaseParams.newBuilder()
                .setPurchaseToken(purchase.purchaseToken)
                .build()
            billingClient.acknowledgePurchase(params) { }
        }
    }
}

Step 8: Server-Side Verification

// Backend verification
const { google } = require('googleapis');

async function verifyPurchase(packageName, productId, purchaseToken) {
    const auth = new google.auth.GoogleAuth({
        keyFile: 'service-account-key.json',
        scopes: ['https://www.googleapis.com/auth/androidpublisher'],
    });
    
    const androidpublisher = google.androidpublisher({ version: 'v3', auth });
    
    // For subscriptions
    const result = await androidpublisher.purchases.subscriptions.get({
        packageName,
        subscriptionId: productId,
        token: purchaseToken,
    });
    
    return result.data;
}

Step 9: Test the Integration

License Testing

  1. Go to Settings > License testing
  2. Add test account emails
  3. Test accounts get free purchases in sandbox

Testing Flow

  1. Install app from internal testing track
  2. Sign in with license tester account
  3. Complete purchase (sandbox mode)
  4. Verify webhook receives notification
  5. Check purchase in Play Console

Test Scenarios

Scenario How to Test
Successful purchase Complete normal flow
Subscription renewal Wait or use test time controls
Subscription cancel Cancel from Play Store
Payment failed Use test card with decline

Troubleshooting

Error: Item not available

Solutions:

  • Product must be activated in Play Console
  • App must be published to at least internal testing
  • User must be signed in with Google account

Error: Service unavailable

Solutions:

  • Check billing client is connected
  • Retry with exponential backoff
  • Verify Google Play services is up to date

Purchases not showing on server

Solutions:

  • Verify Pub/Sub subscription is active
  • Check service account permissions
  • Verify webhook endpoint is reachable

Security Best Practices

  1. Always Verify on Server - Don't trust client-side verification
  2. Acknowledge Promptly - Purchases auto-refund if not acknowledged
  3. Handle Pending Purchases - Support delayed payment methods
  4. Secure Service Account Key - Never expose in app code
  5. Implement Retry Logic - Network issues are common