Overview

Huawei's In-App Purchases (IAP) service integrates multiple payment methods for global payment and allows you to easily offer in-app purchases. Users can purchase a variety of products or services, including common virtual products and subscriptions, directly within your app. For details, please refer to IAP Service 4.0.

Before using the IAP service, you need to learn about the types of in-app products supported by the 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 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

Hardware Requirements

Software Requirements

To integrate HUAWEI HMS Core services, you must complete the following preparations:

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

You need to enable the IAP service before using it.
1. Sign in to AppGallery Connect and select My apps.

2. Select the app for which the service is to be enabled. The app information page is displayed.

3. Click the Develop tab, click the icon next to Overview, and select Manage APIs.

4. Enable In-App Purchase.

5. On the Develop tab page, go to Earning > In-App Purchases from the navigation tree on the left and click Settings. If this is the first time you configure the IAP service, a dialog box is displayed for you to sign the agreement.

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 select My apps.

2. Select the app for which you want to add a product.

3. Click the Operate tab. Go to Product Operation > Product Management from the navigation tree on the left, click the Products tab, and click Add Product.

4. Configure the 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 SDK provides an entry for all external APIs of the IAP. For Android Studio, Huawei provides the HMS SDK that can be integrated by using the Maven repository. Before development, you need to integrate the HMS SDK into your Android Studio project. Once this is done, you can call the APIs encapsulated in the HMS SDK to connect to the IAP.

Obtaining the Configuration File

Adding Build Dependencies

Configuring Obfuscation Scripts

Before building the APK, configure obfuscation scripts to prevent the HMS SDK from being obfuscated. If obfuscation arises, the HMS SDK may not function properly.

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 click 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.
  3. ● A PurchaseIntentResult object is returned in the success scenario. You need to bring up the returned page to complete payment. The payment result is called back in onActivityResult. In onActivityResult, use the parsePurchaseResultInfoFromIntent method of IapClient to obtain the PurchaseResultInfo object, which contains the payment result. If the payment is successful, signature verification must be performed on the returned data using the public payment key based on Verifying the Signature in the Returned Result. After successful verification, the product is delivered and consumed. If the payment fails, returnCode is returned. Rectify the fault by referring to Troubleshooting and Common Result Codes.

    ● An Exception object is returned in the failure scenario. If the object is an IapApiException object, use getStatusCode to obtain the result code. For details about the fault rectification, please refer to Troubleshooting and Common Result Codes.

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, you need to call the consumeOwnedPurchase API to consume the gem product after successfully delivering the gems in the product to the user. 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:

You can read the links as follow for more information.
Documentation

Download the demo source code used in this codelab from the following address:

Download source code

Code copied