华为应用内支付文档(二)

1,427 阅读8分钟

AndroidManifest.xml文件信息配置如下:

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".ConsumptionActivity"></activity>
</application>
<!--取消一直检查跟新华为HMS-->
<queries>
    <intent>
        <action android:name="com.huawei.hms.core.aidlservice" />
    </intent>
</queries>
主模块下面的gradle如下: plugins { id 'com.android.application' }

android { compileSdkVersion 30 buildToolsVersion "30.0.2"

defaultConfig {
    applicationId "com.qf.myqfpay"
    minSdkVersion 17 //不能低于17
    targetSdkVersion 30
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}


signingConfigs {
    config {
        storeFile file('D:\\worker\\MyQfpay\\qf.jks')
        storePassword '123456'
        keyPassword '123456'
        keyAlias 'qf'
    }
}
buildTypes {
    debug {
        signingConfig signingConfigs.config
    }
    release {
        signingConfig signingConfigs.config
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

}

dependencies {

implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.huawei.hms:iap:5.0.1.300'

} apply plugin: 'com.huawei.agconnect'

工程模块下面的build代码如下: // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() maven { url 'developer.huawei.com/repo/' } } dependencies { classpath "com.android.tools.build:gradle:4.1.1"

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
    classpath 'com.huawei.agconnect:agcp:1.4.1.300'
}

}

allprojects { repositories { google() jcenter() maven { url 'developer.huawei.com/repo/' } } }

task clean(type: Delete) { delete rootProject.buildDir }

一共有八个类代码如下: package com.qf.myqfpay;

import android.text.TextUtils; import android.util.Base64; import android.util.Log;

import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec;

/**

  • 签名工具

  • 用户的公钥必须放到

  • 这个下面来通过这个来判断 */ public class CipherUtil { private static final String TAG = "CipherUtil";

    private static final String SIGN_ALGORITHMS = "SHA256WithRSA"; //这个是公钥替换地址 private static final String PUBLIC_KEY = "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAl8YBI1jOQvXUETCCqFEqHmwff7mMpjp5Lo+MajOcsBwa6ZgIu75AAKu7p2qrGy25bXgtpsYkQItAld3PEijk7FQ9oDDSrozWdOBKWZXHfUSe6hLH5NTGWdcF/HdZUaviBGe60HazB2y3/UYD1p5TV/DRoN+cUAkAo6VMvTemNB3sDSu52DllDyiT0ozNrI1mNhhmAwI1UXaa/QkrtUUuDHS6k7oRYT0XLNWTZGZXBP8iNG1epciOhpuDDmRPEnIkMuO5+g4KP/RNB1gvl6t1eIEW2hAredvr6qpp/OcwA/SRFJm2MULWG23GhjSDxRMDuvdsISrDfip+gq8Z+ZibUQClipEGfOHgE1TYKPCvAmwVTtWXb0WyGNYp6cuNMHJIuINnZo5fqNCaeKD21ZSe60X0PvLmxx3q0R6AvdLahNthMOZ+DPtMPoSEvZYQquVcbGL9VNZXzgS7vzEgvR/vQ//XwlRPdKuxxIrU0fVDdPwxM8StZjJYeda/dgy4d/PxAgMBAAE=";

    /**

    • the method to check the signature for the data returned from the interface

    • @param content Unsigned data

    • @param sign the signature for content

    • @param publicKey the public of the application

    • @return boolean */ public static boolean doCheck(String content, String sign, String publicKey) { if (TextUtils.isEmpty(publicKey)) { Log.e(TAG, "publicKey is null"); return false; }

      if (TextUtils.isEmpty(content) || TextUtils.isEmpty(sign)) { Log.e(TAG, "data is error"); return false; }

      try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] encodedKey = Base64.decode(publicKey, Base64.DEFAULT); PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

       java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
      
       signature.initVerify(pubKey);
       signature.update(content.getBytes("utf-8"));
      
       boolean bverify = signature.verify(Base64.decode(sign, Base64.DEFAULT));
       return bverify;
      

      } catch (NoSuchAlgorithmException e) { Log.e(TAG, "doCheck NoSuchAlgorithmException" + e); } catch (InvalidKeySpecException e) { Log.e(TAG, "doCheck InvalidKeySpecException" + e); } catch (InvalidKeyException e) { Log.e(TAG, "doCheck InvalidKeyException" + e); } catch (SignatureException e) { Log.e(TAG, "doCheck SignatureException" + e); } catch (UnsupportedEncodingException e) { Log.e(TAG, "doCheck UnsupportedEncodingException" + e); } return false; }

    /**

    • get the publicKey of the application
    • During the encoding process, avoid storing the public key in clear text.
    • @return publickey */ public static String getPublicKey(){ return PUBLIC_KEY; }

}

