Library Integration

This section explains how to integrate the now.gg Payments module with your development environment.

This document features various code samples based on the official now.gg sample apps and games. You can check the in-line references throughout the document to better understand and implement now.gg Payments module.


Add SDK Libraries

Start by downloading the now.gg SDK package that contains the Payments libraries and add them to your development environment by following the steps below:

Note: The now.gg SDK package contains the Payments module, demo app, and sample code.

1. Extract the compressed now.gg Payments module and locate the .aar file in the package:

 Payments.aar

2. Add the following dependency to the build.gradle file of your game:

dependencies {
         implementation fileTree(dir: 'libs', include: ['*.aar'])
 }


Using the Billing Client

Purchase Lifecycle

The Purchase Lifecycle flow for now.gg Payments is as follows:

  • List the available products that the user can buy.
  • Purchase flow initiation for the user to buy the listed in-app products.
  • Processing the purchase:
    • Purchase verification.
    • Delivery of product/content to the user.
    • Marking purchase as consumed/delivered to allow re-purchase of the product.

Understanding Some Terms

now.gg Payments uses Purchase Tokens and Order IDs to track the products and transactions within your game or App.

Purchase Tokens
  • Purchase Token(s) represent the buyer’s ownership/entitlement of a product.
  • They are associated with a specific product and related product SKU.
  • Purchase Token(s) is only generated after a successful in-app purchase by the user.
  • In the case of a one-time purchase product, a new purchase token is generated for every
    transaction.
Order IDs
  • An Order ID is a reference to a financial transaction with the now.gg Billing Service.
  • Order IDs are used as references to track purchases, refunds, or any disputes.
  • Unique Order IDs are created for every financial transaction.
Entitlement

The term entitlement or granting of entitlement is used to signify ‘delivery of in-app product’ to the user after a product purchase.

Error Handling
The errors and responses encountered within your app/game are categorized using Billing Response codes.
Example: A SERVICE_DISCONNECTED error code suggests the connection between the now.gg payments service, and your game is disconnected. The solution to this is to reinitialize your app/game to re-establish the connection with now.gg payments service.

1. Create and Initialize a BillingClient

The BillingClient interface acts as the primary communication mechanism between the now.gg Payments module and your app/game. It is responsible for providing both synchronous and asynchronous methods for general billing operations.

BillingClient Creation                       --> Use newBuilder()

 Setup Updates of Purchase                    --> Call setListener()

 Receive updates throughout app/game          --> PurchasesUpdatedListener

 Set APP ID for billing                       --> use setAppId(APP_ID)

 Set IN_GAME_ID to use existing in-game login --> use setInGameId(IN_GAME_ID)
References

Integration Flows

We have provided two workflows for you to integrate now.gg Payments module. You may implement either one of them based on your requirements.

A. Using now.gg Login

In case the game integrates now.gg Login, we recommend this approach. The following workflow explains this approach:

  • The Payments module utilizes now.gg Login for user identification and the purchases are linked to the user’s now.gg account.
  • If a payment is initiated and now.gg Login is not present, the Payments module initiates now.gg Login.
B. Using existing in-game Login

In case the game does not integrate now.gg Login, we recommend this approach. The following workflow explains this approach:

  • The game does not integrate now.gg Login and just passes the user’s existing in-game user ID IN_GAME_ID to now.gg Payments module to identify the user and process the purchase.

A. Using now.gg Login

With this flow, you can create and initialize the BillingClient, use now.gg Login to identify the user making the purchase and process the product delivery after purchase.

The following sample code illustrates this flow to create and initialize the BillingClient.

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(int billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
 };

 private BillingClient billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .setAppId(APP_ID)
    .build();
private val purchasesUpdatedListener =
    PurchasesUpdatedListener { billingResult, purchases ->
        // To be implemented in a later section.
    }

 private var billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .setAppId(APP_ID)
    .build()

Important Information

  • APP_ID is a unique identifier for your app.
  • The corresponding APP_ID can be found on the nowStudio, within the credentials tab under your app details – more information.
  • The APP_ID will be available only after you have added your app/game to nowStudio.

B. Using Existing in-game Login

With this flow, you can create and initialize the BillingClient, use your existing in-game login to identify the user and process the product delivery after purchase.

