Implement Google Play Billing Library version 2
Asked Answered
D

4

8

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 implementing it.

The documentation is very short and provides only a part of the necessary code: https://developer.android.com/google/play/billing/billing_library_overview

The only sample provided is made with Kotlin: https://github.com/android/play-billing-samples

Seems like they forgot about Java developers...

Does anyone know a tutorial online or succeeded to implement it? My current code is far from working to be published yet.

Danley answered 14/1, 2020 at 14:37 Comment(4)
Check that example link.medium.com/fpC33o2ff3 it is pretty clearPlethoric
@VolkanAlbayrak Thanks but this example is based on version 1, not version 2Danley
@YoannHercouet if you have found any solution kindly share it. i am also looking for any tutorial in java. if you have successfully implemented kindly share code.Ciracirca
@MuhammadFarhanKhan I added my answer, hope it helps!Danley
D
12

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:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.vending.BILLING" />

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<String> mSkuList = new ArrayList<>();
    private List<SkuDetails> 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<SkuDetails> 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<Purchase> 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.

Danley answered 10/4, 2020 at 9:53 Comment(14)
how you call purchase(String sku) function from fragment, how you pass SKUs from there, please tell me this, and did you implemented it in live app and its working fine. And is onAcknowledgePurchaseResponse method will automatically acknowledge the purchase, and also not implemented consumeAsync is it not neededSurgeonfish
@Surgeonfish Create an interface between the Activity and the Fragment, the interface is basically : public interface PurchaseListener { void purchase(String sku); }. And yes it is working in a production environment.Danley
I implemented everything and looks fine, only " mSkuList.add(Parameters.UNIT_P1);" - Parameters showing error, for now I removed it and did like this mSkuList.add(ITEM_SKU_ADS), so will it work with your implementation or I have to add it with Parameters and if so then how to resolve thisSurgeonfish
@Surgeonfish Parameters is just a class I created to put things like constants, it represents indeed an SKUDanley
Everything is working fine, just one thing - if (purchase.isAcknowledged()) { Log.e(TAG, purchase.getSku()); switch (purchase.getSku()) { case Parameters.UNIT_P1: not working, means when user uninstalled app and reinstall it sharedprederence data is lost and we have to check previous purchase, but this not getting previous purchase. please help me in thisSurgeonfish
@Surgeonfish All the purchases made by the user are checked everytime and returned with "purchasesResult ".Danley
yes I applied it like your code but when i uninstall app and reinstall it to check then its not give item purchased result so unable to change shared preference, means if (purchasesResult.getPurchasesList() != null) { } not giving purchase.getSku() true or if (purchase.isAcknowledged()) true , so I want to know how to do that to restore purchaseSurgeonfish
This is coming from Google In-app directly so you can maybe check your app listing if everything is setup correctly. If purchases have been made previously, they will come back with queryPurchases(BillingClient.SkuType.INAPP)Danley
@ Yoann Hercouet - yes item is purchased and consumed but problem arises when I reinstall app and try to get previous purchase, but I did not used queryPurchases(BillingClient.SkuType.INAPP) because its not in your example, I only used if (purchasesResult.getPurchasesList() != null) { } purchase.getSku() true or if (purchase.isAcknowledged()) which is not giving result,so tell me how to and where to apply queryPurchases,thanks in advanceSurgeonfish
sorry sorry I used it I meant I did not use queryHistoryPurchases that given in document and (purchasesResult.getPurchasesList() != null) { } purchase.getSku - not giving previously purchased item, so please look into it, everything else is working fine also iit is acknowledging it correctly otherswise google refund the payment, so only checking previous purchase thing left , need help in thisSurgeonfish
Hi. Do you use the one without backend server? This is my first time implementing any purchaes. Is it safe not to use the backend server? If yes, pls give me a hint to secure the code. thanks.Prophylactic
@NamikazeMinato yes it is without the backend server. It should be safer but it seems to be a lot of additional work and that requires to setup a server. For now I did not increase the security, I think the worse case scenario would be that a hacker somehow succeeds to grant the purchases but that's a lot of work for a little reward...Danley
@NamikazeMinato Sometimes I setup parameters inside the gradle.properties file. This is explained here: medium.com/@geocohn/…Danley
Thanks. I'll check that out. I have already installed the backend server according to the google sample. I should say it has looots of problems and even google devs don't care apparently. But anyway It works. If you are interested, we can work on it together. I can show you how to do that if you are stocked.Prophylactic
T
3

Here is my implementation using billing 2.1.0 in Kotlin. You can easily convert it to Java if you see the whole picture (that's why I'm pasting you the entire activity).

class GoPremiumActivity : AppCompatActivity(), PurchasesUpdatedListener, AcknowledgePurchaseResponseListener {

    private lateinit var billingClient: BillingClient
    private val skuList = listOf(CStr.PRODUCT_ADS_REMOVE.value)
    private var skuDetails: SkuDetails? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.go_premium)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        setupBillingClient()

        purchaseButton.setOnClickListener {
            val flowParams = BillingFlowParams.newBuilder()
                    .setSkuDetails(skuDetails)
                    .build()
            billingClient.launchBillingFlow(this@GoPremiumActivity, flowParams)
        }
    }


    private fun setupBillingClient() {
        billingClient = BillingClient
                .newBuilder(this@GoPremiumActivity)
                .enablePendingPurchases()
                .setListener(this@GoPremiumActivity)
                .build()

        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult?) {
                if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK) {
                    getAvailableProducts()

                    val purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP)
                    val purchase = purchasesResult.purchasesList.firstOrNull { it.sku == CStr.PRODUCT_ADS_REMOVE.value}
                    if (purchase?.isAcknowledged == true) {
                        Global.prefs.adsRemovalPurchased = true
                        finish()
                    }
                } else {
                    showGeneralError()
                }
            }

            override fun onBillingServiceDisconnected() {
                /*DO NOTHING*/
            }
        })
    }

    fun getAvailableProducts() {
        if (billingClient.isReady) {
            val params = SkuDetailsParams
                    .newBuilder()
                    .setSkusList(skuList)
                    .setType(BillingClient.SkuType.INAPP)
                    .build()
            billingClient.querySkuDetailsAsync(params) { responseCode, skuDetailsList ->
                if (responseCode.responseCode == BillingClient.BillingResponseCode.OK) {
                    skuDetails = skuDetailsList.firstOrNull()
                    skuDetails?.let {
                        purchaseButton.text = String.format("BUY %s", it.price)
                        showSuccessOrError(success = true)
                    } ?: run {
                        showSuccessOrError(success = false)
                    }
                } else {
                    showGeneralError()
                }
            }
        } else {
            showGeneralError()
        }
    }

    override fun onPurchasesUpdated(billingResult: BillingResult?, purchases: MutableList<Purchase>?) {
        if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
            val purchase = purchases.firstOrNull { it.sku == CStr.PRODUCT_ADS_REMOVE.value}
            if (purchase?.purchaseState == Purchase.PurchaseState.PURCHASED) {
                if (!purchase.isAcknowledged) {
                    val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                            .setPurchaseToken(purchase.purchaseToken)
                            .build()
                    billingClient.acknowledgePurchase(acknowledgePurchaseParams, this@GoPremiumActivity)
                }
            }
        } else if (billingResult?.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
            Global.prefs.adsRemovalPurchased = true
            finish()
        } else {
            Global.prefs.adsRemovalPurchased = false
            showSuccessOrError(success = true)
        }
    }

    override fun onAcknowledgePurchaseResponse(billingResult: BillingResult?) {
        if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK) {
            showThankYouDialog(this@GoPremiumActivity)
            Global.prefs.adsRemovalPurchased = true
        }
    }

    private fun showSuccessOrError(success: Boolean) {
        purchaseProgressBar.visibility = View.GONE
        if (success) {
            purchaseButton.visibility = View.VISIBLE
        } else {
            purchaseUnavailable.visibility = View.VISIBLE
        }
    }

    private fun showGeneralError() {
        purchaseProgressBar.visibility = View.GONE
        purchaseUnavailable.visibility = View.VISIBLE
    }

    companion object {
        fun newIntent(context: Context): Intent {
            return Intent(context, GoPremiumActivity::class.java)
        }
    }

    override fun onSupportNavigateUp(): Boolean {
        finish()
        return true
    }

    public override fun onDestroy() {
        super.onDestroy()
    }

    override fun onPause() {
        super.onPause()
        if (isFinishing) {
            finish()
        }
    }

    private fun showThankYouDialog(context: Context) {
        //Show dialog
    }
}

