Back to the RevenueCat homepage
RevenueCat SDKGoogle Play Billing

Chapter 16: Testing Your Integration

A billing integration that works on your development machine does not mean it works in production. Users have different payment methods, live in different countries, and encounter subscription states your app may never have seen during development. If you ship without thorough testing, you will find bugs when real money is involved, and those bugs are expensive.

Google provides a comprehensive set of testing tools for Play Billing. You can simulate purchases without being charged, accelerate subscription lifecycles that would normally take weeks, test specific error scenarios, and validate behavior across regions. This chapter walks you through all of them and gives you a concrete set of test cases to run before every release.

License Testers

License testers are Google accounts that can make test purchases in your app without being charged real money. This is the foundation of all billing testing. Without license testers configured, every purchase attempt either costs real money or fails entirely.

Setting Up License Testers

To add a license tester, go to the Google Play Console, navigate to Setup > License testing, and add the Gmail address of the Google account you want to use for testing. The account must be a valid Gmail or Google Workspace address.

A few things to know about license testers:

  • License testers are configured at the developer account level, not per app. Once you add a tester, they can test purchases across all your apps.
  • The tester must be signed into the Google account on the test device. If the device has multiple accounts, the account used in the Google Play Store app is the one that matters.
  • Changes take effect within minutes. You do not need to publish a new version of your app after adding a tester.
  • License testers can test purchases on apps that are not yet published, as long as the app has been uploaded to a test track (internal, closed, or open) at least once.

Test Payment Methods

When a license tester initiates a purchase, the Google Play purchase dialog shows a special set of test payment instruments instead of real payment methods. These test instruments let you simulate different payment outcomes:

Test card, always approves: This instrument completes the purchase immediately with a successful result. Use this for the majority of your testing. It validates that your purchase flow, acknowledgment logic, and entitlement granting all work correctly.

Test card, always declines: This instrument causes the payment to fail. Your PurchasesUpdatedListener receives a BillingResponseCode.ERROR result. Use this to verify that your app handles payment failures gracefully, shows appropriate error messages, and does not grant entitlements when payment fails.

Slow test card, always approves: This instrument introduces a delay before the purchase completes. The purchase initially enters a pending state, then completes after a few minutes. Use this to test your pending purchase handling, which is especially important if you have enabled pending transactions.

Slow test card, always declines: This instrument introduces a delay and then fails. The purchase enters a pending state and eventually transitions to a failed state. Use this to verify your app handles the case where a pending purchase is ultimately declined.

These test instruments only appear for license tester accounts. Regular users never see them.

Auto Refund for Unacknowledged Test Purchases

When a license tester makes a purchase and your app does not acknowledge it within 3 minutes, Google automatically refunds and revokes the purchase. This mirrors the production behavior where unacknowledged purchases are refunded after 3 days, but the dramatically shorter window makes it practical for testing.

This 3 minute window is actually one of the most useful testing tools at your disposal. It lets you verify that your acknowledgment logic works correctly:

  1. Make a test purchase.
  2. Intentionally do not acknowledge it (comment out your acknowledgment code or disconnect from your server).
  3. Wait 3 minutes.
  4. Verify that the purchase disappears from queryPurchasesAsync and that your app revokes the entitlement.

If your app still shows premium content after those 3 minutes, your entitlement logic has a bug. You are probably caching the entitlement locally without rechecking purchase validity.

This behavior applies only to test purchases from license testers. Production purchases have a 3 day acknowledgment window.

Test Tracks

Before a license tester can install and test your app, you need to upload a build to one of Google Play's test tracks. Google provides three test tracks, each designed for a different stage of your release process.

Internal Testing Track

The internal testing track is the fastest way to get a build onto a test device. Builds uploaded to this track are available almost immediately (usually within minutes). You can have up to 100 testers on this track.

Use internal testing for day to day development and QA. Because builds are available quickly, you can iterate rapidly on billing changes without waiting for Google's review process.