/**

  • 错误码信息 / public class Constants { /* 用于拉起 pmsPay 页面的 requestCode */ public static final int REQ_CODE_BUY = 4002;

    /** requestCode 用于拉出 isEnvReady 接口的登录页面 */ public static final int REQ_CODE_LOGIN = 2001; }

package com.qf.myqfpay;

import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast;

import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity;

import com.huawei.hms.iap.Iap; import com.huawei.hms.iap.IapClient; import com.huawei.hms.iap.entity.InAppPurchaseData; import com.huawei.hms.iap.entity.OrderStatusCode; import com.huawei.hms.iap.entity.OwnedPurchasesResult; import com.huawei.hms.iap.entity.ProductInfo; import com.huawei.hms.iap.entity.ProductInfoResult; import com.huawei.hms.iap.entity.PurchaseIntentResult; import com.huawei.hms.iap.entity.PurchaseResultInfo; import com.huawei.hms.support.api.client.Status;

import org.json.JSONException;

import java.util.ArrayList; import java.util.List;

/**

  • 消耗品 */ public class ConsumptionActivity extends AppCompatActivity { private static final String PURCHASETOKEN_KEY = "purchasetokenSet"; private static final String GEMS_COUNT_KEY = "gemsCount"; private static final String DATA_NAME = "database";

    private String TAG = "ConsumptionActivity"; private TextView countTextView; //产品显示列表 private ListView consumableProductsListview; //产品获取集合 private List consumableProducts = new ArrayList(); //产品列表适配器 private ProductListAdapter adapter; //历史记录 private Button purchaseHisBtn; //华为提供接口类 private IapClient mClient; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_consumption); mClient = Iap.getIapClient(this); initView(); //检查是否存在用户已购买但未发货的消耗品。 queryPurchases(null); }

    private void initView() { findViewById(R.id.progressBar).setVisibility(View.VISIBLE); findViewById(R.id.content).setVisibility(View.GONE); countTextView = (TextView) findViewById(R.id.gems_count); countTextView.setText(String.valueOf(getCountOfGems(this))); consumableProductsListview = (ListView) findViewById(R.id.consumable_product_list1); consumableProductsListview.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { buy(position);

         }
     });
     purchaseHisBtn = (Button) findViewById(R.id.enter_purchase_his);
     purchaseHisBtn.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View view) {
             //历史记录
    

// Intent intent = new Intent(ConsumptionActivity.this, PurchaseHistoryActivity.class); // startActivity(intent); } }); //获取有几款产品 queryProducts(); }

/**
 * 查询产品
 * productIds添加产品Id
 * 很重要,很重要
 */
private void queryProducts() {
    List<String> productIds = new ArrayList<String>();
    productIds.add("123456789");
    IapRequestHelper.obtainProductInfo(mClient, productIds, IapClient.PriceType.IN_APP_CONSUMABLE, new IapApiCallback<ProductInfoResult>() {
        @Override
        public void onSuccess(ProductInfoResult result) {
            Log.i(TAG, "obtainProductInfo, success");
            if (result == null) {
                return;
            }
            if (result.getProductInfoList() != null) {
                consumableProducts = result.getProductInfoList();
            }
            showProducts();
        }

        @Override
        public void onFail(Exception e) {
            Log.e(TAG, "obtainProductInfo: " + e.getMessage());
            showProducts();
        }
    });
}

private void showProducts() {
    findViewById(R.id.progressBar).setVisibility(View.GONE);
    findViewById(R.id.content).setVisibility(View.VISIBLE);
    adapter = new ProductListAdapter(ConsumptionActivity.this, consumableProducts);
    consumableProductsListview.setAdapter(adapter);
    adapter.notifyDataSetChanged();
}

/**
 * 调用obtainOwnedPurchases接口
 * 获取用户已购买但未发货的消耗品数据。
 */
private void queryPurchases(String continuationToken) {
    final String tag = "obtainOwnedPurchases";
    IapRequestHelper.obtainOwnedPurchases(mClient, IapClient.PriceType.IN_APP_CONSUMABLE, continuationToken, new IapApiCallback<OwnedPurchasesResult>() {
        @Override
        public void onSuccess(OwnedPurchasesResult result) {
            if (result == null) {
                Log.e(TAG, tag + " result is null");
                return;
            }
            Log.i(TAG, "obtainOwnedPurchases, success");
            if (result.getInAppPurchaseDataList() != null) {
                List<String> inAppPurchaseDataList = result.getInAppPurchaseDataList();
                List<String> inAppSignature= result.getInAppSignature();
                for (int i = 0; i < inAppPurchaseDataList.size(); i++) {
                    final String inAppPurchaseData = inAppPurchaseDataList.get(i);
                    final String inAppPurchaseDataSignature = inAppSignature.get(i);
                    deliverProduct(inAppPurchaseData, inAppPurchaseDataSignature);
                }
            }
            if (!TextUtils.isEmpty(result.getContinuationToken())) {
                queryPurchases(result.getContinuationToken());
            }
        }

        @Override
        public void onFail(Exception e) {
            Log.e(TAG, "obtainOwnedPurchases, type=" + IapClient.PriceType.IN_APP_CONSUMABLE + ", " + e.getMessage());
        }
    });

}

/**
 * 交付产品
 * @param inAppPurchaseDataStr
 * @param inAppPurchaseDataSignature
 */
private void deliverProduct(final String inAppPurchaseDataStr, final String inAppPurchaseDataSignature) {
    if (CipherUtil.doCheck(inAppPurchaseDataStr, inAppPurchaseDataSignature, CipherUtil.getPublicKey())) {
        try {
            InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseDataStr);
            if (inAppPurchaseDataBean.getPurchaseState() != InAppPurchaseData.PurchaseState.PURCHASED) {
                return;
            }
            String purchaseToken = inAppPurchaseDataBean.getPurchaseToken();
            String productId = inAppPurchaseDataBean.getProductId();
            if (DeliveryUtils.isDelivered(ConsumptionActivity.this, purchaseToken)) {
                Toast.makeText(this, productId + " has been delivered", Toast.LENGTH_SHORT).show();
                IapRequestHelper.consumeOwnedPurchase(mClient, purchaseToken);
            } else {
                if (DeliveryUtils.deliverProduct(this, productId, purchaseToken)) {
                    Log.i(TAG, "delivery success");
                    Toast.makeText(this, productId + " delivery success", Toast.LENGTH_SHORT).show();
                    updateNumberOfGems();
                    // To consume the product after successfully delivering.
                    IapRequestHelper.consumeOwnedPurchase(mClient, purchaseToken);
                } else {
                    Log.e(TAG, productId + " delivery fail");
                    Toast.makeText(this, productId + " delivery fail", Toast.LENGTH_SHORT).show();
                }
            }

        } catch (JSONException e) {
            Log.e(TAG, "delivery:" + e.getMessage());
        }
    } else {
        Log.e(TAG, "delivery:" + getString(R.string.verify_signature_fail));
        Toast.makeText(this, getString(R.string.verify_signature_fail), Toast.LENGTH_SHORT).show();
    }
}