I can remove it if you specifically want it in Java.

Tina answered 14/1, 2020 at 15:14 Comment(3)
I was also looking for a simple example for google play billing library v.2 in java but after a week reading and reading, I have could implement the java code, I post it down here.Proprietress
@Proprietress - your given code showing this error <MainActivity> has no zero argument constructor, please help me to on this, do you get better implementationSurgeonfish
I added the usage in the commentsProprietress
P
1

I'm begginer on Android Studio and i'm implementing the billing library 2.1.0. After a week of reading the android studio documentation and many tutorial about billing library I have made this java class, but i feel is not good enough, at least it do what it has to do. If you find any way to improve it, comment it. Thanks:

1.- Class Pago.java:

package com.example.billing;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.Nullable;

import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.example.R;

import static com.android.billingclient.api.BillingClient.BillingResponseCode.SERVICE_TIMEOUT;
import static com.android.billingclient.api.BillingClient.BillingResponseCode.OK;
import static com.android.billingclient.api.BillingClient.BillingResponseCode.USER_CANCELED;
import static com.android.billingclient.api.BillingClient.BillingResponseCode.BILLING_UNAVAILABLE;
import static com.android.billingclient.api.BillingClient.BillingResponseCode.ITEM_UNAVAILABLE;
import static com.android.billingclient.api.BillingClient.BillingResponseCode.ERROR;
import static com.android.billingclient.api.BillingClient.SkuType.INAPP;

