package com.image.finder.utils; import android.app.Activity; import android.text.TextUtils; import android.util.Log; import com.android.billingclient.api.AcknowledgePurchaseParams; import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingClient.BillingResponseCode; import com.android.billingclient.api.BillingClient.ProductType; import com.android.billingclient.api.BillingClientStateListener; import com.android.billingclient.api.BillingFlowParams; import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams; import com.android.billingclient.api.BillingResult; import com.android.billingclient.api.ProductDetails; import com.android.billingclient.api.Purchase; import com.android.billingclient.api.QueryProductDetailsParams; import com.android.billingclient.api.QueryProductDetailsParams.Product; import com.android.billingclient.api.QueryPurchasesParams; import com.image.finder.AppConfig; import com.image.finder.data.ThisApp; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; public class BillingHelper { private static final String TAG = BillingHelper.class.getSimpleName(); private BillingClient billingClient; private Activity activity; private ResponseListener responseListener; private List productDetailsList; private boolean productFinish; private boolean restoreFinish; private boolean purchased; private String product_id = "up_premium_sub"; private String productType = ProductType.SUBS; private String currentPurchase = null; private String currentToken = null; public BillingHelper(Activity activity) { this.activity = activity; product_id = AppConfig.billing.billing_product_id; } public void initAndConnect() { productFinish = false; restoreFinish = false; init(); startConnection(); } public static boolean checkPurchased() { return ThisApp.pref().isPurchased(AppConfig.billing.billing_product_id); } public BillingClient getBillingClient() { return billingClient; } public void init() { Log.d(TAG, "init"); BillingClient.Builder builder = BillingClient.newBuilder(ThisApp.get()); builder.setListener((billingResult, purchases) -> { Log.d(TAG, "init : setListener"); if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { for (Purchase purchase : purchases) { verifyPurchase(purchase); } } if (responseListener != null) { Log.d(TAG, "init : onReload"); responseListener.onReload(false); } }); builder.enablePendingPurchases(); billingClient = builder.build(); } public void startConnection() { Log.d(TAG, "startConnection"); billingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(BillingResult billingResult) { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { // restore old purchase restorePurchase(); // The BillingClient is ready. You can query purchases here. showProductsAvailable(); } } @Override public void onBillingServiceDisconnected() { startConnection(); } }); } private void showProductsAvailable() { Log.d(TAG, "showProductsAvailable"); QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList( List.of(Product.newBuilder().setProductId(product_id).setProductType(productType).build()) ).build(); billingClient.queryProductDetailsAsync(queryProductDetailsParams, (billingResult, productDetailsList) -> { this.productDetailsList = productDetailsList; productFinish = true; Log.d(TAG, "showProductsAvailable : productFinish " + productDetailsList.size()); if (!productDetailsList.isEmpty()) { if (responseListener != null && restoreFinish) { Log.d(TAG, "showProductsAvailable : responseListener"); responseListener.onLoaded(this.productDetailsList, purchased, currentPurchase); } } else { if (responseListener != null) responseListener.onFailed("Empty products"); } }); } public void launchPurchase(ProductDetails productDetails, String token) { Log.d(TAG, "launchPurchase | " + productDetails.getProductId()); ProductDetailsParams.Builder productParams = ProductDetailsParams.newBuilder(); productParams.setProductDetails(productDetails); if (!TextUtils.isEmpty(token)) { productParams.setOfferToken(token); } ProductDetailsParams productDetailsParams = productParams.build(); List productDetailsParamsList = List.of(productDetailsParams); BillingFlowParams.Builder builder = BillingFlowParams.newBuilder(); builder.setProductDetailsParamsList(productDetailsParamsList); /* if(purchased && !TextUtils.isEmpty(currentToken)){ BillingFlowParams.SubscriptionUpdateParams oldBillingParams = BillingFlowParams.SubscriptionUpdateParams.newBuilder() .setOldPurchaseToken(currentToken) .setSubscriptionReplacementMode(BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE) .build(); builder.setSubscriptionUpdateParams(oldBillingParams); } */ BillingFlowParams billingFlowParams = builder.build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams); } void verifyPurchase(Purchase purchase) { Log.d(TAG, "verifyPurchase | " + purchase.getOrderId()); if (!purchase.isAcknowledged()) { billingClient.acknowledgePurchase(AcknowledgePurchaseParams .newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(), billingResult -> { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { ThisApp.pref().setPurchased(product_id, true); if (responseListener != null) { Log.d(TAG, "verifyPurchase : onReload"); responseListener.onReload(true); } } }); } } private void restorePurchase() { Log.d(TAG, "restoreOldPurchase"); billingClient.queryPurchasesAsync(QueryPurchasesParams.newBuilder().setProductType(productType).build(), (billingResult, list) -> { restoreFinish = true; if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { if (list.size() > 0) { for (Purchase purchase : list) { if (purchase.getProducts().contains(product_id) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { purchased = true; currentPurchase = purchase.getPurchaseToken(); currentToken = purchase.getPurchaseToken(); Log.d(TAG, "restoreOldPurchase : Successfully restored"); ThisApp.pref().setPurchased(product_id, true); } } } else { Log.d(TAG, "restoreOldPurchase : Oops, No purchase found"); purchased = false; ThisApp.pref().setPurchased(product_id, false); // set 0 to de-activate premium feature } if (responseListener != null && productFinish) { Log.d(TAG, "restoreOldPurchase : responseListener"); responseListener.onLoaded(this.productDetailsList, purchased, currentPurchase); } } }); } public static boolean checkBilling() { if (!AppConfig.billing.billing_enable) { return true; } if (ThisApp.pref().isPurchased(AppConfig.billing.billing_product_id)) { // already purchase return true; } String today = new SimpleDateFormat("dd/MM/yyyy", Locale.US).format(new Date()); if (!ThisApp.pref().getLastDate().equals(today)) { // reset count if date changed ThisApp.pref().setFeatureUseLimit(0); ThisApp.pref().setLastDate(today); } int count = ThisApp.pref().getFeatureUseLimit() + 1; ThisApp.pref().setFeatureUseLimit(count); if (count > AppConfig.billing.billing_feature_use_limit) { return false; } return true; } private static final Map DURATION_MAP = new HashMap<>(); static { DURATION_MAP.put("P1W", "/ 1 week"); DURATION_MAP.put("P1M", "/ 1 month"); DURATION_MAP.put("P3M", "/ 3 months"); DURATION_MAP.put("P6M", "/ 6 months"); DURATION_MAP.put("P1Y", "/ 1 year"); } public static String convertDuration(String durationString) { if (DURATION_MAP.containsKey(durationString)) { return DURATION_MAP.get(durationString); } else { return durationString; } } public void setResponseListener(ResponseListener responseListener) { this.responseListener = responseListener; } public interface ResponseListener { void onLoaded(List products, boolean purchased, String currentPurchase); void onFailed(String message); void onReload(boolean success); } public enum FeatureUseType implements Serializable { DAILY, WEEKLY, MONTHLY } }