/**
 * 更新产品数量
 */
private void updateNumberOfGems() {
    // Update the number of gems.
    String countOfGems = String.valueOf(DeliveryUtils.getCountOfGems(ConsumptionActivity.this));
    countTextView.setText(countOfGems);
}

/**
 * 够买产品
 * @param index
 */
private void buy(int index) {
    ProductInfo productInfo = consumableProducts.get(index);
    IapRequestHelper.createPurchaseIntent(mClient, productInfo.getProductId(), IapClient.PriceType.IN_APP_CONSUMABLE, new IapApiCallback<PurchaseIntentResult>() {
        @Override
        public void onSuccess(PurchaseIntentResult result) {
            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.
            IapRequestHelper.startResolutionForResult(ConsumptionActivity.this, status, Constants.REQ_CODE_BUY);
        }

        @Override
        public void onFail(Exception e) {

//

        }
    });
}

/**
 *
 * @param requestCode
 * @param resultCode
 * @param data
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.i(TAG, "onActivityResult");
    super.onActivityResult(requestCode, resultCode, data);
    //支付成功否
    if (requestCode == Constants.REQ_CODE_BUY) {
        if (data == null) {
            Log.e(TAG, "数据为空");
            return;
        }
        PurchaseResultInfo purchaseIntentResult = Iap.getIapClient(this).parsePurchaseResultInfoFromIntent(data);
        switch(purchaseIntentResult.getReturnCode()) {
            case OrderStatusCode.ORDER_STATE_CANCEL:
                Toast.makeText(this, "订单已取消!", Toast.LENGTH_SHORT).show();
                break;
            case OrderStatusCode.ORDER_STATE_FAILED:
            case OrderStatusCode.ORDER_PRODUCT_OWNED:
                queryPurchases(null);
                break;
            //订单成功
            case OrderStatusCode.ORDER_STATE_SUCCESS:
                deliverProduct(purchaseIntentResult.getInAppPurchaseData(), purchaseIntentResult.getInAppDataSignature());
                break;
            default:
                break;
        }
        return;
    }
}
/**
 * 获取当前产品数量
 * @param context Context.
 * @return long
 */
