首页 > 解决方案 > In App Billing - SKU 在计费过程中为空

问题描述

我正在尝试添加 3 个按钮以在应用程序中购买 3 种不同的东西。

但是,在按下三个按钮中的一个后,虽然可以成功购买第一个产品,但其他两个按钮将不再起作用,无法启动购买过程,甚至重新启动应用程序。按下其他两个按钮的吐司建议说the SKU cannot be null

问题

如何更新代码以使所有 3 个按钮都可以继续购买?

我已经实现了以下内容:

代码详情

IabHelper班级

public class IabHelper
{
private String TAG = IabHelper.class.getSimpleName();

private Context context;
private BillingClient mBillingClient;
private IAPHelperListener IAPHelperListener;
private List<String> skuList;

 *  @param context           It will be used to get an application context to bind to the in-app billing service.
 * @param IAPHelperListener Your listener to get the response for your query.
 * @param skuList


public IabHelper(Context context, IAPHelperListener IAPHelperListener, List<String> skuList) {
    this.context = context;
    this.IAPHelperListener = IAPHelperListener;
    this.skuList = skuList;
    this.mBillingClient = BillingClient.newBuilder(context)
            .enablePendingPurchases()
            .setListener(getPurchaseUpdatedListener())
            .build();
    if (!mBillingClient.isReady()) {
        Log.d(TAG, "BillingClient: Start connection...");
        startConnection();
    }
}

private void startConnection() {
    mBillingClient.startConnection(new BillingClientStateListener() {
        @Override
        public void onBillingSetupFinished(BillingResult billingResult) {
            int billingResponseCode = billingResult.getResponseCode();
            Log.d(TAG, "onBillingSetupFinished: " + billingResult.getResponseCode());
            if (billingResponseCode == BillingClient.BillingResponseCode.OK) {
                getPurchasedItems();
                getSKUDetails(skuList);
            }
        }

        @Override
        public void onBillingServiceDisconnected() {
            Log.d(TAG, "onBillingServiceDisconnected: ");
        }
    });
}

public void getPurchasedItems() {
    Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
    if (IAPHelperListener != null)
        IAPHelperListener.onPurchasehistoryResponse(purchasesResult.getPurchasesList());
}

public void getSKUDetails(List<String> skuList) {
    final HashMap<String, SkuDetails> skuDetailsHashMap = new HashMap<>();
    SkuDetailsParams skuParams = SkuDetailsParams.newBuilder().setType(BillingClient.SkuType.INAPP).setSkusList(skuList).build();
    mBillingClient.querySkuDetailsAsync(skuParams, new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                for (SkuDetails skuDetails : skuDetailsList) {
                    skuDetailsHashMap.put(skuDetails.getSku(), skuDetails);
                }
                if (IAPHelperListener != null)
                    IAPHelperListener.onSkuListResponse(skuDetailsHashMap);
            }
        }
    });
}

 * @param skuDetails skudetails of the product to be purchased
 *                   Developer console.

public void launchBillingFLow(final SkuDetails skuDetails) {
    if(mBillingClient.isReady()){
        BillingFlowParams mBillingFlowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
        mBillingClient.launchBillingFlow((Activity) context, mBillingFlowParams);
    }
}

private PurchasesUpdatedListener getPurchaseUpdatedListener()
{
    return (billingResult, purchases) -> {
        int responseCode = billingResult.getResponseCode();
        if (responseCode == BillingClient.BillingResponseCode.OK && purchases != null)
        {
            //here when purchase completed
            for (Purchase purchase : purchases)
            {                  
                //For non consumable I will acknowledge purchase
                //For consumable I will consume purchase

                boolean all_non_consumable = true;
                if (all_non_consumable)
                {
                    acknowledgePurchase(purchase); // in marksix all are non consumable
                }
                else
                {
                    consumePurchase(purchase);
                }
            }
        } else if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
            // Handle an error caused by a user cancelling the purchase flow.
            Log.d(TAG, "user cancelled");
        } else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
            Log.d(TAG, "item already owned");
        } else if (responseCode == BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) {
            Log.d(TAG , "service disconnected");
            startConnection();
        }
    };
}