import java.util.ArrayList;
import java.util.List;


public class Pagos implements PurchasesUpdatedListener, BillingClientStateListener, SkuDetailsResponseListener, ConsumeResponseListener {


    private BillingClient billingClient;
    private Context contextPago;
    private String skuId;
    private List<SkuDetails> misProductos;


    // Constructor de la clase Pagos
    public Pagos(Context context) {

        contextPago = context;

    }


    // Asigna el sku del producto que se quiere comprar
    public void comprar(String skuId) {

        this.skuId = skuId;
        configurarBillingClient();

    }


    // Configura el Billing Client para iniciar la conexión con Google Play Console
    private void configurarBillingClient() {

        //  1. Configura el Billing Client
        billingClient = BillingClient.newBuilder(contextPago)
                .enablePendingPurchases()
                .setListener(this)
                .build();

        // 2. Inicia la conexión y asigna los Listener
        billingClient.startConnection(this);

    }


    @Override
    // Evento salta al llamar billingClient.startConnection()
    public void onBillingSetupFinished(BillingResult billingResult) {

        // Busca compras en el Servidor de Google y las marca como consumidas
        consumeCompras();

        // Verifica que la versión de Play Store sea compatible con INAPP
        if (!billingClient.isReady()) {
            String mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_VERSIÓN_NO_COMPATIBLE);
            Toast.makeText(contextPago, mensaje, Toast.LENGTH_LONG).show();
            return;
        }

        // Verifica que la versión de Play Store sea compatible con Suscripciones
        // if (billingClient.isFeatureSupported(SUBSCRIPTIONS).getResponseCode() != OK) {
        //     String mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_VERSIÓN_NO_COMPATIBLE);
        //     Toast.makeText(contextPago, mensaje, Toast.LENGTH_LONG).show();
        //     return; //GooglePlayNoSoportaComprasDeSuscripciones
        // }