Closed Testing Track

Closed testing requires you to create a tester list and manage who has access. Builds may take longer to be available compared to internal testing. You can create multiple closed testing tracks for different groups of testers.

Use closed testing for beta programs or when you want to test with a specific group of external users before a wider release.

Open Testing Track

Open testing makes your app available to anyone who wants to join the test. Users can find your test through the Play Store listing. This track goes through app review, similar to production releases.

Use open testing for large scale pre release testing where you want real user feedback before going to production.

For billing testing specifically, the internal testing track is almost always what you want. It gets builds onto devices fast, and combined with license tester accounts, gives you a complete test environment.

Play Billing Lab

Play Billing Lab is a companion app from Google that gives you fine grained control over billing test scenarios that would otherwise be difficult or impossible to reproduce. Install it from the Google Play Store on your test device.

Changing Play Country

By default, Google determines a user's country based on their IP address and account settings. This makes it difficult to test how your app behaves for users in different regions. Play Billing Lab lets you override the Play country on your test device, so you can see the exact prices, currencies, and available payment methods that users in that country would see.

To change the country, open Play Billing Lab, select your app, and choose a different country from the list. Your app will then receive product details with pricing localized to that country. This is essential for testing multi region pricing configurations and verifying that your UI handles different currencies and price formats correctly.

Testing Trial and Intro Offers Repeatedly

In production, a user can only redeem a free trial or introductory offer once per product. This is a problem for testing because after you use the trial on your test account, you cannot test the trial flow again without creating a new account.

Play Billing Lab removes this restriction. You can reset the trial and introductory offer eligibility for your test account, letting you test trial sign up flows as many times as you need. This is invaluable for verifying that your app correctly displays offer pricing, handles the transition from trial to paid, and manages cancellations during a trial period.

Testing Price Changes

When you change the price of a subscription in the Play Console, existing subscribers may need to opt in to the new price (for price increases) or are automatically moved to the new price (for price decreases). Testing this flow in production would require creating a subscription, waiting for a renewal period, and then changing the price.

Play Billing Lab lets you simulate price change scenarios directly. You can test both opt in required price increases and automatic price changes, verifying that your app shows the right messaging and handles the user's response correctly.

Accelerating Subscription State Transitions

One of the most powerful features of Play Billing Lab is the ability to manually trigger subscription state transitions. Instead of waiting for time based events to happen naturally (even with accelerated test timelines), you can force a subscription into specific states: renewal, grace period, account hold, pause, cancellation, and more.

This gives you deterministic control over testing. Rather than hoping the timing works out, you can put a subscription into exactly the state you want to test and verify your app's behavior immediately.

Accelerated Renewal Periods

Test subscriptions renew on a compressed schedule. This is automatic for all license tester purchases and does not require any configuration. The mapping is:

Production Period

Test Period

1 week

5 minutes

1 month

5 minutes

3 months

10 minutes

6 months

15 minutes

1 year

30 minutes

This means a monthly subscription that would take 30 days to renew in production renews every 5 minutes during testing. A yearly subscription renews every 30 minutes. This compression lets you test multiple renewal cycles in a single testing session.

The compressed schedule applies to all time based subscription events, not just renewals. Trial periods, grace periods, account holds, and paused states all run on the same accelerated timeline.

Maximum 6 Test Renewals

Test subscriptions do not renew indefinitely. After 6 renewals, Google automatically cancels the test subscription. This means you get the initial purchase plus 6 renewal events before the subscription expires.

Plan your testing around this limit. If you need to test behavior beyond 6 renewals, you will need to create a new test subscription. For most scenarios, 6 renewals is plenty to verify that your renewal handling, entitlement checks, and RTDN processing work correctly.

After the 6th renewal, the subscription enters the expired state as if the user had cancelled. Your app should handle this transition the same way it handles any other expiration.

Time Compression for Subscription States