public static long getCountOfGems(Context context) {
    SharedPreferences sharedPreferences = context.getSharedPreferences(DATA_NAME, Context.MODE_PRIVATE);
    long count = sharedPreferences.getLong(GEMS_COUNT_KEY, 0);
    return count;
}

}

package com.qf.myqfpay;

import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils;

import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set;

public class DeliveryUtils { private static final String PURCHASETOKEN_KEY = "purchasetokenSet"; private static final String GEMS_COUNT_KEY = "gemsCount"; private static final String DATA_NAME = "database";

private static Map<String, Integer> getNumOfGems() {
    Map<String, Integer> map = new HashMap<String, Integer>();

    map.put("CProduct01", 5);
    map.put("CustomizedCProduct01", 10);

    return map;
}

/**
 确定购买的商品是否已发货。 @param 上下文上下文。
 @param purchasetoken 产品支付时由华为支付服务器生成,
 通过InAppPurchaseData返回给应用。 @return 布尔值
 */
public static boolean isDelivered(Context context, String purchasetoken) {
    SharedPreferences sharedPreferences = context.getSharedPreferences(DATA_NAME, Context.MODE_PRIVATE);
    Set<String> stringSet = sharedPreferences.getStringSet(PURCHASETOKEN_KEY, null);
    if (stringSet != null && stringSet.contains(purchasetoken)) {
        return true;
    }
    return false;
}

/**
 * Ship and return the shipping result.
 * @param context Context.
 * @param productId Id of the purchased product.
 * @param purchaseToken Generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData.
 * @return boolean
 */
public static boolean deliverProduct(Context context, String productId, String purchaseToken) {
    if (TextUtils.isEmpty(productId) || TextUtils.isEmpty(purchaseToken)) {
        return false;
    }
    if (!getNumOfGems().containsKey(productId)) {
        return false;
    }
    SharedPreferences sharedPreferences = context.getSharedPreferences(DATA_NAME, Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPreferences.edit();

    long count = sharedPreferences.getLong(GEMS_COUNT_KEY, 0);
    count += getNumOfGems().get(productId);
    editor.putLong(GEMS_COUNT_KEY, count);

    Set<String> stringSet = sharedPreferences.getStringSet(PURCHASETOKEN_KEY, new HashSet<String>());
    stringSet.add(purchaseToken);
    editor.putStringSet(PURCHASETOKEN_KEY, stringSet);
    return editor.commit();
}

/**
 * 获取当前产品数量.
 * @param context Context.
 * @return long
 */
public static long getCountOfGems(Context context) {
    SharedPreferences sharedPreferences = context.getSharedPreferences(DATA_NAME, Context.MODE_PRIVATE);
    long count = sharedPreferences.getLong(GEMS_COUNT_KEY, 0);
    return count;
}

}

package com.qf.myqfpay;

/**

  • 用于从 IAP api 回调结果.

  • @since 2019/12/9 */ public interface IapApiCallback {

    /** 请求成功。 @param result 成功响应的结果。 */ void onSuccess(T result);

    /** 回调失败。 @param e 来自 IAPSDK 的异常。 */ void onFail(Exception e); }

package com.qf.myqfpay;

import android.app.Activity; import android.content.IntentSender; import android.util.Log;

import com.huawei.hmf.tasks.OnFailureListener; import com.huawei.hmf.tasks.OnSuccessListener; import com.huawei.hmf.tasks.Task; import com.huawei.hms.iap.IapApiException; import com.huawei.hms.iap.IapClient; import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseReq; import com.huawei.hms.iap.entity.ConsumeOwnedPurchaseResult; import com.huawei.hms.iap.entity.OwnedPurchasesReq; import com.huawei.hms.iap.entity.OwnedPurchasesResult; import com.huawei.hms.iap.entity.ProductInfoReq; import com.huawei.hms.iap.entity.ProductInfoResult; import com.huawei.hms.iap.entity.PurchaseIntentReq; import com.huawei.hms.iap.entity.PurchaseIntentResult; import com.huawei.hms.support.api.client.Status;

import java.util.List;

public class IapRequestHelper { private final static String TAG = "IapRequestHelper";

/**
 * 获取在 AppGallery Connect 中配置的应用内产品详细信息。
 *
 * @param iapClient  IapClient 实例调用获取产品信息 API。
 * @param productIds 要查询的产品 ID 列表。每个产品 ID
 *                   必须存在并且在当前应用中是唯一的。
 * @param type       应用内产品类型。该值包含:
 *                   0:消耗品 1:不可消耗品 2 自动更新订阅
 * @param callback   IapApiCallback
 */
public static void obtainProductInfo(IapClient iapClient, final List<String> productIds, int type, final IapApiCallback callback) {
    Log.i(TAG, "call obtainProductInfo");

    Task<ProductInfoResult> task = iapClient.obtainProductInfo(createProductInfoReq(type, productIds));
    task.addOnSuccessListener(new OnSuccessListener<ProductInfoResult>() {
        @Override
        public void onSuccess(ProductInfoResult result) {
            Log.i(TAG, "obtainProductInfo, success");
            callback.onSuccess(result);
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.e(TAG, "obtainProductInfo, fail");
            callback.onFail(e);
        }
    });
}

/**
 * 创建一个 ProductInfoReq 对象。
 *
 * @param type 应用内产品类型。该值包含: 0:消耗品 1:非消耗品 2
 *             自动续订订阅 @param productIds
 *             要查询的产品 ID 列表。每个产品
 *             ID 必须存在并且在当前应用中是唯一的。
 * @return 产品信息请求
 */
private static ProductInfoReq createProductInfoReq(int type, List<String> productIds) {
    ProductInfoReq req = new ProductInfoReq();
    req.setPriceType(type);
    req.setProductIds(productIds);
    return req;
}

/**
 * 查询所有已订阅的应用内商品信息,包括消耗品、非消耗品和自动续期订阅。
 * 如果返回消耗品,则系统需要进行交付并调用consumeOwnedPurchase API 来消费产品。
 * 如果退回非消耗品,则无需消耗应用内商品。如果返回订阅,
 * 则返回该用户在该应用下的所有现有订阅关系。
 *
 * @param mClient  IapClient 实例调用获取OwnedPurchases API。
 * @param type     应用内产品类型。该值包含:
 *                 0:消耗品
 *                 1:不可消耗品
 *                 2 自动更新订阅
 * @param callback IapApiCallback
 */
public static void obtainOwnedPurchases(IapClient mClient, final int type, String continuationToken, final IapApiCallback callback) {
    Log.i(TAG, "call obtainOwnedPurchases");
    Task<OwnedPurchasesResult> task = mClient.obtainOwnedPurchases(IapRequestHelper.createOwnedPurchasesReq(type, continuationToken));
    task.addOnSuccessListener(new OnSuccessListener<OwnedPurchasesResult>() {
        @Override
        public void onSuccess(OwnedPurchasesResult result) {
            Log.i(TAG, "obtainOwnedPurchases, success");
            callback.onSuccess(result);

        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.e(TAG, "obtainOwnedPurchases, fail");
            callback.onFail(e);
        }
    });

}

/**
 * 创建一个OwnedPurchasesReq 对象。 @param type type 应用内商品类型。该值包含:
 * 0:消耗品 1:不可消耗品 2 自动更新订阅 @param continuationToken
 * 从获取OwnedPurchases api 或获取OwnedPurchaseRecord api 返回的数据位置标志。
 * @return OwnedPurchasesReq
 */
private static OwnedPurchasesReq createOwnedPurchasesReq(int type, String continuationToken) {
    OwnedPurchasesReq req = new OwnedPurchasesReq();
    req.setPriceType(type);
    req.setContinuationToken(continuationToken);
    return req;
}
/**
 使用 priceType 0 消费所有未消费的购买。
 @param iapClient IapClient 实例调用消耗 OwnedPurchase API。
 @param purchaseToken 在产品支付过程中由华为支付服务器生成,
 通过InAppPurchaseData返回给应用。
 */
public static void consumeOwnedPurchase(IapClient iapClient, String purchaseToken) {
    Log.i(TAG, "call consumeOwnedPurchase");
    Task<ConsumeOwnedPurchaseResult> task = iapClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(purchaseToken));
    task.addOnSuccessListener(new OnSuccessListener<ConsumeOwnedPurchaseResult>() {
        @Override
        public void onSuccess(ConsumeOwnedPurchaseResult result) {
            // Consume success.
            Log.i(TAG, "consumeOwnedPurchase success");
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            if (e instanceof IapApiException) {
                IapApiException apiException = (IapApiException)e;
                int returnCode = apiException.getStatusCode();
                Log.e(TAG, "consumeOwnedPurchase fail, IapApiException returnCode: " + returnCode);
            } else {
                // Other external errors
                Log.e(TAG, e.getMessage());
            }

        }
    });

}
/**
 创建一个 ConsumeOwnedPurchaseReq 对象。
 @param purchaseToken 在产品支付过程中由华为支付服务器生成,
 通过InAppPurchaseData返回给应用。应用程序将此参数传递给华为支付服务器更新订单状态,
 然后交付应用程序内产品。 @return ConsumeOwnedPurchaseReq
 */
private static ConsumeOwnedPurchaseReq createConsumeOwnedPurchaseReq(String purchaseToken) {
    ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
    req.setPurchaseToken(purchaseToken);
    req.setDeveloperChallenge("testConsume");
    return req;
}
/**
 在 PMS @param iapClient IapClient 实例中为应用内产品创建订单以调用
 createPurchaseIntent API。要支付的应用内商品的
 @param productId ID。应用内产品 ID 是您在
 AppGallery Connect 中的应用内产品配置过程中设置的产品 ID。
 @param type 应用内产品类型。该值包含:
 0:消耗品 1:不可消耗品 2 自动更新订阅 @param callback IapApiCallback
 */
public static void createPurchaseIntent(final IapClient iapClient, String productId, int type, final IapApiCallback callback) {
    Log.i(TAG, "call createPurchaseIntent");
    Task<PurchaseIntentResult> task = iapClient.createPurchaseIntent(createPurchaseIntentReq(type, productId));
    task.addOnSuccessListener(new OnSuccessListener<PurchaseIntentResult>() {
        @Override
        public void onSuccess(PurchaseIntentResult result) {
            Log.i(TAG, "createPurchaseIntent, success");
            callback.onSuccess(result);
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.e(TAG, "createPurchaseIntent, fail");
            callback.onFail(e);

        }
    });
}
/**
 * 创建一个 PurchaseIntentReq 对象。 @param type 应用内产品类型。该值包含:
 * 0:消耗品
 * 1:非消耗品
 * 2 自动更新订阅 @param productId 要支付的应用内商品的 ID。
 * 应用内产品 ID 是您在 AppGallery Connect
 * 中的应用内产品配置过程中设置的产品 ID。
 * @return PurchaseIntentReq
 */
private static PurchaseIntentReq createPurchaseIntentReq(int type, String productId) {
    PurchaseIntentReq req = new PurchaseIntentReq();
    req.setPriceType(type);
    req.setProductId(productId);
    req.setDeveloperPayload("testPurchase");
    return req;
}
/**
 开始一项活动。
 @param activity 启动新页面的活动。
 @param status 该参数包含支付页面的pendingIntent 对象。
 @param reqCode 结果代码.
 */
public static void startResolutionForResult(Activity activity, Status status, int reqCode) {
    if (status == null) {
        Log.e(TAG, "status is null");
        return;
    }
    if (status.hasResolution()) {
        try {
            status.startResolutionForResult(activity, reqCode);
        } catch (IntentSender.SendIntentException exp) {
            Log.e(TAG, exp.getMessage());
        }
    } else {
        Log.e(TAG, "intent is null");
    }
}

}

