sagar raval
sagar raval

Reputation: 23

Google Play Billing PurchasesUpdatedListener returning SERVICE_DISCONNECTED

Im trying to add Google PLay Billing in my app from last few days , in the previous question i mentioned that the launch flow is opening the real payment method instead of test cards

view the previous question here

now after some modification in the handlePurchase() & PurchasesUpdatedListener and finally adding the verify java class to verify the purchase im getting this message from the PurchasesUpdatedListener 'SERVICE_DISCONNECTED'

This is the code

HelpActivity.Java

public class HelpActivity extends AppCompatActivity {
    AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() {
        @Override
        public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
            Toast.makeText(HelpActivity.this, "Acknowledge", Toast.LENGTH_SHORT).show();
        }
    };
    private BillingClient billingClient;
    private final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
        @Override
        public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {

                for (Purchase purchase : purchases) {
                    handlePurchase(purchase);
                }


            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                // Handle an error caused by a user cancelling the purchase flow.
                Toast.makeText(HelpActivity.this, "Purchase Canceled", Toast.LENGTH_SHORT).show();
                //Note!!!! only this toast message is being shown in this entre process
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {

                for (Purchase purchase : purchases) {
                    handlePurchase(purchase);
                }
                Toast.makeText(HelpActivity.this, "You already own the item", Toast.LENGTH_SHORT).show();


            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.BILLING_UNAVAILABLE) {

                Toast.makeText(HelpActivity.this, "BILLING_UNAVAILABLE", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ERROR) {

                Toast.makeText(HelpActivity.this, "ERROR", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.DEVELOPER_ERROR) {

                Toast.makeText(HelpActivity.this, "DEVELOPER_ERROR", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_NOT_OWNED) {

                Toast.makeText(HelpActivity.this, "ITEM_NOT_OWNED", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_UNAVAILABLE) {

                Toast.makeText(HelpActivity.this, "ITEM_UNAVAILABLE", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED) {

                Toast.makeText(HelpActivity.this, "FEATURE_NOT_SUPPORTED", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.NETWORK_ERROR) {

                Toast.makeText(HelpActivity.this, "FEATURE_NOT_SUPPORTED", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) {

                Toast.makeText(HelpActivity.this, "SERVICE_DISCONNECTED", Toast.LENGTH_SHORT).show();
            } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE) {

                Toast.makeText(HelpActivity.this, "SERVICE_UNAVAILABLE", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(HelpActivity.this, " " + billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    };
    private MaterialButton removeAdsBtn;
    private MaterialButton restorePurchaseBtn;
    private ProductDetails productDetails;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_help_avtivity);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // Set vertical orientation
        Window window = this.getWindow();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.setStatusBarColor(this.getResources().getColor(R.color.black));
        }
        restorePurchaseBtn = findViewById(R.id.restoreAdsBtn);
        removeAdsBtn = findViewById(R.id.removeAdsBtn);
        // Initialize BillingClient

        setupBillingClient();
        restorePurchaseBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               /* restorePurchases();*/
            }
        });
        removeAdsBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Call the method to initiate the billing flow
                setupBillingClient();
                initiateBillingFlow();
            }
        });
    }
    public void setupBillingClient() {
        billingClient = BillingClient.newBuilder(this).setListener(purchasesUpdatedListener).enablePendingPurchases().build();


        establishConnection();
    }
    void establishConnection() {

        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    // The BillingClient is ready. You can query purchases here.
                    QueryPurchase();
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
                establishConnection();
            }
        });
    }
    public void QueryPurchase() {
        QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(ImmutableList.of(QueryProductDetailsParams.Product.newBuilder().setProductId("removeads_rickroll").setProductType(BillingClient.ProductType.INAPP).build())).build();

        billingClient.queryProductDetailsAsync(queryProductDetailsParams, new ProductDetailsResponseListener() {
            public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    // Check if the productDetailsList is not empty
                    if (productDetailsList != null) {

                        for (ProductDetails productDetails1 : productDetailsList) {
                            productDetails = productDetails1;
                        }


                    }
                }
            }
        });
    }
    public void initiateBillingFlow() {
        if (productDetails != null) {
            ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
                    ImmutableList.of(
                            BillingFlowParams.ProductDetailsParams.newBuilder()
                                    .setProductDetails(productDetails)
                                    .build());
            BillingFlowParams billingFlowParams =
                    BillingFlowParams.newBuilder()
                            .setProductDetailsParamsList(productDetailsParamsList)
                            .build();

            billingClient.launchBillingFlow(HelpActivity.this, billingFlowParams);
        }
    }


    private void handlePurchase(Purchase purchase) {
        ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();

        ConsumeResponseListener listener = new ConsumeResponseListener() {
            @Override
            public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    // Handle the success of the consume operation.
                }
            }
        };


        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
            //Verify Purchase
            if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
                Toast.makeText(this, "Error:- Invalid Purchase", Toast.LENGTH_SHORT).show();
                return;
            }
            if (!purchase.isAcknowledged()) {
                AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();
                billingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
                // Grant the user access to remove ads
                // Save the purchase details to SharedPreferences to remember the purchase
                Toast.makeText(HelpActivity.this, "Thank you for the purchase", Toast.LENGTH_SHORT).show();
                Toast.makeText(HelpActivity.this, "Restarting the app, Please wait ...", Toast.LENGTH_SHORT).show();
                SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putBoolean("ads_removed", true);
                editor.apply();
                Intent intent = new Intent(HelpActivity.this, MainActivity.class);
                startActivity(intent);

            } else {
                Toast.makeText(this, "Already Purchased", Toast.LENGTH_SHORT).show();
                SharedPreferences preferences = getSharedPreferences("adremoveSP", MODE_PRIVATE);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putBoolean("ads_removed", true);
                editor.apply();
                Intent intent = new Intent(HelpActivity.this, MainActivity.class);
                startActivity(intent);
            }
        } else if (purchase.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE) {
            Toast.makeText(this, "UNSPECIFIED_STATE", Toast.LENGTH_SHORT).show();
        } else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) {
            Toast.makeText(this, "PENDING", Toast.LENGTH_SHORT).show();
        }

        billingClient.consumeAsync(consumeParams, listener);


    }

    private boolean verifyValidSignature(String originalJson, String signature) {
        try {
            String base64Key = "<MyBaseKey>";
            return Verify.verifyPurchase(base64Key, originalJson, signature);
        } catch (IOException e) {
            return false;
        }
    }

    protected void onResume() {
        super.onResume();
        billingClient.queryPurchasesAsync(QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), (billingResult, list) -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                for (Purchase purchase : list) {
                    if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && !purchase.isAcknowledged()) {
                        handlePurchase(purchase);
                    }
                }
            }
        });
    }

}