Beyond renewal periods, Google also compresses the duration of intermediate subscription states during testing:

Subscription State

Test Duration

Free trial

3 minutes

Grace period

5 minutes

Account hold

10 minutes

Pause

Varies (compressed)

These compressed durations mean you can test an entire subscription lifecycle in minutes rather than days:

  1. A user starts a free trial (3 minutes in test).
  2. The trial converts to a paid subscription (immediate).
  3. A payment fails and the subscription enters grace period (5 minutes in test).
  4. Grace period expires without recovery and the subscription enters account hold (10 minutes in test).
  5. Account hold expires and the subscription is cancelled.

In production, that sequence would take a billing period plus 7 to 30 days depending on your grace period and account hold configuration. In testing, you can observe the entire flow in under 20 minutes.

11 Subscription Test Cases

Before shipping any billing integration to production, you should verify these 11 scenarios. Each one tests a distinct subscription state transition that real users will encounter.

1. New Subscription Purchase

What to test: A user buys a subscription for the first time.

Steps:

  1. Query products and verify that subscription details load correctly.
  2. Launch the billing flow with the "always approves" test card.
  3. Verify that PurchasesUpdatedListener receives the purchase with BillingResponseCode.OK.
  4. Acknowledge the purchase.
  5. Verify that queryPurchasesAsync returns the active subscription.
  6. Confirm your app grants the correct entitlement.

What can go wrong: Forgetting to acknowledge the purchase (it will be refunded in 3 minutes for test, 3 days in production). Granting the wrong entitlement level. Not handling the case where PurchasesUpdatedListener delivers the result after your activity has been destroyed.

2. Renewal

What to test: An active subscription renews successfully.

Steps:

  1. Start with an active test subscription.
  2. Wait for the accelerated renewal period (5 minutes for monthly).
  3. Verify that queryPurchasesAsync still returns the subscription as active.
  4. Check that your server receives an RTDN with SUBSCRIPTION_RENEWED notification type.
  5. Confirm the entitlement remains active.

What can go wrong: Your server not processing RTDNs correctly, leading to stale entitlement data. Your app not refreshing purchase state after renewal.

3. Grace Period Entry and Recovery

What to test: A payment fails at renewal, the subscription enters grace period, and the user fixes their payment method.

Steps:

  1. Start with an active test subscription.
  2. Use Play Billing Lab to trigger a payment failure at renewal.
  3. Verify the subscription enters grace period. During grace period, the user should still have access.
  4. Verify your app shows a message encouraging the user to fix their payment method.
  5. Fix the payment method (or use Play Billing Lab to recover).
  6. Verify the subscription returns to active state and the entitlement continues.

What can go wrong: Revoking access during grace period. Not notifying the user that their payment failed. Not re granting access after recovery.

4. Account Hold Entry and Recovery

What to test: A subscription enters account hold after grace period expires (or directly if you have not enabled grace period), and the user recovers.

Steps:

  1. Start with a subscription in grace period (or trigger a payment failure).
  2. Let the grace period expire (5 minutes in test).
  3. Verify the subscription enters account hold. During account hold, the user should not have access.
  4. Verify your app shows a message that their subscription is on hold.
  5. Simulate the user fixing their payment method.
  6. Verify the subscription reactivates and access is restored.

What can go wrong: Still granting access during account hold. Not providing a deep link to the Play Store subscription management page where the user can fix their payment. Not restoring access after recovery.

5. User Cancellation

What to test: A user cancels their subscription through Google Play.

Steps:

  1. Start with an active test subscription.
  2. Cancel the subscription through the Google Play Store app on the device (Settings > Payments & subscriptions > Subscriptions).
  3. Verify that the subscription remains active until the end of the current billing period.
  4. Verify your app shows that the subscription will not renew.
  5. After the billing period ends (accelerated in test), verify the entitlement is revoked.

What can go wrong: Revoking access immediately at cancellation instead of at period end. Not communicating to the user when their access will end. Not handling the expired state after the period ends.

