简介

华为应用内支付(In-App Purchase,IAP)服务聚合了多种支付通道,支持全球支付,为应用提供便捷的应用内支付体验,且接入流程简便,可以帮助您快速获取收入。通过应用内支付,用户可以在您的应用内购买各种类型的商品或服务,包括普通虚拟商品和订阅商品服务。具体可参阅支付服务HMS3.0

在此之前,您需要了解IAP支持的几种商品类型:

商品定义

描述

示例

消耗商品

仅能使用一次,消耗使用后即刻失效,需再次购买。

游戏中额外生命、钻石等

非消耗商品

一次性购买,永久拥有,无需消耗。

游戏中额外的游戏关卡或应用中无时限的高级会员

订阅商品

用户购买后在一段时间内允许访问增值功能或内容,周期结束后自动续期购买下一期的服务。

应用中有时限的高级会员,如视频月度会员等

开发者需要,

您将建立什么

在这个Codelab中,您将创建一个demo,通过调用华为支付服务的API来模拟游戏中购买宝石的过程。在Demo Project中您可以体验到:

您将会学到什么

硬件要求

软件要求

集成HUAWEI HMS能力,需要完成以下准备工作

具体操作,请按照《HMS Core 3.0集成准备》中详细说明来完成。

在接入IAP之前应用需要开通支付服务。
1、使用已注册的企业账号登录 AppGallery Connect 网站 ,选择"我的应用"。

2、选择需要开通服务的应用所属产品的名称,进入应用信息页面。
3、点击"开发"页签,点击"概览"后的图标,选择"API管理"。

4、打开In-App Purchase所在行的开关。

5、在"开发"页签,左侧导航选择"商业服务 > IAP",点击"设置"。
如果首次配置会弹出签署协议弹框。

配置一个商品,表示10个宝石,类型为消耗型商品,购买完成后通过执行消耗操作即可再次购买。下面是配置流程:
1、登录 AppGallery Connect 网站,选择"我的应用"。
2、在产品列表中找到应用所在的产品,点击相关应用链接。

3、选择"运营"页签,在左侧导航栏选择"产品运营 > 商品管理",选择"商品列表"页签,点击"添加商品"。

说明

如果应用还未申请支付服务,则会弹出"请添加支付服务"的提示,请申请支付服务后再创建商品。

4、配置商品信息,完成后点击"保存"。

5、在弹出生效提示框,点击"确定",完成配置。
配置完成后,商品列表中此商品的状态为"有效"状态。

HMS SDK中提供了IAP所有对外接口的入口。针对Android Studio开发环境,华为提供了maven仓集成方式的HMS SDK包。在开始开发前,您需要将HMS SDK集成到您的Android Studio项目中,之后您可以通过调用HMS SDK封装好的接口方法来接入IAP。

获取配置文件

添加编译依赖

配置混淆脚本

开发者编译APK前需要配置不要混淆HMS SDK,避免功能异常。

本章节为您简单展示一次支付的流程。在此之前,您需要在手机上登录华为账号。

步骤1:展示商品信息

通过getSkuDetail接口查询已在AGC系统中配置的宝石商品,成功回调场景下根据返回的商品信息在您的界面中展示宝石商品。
DemoActivity.java