        // Verifica que la Configuración se haya hecho bien, sino muestra mensaje de error
        if (verificaResponseCode(billingResult.getResponseCode()) == OK) {
            consultaProductos();
        }

    }


    // Asigna los elemento que se consultarán a Google y los envía con querySkuDetailsAsync
    private void consultaProductos() {

        // Inicializa constantes
        String ITEM_SKU_1 = "android.test.item_unavailable";
        String ITEM_SKU_2 = "android.test.canceled";
        String ITEM_SKU_3 = "android.test.purchased";
        String ITEM_SKU_4 = "donar";
        String ITEM_SKU_5 = "prueba.1";

        // Agrega los productos que se consultarán a Google
        List<String> skuList = new ArrayList<>();
        skuList.add(ITEM_SKU_1);
        skuList.add(ITEM_SKU_2);
        skuList.add(ITEM_SKU_3);
        skuList.add(ITEM_SKU_4);
        skuList.add(ITEM_SKU_5);
        // TODO Cambiar el ingreso manual de items por una consulta a servidor propio de backend seguro.

        SkuDetailsParams.Builder skuDetailsParams = SkuDetailsParams
                .newBuilder()
                .setSkusList(skuList)
                .setType(INAPP);

        // Envía consulta a Google y devuelve el listado de productos mediante onSkuDetailsResponse
        billingClient.querySkuDetailsAsync(skuDetailsParams.build(), this);

    }


    @Override
    // Evento salta cuando Google envía los detalles de los Productos en Venta
    public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {

        if (verificaResponseCode(billingResult.getResponseCode()) == OK) {
            if (skuDetailsList != null) {
                misProductos = skuDetailsList;
                muestraDialogoCompra();
            } else {
                String mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_NO_SKUDETAILSLIST);
                Toast.makeText(contextPago, mensaje, Toast.LENGTH_LONG).show();
            }
        }

    }


    // Lanza el dialogo de compra de Google
    private void muestraDialogoCompra() {

        BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                .setSkuDetails(getSkuIdDetails())
                .build();
        billingClient.launchBillingFlow((Activity) contextPago, flowParams);

    }


    // Obtiene el Producto que se comprará según el Sku ingresado mediante comprar(sku);
    private SkuDetails getSkuIdDetails() {

        if (misProductos == null) return null;
        for (SkuDetails skuProducto : misProductos) {
            if (skuId.equals(skuProducto.getSku())) return skuProducto;
        }
        return null;

    }


    @Override
    // Evento salta cuando se finaliza el Proceso de compra
    public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> list) {

        if (verificaResponseCode(billingResult.getResponseCode()) == OK) {
            // Validar compra con consulta a Google para evitar ingeniería inversa de hackers
            if (validaCompra()) {
                // Compra confirmada
                Log.i("Pagos", "Compra encontrada en servidor");
            } else {
                // Compra no encontrada: Mensaje de error - Revocar privilegios
                Log.i("Pagos", "Compra no encontrada posible hacker");
            }
            consumeCompras();
        }

    }


    // Valida la compra y Devuelve True si encuentra la compra del usuario en el Servidor de Google
    private boolean validaCompra() {

        List<Purchase> purchasesList = billingClient.queryPurchases(INAPP).getPurchasesList();
        if (purchasesList != null && !purchasesList.isEmpty()) {
            for (Purchase purchase : purchasesList) {
                if (purchase.getSku().equals(skuId)) {
                    return true;
                }
            }
        }
        return false;

    }


    // Busca compras en el Servidor de Google y las marca como consumidas
    private void consumeCompras() {

        Purchase.PurchasesResult queryPurchases = billingClient.queryPurchases(INAPP);
        if (queryPurchases.getResponseCode() == OK) {
            List<Purchase> purchasesList = queryPurchases.getPurchasesList();
            if (purchasesList != null && !purchasesList.isEmpty()) {
                for (Purchase purchase : purchasesList) {
                    ConsumeParams params = ConsumeParams.newBuilder()
                            .setPurchaseToken(purchase.getPurchaseToken())
                            .build();
                    billingClient.consumeAsync(params, this);
                }
            }
        }

    }


    @Override
    // Evento salta cuando se ha consumido un producto, Si responseCode = 0, ya se puede volver a comprar
    public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
        if (billingResult.getResponseCode() == OK) {
            Log.i("Pagos", "Token de Compra: " + purchaseToken + " consumida");
        } else {
            Log.i("Pagos", "Error al consumir compra, responseCode: " + billingResult.getResponseCode());
        }
    }


    @Override
    // Evento salta cuando se pierde la conexión durante una compra
    public void onBillingServiceDisconnected() {
        billingClient.startConnection(this);
    }


    // Verifica que el estado del responseCode sea OK, si no muestra mensaje de Error
    private int verificaResponseCode(int responseCode) {

        if (responseCode == OK) return OK;
        if (responseCode == USER_CANCELED) return USER_CANCELED;

        String mensaje = "";
        switch (responseCode) {
            case SERVICE_TIMEOUT:
                mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_SERVICE_TIMEOUT);
                break;
            case BILLING_UNAVAILABLE:
                mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_BILLING_UNAVAILABLE);
                break;
            case ITEM_UNAVAILABLE:
                mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_ITEM_UNAVAILABLE);
                break;
            case ERROR:
                mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_ERROR);
                break;
            default:
                mensaje = contextPago.getString(R.string.PAGOS_MENSAJE_ERROR) + " código: " + responseCode;
                break;
        }
        Toast.makeText(contextPago, mensaje, Toast.LENGTH_LONG).show();
        return responseCode;

    }


}

3.- Manifest

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.vending.BILLING" />

4.- build.gradle

// Google Play Billing Library
implementation 'com.android.billingclient:billing:2.1.0'

5.- Usage, place this code wherever you want to show the billing component:

private final String SKU_UNAVAILABLE = "android.test.item_unavailable";
private final String SKU_CANCELED = "android.test.canceled";
private final String SKU_PURCHASED = "android.test.purchased";
private final String SKU_DONAR = "donar";


private void donar() {
    Pagos pagos = new Pagos(this);
    pagos.comprar(SKU_DONAR);
    cargandoDialogoCompra(true);
}

You can change SKU_DONAR, to SKU_UNAVAILABLE, SKU_CANCELED, SKU_PURCHASED because these are items for testing purposes and as i read its not neccesary to add them to play console

6.- Google Play console

Presencia en Google Play Store -> Productos integrados en la aplicación -> Productos administrados:

Donación (donar) PEN 9.99

That's all, please improve my code, thanks to you all.

Proprietress answered 15/3, 2020 at 14:12 Comment(0)
R
1

Here is a sample app for Google Play Billing version 2 in Java:

Classy Taxi in Java

Rose answered 17/5, 2020 at 2:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.