To implement your existing in-game login with now.gg Payments module, you should pass the existing in-game user ID (IN_GAME_ID) while creating and initializing the BillingClient, as illustrated in the following sample code:

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(int billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
 };

 private BillingClient billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .setAppId(APP_ID)
    .setInGameId(IN_GAME_ID) // unique ID to identify the user.
    .build();
private val purchasesUpdatedListener =
    PurchasesUpdatedListener { billingResult, purchases ->
        // To be implemented in a later section.
    }

 private var billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .setAppId(APP_ID)
    .setInGameId(IN_GAME_ID) // unique ID to identify the user.
    .build()

Important Information:

  • IN_GAME_ID is a unique identifier for your user.
    • Please ensure that your IN_GAME_ID is unique for every user.
  • Whenever IN_GAME_ID is passed while initializing the BillingClient, now.gg login will not be presented to the user. Instead, the user will go directly through the payment flow.
  • Refund Processing (IN_GAME_ID Flow)– To process a refund for an order, please follow the process illustrated here.

2. Establish Connection

now.gg Payments service has an asynchronous connection process and requires you to implement a BillingClientStateListener to capture callback on completion of client setup.

  • To establish a connection with now.gg Payments Service, you need to call startConnection().
  • To handle lost connections, you need to define a retry logic. The retry logic implementation will require you to override the callback method. onBillingServiceDisconnected().
  • While overriding the callback method onBillingServiceDisconnected(), ensure that your BillingClient is calling the startConnection() method to re-establish connection with now.gg Payments Service.
  • Maintain the connection with the BillingClient before executing any methods.

Note: You may choose to write your own retry logic. However, we have also provided a retry logic that you may choose to implement.

References

Example

billingClient.startConnection(new BillingClientStateListener() { 
         @Override 
     public void onBillingSetupFinished(int billingResult) { 
             if (billingResult ==  0) { 
                 // The BillingClient is ready. You can query purchases here. 
         } 
     } 
     @Override 
     public void onBillingServiceDisconnected() { 
             // Try to restart the connection on the next request to 
         // Payments module by calling the startConnection() method. 
     } 
 });
billingClient.startConnection(object: BillingClientStateListener { 
    override fun onBillingSetupFinished(billingResult: int) { 
         if (billingResult == 0) { 
             // The BillingClient is ready. You can query purchases here. 
         } 
     } 
     override fun onBillingServiceDisconnected() { 
         // Try to restart the connection on the next request to 
         // Payments module by calling the startConnection() method. 
     } 
 });


Products and Purchases

This section walks you through the process of querying and listing the available in-app products in your app/game and handling the purchases.

The steps for this flow are:

  1. List Available Products.
  2. Purchase Flow Initiation.
  3. Processing the Purchases.

Before you begin, check that you have:

1. List Available Products

The first step here is to query for available products and list them to your users. To perform a query for in-app product details, you need to call querySkuDetailsAsync(). When you query for SKU details, the now.gg Payments module will return localized product information to be displayed to your users.

Note: Before displaying the in-app products to the user, you should query the SKU details, as it returns updated and relevant product information.

SKU Types and Order Strings

Below are the SKUType associated with now.gg Payments:

  • SkuType.SUBS – Subscriptions (coming soon)
  • SkuType.INAPP – in-app purchases

While you call querySkuDetailsAsync(), remember to pass an instance of SkuDetailsParams, which specifies the SKUType along with the product ID that you configured in the nowStudio.

Results

After querying for available products, you must assign a listener to capture the results of the Asynchronous operation you passed.

The following example will showcase the implementation of a SkuDetailsResponseListener interface and will allow you to override the onSkuDetailsResponse(), to notify the listener when the query is finished.

References
List<String> skuList = new ArrayList<> (); 
 skuList.add("premium_upgrade"); 
 skuList.add("gas"); 
 SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); 
 params.setSkusList(skuList).setType(SkuType.INAPP); 
 billingClient.querySkuDetailsAsync(params.build(), 
     new SkuDetailsResponseListener() { 
             @Override 
         public void onSkuDetailsResponse(int billingResult, 
                 List<SkuDetails> skuDetailsList) { 
                 // Process the result. 
         } 
     });
