highRevenueCat Blog·February 2, 2026

Handling edge cases in Google Play Billing

Google Play Billing provides a comprehensive API surface for handling in-app purchases and subscriptions on Android. Most developers are comfortable with the standard purchase flow: launch the billing flow, receive a result, acknowledge the purchase, and grant entitlements. But production billing systems must handle a wider range of scenarios that are often underrepresented in tutorials and sample code. Pending purchases, multi-quantity consumables, subscription downgrades with proration, and the ITEM_ALREADY_OWNED response are all situations your app will encounter in the real world, and mishandling any of them can result in lost revenue, confused users, or failed purchases.

In this article, we’ll explore the most common edge cases in Google Play Billing, understand why they occur, examine how to handle each one correctly with the Play Billing Library, and see how RevenueCat simplifies these scenarios so you can focus on your product instead of billing infrastructure.

The fundamental problem: the happy path is not enough

Most billing implementations start from the sample code in the Android documentation:

This handles a successful, immediate purchase. But what happens when the payment is delayed by 48 hours because the user is paying at a convenience store? What happens when the user already owns the item because a previous acknowledgment failed silently? What happens when a subscription downgrade takes effect at the next renewal instead of immediately? Each of these scenarios requires specific handling, and ignoring them leads to support tickets, refund requests, and lost subscribers.

Pending purchases: when payment is not immediate

Not all purchases complete instantly. Certain payment methods, including cash payments at convenience stores, bank transfers, and some carrier billing options, require asynchronous processing. When a user initiates a purchase with one of these methods, Google Play returns a purchase in the PENDING state rather than the PURCHASED state.

Why pending purchases happen

Pending purchases are common in markets where credit card penetration is low:

Payment methodCommon regionsTypical processing time
Cash payments (convenience stores)Japan, Mexico, Indonesia24-48 hours
Bank transfersGermany, Netherlands, Brazil1-3 business days
Carrier billing (some carriers)VariousMinutes to hours

If your app is available globally, you’re bound to encounter pending purchases. Ignoring this state means users in these regions cannot purchase your products at all — or worse, they see confusing behavior where their purchase ‘disappears’.

Detecting and handling the pending state

The PurchasesUpdatedListener receives pending purchases alongside completed ones. The critical distinction is in the purchaseState field:

The key rule is: do not grant entitlements for pending purchases. The user has not paid yet. Instead, record the pending purchase and communicate the status clearly:

Completing a pending purchase

When the payment is eventually confirmed, your app receives an updated purchase via onPurchasesUpdated or through queryPurchasesAsync. The purchaseState will now be PURCHASED, and you can proceed with acknowledgment and entitlement granting.

However, there is a subtlety: the user might not have your app open when the payment completes. Your backend should handle this through Real-Time Developer Notifications (RTDN). When you receive a ONE_TIME_PRODUCT_PURCHASED or SUBSCRIPTION_PURCHASED notification for a previously pending token, your backend should update the entitlement and notify the user:

</

Key Insights

1

Edge cases like pending purchases, subscription downgrades with proration, and ITEM_ALREADY_OWNED responses are common in production and require proper handling

2

Mishandling Google Play Billing edge cases results in lost revenue, user confusion, and failed purchases

3

RevenueCat simplifies complex billing edge case handling for developers

Handling edge cases in Google Play Billing | ASO News