One-time purchase products have a simpler lifecycle than subscription products, but there are still several states and transition events that your backend needs to be able to handle properly.
New one-time product purchases
After the user completes the billing flow, your app can see information about the new purchase in one of the following ways:
- Setup
Real-time developer notifications
and enableGet all notifications for subscriptions and one-time products
to receive updates on the status of purchases. - Implement the
PurchasesUpdatedListener
interface fromBillingClient
to automatically receive purchase updates. - Call the
BillingClient.queryPurchasesAsync()
method.
After receiving the new purchase, use the
getPurchaseState
method or
purchases.products.get in Play Developer API
to determine the payment state of the new purchase.
Real-time developer notifications
When a user purchases or cancels the purchase of a one-time product,
Google Play sends a
OneTimeProductNotification
message. To update your backend purchase state, use the purchase token provided
in the OneTimeProductNotification
object to call the
purchases.products.get
method. This method provides the latest purchase and consumption status given a
purchase token.
You should handle transaction-related RTDNs in your secure backend.
Handle completed transactions
When a user completes a one-time product purchase, Google Play sends a
OneTimeProductNotification
message with the type ONE_TIME_PRODUCT_PURCHASED
.
When you receive this RTDN, process the purchase as described in Process
one-time product purchases in your backend.
Handle canceled transactions
When a one-time product purchase is canceled, Google Play sends a
OneTimeProductNotification
message with the type ONE_TIME_PRODUCT_CANCELED
if you have configured to receive real-time developer notifications.
For example, this can occur if the user doesn't complete payment within the
required timeframe, or if the purchase is revoked by the developer or by customer
request. When your backend server receives this notification, call the
purchases.products.get
method to get the latest purchase state, then update your backend accordingly,
including user entitlements.
If a one-time product purchase in Purchased
state gets refunded, you will also
be made aware via the Voided Purchases API.
Process one-time product purchases in your backend
Whether you have detected a new purchase via a ONE_TIME_PRODUCT_PURCHASED
RTDN
or you have been made aware in-app through
PurchasesUpdatedListener
or manually fetching purchases in your
app's onResume()
method, you must process the new purchase. We recommend that
you handle purchase processing in your backend for better security.
Follow these steps to process a new one-time purchase:
- Query the
purchases.products.get
endpoint to obtain the latest one-time product purchase status. To call this method for a purchase, you need the correspondingpurchaseToken
either from your app or from theONE_TIME_PRODUCT_PURCHASED
RTDN. - Call
getPurchaseState()
and make sure that the purchase state isPURCHASED
. - Verify the purchase.
- Give the user access to the content. The user account associated with the
purchase can be identified with the
obfuscatedExternalAccountId
field frompurchases.products.get
, if one was set usingsetObfuscatedAccountId()
when the purchase was made.- For non-consumable product purchases, acknowledge delivery of the content
by calling the
purchases.products.acknowledge
method. Make sure that the purchase hasn't been previously acknowledged by checking theacknowledgementState
field. - If the product is consumable, mark the item as consumed by calling the
purchases.products.consume
method so that the user can buy the item again after they have consumed it. This method also acknowledges the purchase.
- For non-consumable product purchases, acknowledge delivery of the content
by calling the
There are also purchase acknowledgement and consume methods available in the Play Billing Library that allow you to process purchases from you app, but we recommend that you handle processing in your backend if you have one for a more secure implementation.