Implement Google Play Billing Library version 2

前端 未结 4 883
傲寒
傲寒 2021-02-03 14:48

Google published a brand new version to handle the payments in Android but after searching quite a while I cannot find a single example or tutorial from someone who succeeded im

4条回答
  •  温柔的废话
    2021-02-03 15:13

    Thanks @Webfreak, your answer for Kotlin guided to me to the right direction.

    Here is how I implemented it for Java:

    First add the 'billingclient' library to gradle :

    implementation 'com.android.billingclient:billing:X.X.X'
    

    And add the required permissions in the manifest file:

    
    
    

    The Activity must implements the following interfaces:

    public class MainActivity extends AppCompatActivity implements
            ...
            PurchasesUpdatedListener,
            AcknowledgePurchaseResponseListener {
    

    Then I initialize the billing client inside the onCreate method:

    /** IN-APPS PURCHASE */
        private BillingClient mBillingClient;
        private long mLastPurchaseClickTime = 0;
        private List mSkuList = new ArrayList<>();
        private List mSkuDetailsList = new ArrayList<>();
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // AppPrefs is just a standalone class I used to get or set shared preferences easily
            mPrefs = AppPrefs.getInstance(this);
    
    
            // Rest of your code ...
    
    
            /** IN-APP PURCHASES */
            // Initialize the list of all the in-app product IDs I use for this app
            mSkuList.add(Parameters.UNIT_P1);// NoAdsPurchased
            mSkuList.add(Parameters.UNIT_P2);// CustomizationPurchased
            mSkuList.add(Parameters.UNIT_P3);// ChartsPurchased
    
            // Initialize the billing client
            setupBillingClient();
    
            // Apply the upgrades on my app according to the user's purchases
            applyUpgrades();
        }
    

    The method setting up the billing client is here, along with the metyhod I use to retrieve the avaialble in-app products fro the app:

    private void setupBillingClient() {
            mBillingClient = BillingClient
                    .newBuilder(MainActivity.this)
                    .enablePendingPurchases() // Useful for physical stores
                    .setListener(MainActivity.this)
                    .build();
    
            mBillingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(BillingResult billingResult) {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        // Load the available products related to the app from Google Play
                        getAvailableProducts();
    
                        Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);// Or SkuType.SUBS if subscriptions
    
                        // Init all the purchases to false in the shared preferences (security prevention)
                        mPrefs.setNoAdsPurchased(false);
                        mPrefs.setCustomizationPurchased(false);
                        mPrefs.setChartsPurchased(false);
    
                        // Retrieve and loop all the purchases done by the user
                        // Update all the boolean related to the purchases done in the shared preferences
                        if (purchasesResult.getPurchasesList() != null) {
                            for (Purchase purchase : purchasesResult.getPurchasesList()) {
                                if (purchase.isAcknowledged()) {
                                    Log.e(TAG, purchase.getSku());
    
                                    switch (purchase.getSku()) {
                                        case Parameters.UNIT_P1:
                                            mPrefs.setNoAdsPurchased(true);
                                            break;
                                        case Parameters.UNIT_P2:
                                            mPrefs.setCustomizationPurchased(true);
                                            break;
                                        case Parameters.UNIT_P3:
                                            mPrefs.setChartsPurchased(true);
                                            break;
                                    }
                                }
                            }
                        }
                    }
                }
    
                @Override
                public void onBillingServiceDisconnected() {
                    // Try to restart the connection on the next request to
                    // Google Play by calling the startConnection() method.
                    // TODO Note: It's strongly recommended that you implement your own connection retry policy and override the onBillingServiceDisconnected() method. Make sure you maintain the BillingClient connection when executing any methods.
                    Log.e(TAG, "onBillingServiceDisconnected");
                }
            });
        }
    
        private void getAvailableProducts() {
            if (mBillingClient.isReady()) {
                SkuDetailsParams params = SkuDetailsParams
                        .newBuilder()
                        .setSkusList(mSkuList)
                        .setType(BillingClient.SkuType.INAPP)
                        .build();
    
                mBillingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
                    @Override
                    public void onSkuDetailsResponse(BillingResult billingResult, List skuDetailsList) {
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                            mSkuDetailsList = skuDetailsList;
                        }
                    }
                });
            }
        }
    

    When a purchase is done by the user (I allow purchases on several Fragments in my app), I call this function on the main Activity (using an interface):

    @Override
        public void purchase(String sku) {
            // Mis-clicking prevention, using threshold of 3 seconds
            if (SystemClock.elapsedRealtime() - mLastPurchaseClickTime < 3000){
                Log.d(TAG, "Purchase click cancelled");
                return;
            }
            mLastPurchaseClickTime = SystemClock.elapsedRealtime();
    
            // Retrieve the SKU details
            for (SkuDetails skuDetails : mSkuDetailsList) {
                // Find the right SKU
                if (sku.equals(skuDetails.getSku())) {
                    BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                            .setSkuDetails(skuDetails)
                            .build();
                    mBillingClient.launchBillingFlow(MainActivity.this, flowParams);
                    break;
                }
            }
        }
    

    Here I implement the methods inherited:

    @Override
        public void onPurchasesUpdated(BillingResult billingResult, @Nullable List purchases) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
                for (Purchase purchase : purchases) {
                    handlePurchase(purchase);
                }
            } else {
                displayError(R.string.inapp_purchase_problem, billingResult.getResponseCode());
            }
        }
    
        private void handlePurchase(Purchase purchase) {
            if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
                // Grant entitlement to the user.
                applyPurchase(purchase);
    
                // Acknowledge the purchase if it hasn't already been acknowledged.
                if (!purchase.isAcknowledged()) {
                    AcknowledgePurchaseParams acknowledgePurchaseParams =
                            AcknowledgePurchaseParams.newBuilder()
                                    .setPurchaseToken(purchase.getPurchaseToken())
                                    .build();
                    mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, MainActivity.this);
                }
            }
        }
    
        @Override
        public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                displayError(R.string.inapp_purchase_success, billingResult.getResponseCode());
            }
        }
    

    The method I added to acknowledge a purchase on my app:

    private void applyPurchase(Purchase purchase) {
    
            switch (purchase.getSku()) {
                case Parameters.UNIT_P1:
                    mPrefs.setNoAdsPurchased(true);
                    break;
                case Parameters.UNIT_P2:
                    mPrefs.setCustomizationPurchased(true);
                    break;
                case Parameters.UNIT_P3:
                    mPrefs.setChartsPurchased(true);
                    break;
            }
    
            // I remove the ads right away if purchases
            if(mPrefs.getNoAdsPurchased()) {
                destroyAds();
            }
        }
    

    This last method is used to apply all the upgrades/purchases on the app (with an example with the removal of the ads):

    private void applyUpgrades() {
            // No ads
            if (mPrefs.getNoAdsPurchased()) {
                destroyAds();
            } else {
                loadAds();
            }
    
            if (mPrefs.getCustomizationPurchased()) {
                // Allow customization
                // ...
            }
    
            if (mPrefs.getChartsPurchased()) {
                // Allow charts visualization
                // ...
            }
        }
    

    I guess this solution is not perfect yet but it is working, I will modify the code if I find improvements.

提交回复
热议问题