public void acknowledgePurchase(Purchase purchase)
{
    if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && isSignatureValid(purchase))
    {
        //This is for Consumable product
        AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();
        mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener()
        {
            @Override
            public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
                Log.d("purchase", "Purchase Acknowledged");
            }
        });

        if (IAPHelperListener != null)
            IAPHelperListener.onPurchaseCompleted(purchase);
    }
}

public void consumePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && isSignatureValid(purchase))
    {
        //This is for Consumable product
        ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();
        mBillingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
            @Override
            public void onConsumeResponse(BillingResult billingResult, String s) {
                Log.d("purchase", "Purchase Consumed");
            }
        });

        if (IAPHelperListener != null)
            IAPHelperListener.onPurchaseCompleted(purchase);
    }
}

private boolean isSignatureValid(Purchase purchase) {
    return com.pearappx.inappbilling.Security.verifyPurchase(Settings2.base64EncodedPublicKey, purchase.getOriginalJson(), purchase.getSignature());
}

public void endConnection() {
    if (mBillingClient != null && mBillingClient.isReady()) {
        mBillingClient.endConnection();
        mBillingClient = null;
    }
}

public interface IAPHelperListener {
    void onSkuListResponse(HashMap<String, SkuDetails> skuDetailsHashMap);
    void onPurchasehistoryResponse(List<Purchase> purchasedItem);
    void onPurchaseCompleted(Purchase purchase);
}

}

`活动课

public class Settings2 extends Activity implements IabHelper.IAPHelperListener
{   
IabHelper iapHelper;
HashMap<String, SkuDetails> skuDetailsHashMap = new HashMap<>();
final String ITEM_SKU_10C = "com.10c";
final String ITEM_SKU_15C = "com.15c";
final String ITEM_SKU_20C = "com.20c";
final String TEST = "android.test.purchased"; //This id can be used for testing purpose
private List<String> skuList = Arrays.asList(ITEM_SKU_10C, ITEM_SKU_15C, ITEM_SKU_20C, TEST);
Button buy_10C_Button; //non-consumable (no ad = non-consumable, coins = consumable)
Button buy_15C_Button; //non-consumable
Button buy_20C_Button; //non-consumable

...

public void onCreate(Bundle savedInstanceState) 
    {
    iapHelper = new IabHelper(this, this, skuList);

    buy_10C_Button.setOnClickListener(paramAnonymousView -> purchase_action(""+ITEM_SKU_10C));
    buy_15C_Button.setOnClickListener(paramAnonymousView -> purchase_action(""+ITEM_SKU_15C));
    buy_20C_Button.setOnClickListener(paramAnonymousView -> purchase_action(""+ITEM_SKU_20C));

public void purchase_action(String pur_item)
{
    button_pressed = pur_item;
    if(!skuDetailsHashMap.isEmpty())  //skuDetailsHashMap is NOT empty
    {
        iapHelper.launchBillingFLow(skuDetailsHashMap.get(pur_item));
        Utilities.system_toast(Settings2.this, "purchase action", "going through");
    }
    else
    {
        try
        {
            Utilities.system_toast(Settings2.this, "purchase action", "nothing happen");
        }
        catch (Exception ex)
        {
            Utilities.system_toast(Settings2.this, "purchase action", "ex= " ex.getText.toString());            
        }
    }
}

@Override
public void onSkuListResponse(HashMap<String, SkuDetails> skuDetails)
{
    skuDetailsHashMap = skuDetails;
}

@Override
public void onPurchasehistoryResponse(List<Purchase> purchasedItems)
{
    if (purchasedItems != null)
    {
        for (Purchase purchase : purchasedItems) 
   {
            String sku = purchase.getSkus().toString();
            countC_upload_parse(sku);
        }
    }
}

@Override
public void onPurchaseCompleted(Purchase purchase) {
    Toast.makeText(getApplicationContext(), "Purchase Successful", Toast.LENGTH_SHORT).show();
    String sku = purchase.getSkus().toString();
    countC_upload_parse(sku);
}

标签: androidin-app-billing

解决方案


推荐阅读