6. Developer Cancellation

What to test: You (the developer) revoke a subscription through the Google Play Developer API.

Steps:

  1. Start with an active test subscription.
  2. Call the purchases.subscriptions.revoke or purchases.subscriptionsv2.revoke API endpoint from your server.
  3. Verify that the subscription is immediately revoked (unlike user cancellation, developer revocation takes effect right away).
  4. Verify your app revokes the entitlement promptly.

What can go wrong: Your app not checking for revocations frequently enough. Caching entitlements locally without server validation.

7. Pause and Resume

What to test: A user pauses their subscription and later resumes it.

Steps:

  1. Configure your subscription in the Play Console to allow pausing.
  2. Start with an active test subscription.
  3. Pause the subscription through the Play Store app.
  4. Verify that the subscription remains active until the current period ends, then enters the paused state.
  5. Verify your app revokes access when the paused state begins.
  6. Resume the subscription (through the Play Store app or by using Play Billing Lab).
  7. Verify the subscription reactivates and access is restored.

What can go wrong: Not supporting pause at all and confusing users who paused through the Play Store. Not restoring access after resume.

8. Upgrade and Downgrade with Each Replacement Mode

What to test: A user changes their subscription plan using each of the available replacement modes.

Steps:

  1. Start with an active subscription on one plan.
  2. For each replacement mode (WITH_TIME_PRORATION, CHARGE_PRORATED_PRICE, CHARGE_FULL_PRICE, WITHOUT_PRORATION, DEFERRED), launch a plan change flow.
  3. Verify that a new purchase token is generated for each plan change.
  4. Verify that the old purchase token is linked via linkedPurchaseToken.
  5. Verify that your backend correctly transfers the entitlement from the old token to the new one.
  6. Verify the financial behavior matches the replacement mode (immediate switch vs. deferred, prorated charge vs. full charge).

What can go wrong: Not handling the new purchase token. Not retiring the old purchase token, leading to duplicate entitlements. Using CHARGE_PRORATED_PRICE for a downgrade (it will fail).

9. Pending Transaction Completion

What to test: A purchase that requires additional action before completing (such as cash based payment methods or parental approval).

Steps:

  1. Enable pending transactions in your BillingClient setup.
  2. Make a purchase using the "slow test card, always approves" payment method.
  3. Verify that your PurchasesUpdatedListener receives a purchase with PENDING state.
  4. Verify your app does not grant the entitlement yet.
  5. Wait for the purchase to complete.
  6. Verify that PurchasesUpdatedListener fires again with the completed purchase.
  7. Acknowledge the purchase and grant the entitlement.

What can go wrong: Granting access for pending purchases. Not listening for the completion event. Not handling the case where a pending purchase is declined.

10. Restore After Cancellation

What to test: A user cancels their subscription but then re subscribes before the current period ends.

Steps:

  1. Start with an active subscription.
  2. Cancel it through the Play Store.
  3. Before the period ends, open the Play Store subscription management and tap "Resubscribe".
  4. Verify the subscription returns to active, auto renewing state.
  5. Verify no gap in entitlement access.

What can go wrong: Your app showing a "buy" flow instead of directing the user to the Play Store to restore. Misinterpreting the restore as a new purchase.

11. Resubscribe After Expiration

What to test: A user's subscription has fully expired and they purchase again.

Steps:

  1. Let a test subscription fully expire (cancel it and wait for the period to end).
  2. Purchase the same subscription again.
  3. Verify a new purchase token is issued.
  4. Verify your app grants a fresh entitlement.
  5. Verify your backend treats this as a new subscription, not a continuation of the old one.

What can go wrong: Trying to link the new purchase to the old one incorrectly. Not granting access because your system thinks the user already has an expired subscription for this product.

Testing Pending Transactions

Pending transactions deserve extra attention because they represent a fundamentally different purchase flow. In markets where cash based payment methods are common (Brazil, Japan, Indonesia, and others), a significant portion of your purchases may go through a pending state before completing.

