Google In-App Purchases (Legacy)
Deprecation Warning
This document is deprecated and is provided for legacy purposes only. If you have just purchased this plugin and are looking to implement it for the first time, or are upgrading to the latest Google Play Billing Library to comply with Google's recommendation, please use the new and updated documentation here.
Overview
Accept payments within your app seamlessly using Google Play's Billing Library to facilitate transactions such as one-time purchase, subscription, or promo code redemption.
In-App Purchases (IAP) provide additional channels to monetize your app. Google Play allows for standard user-initated in-app purchases and auto-renewing subscription purchases.
Generally, if your app accepts payment for any digital goods or memberships, Google Play requires you to offer in-app purchases as an option for digital content, unless such content can also be consumed outside of the app. You are still free to implement other payment methods on your website separate from the mobile apps.
Median’s Google Play In-App Purchase flow includes these steps:
- Create In-App Purchase items in the Google Play Console website
- Host a JSON file on your website with a list of purchasable items
- Display a web UI listing purchasable items
- Initiate a purchase when requested by a user
- Verify and fulfil purchase through Google Play
- Provide users with subscription management
Once the premium module has been added to your app, you may use the following Median JavaScript Bridge commands to access its functionality.
Configure IAP in Google Play Console
Create your app in the Google Play Console. The package name of your app is a globally unique identifier that identifies your app on Google Play. Your account "claims" that package name the first time you upload your app to the Google Play Console. Make sure that any pages with grey checkmarks are completed so that the checkmark turns green. The app must be fully configured in Google Play before you can create and test in-app purchases.
In the Google Play Console, go to Store Presence -> In-App Products. Create either a Managed product (standard in-app purchase), or a Subscription. The product id will be used later in code to identify the item. Title and description are used in Google Play listings, and will also be passed to your app. If you wish to have different tiers of subscriptions (e.g. a monthly and annual subscription), they must be created as separate subscriptions.
Host a JSON file with available products
Create a JSON file that lists the product IDs you would like to offer for sale. This allows you to in real time add or remove products without publishing a new version of your app. The following example shows three products for sale: a regular in-app purchase, and two subscription products. The product IDs for each must be placed in the correct section: inappProducts
vs subProductions
.
{
"inappProducts": ["remove_ads"],
"subProducts": ["subscription_monthly", "subscription_annual"]
}
You must then host the JSON file on your website to be accessible by your app. We are using https://median.dev/iap/productsGoogle.json
App Configuration
Now you are ready to enable and configure the plugin within the Native Plugins tab of the App Studio. You will need to enter:
productsUrl
- The productsUrl
should point to the JSON file on your website. When your app launches, it will make an HTTP GET request to your productsUrl
.
Purchased Products and IAP Status
The app will verify the list of products with Google Play and retrieve each product's information. Then it will execute a JavaScript function on your website defined as median_info_ready
with a single object parameter. Alternatively you can use the Median JavaScript Bridge to obtain this data at runtime by calling the method median.iap.info()
.
↔️Median JavaScript Bridge
You define this function on your page, but do not actually call it.
If present on a page it will be called by the app when the page is loaded.function median_info_ready(inAppPurchases) { console.log(inAppPurchases); }
Or you may return the purchase data at runtime via a promise (in async function)
var purchaseData = await median.iap.info(); console.log(inAppPurchases);
In both cases
inAppPurchases
will be of the form:{ "inAppPurchases": { "platform": "GooglePlay", "products": [{ "productID": "remove_ads", "type": "inapp", "price": "$0.99", "price_amount_micros": 990000, "price_currency_code": "USD", "title": "Remove ads", "description": "Enjoy an ad-free experience" }, { "productID": "member_basic_w", "type": "subs", "price": "$0.99", "price_amount_micros": 990000, "price_currency_code": "USD", "subscriptionPeriod": "P1W", "title": "Weekly Membership", "description": "Access member content (autorenews weekly)" }] } }
Each product will have the following fields:
productID
: the identifier set in Google Play that matches those in theproductsUrl
.type
: "inapp" or "subs"price
: the price you should display to your user. It will be localized to their currency.price_amount_micros
: the price in micro-units, e.g. 1 USD = 1000000 micros.price_currency_code
: the 3-letter currency codetitle
: from Google Play Consoledescription
: from Google Play Console
Display web UI for purchases
Create a page on your website that shows the available items for purchase. It should wait for the median_info_ready
function to be called and then populate the items for purchase. The price must be shown using the price
string, as different users may have different language and currency settings.
Initiate purchase
↔️Median JavaScript Bridge
When a user decides to purchase an IAP, run the JavaScript function:
median.iap.purchase({'productID': 'product_id'});
The Median app will then start the in-app purchase flow.
The
median.iap.purchase({'productID': 'product_id'});
call supports these additional parameters for auto-renewing subscriptions:
previousProductID
– the product ID we are replacing. This is required to support subscription upgrades and downgrades (i.e. converting a monthly subscription to annual, or changing a subscription tier from basic to premium membership).
previousProductID
is no longer accepted for purchase. Instead, usepreviousPurchaseToken
. If this field is empty, theprorationMode
is also ignored- The history/details of purchase now contain a list of Product IDs. Like how the library kept the
productID
field (which is the first item of the list), we also kept theproductID
in our response data for compatibility support. Hence, the web component shall get bothproductID
andproductID
s formedian_iap_purchases
prorationMode
– determines how to credit the user for a mid-period subscription change. Valid values include:
IMMEDIATE_WITH_TIME_PRORATION
– Replacement takes effect immediately, and the remaining time will be prorated and credited to the user. This is the current default behavior.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
- Replacement takes effect immediately, and the billing cycle remains the same. The price for the remaining period will be charged. This option is only available for subscription upgrade.IMMEDIATE_WITHOUT_PRORATION
– Replacement takes effect immediately, and the new price will be charged on next recurrence time. The billing cycle stays the same.DEFERRED
- Replacement takes effect when the old plan expires, and the new price will be charged at the same time.median.iap.purchase({'productID': 'annual_membership', 'previousPurchaseToken': 'aaaabbbbccccddddeeeeffff', 'prorationMode': 'IMMEDIATE_AND_CHARGE_PRORATED_PRICE'});
Purchase verification and fulfillment
On app launch, and after any purchases are made, the app will call a JavaScript function on your page named median_iap_purchases
with a single object parameter. Here is an example object:
{
"platform": "GooglePlay",
"allPurchases": [
{
"orderId": "GPA.3309-4129-7588-25875",
"packageName": "io.median.android",
"productID": "member_basic_w",
"purchaseTime": 1567462252415,
"purchaseState": 0,
"purchaseToken": "fffpnbenegliokdcifadiihi.AO-J1OzGRezs5VkyKoyhYb-HgLEVG5XxswFLcLOyAnyy48sQPii2Yf6JYJe-Hm44FZT7ctkkkTlhRat15hoBWMnwXPzSgzlnCaYOvFRI_Yk5bzrBLXwOW-Mad1j9NsdoYkNywrOmJDzJ",
"autoRenewing": true,
"acknowledged": true,
"purchaseTimeString": "2019-09-02T22:10:52.415Z",
"purchaseStateString": "purchased"
}
]
}
Each purchase item in the allPurchases
array may have these fields:
orderId
: identifies the purchase transactionpackageName
: should match thepackageName
of your appproductID
: the identifier for what was purchasedpurchaseTime
: milliseconds since the unix epoch (Jan 1 1970)purchaseTimeString
: formatted as a stringpurchaseState
: 0 – purchased, 1 – canceled, 2 – pendingpurchaseString
: “purchased”, “canceled”, “pending”, or “unknown”acknowledged
: indicates the app has confirmed the purchase with Google PlayautoRenewing
: indicates the purchase will autorenew. Note that this will be set to false if the user cancels their subscription.purchaseToken
: a string that can be used to verify the purchase with Google.
The allPurchases
array will list all current subscriptions. Any expired subscriptions will not longer appear.
Subscription Management
You may wish to provide links for your users to manage their subscriptions.
↔️Median JavaScript Bridge
To open the Google Play page that lists all subscriptions for all of the user’s apps (not just your app), open the URL:
median.iap.manageAllSubscriptions();
To allow the user to manage just your app’s subscription with the specified product ID, open the URL:
median.iap.manageSubscription({'productID': 'product_id'});
Testing process
Testing your in-app purchase flow requires your app be properly set up in Google Play to be distributed.
Your test devices (including emulators) must have Google Play Store installed and be signed into a Gmail or Google Apps for Business account. You may use your day-to-day account. In your Developer Account -> Account details, go to the License Testing section. Add the email addresses for the test accounts.
On your app’s management page, create an internal test track. Add the test user email to a user list. Once added, each test user go to the Opt-in URL (looks like https://play.google.com/apps/internaltest/1234321234...
) and accept the Opt-In. Create a release build (or use the Median-built apk) and upload it to the internal test track. The test users should then be able to find the app in the Google Play Store on their devices and install it.
Subsequent releases to the internal test track will immediately be available to the test devices by checking for new updates in the Google Play Store app.
Any in-app purchases can be tested without actual payment being exchanged. Auto-renewing subscriptions will renew every 5 minutes until they are canceled.
Google Play Billing References
https://developer.android.com/google/play/billing/billing_overview
https://medium.com/bleeding-edge/testing-in-app-purchases-on-android-a6de74f78878
Updated about 2 months ago