fun querySkuDetails() { 
 val skuList = ArrayList<String>() 
 skuList.add("premium_upgrade") 
 skuList.add("gas") 
 val params = SkuDetailsParams.newBuilder() 
 params.setSkusList(skuList).setType(SkuType.INAPP) 
    withContext(Dispatchers.IO) { 
            billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList -> 
            // Result Processing 
        } 
    } 
 }

More Information

  • A user can re-purchase a consumable item only after the first purchase order has been completed and the product is delivered to the user.
  • The SkuDetails object can be called using various methods to list information about the in-app products.
  • now.gg Payments module stores all the query results in a list of SkuDetails object.

2. Purchase Flow Initiation

After you have listed the available products for the user to buy, you must initiate the purchase flow from the app/game.

To start the purchase flow, you will need to call the launchBillingFlow() method from the App’s main thread.

Here, the BillingFlowParams object is called for a reference, as it contains relevant details stored within the SkuDetails object. The SkuDetails object is obtained by calling querySkuDetailsAsync().

Creating the BillingFlowParams object

To start the purchase flow, you need to create the BillingFlowParams object using the BillingFlowParams.Builder class. Use the following code snippets as reference:

References:
// An activity reference for billing flow initiation 
 Activity activity = ...; 
      
 // Call querySkuDetailsAsync() to retrieve a value for 'skuDetails'. 
 BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() 
    .setSkuDetails(skuDetails) 
    .build(); 
 billingClient.launchBillingFlow(activity, billingFlowParams); 
      
     //Result
// An activity reference for billing flow initiation 
 val activity : Activity = ...; 
  
 // Call querySkuDetailsAsync() to retrive a value for 'skuDetails'. 
 val flowParams = BillingFlowParams.newBuilder() 
         .setSkuDetails(skuDetails) 
         .build() 
 billingClient.launchBillingFlow(activity, flowParams)
 //Result
Result Assessment

You can use BillingClient.BillingResponse as a reference for the return response codes.

  • A response code OK (0) indicates a successful launch.
  • A Purchase Screen is displayed by the system after a successful call to launchBillingFlow().
  • now.gg Payments module calls onPurchasesUpdated() to communicate the purchase result to a listener that implements the PurchasesUpdatedListener interface.
  • This listener is specified while you initialize the billing client using the setListener() method.

The following example outlines the process to override the onPurchasesUpdated() method:

@Override 
 void onPurchasesUpdated(int billingResult, List<Purchase>) { 
     if (billingResult == 0 && purchases != null) { 
         for (Purchase purchase : purchases) { 
             handlePurchase(purchase); 
         } 
     } else if (billingResult == 1) { 
             // Error Handling - Caused due to user terminating the purchase flow 
     } else { 
             // Error Handling - Any other causes (based on error code) 
     } 
 }
override fun onPurchasesUpdated(billingResult: int, purchases: List<Purchase>?) { 
     if (billingResult == 0 && purchases != null) { 
         for (purchase in purchases) { 
                 handlePurchase(purchase)
         } 
     } else if (billingResult == 1) { 
             // Error Handling - Caused due to the user terminating the purchase flow. 
     } else { 
             // Error Handling - Any other causes (based on error code) 
     } 
 }

Note: The onPurchasesUpdated() method should be implemented to handle possible response codes.

References:

More Information

  • A Purchase Token is generated after a successful purchase.
    • The purchase token is also a Unique Identifier representing the in-app purchased product and the corresponding user who made the purchase.
  • An order ID is generated for every subsequent order and can be used for any disputes or refund references with the now.gg Payments Service.
  • A copy of every purchase with the order id and other relevant information is also emailed to the user.

3. Processing the Purchases

This section focuses on processing the purchases. After you have started the purchase flow, you are required to process the purchases in the following manner:

  1. Purchase Verification
  2. Granting Entitlement
  3. Purchase Results
  4. Get Unconsumed Purchases

1. Purchase Verification

Verifying the purchase enables you to establish the authenticity of a purchase. The best practice for reducing fraud is to authenticate every purchase before assigning the product to the user.

You can verify the purchases using either of the following two methods:

  • Using Verify Payment API.
  • Using a Public key.
A. Using VerifyPayment API

The first method to verify the purchase is using verifyPayment API. You can use this API to verify the purchases with our backend server.

To verify the purchase, call the verifyPayment API from your app backend server with purchaseToken and orderId, as illustrated in the following sample request code:

