Saturday, 9 October 2021

In App Billing - SKU being null in billing process

I am trying to add 3 buttons for purchase of 3 different things in the app.

However, after pressing one of the three buttons, while the first product can be purchased successfully, the other two buttons would not function anymore and cannot initiate the purchase process, even the app is restarted. The toast pressing the other two buttons advised that the SKU cannot be null

Question

How can the codes be updated such that all the 3 buttons can proceed purchase?

I have implemented the below:

Code details

IabHelper class

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);
}

}

`Acitivity class

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);
}


from In App Billing - SKU being null in billing process

No comments:

Post a Comment