/** * Initialize products information and show the products */ private void initProduct() { 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<SkuDetailResult> task = iapClient.getSkuDetail(createSkuDetailReq()); task.addOnSuccessListener(new OnSuccessListener<SkuDetailResult>() { @Override public void onSuccess(SkuDetailResult result) { if (result != null && !result.getSkuList().isEmpty()) { showProduct(result.getSkuList()); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Toast.makeText(DemoActivity.this, "error", Toast.LENGTH_SHORT).show(); } }); } private static SkuDetailReq createSkuDetailReq() { SkuDetailReq skuDetailRequest = new SkuDetailReq(); skuDetailRequest.priceType = PRODUCT_TYPE_CONSUMABLE; ArrayList<String> skuList = new ArrayList<>(); skuList.add("CProduct1"); skuDetailRequest.skuIds = skuList; return skuDetailRequest; }

效果如下:

步骤2:购买商品

用户点击页面显示的宝石图标,可对商品进行购买。该过程使用getBuyIntent接口来完成购买。
通过addOnSuccessListener可监听接口成功的场景。该场景下您需要拉起返回的页面来完成支付流程,结果通过onActivityResult中回调。在onActivityResult中,可以使用IapClientgetBuyResultInfoFromIntent方法获取到BuyResultInfo对象,该对象包括了此次支付的结果信息。支付成功场景下,应用需要参考对响应消息验签的方法使用支付公钥对返回数据进行验签。公钥获取请参考查询支付服务信息。检验成功之后,发放道具,同时执行消耗操作。支付失败场景下,会返回不同的returnCode,可参考异常处理及通用错误码 并根据实际情况进行处理。
相对的,您可以通过addOnFailureListener来监听接口失败的场景,您需要参考异常处理及通用错误码 并根据实际情况进行处理。
DemoActivity.java

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) { product = (String) products.get(pos).get(item_skuid); getBuyIntent(DemoActivity.this, product, PRODUCT_TYPE_CONSUMABLE); } }); /** * create orders for in-app products in the PMS * @param activity indicates the activity object that initiates a request. * @param skuId 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 getBuyIntent(final Activity activity, String skuId, int type) { Log.d(TAG, "call getBuyIntent"); IapClient mClient = Iap.getIapClient(activity); Task<GetBuyIntentResult> task = mClient.getBuyIntent(createGetBuyIntentReq(type, skuId)); task.addOnSuccessListener(new OnSuccessListener<GetBuyIntentResult>() { @Override public void onSuccess(GetBuyIntentResult result) { Log.d(TAG, "getBuyIntent, onSuccess"); // you should pull up the page to complete the payment process Status status = result.getStatus(); try { status.startResolutionForResult(activity, REQ_CODE_BUY); } catch (IntentSender.SendIntentException exp) { Log.e(TAG, exp.getMessage()); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { if (e instanceof IapApiException) { IapApiException apiException = (IapApiException)e; int returnCode = apiException.getStatusCode(); Log.d(TAG, "getBuyIntent, returnCode: " + returnCode); // handle error scenarios } else { // Other external errors Log.e(TAG, e.getMessage()); } } }); } /** * Create a GetBuyIntentReq request * @param type In-app product type. * @param skuId 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 GetBuyIntentReq */ private static GetBuyIntentReq createGetBuyIntentReq(int type, String skuId) { GetBuyIntentReq request = new GetBuyIntentReq(); request.productId = skuId; request.priceType = type; request.developerPayload = "test"; return request; } protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQ_CODE_BUY) { if (data != null) { BuyResultInfo buyResultInfo = Iap.getIapClient(this).getBuyResultInfoFromIntent(data); if (buyResultInfo.getReturnCode() == OrderStatusCode.ORDER_STATE_SUCCESS) { // verify signature of payment results boolean success = CipherUtil.doCheck(buyResultInfo.getInAppPurchaseData(), buyResultInfo.getInAppDataSignature(), Key.getPublicKey()); if (success) { // call the consumption interface to consume it after delivering the product to your user consumePurchase(this, buyResultInfo.getInAppPurchaseData()); } else { Toast.makeText(this, "Pay successful,sign failed", Toast.LENGTH_SHORT).show(); } return; } else { Toast.makeText(this, "Pay failed", Toast.LENGTH_SHORT).show(); return; } } else { Log.i(TAG, "data is null"); } return; } }

效果图如下:

步骤3:消耗商品

当用户购买宝石成功之后,您需要向用户发放相应数量的宝石,然后调用consumePurchase接口执行消耗操作,之后可重复购买该商品。
DemoActivity.java

/** * consume the unconsumed purchase with type 0 * @param inAppPurchaseData JSON string that contains purchase order details. */ private void consumePurchase(final Context context, String inAppPurchaseData) { Log.i(TAG, "call consumePurchase"); IapClient mClient = Iap.getIapClient(context); Task<ConsumePurchaseResult> task = mClient.consumePurchase(createConsumePurchaseReq(inAppPurchaseData)); task.addOnSuccessListener(new OnSuccessListener<ConsumePurchaseResult>() { @Override public void onSuccess(ConsumePurchaseResult result) { // Consume success Log.i(TAG, "consumePurchase 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) { Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); if (e instanceof IapApiException) { IapApiException apiException = (IapApiException)e; int returnCode = apiException.getStatusCode(); Log.i(TAG, "consumePurchase fail,returnCode: " + returnCode); } else { // Other external errors Log.e(TAG, e.getMessage()); Toast.makeText(context, context.getString(R.string.external_error), Toast.LENGTH_SHORT).show(); } } }); } /** * Create a ConsumePurchaseReq request * @param purchaseData which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData. *The app transfers this parameter for the Huawei payment server to update the order status and then deliver the in-app product. * @return ConsumePurchaseReq */ private ConsumePurchaseReq createConsumePurchaseReq(String purchaseData) { ConsumePurchaseReq consumePurchaseRequest = new ConsumePurchaseReq(); String purchaseToken = ""; try { JSONObject jsonObject = new JSONObject(purchaseData); purchaseToken = jsonObject.optString("purchaseToken"); } catch (JSONException e) { } consumePurchaseRequest.purchaseToken = purchaseToken; return consumePurchaseRequest; }

干得好,你已经成功完成了Codelab并学到了:

您可以阅读下面链接,了解更多相关信息。
相关文档

本Codelab中所用demo源码下载地址如下:

源码下载

已复制代码