curl --location 'https://payments.now.gg/v1/console/order/verifyPayment'\
      --header 'publisherToken: <your_publisherToken_here>'\
      --header 'Content-Type: application/json'\
      --data '{
  "purchaseToken": "<your_purchaseToken_here>",
  "orderId": "<your_orderId_here>"
  }'
import requests
 import json

 url = "https://payments.now.gg/v1/console/order/verifyPayment"

 payload = json.dumps(
     {
         "purchaseToken": "<your_purchaseToken_here>",
         "orderId": "<your_orderId_here>",
     }
 )
 headers = {
     "publisherToken": "<your_publisherToken_here>",
     "Content-Type": "application/json",
 }

 response = requests.request("POST", url, headers=headers, data=payload)

 print(response.text)

Important Information

  • The verifyPayment API must be called from your app backend server.
  • The publisherToken can be found under the Account Information section of nowStudio. (More Information).
  • The purchaseToken and orderId are returned after a successful purchase. You can refer to the following reference documents for more information:

B. Using Your Public Key

The second method to verify the purchase is using a public key. An implementation for verifying purchases using the public key has been illustrated in the demo’s billingManager.java.

  • Open billingManager.java
  • Locate the verifyValidSignature function and then locate verifyPurchase.

By referring to the implementation using the above-mentioned functions in our demo’s billingManager.java, you can implement the purchase verification in your App.

Note:

  • You can generate your public key by following the steps listed here.
  • If your app has a backend server, we recommend that you validate the purchase on your backend server.
  • It is recommended to authenticate every purchase before granting the entitlement of the product to the user.

2. Granting Entitlement

After you have verified the purchase, your App can now grant the entitlement of the purchased product to the user and mark the product as consumed.

To grant the entitlement, call the consumeAsync() method. This method will grant the entitlement of a product to the user and make the product available for re-purchase once it has been consumed/delivered to the user.

Using consumeAsync():

  • Include the purchase token with consumeAsync() to update the status of the purchased product toconsumed, allowing the now.gg billing service to make it available for re-purchase.
  • To update the result of the consumption operation, you should pass an object that implements the ConsumeResponseListener interface.
  • When the consumption operation is complete, now.gg Billing Service calls onConsumeResponse(), which you can override.

Important Information:

  • You should keep a check on multiple entitlement grants for the same purchase using purchase tokens. This may occur if the consumption request fails and the object that updates the ConsumeResponseListener interface is not updated.

The following code snippet demonstrates purchase verification and consumption operation:

References:
void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
    Purchase purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant the entitlement to the user.

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(int billingResult, String purchaseToken) {
            if (billingResult == 0) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(purchase.getPurchaseToken(), listener);
}
fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
    val purchase: Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant the entitlement to the user.


    billingClient.consumeAsync(purchase.getPurchaseToken(), {
        billingResult,
        outToken - >
        if (billingResult == 0) {
            // Handle the success of the consume operation.
        }
    })
}

3. Purchase Results

The PurchasesUpdatedListener provides you with the purchase results. However, in some scenarios, your app/game might not be updated with all the successful purchases a user has made.

Some of these scenarios include:

  • Multiple devices
    In this scenario, the user purchased an item on one device and wants to use it on another device where the purchased product is not updated.
  • Connectivity/Network loss
    The app/game may fail to receive a successful purchase notification from PurchasesUpdatedListener, due to connectivity/network loss.
  • Purchases made outside the App
    In this scenario, the purchases made outside your app/game need to be handled for the user to be able to use them.

To successfully process purchases related to the scenarios listed above:

  • In your onResume() and onCreate() methods, make sure that your app calls BillingClient.queryPurchases(SkuType.INAPP).

4. Getting Unconsumed Purchases

You can use the queryPurchases method to return the list of unconsumed purchases for a user, as illustrated below:

Purchase.PurchasesResult result = BillingClient.queryPurchases(BillingClient.SkuType.INAPP);
 if (result.getResponseCode() == BillingClient.BillingResponse.OK) {
     List < Purchase > purchases = result.getPurchasesList();
 }
val result = BillingClient.queryPurchases(BillingClient.SkuType.INAPP)
 if (result.responseCode == BillingClient.BillingResponse.OK) {
     val purchases = result.purchasesList
 }

Important Information

×
Text copied to clipboard
Link copied to clipbord
Questions? Please reach out to us at dev-support@now.gg