To enable pending transactions in your BillingClient:

kotlin
val billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases(
        PendingPurchasesParams.newBuilder()
            .enableOneTimeProducts()
            .enablePrepaidPlans()
            .build()
    )
    .build()

When you test pending transactions, verify these specific behaviors:

Your app does not grant access for pending purchases. Check the purchase state before granting entitlements:

kotlin
if (purchase.purchaseState ==
    Purchase.PurchaseState.PURCHASED
) {
    // Grant entitlement and acknowledge
} else if (purchase.purchaseState ==
    Purchase.PurchaseState.PENDING
) {
    // Show "purchase pending" UI, no access yet
}

Your app updates when the purchase completes. The PurchasesUpdatedListener fires again when the pending purchase transitions to PURCHASED or is cancelled. Verify you handle both outcomes.

Your server handles pending RTDNs. If you use Real Time Developer Notifications, you receive a ONE_TIME_PRODUCT_PURCHASED or subscription notification when the purchase completes. Verify your server processes these correctly.

Your UI communicates the pending state clearly. Users need to know their purchase is not yet complete and what they need to do (for example, complete payment at a convenience store).

Testing Promo Codes

Promo codes let you give users free access to one time products or subscriptions. You create promo codes in the Google Play Console under Monetize > Promo codes.

To test promo code redemption:

  1. Create a promo code campaign in the Play Console for your product.
  2. Generate one or more codes.
  3. On your test device, open the Google Play Store app and go to the Redeem code section.
  4. Enter the promo code.
  5. Verify that your app receives the purchase through PurchasesUpdatedListener or queryPurchasesAsync.
  6. Verify that the purchase can be acknowledged and that your app grants the correct entitlement.

Promo code purchases behave like regular purchases from your app's perspective. The main difference is that the user does not go through your app's purchase flow. The purchase arrives via the Play Store, and your app discovers it the next time it queries purchases or receives an RTDN.

Test that your app handles this "out of band" purchase discovery. If your app only checks for purchases during its own purchase flow, promo code redemptions will be missed until the user restarts the app.

Testing in Different Regions

Billing behavior varies by region. Prices differ, available payment methods change, and some features (like pending transactions or specific offer types) may only apply in certain markets. You need to verify your app works correctly across your target regions.

Use Play Billing Lab to switch your test device's Play country and verify:

  • Prices display correctly. Different countries have different currencies and number formats. Verify your UI handles long currency symbols, right to left currencies, and prices with varying decimal precision.
  • Payment methods are appropriate. Some countries support carrier billing, cash payments, or region specific payment methods. Make sure your purchase flow does not break when users encounter these options.
  • Tax handling works. Google handles tax calculation, but your receipts and UI may need to display tax information differently depending on the region.
  • Product availability is correct. If you have restricted certain products to specific countries in the Play Console, verify that users in excluded countries do not see those products and that your app handles empty product lists gracefully.

You should test at minimum with one country from each of your major markets. If you sell globally, test with a US account, a European account (to verify EU specific requirements), and at least one account from a market where cash based payments are prevalent.

Testing with Real Payment Methods

Before your first production release, you should make at least one real purchase. Test purchases with license tester accounts cover most scenarios, but there are behaviors you can only verify with real money:

  • The actual payment flow. Real payment methods go through Google's payment processing, which can behave differently from test instruments. Card verification, 3D Secure prompts, and payment provider timeouts only happen with real transactions.
  • Refund processing. Request a refund through Google Play for your test purchase and verify that your app and server handle the refund notification correctly.
  • Receipt validation. Verify that your server can validate real purchase tokens against the Google Play Developer API and that the response matches what you expect.
  • Financial reporting. Check that the purchase appears in your Google Play Console financial reports with the correct amount, currency, and tax information.

