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
- Go to Google Play Console
- Select your app or create a new one
- Complete app listing and content rating
- Upload at least an internal testing APK
Step 2: Create In-App Products
One-Time Products (Managed Products)
- Go to Monetize > Products > In-app products
- Click Create product
- 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 |
- Click Save then Activate
Subscriptions
- Go to Monetize > Products > Subscriptions
- Click Create subscription
- 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 |
- Add a base plan:
- Plan ID:
monthly-plan - Billing period: Monthly
- Price: $9.99/month
- Free trial: Optional
- Grace period: 3 days recommended
- Click Save then Activate
Pricing Templates
Set up consistent pricing across regions:
- Go to Monetize > Pricing templates
- Create a template for your pricing strategy
- Apply to products for automatic currency conversion
Step 3: Set Up Real-time Developer Notifications (RTDN)
RTDN sends purchase updates to your server.
- Go to Monetize > Monetization setup
- Find Real-time developer notifications
- Enter your Cloud Pub/Sub topic:
projects/your-project/topics/play-billing-notifications
Create Pub/Sub Topic
- Go to Google Cloud Console
- Navigate to Pub/Sub > Topics
- Click Create Topic
- Topic name:
play-billing-notifications - Add subscription for your webhook endpoint
Step 4: Configure Service Account
For server-side verification:
- Go to Google Cloud Console
- Navigate to IAM & Admin > Service Accounts
- Click Create Service Account
- Name:
play-billing-verifier - Click Create and Continue
Grant Permissions
In Google Play Console:
- Go to Users and permissions > Invite new users
- Add your service account email
- Grant permissions:
- Financial data, orders, and cancellation survey responses (View)
- Manage orders and subscriptions
Download Key File
- In Google Cloud Console, go to your service account
- Click Keys > Add Key > Create new key
- Select JSON format
- Download and secure the key file
Step 5: Configure in OpenDev
- Log in to OpenDev Platform
- Go to your application's Payment Configuration
- 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:
- Go to Product Tiers in OpenDev
- 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
- Go to Settings > License testing
- Add test account emails
- Test accounts get free purchases in sandbox
Testing Flow
- Install app from internal testing track
- Sign in with license tester account
- Complete purchase (sandbox mode)
- Verify webhook receives notification
- 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
- Always Verify on Server - Don't trust client-side verification
- Acknowledge Promptly - Purchases auto-refund if not acknowledged
- Handle Pending Purchases - Support delayed payment methods
- Secure Service Account Key - Never expose in app code
- Implement Retry Logic - Network issues are common