Verify.Java

public class Verify {
    private static final String TAG = "IABUtil/Security";
    private static final String KEY_FACTORY_ALGORITHM = "RSA";

    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) throws IOException{
        if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) || TextUtils.isEmpty(signature)){
            return false;
        }

        PublicKey key = generatePublicKey(base64PublicKey);
        return verifyM(key,signedData,signature);
    }
    public static PublicKey generatePublicKey(String encodedPublicKey)throws IOException{
        try {
            byte[] decodedKey = Base64.decode(encodedPublicKey,Base64.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
            return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
        }catch (NoSuchAlgorithmException e){
            throw new RuntimeException(e);
        }
        catch (InvalidKeySpecException e){
            String msg = "Invalid Key Specification: "+ e;
            throw new IOException(msg);
        }
    }
    public static boolean verifyM(PublicKey publicKey,String signedData, String signature){
        byte [] signatureBytes;
        try{
            signatureBytes = Base64.decode(signature,Base64.DEFAULT);
        }catch (IllegalArgumentException e){
            return false;
        }
        try {
            Signature signatureAlgorithm = Signature.getInstance(SIGNATURE_ALGORITHM);
            signatureAlgorithm.initVerify(publicKey);
            signatureAlgorithm.update(signedData.getBytes());

            if (!signatureAlgorithm.verify(signatureBytes)){
                return false;
            }
            return true;
        }catch (IllegalArgumentException e){
            return false;
        }catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e){
            return false;
        }
    }
}

Upvotes: 1

Views: 301

Answers (0)

Related Questions