Overview

HUAWEI In-App Purchases (IAP) aggregates multiple payment methods for global payments, and can be easily integrated into your app to help you increase revenue. Users can purchase a variety of products or services, including common virtual products and subscriptions, directly within your app. For details, please refer to HUAWEI IAP 5.0.

First of all, you need to understand three types of in-app products supported by HUAWEI IAP.

Type

Description

Example

Consumable

Consumables are used once, are depleted, and can be purchased again.

Extra lives and gems in a game

Non-consumable

Non-consumables are purchased once and do not expire.

Extra game levels in a game or permanent membership of an app

Auto-renewable subscriptions

Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.

Non-permanent membership of an app, such as a monthly video membership

You need to perform the following operations:

What You Will Create

In this codelab, you will create a demo project and use the APIs of HUAWEI IAP to simulate the process of purchasing gems in a game. You can explore the following processes in the demo project:

What You Will Learn

In this codelab, you will learn how to:

Hardware Requirements

Software Requirements

To integrate HUAWEI IAP, you must complete the following preparations:

For details, please refer to Preparations for Integrating HUAWEI HMS Core.

You need to enable HUAWEI IAP before using it.

  1. Sign in to AppGallery Connect and click My projects.
  2. Find your project and click the app for which you want to enable HUAWEI IAP.
  3. On the page that is displayed, go to Project settings > Manage APIs. Then enable In-App Purchases.
  4. Go to Earning > In-App Purchases from the navigation tree on the left and click Settings.If this is the first time you configure HUAWEI IAP, a dialog box will be displayed for you to sign the agreement.
  5. After the configuration is successful, the page displays the public key used for subsequent payment signature verification and a parameter for configuring the subscription notification URL.

Configure a consumable of 10 gems that users can purchase, consume, or purchase again. The configuration procedure is as follows:

  1. Sign in to AppGallery Connect and click My apps.
  2. Select the app for which you want to add a product.
  3. Click the Operate tab. Go to Products > Product Management from the navigation tree on the left, click the Products tab, and click Add Product.
  4. Configure product information and click Save.
  5. In the dialog box that is displayed, click OK to validate the product information.
    After the configuration is complete, the product is in a valid state in the product list.

The HMS Core SDK provides an entry for all external APIs of the IAP. For Android Studio, Huawei provides the HMS Core SDK that can be integrated by using the Maven repository. Before development, you need to add the HMS Core SDK to your Android Studio project. Once this is done, you can call the APIs encapsulated in the HMS Core SDK to connect to the IAP.

Obtaining the Configuration File

  1. Sign in to AppGallery Connect, click My projects, find your project, and click your app. On the page that is displayed, go to Project Setting > General information.
  2. In the App information area, click agconnect-services.json to download the configuration file.
  3. Copy the agconnect-services.json file to the app's root directory.

Adding Build Dependencies

  1. Open the build.gradle file in the app directory.
  2. Add build dependencies in the dependencies block.
    dependencies { implementation 'com.huawei.hms:iap:4.0.4.301' }
  3. Open the modified build.gradle file again. You will find a Sync Now link in the upper right corner of the page. Click Sync Now and wait until the synchronization is complete.

Configuring Obfuscation Scripts

Before building the APK, configure the obfuscation configuration file to prevent the HMS Core SDK from being obfuscated.

  1. Open the obfuscation configuration file proguard-rules.pro of your project.
  2. Add configurations to exclude the HMS Core SDK from obfuscation.
    -ignorewarning -keepattributes *Annotation* -keepattributes Exceptions -keepattributes InnerClasses -keepattributes Signature -keepattributes SourceFile,LineNumberTable -keep class com.hianalytics.android.**{*;} -keep class com.huawei.updatesdk.**{*;} -keep class com.huawei.hms.**{*;}

This section describes a payment process. You need to sign in with your HUAWEI ID on your phone before making a payment.

Step 1: Display product information.

Call the obtainProductInfo API to query the gem product configured in AppGallery Connect. If the API call is successful, the gem product is displayed on the phone based on the returned product information.
DemoActivity.java