package com.qf.myqfpay;

import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView;

import com.huawei.hms.iap.IapClient; import com.huawei.hms.iap.entity.ProductInfo;

import java.util.List;

/**

  • 列表适配器 */ public class ProductListAdapter extends BaseAdapter { private Context mContext; private List productInfos;

    public ProductListAdapter(Context context, List productInfos) { mContext = context; this.productInfos = productInfos; } @Override public int getCount() { return productInfos.size(); }

    @Override public View getView(int position, View convertView, ViewGroup parent) { ProductInfo productInfo = productInfos.get(position); ProductListViewHolder holder = null; if (null == convertView) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_layout, null); holder = new ProductListViewHolder(convertView); convertView.setTag(holder); } else { holder = (ProductListViewHolder) convertView.getTag(); }

     holder.productName.setText(productInfo.getProductName());
     holder.productPrice.setText(productInfo.getPrice());
     if (productInfo.getPriceType() == IapClient.PriceType.IN_APP_NONCONSUMABLE) {
         holder.imageView.setVisibility(View.GONE);
     }
    
     return convertView;
    

    }

    @Override public long getItemId(int position) { return position; }

    @Override public Object getItem(int position) { if (productInfos != null && productInfos.size() > 0) { return productInfos.get(position); } return null; }

    static class ProductListViewHolder { TextView productName; TextView productPrice; ImageView imageView;

     ProductListViewHolder(View view) {
         productName = (TextView) view.findViewById(R.id.item_name);
         productPrice = (TextView) view.findViewById(R.id.item_price);
         imageView = (ImageView) view.findViewById(R.id.item_image);
     }
    

    } }

package com.qf.myqfpay;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView xiaoPay, feiPay;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    init();

}

private void init() {
    xiaoPay = findViewById(R.id.tv);
    feiPay = findViewById(R.id.tv_fei);
    xiaoPay.setOnClickListener(this);
    feiPay.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.tv:
            startActivity(new Intent(this,ConsumptionActivity.class));
            break;
        case R.id.tv_fei:
            break;
        default:
            break;
    }
}

}