This topic describes how to handle subscription lifecycle events, such as
renewals and expirations. It also describes additional subscription features
such as offering promotions and allowing your users to manage their own
subscriptions.
If you haven't configured subscription products for your app, see
Create and configure your products
.
Subscriptions overview
A
subscription
represents a set of benefits users can access during
a specified time period. For example, a subscription might entitle a user
to access a music streaming service.
You can have multiple subscriptions within the same app, either to
represent different sets of benefits, or different tiers of a single
set of benefits ("Silver" and "Gold" tiers, for example).
Through
base plans
and
offers
, you can create multiple configurations
for the same subscription product. For example, you can create an
introductory offer for users who have never subscribed to your app. Similarly,
you can create an upgrade offer for users who are already subscribed.
For a detailed overview of subscription products, base plans, and offers,
see the documentation in the
Play Console Help Center
.
Prepaid plans integration
Prepaid plans
do not
automatically renew upon expiration. To extend their
subscription entitlement without interruption, the user must
top-up
a
prepaid plan for the same subscription.
For top-ups, launch the billing flow as you would with the original
purchase. You do not need to indicate that a purchase is a top-up.
Prepaid plan top-ups always use the
CHARGE_FULL_PRICE
replacement mode, and you don't need to set this mode explicitly.
The user is immediately charged for a full billing period,
and their entitlement is extended by the duration specified in the top-up.
After a top-up, the following fields in the
Purchase
result object are updated to reflect the most recent top-up purchase:
- Order id
- Purchase time
- Signature
- Purchase token
- Acknowledged
The following
Purchase
fields always contain the same data found in
the original purchase:
- Package name
- Purchase state
- Products
- Auto renewing
Prepaid purchase acknowledgement
Similar to auto-renewing subscriptions, you must acknowledge prepaid plans
after purchase. Both the initial purchase and any top-ups need to be
acknowledged. For more information, see
Processing purchases
.
Due to the potential for short prepaid plan durations, it is important to
acknowledge the purchase as soon as possible.
Prepaid plans with a duration of one week or longer must be acknowledged
within three days.
Prepaid plans with a duration shorter than one week must be acknowledged
within half the plan duration. For example, developers have 1.5 days to
acknowledge a three-day prepaid plan.
Use deep links to allow users to manage a subscription
Your app should include a link on a settings or preferences screen that allows
users to manage their subscriptions, which you can incorporate into your app's
natural look and feel.
You can include a deep link from your app to the Google Play subscriptions
center for non-expired subscriptions, which you can determine using the
subscriptionState
field of the
subscription resource
.
Based on this, there are several ways you can deep link into the Play
Store subscriptions center.
Link to the subscriptions center
Use the following URL to direct users to the page that shows all of their
subscriptions, as shown in figures 1 and 2:
https://play.google.com/store/account/subscriptions
This deep link could be useful to help a user restore a canceled subscription
from the Play Store subscriptions center.
Link to a specific subscription management page (recommended)
To directly link to the management page for a non-expired subscription, indicate
the package name and
productId
associated with the purchased subscription. To
programmatically determine the
productId
for an existing subscription, query
your app's backend or call
BillingClient.queryPurchasesAsync()
for a list
of subscriptions associated with a particular user. Each subscription contains
the corresponding
productId
as part of the subscription status information.
Each
SubscriptionPurchaseLineItem
object associated with a
subscription purchase contains the
productId
value associated with the
subscription that the user purchased in that line item.
Use the following URL to direct users to a specific subscription management
screen, replacing "your-sub-product-id" and "your-app-package" with the
productId
and app package name respectively:
https://play.google.com/store/account/subscriptions?sku=your-sub-product-id&package=your-app-package
The user is then able to manage their payment methods and access features
including cancellation, resubscription, and pause.
Allow users to upgrade, downgrade, or change their subscription
You can provide existing subscribers with various options to change their
subscription plan to better meet their needs:
- If you sell multiple subscription tiers, such as "basic" and "premium"
subscriptions, you can allow users to switch tiers by purchasing a different
subscription's base plan or offer.
- You can allow users to change their current billing period, such as switching
from a monthly to an annual plan.
- You can also allow users to switch between auto-renewing and prepaid plans.
You can encourage any of these changes by providing subscription offers to
provide a discount to eligible users. For example, you could create an offer
providing a 50% discount on the first year when switching from a monthly to an
annual plan, and limit this offer to users subscribed to a monthly
plan who haven't purchased this offer. More information on offer eligibility
criteria is available
in the Help Center
Figure 3 shows an example app with three different plans:
Your app could show a screen similar to figure 3, giving users options to change
their subscription. In all cases, it should be clear to users what their current
subscription plan is, and what options they have for changing it.
When users decide to upgrade, downgrade, or change their subscription, you
specify a
replacement mode
that determines how the prorated value of the
current paid billing period is applied, and when any entitlement change occurs.
Replacement modes
The following table lists available replacement modes and example usage.
Replacement mode
|
Description
|
Example usage
|
WITH_TIME_PRORATION
|
The subscription is upgraded or downgraded immediately. Any time remaining is adjusted based on the price difference, and credited toward the new subscription by pushing forward the next billing date. This is the default behavior.
|
Upgrade to a more expensive tier, without any immediate additional payment.
|
CHARGE_PRORATED_PRICE
|
The subscription is upgraded immediately, and the billing cycle remains the same. The price difference for the remaining period is then charged to the user.
Note:
This option is available only for a
subscription upgrade, where the price per unit of time
increases.
|
Upgrade to a more expensive tier, without changing the billing date.
|
CHARGE_FULL_PRICE
|
The subscription is upgraded or downgraded immediately, and the user is
charged full price for the new entitlement immediately. The remaining value
from the previous subscription is either carried over for the same
entitlement, or prorated for time when switching to a different entitlement.
Note:
If the new subscription has a free trial or
introductory offer, the user is charged $0 or the price of the introductory
offer, whichever applies, at the time of upgrade or downgrade.
|
Upgrade from shorter to longer billing period.
|
WITHOUT_PRORATION
|
The subscription is upgraded or downgraded immediately, and the new price is charged when the subscription renews. The billing cycle remains the same.
|
Upgrade to a higher subscription tier while retaining any remaining free period.
|
DEFERRED
|
The subscription is upgraded or downgraded only when the subscription renews, but the new purchase is issued immediately with a start date in the future for the new entitlement, so the developer can allow users to make additional changes if they want. For example, they can revert to the original plan or initiate a new deferred plan change.
|
Downgrade to a less expensive tier.
|
To learn more about different upsell and winback applications of
upgrade or downgrade offers, read the offers and promotions guide.
Set the replacement mode for a purchase
You can use different replacement modes for different types of subscription
transitions, based on your preferences and business logic. This section explains
how to set a replacement mode for a change in a subscription and the limitations
that apply.
Resubscribe or switch plans within the same subscription
You can specify a default replacement mode in the Google Play Console. This
setting lets you choose when to charge current subscribers if they purchase
a different base plan or offer for the same subscription or resubscribe after a
cancellation. The available options are
Charge immediately
, equivalent to
CHARGE_FULL_PRICE
, and
Charge at the next billing date
, equivalent to
WITHOUT_PRORATION
. These are the only relevant replacement modes when
switching base plans within the same subscription.
For example, if you are implementing a winback offer for the same plan after the
user cancels but before the subscription ends, you can process the new purchase
as a regular purchase without indicating any values in
SubscriptionUpdateParams
. The system uses the default replacement mode you
configured in the subscription and automatically handles the plan transition
from the old purchase to the new purchase.
Switch plans across subscriptions, or override the default replacement mode
If the user is changing subscription
products?purchasing
a different
subscription?or if you want to override the default replacement mode for any
reason, you specify the proration rate
at runtime
as part of the purchase flow
parameters.
To correctly provide
SubscriptionUpdateParams
as part of your runtime purchase
flow configuration, note the following restrictions:
- When upgrading, downgrading, or doing a same-subscription switch
to
a
prepaid plan
from
either a prepaid plan or an auto-renewing plan, the only
allowed proration mode is
CHARGE_FULL_PRICE
. If you specify any other
proration mode, the purchase fails and an error is shown to the user.
- When switching plans within the same subscription
to
an auto-renewing plan
from
either a prepaid plan or an auto-renewing plan, valid proration modes
are
CHARGE_FULL_PRICE
and
WITHOUT_PRORATION
. If you specify any other
proration mode, the purchase fails and an error is shown to the user.
Replacement examples and behaviors
To understand how each proration mode works, consider the following scenario:
Samwise has a subscription to online content from the Country Gardener app. He
has a monthly subscription to the
Tier 1
version of the content,
which is text-only. This subscription costs him
$2 per month
, and it renews
on the first of the month.
On April 15, Samwise chose to upgrade to the annual version of the
Tier 2
subscription, which includes video updates and costs
$36 per year
.
When upgrading the subscription, the developer selects a proration mode. The
following list describes how each proration mode affects Samwise's subscription:
WITH_TIME_PRORATION
Samwise's
Tier 1
subscription ends immediately. Because he paid for a full
month (April 1-30) but upgraded halfway through the subscription period, half of
a month's subscription ($1) is applied to his new subscription. However, because
that new subscription costs $36 per year, the $1 credit balance pays for only 10
days (April 16-25); so on April 26, he is charged $36 for a new subscription and
another $36 on April 26th of each year following.
You should call your app's
PurchasesUpdatedListener
the moment the
purchase succeeds, and you are able to retrieve the new purchase as part of a
queryPurchasesAsync()
call. Your backend immediately receives a
SUBSCRIPTION_PURCHASED
Real Time Developer Notification.
CHARGE_PRORATED_PRICE
This mode can be used because the
Tier 2
subscription price per time unit
($36/year = $3/month) is greater than the
Tier 1
subscription price per time
unit ($2/month). Samwise's
Tier 1
subscription ends immediately. Because he
paid for a full month but used only half of it, half of a month's subscription
($1) is applied to his new subscription. However, because that new subscription
costs $36/year, the remaining 15 days costs $1.50; so he is charged the
difference of $0.50 for his new subscription. On May 1st, Samwise is charged $36
for his new subscription tier and another $36 on May 1 of each year following.
You should call your app's
PurchasesUpdatedListener
the moment the
purchase succeeds, and you are able to retrieve the new purchase as part of
a
queryPurchasesAsync()
call. Your backend immediately receives a
SUBSCRIPTION_PURCHASED
Real Time Developer Notification.
WITHOUT_PRORATION
Samwise's
Tier 1
subscription is immediately upgraded to
Tier 2
with no
extra charge, and on May 1st he is charged $36 for his new subscription tier and
another $36 on May 1 of each year following.
You should call your app's
PurchasesUpdatedListener
the moment the
purchase succeeds, and you are able to retrieve the new purchase as part of
a
queryPurchasesAsync()
call. Your backend immediately receives a
SUBSCRIPTION_PURCHASED
Real Time Developer Notification.
DEFERRED
Samwise's
Tier 1
subscription continues until it expires on April 30. On May
1st, the
Tier 2
subscription takes effect, and Samwise is charged $36 for
his new subscription tier.
You should call your app's
PurchasesUpdatedListener
the moment the
purchase succeeds, and you are able to retrieve the new purchase as part of
a
queryPurchasesAsync()
call. Your backend immediately receives a
SUBSCRIPTION_PURCHASED
Real Time Developer Notification. You should
process the purchase
the same way you would process any other new purchase
at that point. In particular, make sure you acknowledge the new purchase. Note
that the
startTime
of the new subscription is populated at
the moment the replacement is effective, which happens when the old
subscription expires. At that point, you receive a
SUBSCRIPTION_RENEWED
RTDN for the new subscription plan. Read more about the
ReplacementMode.DEFERRED
behavior in
Handle deferred replacement
.
CHARGE_FULL_PRICE
Samwise's
Tier 1
subscription ends immediately. His
Tier 2
subscription
begins today and he is charged $36. Because he paid for a full month but used
only half of it, half of a month's subscription ($1) is applied to his new
subscription. Because that new subscription costs $36/year, he would get 1/36th
of a year added on to his subscription period (~10 days). Therefore, Samwise's
next charge would be 1 year and 10 days from today for $36. After that, he is
charged $36 each year following.
When choosing a proration mode, be sure to review our
replacement recommendations
.
Trigger subscription changes in-app
Your app can offer users an upgrade or downgrade using the same steps as with
launching a purchase flow
. However, when upgrading or downgrading, you
need to provide details for the current subscription, the future (upgraded or
downgraded) subscription, and the replacement mode to use, as shown in the
following example:
Kotlin
val offerToken = productDetails
.getSubscriptionOfferDetails(selectedOfferIndex)
.getOfferToken()
val billingParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setOfferToken(offerToken)
.build()
)
).setSubscriptionUpdateParams(
BillingFlowParams.SubscriptionUpdateParams.newBuilder()
.setOldPurchaseToken("old_purchase_token")
.setSubscriptionReplacementMode(
BillingFlowParams.ReplacementMode.CHARGE_FULL_PRICE
)
.build()
).build()
billingClient.launchBillingFlow(
activity,
billingParams
)
// ...
Java
String offerToken = productDetails
.getSubscriptionOfferDetails(selectedOfferIndex)
.getOfferToken();
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
ImmuableList.of(
ProductDetailsParams.newBuilder()
// fetched via queryProductDetailsAsync
.setProductDetails(productDetails)
// offerToken can be found in
// ProductDetails=>SubscriptionOfferDetails
.setOfferToken(offerToken)
.build()))
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder()
// purchaseToken can be found in Purchase#getPurchaseToken
.setOldPurchaseToken("old_purchase_token")
.setSubscriptionReplacementMode(ReplacementMode.CHARGE_FULL_PRICE)
.build())
.build();
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
// ...
Replacement recommendations
The following table shows diferrent proration scenarios along with what we
recommend for each scenario:
Scenario
|
Recommended replacement mode
|
Result
|
Upgrading to a more expensive tier
|
CHARGE_PRORATED_PRICE
|
The user receives access immediately while keeping the same billing
period.
|
Downgrading to a less expensive tier
|
DEFERRED
|
The user already paid for the more expensive tier, so they keep
access until the next billing date.
|
Upgrading while in a free trial, keeping the trial
|
WITHOUT_PRORATION
|
The user keeps free trial access, but upgrades to a higher tier for
the remainder of the trial.
|
Upgrading while in a free trial - ending access to the free trial
|
CHARGE_PRORATED_PRICE
|
The user receives access to the new tier immediately but no longer
has a free trial.
|
Handle subscription change purchases
Changes of plan are new purchases for all terms and purposes, and they should be
processed and acknowledged as such after the billing flow completes
successfully. In addition to processing the new purchase appropriately, you
have to retire the purchase that is being replaced.
The in-app behavior is the same as for any new purchase. Your app receives the
outcome of the new purchase in your
PurchasesUpdatedListener
, and the
new purchase is available in
queryPurchasesAsync
.
The Google Play Developer API returns a
linkedPurchaseToken
in the
subscription resource
when a purchase replaces an existing
one. Be sure to invalidate the token provided in the
linkedPurchaseToken
to
ensure that the old token is not used to gain access to your services. See
Upgrades, downgrades, and resignups
for information on handling upgrade
and downgrade purchases.
When you receive the new purchase token, follow the same verification process as
with
verifying a new purchase token
. Make sure to acknowledge these
purchases with
BillingClient.acknowledgePurchase()
from the Google Play
Billing Library or
Purchases.subscriptions:acknowledge
from
the Google Play Developer API.
Handle deferred replacement
Deferred replacement mode lets you let a user use up the remaining
entitlement in their old plan before starting on the new plan.
When you use
ReplacementMode.DEFERRED
for a new purchase,
queryPurchasesAsync()
returns a new purchase token after the purchase
flow that remains associated with the old product until the deferred replacement
takes place on the next renewal date, after which the new product is
returned.
In the past you could achieve this user experience with the deprecated
ProrationMode.DEFERRED
, but
ProrationMode.DEFERRED
is deprecated with Play
Billing Library 6. See the following table to understand where the behavior
differs:
Time
|
ProrationMode.DEFERRED (deprecated)
|
ReplacementMode.DEFERRED
|
Right after the purchase flow succeeds (app)
|
PurchasesUpdatedListener
is invoked after purchase with a status of whether the upgrade or downgrade was successful.
Entitlement to the old plan continues until the next renewal date. To ensure that the app gives the right entitlement,
queryPurchasesAsync()
returns a Purchase object with the
original
purchase token and the
original
entitlement until replacement occurs.
The new purchase token is not surfaced, so it can't be processed at this point.
|
PurchasesUpdatedListener
gets invoked after purchase with a status of whether the upgrade or downgrade was successful.
queryPurchasesAsync()
returns purchase with the
new
purchase token right away, and the
original entitlement
associated with it.
The new purchase token is surfaced, so it should be
processed
at this point taking into account when the replacement is to take place.
|
Right after the purchase flow succeeds (backend)
|
SUBSCRIPTION_PURCHASED RTDN is
not
sent after the purchase flow. The backend is not made aware of the new purchase yet.
|
SUBSCRIPTION_PURCHASED RTDN with the old product_id is sent immediately after the purchase flow for the new purchase token.
Calling the
purchases.subscriptionsv2.get
method with the new purchase token returns a purchase having a 'startTime' indicating the purchase time with
two line items
:
- One representing the
old
entitlement and has an 'expiryTime' in the future. The old entitlement will not be renewed and has a
DeferredItemReplacement
containing the product of the
new
entitlement. This indicates a pending replacement of the old entitlement upon its expiration.
- One representing the
newly purchased
entitlement. It has no value set for 'expiryTime'.
SUBSCRIPTION_EXPIRED sent for the
old
purchase token. When calling the
purchases.subscriptionsv2.get
method with the
old
purchase token, it appears as expired (the entitlement for the old plan is transferred to the new purchase for the remaining time).
|
On replacement - first renewal after the purchase flow (app)
|
queryPurchasesAsync()
returns a new Purchase object with the
new
purchase token and entitlement.
The new purchase token is now surfaced, so it should be
processed
.
|
queryPurchasesAsync()
returns purchase with the
new
purchase token right away, and the
new entitlement
associated with it.
The new purchase should have been processed already when the purchase flow succeeded, so the app shouldn't take any special action apart from making sure the right entitlement is granted.
|
On replacement - first renewal after the purchase flow (backend)
|
The new purchase can now be processed and acknowledged when the first SUBSCRIPTION_RENEWED RTDN is sent.
The
linkedPurchaseToken
in the subscription resource can be used to determine which user in your subscription backend, if applicable, should be updated with the new entitlement.
|
New purchase was processed and acknowledged when the SUBSCRIPTION_PURCHASED RTDN was sent for the new purchase token and recorded as the 'startTime'.
With ReplacementMode.DEFERRED, first renewals follow the standard behavior of any other renewal and you don't need to handle special logic for replacements when this event happens.
When calling the
purchases.subscriptionsv2.get
method with the new purchase token returns a purchase with
two line items
:
- One representing the
old
entitlement, with an `expiryTime` in the past and no set value for
DeferredItemReplacement
.
- One representing the
new
entitlement, with an `expiryTime` in the future and the auto_renewing_enabled flag turned on.
|
ReplacementMode.DEFERRED should be used from now on instead of the deprecated
ProrationMode.DEFERRED, as it presents the same behavior regarding entitlement
changes, but offers a way to manage the purchase that is more consistent with
behaviors for other new purchases.
Customer management
Using Real-time developer notifications, you can detect in real time when a
user decides to cancel. When a user cancels, but before their subscription
has expired, you can send them push notifications or in-app messages to ask
them to resubscribe.
After a user has cancelled their subscription, you can try to win them back
either in your app, or through the Play store. The following table describes
various subscription scenarios along with associated winback actions and
app requirements.
|
Before subscription expiration
|
After subscription expiration
|
|
In-app
|
In Play Store
|
In-app
|
In Play Store
|
Winback feature
|
In-app subscription
|
Restore
|
In-app subscription
|
Resubscribe
|
User goes through checkout flow
|
Yes
|
No
|
Yes
|
Yes
|
User subscription remains associated with the same SKU
|
User can sign up for same or different SKU
|
Yes
|
User can sign up for same or different SKU
|
Yes
|
Creates new purchase token
|
Yes
|
No
|
Yes
|
Yes
|
Enabled by default
|
No
|
Yes, support required for all devs
|
No
|
Apps without Billing Library 2.0+: No
Apps with Billing Library 2.0+: Yes. Devs can opt-out in Console.
|
When user is charged
|
If using same SKU: end of current billing period.
If using different SKU: depends on proration mode.
|
End of current billing period
|
Immediately
|
Immediately
|
Implementation required
|
Provide
a re-signup UI in your app
|
Detect change in subscription state
Deep-link to Play Store
|
Provide a re-signup UI in your app
|
Handle out-of-app purchases
|
Before subscription expiration - in-app
For subscriptions that have been canceled but have not yet expired, you can
allow subscribers to restore their subscription within your app by applying the
same in-app product purchase flow as for new subscribers. Ensure your UI
reflects that the user has an existing subscription. For example, you might
want to display the user's current expiration date and recurring price with
a
Reactivate
button.
Most of the time, you will want to offer the user the same price and SKU they
were already subscribed to, as follows:
- Initiate a new subscription purchase with the same SKU.
- The new subscription replaces the old one and renews on the same expiration
date. The old subscription is immediately marked as expired.
- As an example, Achilles has a subscription to Example Music App, and the
subscription is due to expire on August 1. On July 10, he resubscribes to
the one-month subscription at the same price per month. The new subscription
is prorated with the remaining credit, is immediately active, and still
renews on August 1.
If you would like to offer a different price?for example a new free trial or
a winback discount?you can instead offer a different SKU to the user:
- Initiate an
upgrade or downgrade
with the different SKU
using the replacement mode
WITHOUT_PRORATION
.
- The new subscription replaces the old one and renews on the same expiration
date. The user is charged the price of the new SKU, including any
introductory prices, on the original expiration date. If the old subscription
was created using an obfuscated account id, that same id should be passed
to the
BillingFlowParams
for upgrades and downgrades.
- As an example, Achilles has a subscription to Example Music App, and the
subscription is due to expire on August 1. On July 10, he resubscribes to
an annual subscription with an introductory price. The new subscription
is immediately active, and the user is charged the introductory price on
August 1.
- If you decide to include a free trial or intro price in your winback SKU,
ensure that the user is eligible by unchecking the
Allow one free trial per app
box in the Google Play Console, which
restricts the user to getting one free trial per app.
When you receive the purchase token,
process the purchase
just as you
would with a new subscription. Additionally, the Google Play Developer API
returns a
linkedPurchaseToken
in the subscription resource. Be sure to
invalidate the token
provided
in the
linkedPurchaseToken
to ensure that the old token is not used to
gain access to your services.
Before subscription expiration - in Play Store
While the subscription is canceled but still active, users can restore the
subscription in the Google Play subscriptions center by clicking
Resubscribe
(previously
Restore
). This keeps the same subscription
and purchase token.
For more information on restoring subscriptions, see
Restorations
.
After subscription expiration - in-app
You can allow expired subscribers to resubscribe within your app by applying
the same in-app product purchase flow as for new subscribers. Note the
following:
- To offer users a discount, you might want to offer a product ID with
special pricing for your subscription, also called a
winback SKU
.
You can provide the offer in your app, or you can notify the user of
the offer outside of the app, such as in email.
- To start a winback subscription, launch the purchase flow in your
Android app using the Google Play Billing Library. This is the same
process as with a new subscription, but you can determine the SKU
that is available to the user.
- If you decide to include a free trial or intro price in your winback
SKU, ensure that the user is eligible by unchecking the
Allow one free trial per app
box in the Google Play Console, which
restricts the user to getting one free trial per app.
- If the user resubscribes to the same SKU, they are no longer eligible
for free trials or introductory price. Ensure that your UI reflects this.
When you receive the purchase token,
process the purchase
just as you
would with a new subscription. You will not receive a
linkedPurchaseToken
in the subscription resource.
After subscription expiration - in Play Store
If enabled, users can resubscribe to the same SKU for up to one year after
expiration by clicking
Resubscribe
in the Google Play subscriptions
center. This generates a new subscription and purchase token.
Resubscribing is considered an out-of-app purchase, so be sure to
follow best practices for
handling purchases made from outside your app
.
You can create promotion codes to give selected users an extended free trial
to an existing subscription. To learn more, see
Promo codes
.
For free trials, Google Play verifies that the user has a valid payment method
before starting the free trial. Some users may see this verification
as a hold or charge on their payment method. This hold or charge is
temporary and is later reversed or refunded.
After the trial period ends, the user's payment method is charged for
the full subscription amount.
If a user cancels a subscription at any time during the free trial,
the subscription remains active until the end of the trial, and they aren't
charged when the free trial period ends.
Cancel, refund, or revoke
You can use the
Google Play Developer API
to
cancel
,
refund
,
or
revoke
a subscription. This functionality is also available in the
Google Play Console
.
- Cancel
: Users can cancel a subscription on Google Play. You can also
provide an option for users to cancel in your app or on your website. Your
app should handle these cancellations as described in
Cancellations
.
- Refund
: When you refund, the user can continue to use the subscription.
Refunds can be used if, for example, there was a technical error that
prevented the user from accessing your product, but the error has been
resolved. Note that to refund more than the most recent payment, or if you
want to issue a partial refund, you must use the Google Play Console.
- Revoke
: When you revoke, the user immediately loses access to the
subscription. This can be used if, for example, there was a technical
error that prevented the user from accessing your product, and the user
does not want to continue using the product. Your app should handle these
cancellations as described in
Revocations
.
The following table illustrates the differences between cancel, refund, and
revoke.
|
Stops renewal
|
Refund money
|
Revoke access
|
Cancel
|
Yes
|
No
|
No
|
Refund
|
No
|
Yes
|
No
|
Revoke
|
Yes
|
Yes
|
Yes
|
Defer billing for a subscriber
You can advance the next billing date for an auto-renewing subscriber by using
Purchases.subscriptions:defer
from the Google Play Developer API. During the deferral period, the user is
subscribed to your content with full access but is not charged. The
subscription renewal date is updated to reflect the new date.
For prepaid plans, you can use the defer billing API to defer the expiration
time.
Deferred billing allows you to do the following:
- Give users free access as a special offer, such as giving one week free for
purchasing a movie.
- Give free access to customers as a gesture of goodwill.
Billing can be deferred by as little as one day and by as long as one year
per API call. To defer the billing even further, you can call the API again
before the new billing date arrives.
As an example, Darcy has a monthly subscription to online content for the
Fishing Quarterly app. She is normally billed £1.25 on the first of each
month. In March, she participated in an online survey for the app publisher.
The publisher rewards her with six free weeks by deferring the next payment
until May 15, which is six weeks after her previously scheduled billing date
of April 1. Darcy is not charged for April or the beginning of May and still
has access to the content. On May 15, she is charged the normal £1.25
subscription fee for the month. Her next renewal date is now June 15.
When deferring, you might want to notify the user by email or within the app
to notify them that their billing date has changed.
Handling payment declines
If there are payment issues with a subscription renewal, Google will
periodically attempt to renew the subscription for some time before canceling.
This recovery period can consists of a grace period, followed by an account hold
period. During this time, Google sends the user emails and notifications
prompting them to update their payment method.
Upon payment decline, the subscription enters a
grace
period
if one is
configured. During the grace period, you should ensure the user still has access
to the subscription entitlements.
After any grace period has ended, the subscription enters an
account
hold
period. During
account hold, you should ensure the user does not have access to the
subscription entitlements.
You can specify the length of each auto-renewing base plan's grace period and
account hold in the Google Play Console. Specifying lengths less than the
default values may reduce the number of subscriptions recovered from payment
declines.
To maximize the likelihood of subscription recovery during a payment decline,
you can inform your user of a payment issue and ask them to fix it.
You can either do this yourself, as described in the
grace
period
and
account hold
sections, or you can implement the in-app messaging API, where Google shows
a message to users in your app.
In-app messaging
If you've enabled in-app messaging with
InAppMessageCategoryId.TRANSACTIONAL
,
Google Play will show users messaging during grace period and account hold once
per day and provide them an opportunity to fix their payment without leaving the app.
We recommend that you call this API whenever the user opens the app to determine
whether the message should be shown.
If the user successfully recovered their subscription, you will receive a response code of
SUBSCRIPTION_STATUS_UPDATED
along with a purchase token. You should then use this purchase token to call the
Google Play Developer API and refresh the subscription status in your app.
Integrate in-app messaging
To show in-app messaging to user, use
BillingClient.showInAppMessages()
.
Here is an example of triggering the in-app messaging flow:
Kotlin
val inAppMessageParams = InAppMessageParams.newBuilder()
.addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
.build()
billingClient.showInAppMessages(activity,
inAppMessageParams,
object : InAppMessageResponseListener() {
override fun onInAppMessageResponse(inAppMessageResult: InAppMessageResult) {
if (inAppMessageResult.responseCode == InAppMessageResponseCode.NO_ACTION_NEEDED) {
// The flow has finished and there is no action needed from developers.
} else if (inAppMessageResult.responseCode
== InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
// The subscription status changed. For example, a subscription
// has been recovered from a suspend state. Developers should
// expect the purchase token to be returned with this response
// code and use the purchase token with the Google Play
// Developer API.
}
}
})
Java
InAppMessageParams inAppMessageParams = InAppMessageParams.newBuilder()
.addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
.build();
billingClient.showInAppMessages(activity,
inAppMessageParams,
new InAppMessageResponseListener() {
@Override
public void onInAppMessageResponse(InAppMessageResult inAppMessageResult) {
if (inAppMessageResult.responseCode
== InAppMessageResponseCode.NO_ACTION_NEEDED) {
// The flow has finished and there is no action needed from developers.
} else if (inAppMessageResult.responseCode
== InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
// The subscription status changed. For example, a subscription
// has been recovered from a suspend state. Developers should
// expect the purchase token to be returned with this response
// code and use the purchase token with the Google Play
// Developer API.
}
}
});