Google In-App Purchases

Overview

Accept payment 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 provide additional channels to monetize your app. Google Play allows for standard user-initated in-app purchases and autorenewing 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.

GoNative’s in-app purchase flow has these steps:

  1. Create IAP in Google Play Console website
  2. App gets list of purchasable items from your site and verifies with store
  3. Site shows UI for purchasable items
  4. User starts purchase.
  5. Purchase is verified and fulfilled

Implementation Guide

Once the premium module has been added to your app, you may use the following GoNative JavaScript Bridge commands to access its functionality.

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 an APK 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 “set up” 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.

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": ["member_basic_w", "member_basic_m"]
}

Host the JSON file on your web server and make it publicly accessible.

Now you are ready to enable and configure the module. You will need:
productsUrl - The productsUrl should point to the JSON file on your website. When your GoNative app launches, it will make an HTTP GET to your productsUrl.

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 called gonative_info_ready with the following object:

{
    "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 the productsUrl.
  • 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 code
  • title: from Google Play Console
  • description: from Google Play Console

You should create a page on your website that shows the available items for purchase. It should wait for the gonative_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.

️GoNative JavaScript Bridge

When a user decides to purchase an IAP, open the URL:

window.location.href = 'gonative://purchase/product_id';

The GoNative app will then start the in-app purchase flow.

The gonative://purchase/product_id call supports these additional query parameters for autorenewing 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).
    1. previousProductID is no longer accepted for purchase. Instead, use previousPurchaseToken. If this field is empty, the prorationMode is also ignored
    2. 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 the productID in our response data for compatibility support. Hence, the web component shall get both productID and productIDs for gonative_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.
window.location.href = 'gonative://purchase/annual_membership?previousPurchaseToken=aaaabbbbccccddddeeeeffff&prorationMode=IMMEDIATE_AND_CHARGE_PRORATED_PRICE';

On-device verification

On app launch, and after any purchases are made, the app will call a javascript function on your page named gonative_iap_purchases with a single object parameter. Here is an example object:

{
  "platform": "GooglePlay",
  "allPurchases": [
    {
      "orderId": "GPA.3309-4129-7588-25875",
      "packageName": "io.gonative.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 transaction
  • packageName: should match the packageName of your app
  • productID: the identifier for what was purchased
  • purchaseTime: milliseconds since the unix epoch (Jan 1 1970)
  • purchaseTimeString: formatted as a string
  • purchaseState: 0 – purchased, 1 – canceled, 2 – pending
  • purchaseString: “purchased”, “canceled”, “pending”, or “unknown”
  • acknowledged: indicates the app has confirmed the purchase with Google Play
  • autoRenewing: 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.

️GoNative 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:

window.location.href = 'gonative://iap/manageAllSubscriptions';

To allow the user to manage just your app’s subscription with the specified product ID, open the URL:

window.location.href = 'gonative://iap/manageSubscription?productID=PRODUCTID';

Testing process

Testing your in-app purchase flow requires your app be properly set up in Google Play to be distributed.

Your test devices must 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 GoNative-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.

References:
https://developer.android.com/google/play/billing/billing_overview

https://medium.com/bleeding-edge/testing-in-app-purchases-on-android-a6de74f78878