/** * Load products information and show the products */ private void loadProduct() { listView = (ListView)findViewById(R.id.itemlist); // obtain in-app product details configured in AppGallery Connect, and then show the products IapClient iapClient = Iap.getIapClient(DemoActivity.this); Task<ProductInfoResult> task = iapClient.obtainProductInfo(createProductInfoReq()); task.addOnSuccessListener(new OnSuccessListener<ProductInfoResult>() { @Override public void onSuccess(ProductInfoResult result) { if (result != null && !result.getProductInfoList().isEmpty()) { showProduct(result.getProductInfoList()); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, e.getMessage()); Toast.makeText(DemoActivity.this, "error", Toast.LENGTH_SHORT).show(); } }); } private ProductInfoReq createProductInfoReq() { ProductInfoReq req = new ProductInfoReq(); // In-app product type contains: // 0: consumable // 1: non-consumable // 2: auto-renewable subscription req.setPriceType(IapClient.PriceType.INAPP_CONSUMABLE); ArrayList<String> productIds = new ArrayList<>(); // Pass in the item_productId list of products to be queried. // The product ID is the same as that set by a developer when configuring product information in AppGallery Connect. productIds.add("CProduct1"); req.setProductIds(productIds); return req; }

The product information page is similar to the following.

Step 2: Purchase the product.

A user can tap the gem icon on the page to purchase the product. This process uses the createPurchaseIntent API to complete the purchase.
1. Create a task for requesting the createPurchaseIntent API.
2. Set OnSuccessListener and OnFailureListener for the task to receive the API request result.

DemoActivity.java

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) { String productId = (String) products.get(pos).get(DemoActivity.this.item_productId); gotoPay(DemoActivity.this, productId, IapClient.PriceType.INAPP_CONSUMABLE); } }); /** * create orders for in-app products in the PMS. * @param activity indicates the activity object that initiates a request. * @param productId ID list of products to be queried. Each product ID must exist and be unique in the current app. * @param type In-app product type. */ private void gotoPay(final Activity activity, String productId, int type) { Log.i(TAG, "call createPurchaseIntent"); IapClient mClient = Iap.getIapClient(activity); Task<PurchaseIntentResult> task = mClient.createPurchaseIntent(createPurchaseIntentReq(type, productId)); task.addOnSuccessListener(new OnSuccessListener<PurchaseIntentResult>() { @Override public void onSuccess(PurchaseIntentResult result) { Log.i(TAG, "createPurchaseIntent, onSuccess"); if (result == null) { Log.e(TAG, "result is null"); return; } Status status = result.getStatus(); if (status == null) { Log.e(TAG, "status is null"); return; } // you should pull up the page to complete the payment process. if (status.hasResolution()) { try { status.startResolutionForResult(activity, REQ_CODE_BUY); } catch (IntentSender.SendIntentException exp) { Log.e(TAG, exp.getMessage()); } } else { Log.e(TAG, "intent is null"); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, e.getMessage()); Toast.makeText(activity, e.getMessage(), Toast.LENGTH_SHORT).show(); if (e instanceof IapApiException) { IapApiException apiException = (IapApiException)e; int returnCode = apiException.getStatusCode(); Log.e(TAG, "createPurchaseIntent, returnCode: " + returnCode); // handle error scenarios } else { // Other external errors } } }); } /** * Create a PurchaseIntentReq instance. * @param type In-app product type. * @param productId ID of the in-app product to be paid. * The in-app product ID is the product ID you set during in-app product configuration in AppGallery Connect. * @return PurchaseIntentReq */ private PurchaseIntentReq createPurchaseIntentReq(int type, String productId) { PurchaseIntentReq req = new PurchaseIntentReq(); req.setProductId(productId); req.setPriceType(type); req.setDeveloperPayload("test"); return req; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQ_CODE_BUY) { if (data == null) { Toast.makeText(this, "error", Toast.LENGTH_SHORT).show(); return; } PurchaseResultInfo purchaseResultInfo = Iap.getIapClient(this).parsePurchaseResultInfoFromIntent(data); switch(purchaseResultInfo.getReturnCode()) { case OrderStatusCode.ORDER_STATE_SUCCESS: // verify signature of payment results. boolean success = CipherUtil.doCheck(purchaseResultInfo.getInAppPurchaseData(), purchaseResultInfo.getInAppDataSignature(), Key.getPublicKey()); if (success) { // Call the consumeOwnedPurchase interface to consume it after successfully delivering the product to your user. consumeOwnedPurchase(this, purchaseResultInfo.getInAppPurchaseData()); } else { Toast.makeText(this, "Pay successful,sign failed", Toast.LENGTH_SHORT).show(); } return; case OrderStatusCode.ORDER_STATE_CANCEL: // The User cancels payment. Toast.makeText(this, "user cancel", Toast.LENGTH_SHORT).show(); return; case OrderStatusCode.ORDER_PRODUCT_OWNED: // The user has already owned the product. Toast.makeText(this, "you have owned the product", Toast.LENGTH_SHORT).show(); // you can check if the user has purchased the product and decide whether to provide goods // if the purchase is a consumable product, consuming the purchase and deliver product return; default: Toast.makeText(this, "Pay failed", Toast.LENGTH_SHORT).show(); break; } return; } }

The payment pages are similar to the following.

Step 3: Consume the purchased product.

After a user successfully purchases the gem product, it will be delivered to the user, and consumed using the consumeOwnedPurchase API once the delivery is successful. The user will then be able to purchase the product again.
DemoActivity.java

/** * Consume the unconsumed purchase with type 0 after successfully delivering the product, then the Huawei payment server will update the order status and the user can purchase the product again. * @param inAppPurchaseData JSON string that contains purchase order details. */ private void consumeOwnedPurchase(final Context context, String inAppPurchaseData) { Log.i(TAG, "call consumeOwnedPurchase"); IapClient mClient = Iap.getIapClient(context); Task<ConsumeOwnedPurchaseResult> task = mClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(inAppPurchaseData)); task.addOnSuccessListener(new OnSuccessListener<ConsumeOwnedPurchaseResult>() { @Override public void onSuccess(ConsumeOwnedPurchaseResult result) { // Consume success Log.i(TAG, "consumeOwnedPurchase success"); Toast.makeText(context, "Pay success, and the product has been delivered", Toast.LENGTH_SHORT).show(); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, e.getMessage()); Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); if (e instanceof IapApiException) { IapApiException apiException = (IapApiException)e; Status status = apiException.getStatus(); int returnCode = apiException.getStatusCode(); Log.e(TAG, "consumeOwnedPurchase fail,returnCode: " + returnCode); } else { // Other external errors } } }); } /** * Create a ConsumeOwnedPurchaseReq instance. * @param purchaseData JSON string that contains purchase order details. * @return ConsumeOwnedPurchaseReq */ private ConsumeOwnedPurchaseReq createConsumeOwnedPurchaseReq(String purchaseData) { ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq(); // Parse purchaseToken from InAppPurchaseData in JSON format. try { InAppPurchaseData inAppPurchaseData = new InAppPurchaseData(purchaseData); req.setPurchaseToken(inAppPurchaseData.getPurchaseToken()); } catch (JSONException e) { Log.e(TAG, "createConsumeOwnedPurchaseReq JSONExeption"); } return req; }

Well done. You have successfully completed this codelab and learned how to:

For more information, please click the following link:
Documentation

To download the sample code, please click the button below:

Download

Code copied