To minimize cost, use your cheapest product for real payment testing. You can also refund yourself immediately after the purchase. Google processes refunds for self refunding within a few days, and you will only lose the small transaction fee.

One important note: real purchases require the app to be published on a track that the purchasing account has access to. An app only on the internal test track works, but the purchasing account must be part of the internal test track's tester list.

Testing BillingResult Response Codes with the Response Simulator

The Play Billing Library includes a response simulator that lets you force specific BillingResult response codes from any PBL method. This is invaluable for testing error handling paths that are difficult to trigger naturally.

To use the response simulator, you configure it through Play Billing Lab. You can set up rules that make specific API calls return specific response codes. For example, you can configure launchBillingFlow to return ITEM_ALREADY_OWNED, or acknowledgePurchase to return NETWORK_ERROR.

Here are the scenarios you should test with the response simulator:

SERVICE_DISCONNECTED during purchase. Force a disconnection and verify your app reconnects the BillingClient and retries the operation.

NETWORK_ERROR on acknowledgment. Force a network error when acknowledging a purchase and verify your app retries the acknowledgment. Remember, unacknowledged purchases are refunded after 3 days in production.

ITEM_ALREADY_OWNED when launching a purchase. This happens when the user already owns the product. Verify your app refreshes its purchase cache and shows appropriate messaging instead of displaying a confusing error.

USER_CANCELED during the billing flow. Verify your app returns gracefully to the previous screen without showing an error message. Cancellation is a normal user action, not an error.

BILLING_UNAVAILABLE on connection. This occurs when Google Play Billing is not available on the device (outdated Play Store, unsupported device, or restricted region). Verify your app hides or disables purchase options gracefully.

DEVELOPER_ERROR on launch. This means your parameters are wrong (mismatched product IDs, missing offer tokens). While you should catch these during development, verify your app does not crash and logs enough information for you to diagnose the issue.

ERROR (generic) on any operation. This is the catch all error. Verify your app shows a generic retry message and logs the debug message for troubleshooting.

Testing each of these response codes through the simulator takes minutes and saves you from shipping error handling code that has never actually been executed.

A Practical Testing Checklist

Bringing all of this together, here is a checklist you can follow before each release:

Setup:

  • License tester account added in Play Console.
  • App uploaded to internal test track.
  • Test device signed in with license tester account.
  • Play Billing Lab installed on test device.

Core Purchase Flows:

  • New one time product purchase.
  • New subscription purchase.
  • Subscription renewal (wait for accelerated cycle).
  • Upgrade with at least two replacement modes.
  • Downgrade with at least one replacement mode.

Subscription Lifecycle:

  • Grace period entry and recovery.
  • Account hold entry and recovery.
  • User cancellation with access until period end.
  • Restore before expiration.
  • Resubscribe after full expiration.
  • Pause and resume.

Edge Cases:

  • Pending transaction completion and decline.
  • Unacknowledged purchase auto refund (3 minute test window).
  • Promo code redemption.
  • Purchase on one device, verify entitlement on another.

Error Handling:

  • At least 4 response codes tested via response simulator.
  • Network disconnection during purchase flow.
  • App process killed during purchase and relaunched.

Regional:

  • At least one non default country tested via Play Billing Lab.
  • Currency formatting verified for target markets.

Want a simpler approach?

The RevenueCat SDK Handbook covers the same topics — with less code and a managed backend.

Related chapters

  • Chapter 6: The Purchase Flow

    End-to-end from launchBillingFlow() to acknowledgement: payment dialog, callbacks, pending purchases, and multi-quantity.

    Learn more
  • Chapter 11: The Subscription State Machine

    Seven states (ACTIVE through PENDING), every transition, which states grant access, and queryPurchasesAsync() behavior.

    Learn more
  • Chapter 8: Error Handling and Retry Strategies

    Every BillingResponseCode explained. Simple retry, exponential backoff, and the complete error decision tree.

    Learn more
Testing Your Integration | RevenueCat