In-App-Kauf für Android Google Play Billing Library 2.0 implementiert

Hinweis

Die Version der Bibliothek wurde von 1.0 auf 2.0 erhöht, daher habe ich den Inhalt neu geschrieben. Unten finden Sie den Code für den Testversuch.

Ich zeige Ihnen ein Beispiel für Code, der irgendwie funktioniert, aber auf eigenes Risiko. Da es sich um eine Geschichte handelt, bei der es um Geld geht, wird dringend empfohlen, die Informationen der Oberfamilie zu überprüfen. https://developer.android.com/google/play/billing/billing_library_overview?hl=ja

Einführung

Bei der Suche nach Beispielen für die In-App-Kaufverarbeitung unter Android waren viele alt und komplex. Mit der neuen Google Play-Abrechnungsbibliothek war dies relativ einfach zu erreichen, daher werde ich mich daran erinnern.

Für dieses Verfahren sind keine Einstellungen wie "IInAppBillingService.aidl" oder "AndroidManifest.xml" erforderlich. Sie können auch testen, bevor Sie die App auf Google Play hochladen.

Verfahren

Erstellen Sie ein neues Projekt zum Testen.

Hier habe ich eine einfache "Aktivität aktivieren" als Aktivität ausgewählt und den gesamten Code in die Hauptaktivität geschrieben. Der Projektname war "Billing Sample".

Fügen Sie die Abrechnung zu den Abhängigkeiten von build.gradle (Modul :) hinzu.

build.gradle


    dependencies {
		....
		implementation 'com.android.billingclient:billing:2.0.1'
    }

Ändern Sie MainActivity wie folgt.

MainActivity.java


  package xx.xx.xx.xx.billingsample;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
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.Purchase;
import com.android.billingclient.api.PurchaseHistoryRecord;
import com.android.billingclient.api.PurchaseHistoryResponseListener;
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 java.util.ArrayList;
import java.util.List;

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

    TextView textView1;
    private BillingClient billingClient;
    List<SkuDetails> mySkuDetailsList;

    //Wird aufgerufen, wenn die App gestartet wird
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Bereiten Sie die Bedientasten und das Ergebnisausgabefeld vor
        textView1 = findViewById(R.id.text_view1);
        findViewById(R.id.button_get_skus).setOnClickListener(this);
        findViewById(R.id.button_query_owned).setOnClickListener(this);
        findViewById(R.id.button_purchase).setOnClickListener(this);
        findViewById(R.id.button_purchase_history).setOnClickListener(this);

        //Bereiten Sie den Abrechnungsclient vor
        billingClient = BillingClient.newBuilder(this)
                .setListener(this).enablePendingPurchases().build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                int responseCode = billingResult.getResponseCode();
                if (responseCode == BillingClient.BillingResponseCode.OK) {
                    // The BillingClient is ready. You can query purchases here.
                    textView1.setText("Billing Setup OK");
                } else {
                    showResponseCode(responseCode);
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
                textView1.setText("Billing Servise Disconnected. Retry");
            }
        });
    }

    //Wird aufgerufen, wenn auf die Schaltfläche geklickt wird
    @Override
    public void onClick(View v) {
        if (v != null) {
            switch (v.getId()) {
                case R.id.button_get_skus:
                    querySkuList();
                    break;

                case R.id.button_query_owned:
                    queryOwned();
                    break;

                case R.id.button_purchase:
                    startPurchase("android.test.purchased");
                    break;

                case R.id.button_purchase_history:
                    queryPurchaseHistory();
                    break;
                default:
                    break;
            }
        }
    }

    //Wird aufgerufen, wenn die App geschlossen wird
    @Override
    protected void onDestroy() {
        billingClient.endConnection();
        super.onDestroy();
    }

    //Erkundigen Sie sich nach dem Artikel, den Sie kaufen möchten
    void querySkuList() {
        List skuList = new ArrayList<>();
        skuList.add("android.test.purchased");  // prepared by Google
        skuList.add("android.test.canceled");
        skuList.add("android.test.refunded");
        skuList.add("android.test.item_unavailable");
        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
        billingClient.querySkuDetailsAsync(params.build(),
                new SkuDetailsResponseListener() {
                    @Override
                    public void onSkuDetailsResponse(BillingResult billingResult,
                                                     List<SkuDetails> skuDetailsList) {
                        // Process the result.
                        StringBuffer resultStr = new StringBuffer("");
                        int responseCode = billingResult.getResponseCode();
                        if (responseCode == BillingClient.BillingResponseCode.OK) {
                            //Bewahren Sie die Sku-Details für einen späteren Kaufvorgang auf
                            mySkuDetailsList = skuDetailsList;
                            //Liste anzeigen
                            if (skuDetailsList != null) {
                                for (Object item : skuDetailsList) {
                                    SkuDetails skuDetails = (SkuDetails) item;
                                    String sku = skuDetails.getSku();
                                    String price = skuDetails.getPrice();
                                    resultStr.append("Sku=" + sku + " Price=" + price + "\n");
                                }
                            } else {
                                resultStr.append("No Sku");
                            }
                            textView1.setText(resultStr);
                        } else {
                            showResponseCode(responseCode);
                        }
                    }
                });
    }

    //Starten Sie den Kaufvorgang
    void startPurchase(String sku) {
        SkuDetails skuDetails = getSkuDetails(sku);
        if (skuDetails != null) {
            BillingFlowParams params = BillingFlowParams.newBuilder()
                    .setSkuDetails(skuDetails)
                    .build();
            BillingResult billingResult = billingClient.launchBillingFlow(this, params);
            showResponseCode(billingResult.getResponseCode());
        }
    }

    //Details zur angegebenen SKU finden Sie in der Liste
    SkuDetails getSkuDetails(String sku) {
        SkuDetails skuDetails = null;
        if(mySkuDetailsList==null){
            textView1.setText("Exec [Get Skus] first");
        }else {
            for (SkuDetails sd : mySkuDetailsList) {
                if (sd.getSku().equals(sku)) skuDetails = sd;
            }
            if (skuDetails == null) {
                textView1.setText(sku + " is not found");
            }
        }
        return skuDetails;
    }

    //Wird beim Aktualisieren der Kaufergebnisse aufgerufen
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        StringBuffer resultStr = new StringBuffer("");
        int billingResultCode = billingResult.getResponseCode();
        if (billingResultCode == BillingClient.BillingResponseCode.OK
                && purchases != null) {
            for (Purchase purchase : purchases) {
                //Genehmigen Sie den Kauf
                String state = handlePurchase(purchase);
                //Zeigen Sie die gekaufte Sku-Zeichenfolge und das Genehmigungsergebnis an
                String sku = purchase.getSku();
                resultStr.append(sku).append("\n");
                resultStr.append(" State=").append(state).append("\n");
            }
            textView1.setText(resultStr);
        } else {
            // Handle error codes.
            showResponseCode(billingResultCode);
        }
    }

    //Genehmigen Sie den Kauf
    String handlePurchase(Purchase purchase) {
        String stateStr = "error";
        int purchaseState = purchase.getPurchaseState();
        if (purchaseState == Purchase.PurchaseState.PURCHASED) {
            // Grant entitlement to the user.
            stateStr = "purchased";
            // Acknowledge the purchase if it hasn't already been acknowledged.
            if (!purchase.isAcknowledged()) {
                AcknowledgePurchaseParams acknowledgePurchaseParams =
                        AcknowledgePurchaseParams.newBuilder()
                                .setPurchaseToken(purchase.getPurchaseToken())
                                .build();
                billingClient.acknowledgePurchase(acknowledgePurchaseParams, this);
            }
        }else if(purchaseState == Purchase.PurchaseState.PENDING){
            stateStr = "pending";
        }else if(purchaseState == Purchase.PurchaseState.UNSPECIFIED_STATE){
            stateStr = "unspecified state";
        }
        return stateStr;
    }

    //Ergebnisse der Kaufgenehmigung zurückgegeben
    @Override
    public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
        int responseCode = billingResult.getResponseCode();
        if(responseCode != BillingClient.BillingResponseCode.OK) {
            showResponseCode(responseCode);
        }
    }

    //Erkundigen Sie sich nach gekauften Artikeln (Bargeldverarbeitung)
    void queryOwned(){
        StringBuffer resultStr = new StringBuffer("");
        Purchase.PurchasesResult purchasesResult
                = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
        int responseCode = purchasesResult.getResponseCode ();
        if(responseCode== BillingClient.BillingResponseCode.OK){
            resultStr.append("Query Success\n");
            List<Purchase> purchases = purchasesResult.getPurchasesList();
            if(purchases.isEmpty()){
                resultStr.append("Owned Nothing");
            } else {
                for (Purchase purchase : purchases) {
                    resultStr.append(purchase.getSku()).append("\n");
                }
            }
            textView1.setText(resultStr);
        }else{
            showResponseCode(responseCode);
        }
    }

    //Kaufhistorie abfragen (Netzwerkzugriffsverarbeitung)
    void queryPurchaseHistory() {
        billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP,
                new PurchaseHistoryResponseListener() {
                    @Override
                    public void onPurchaseHistoryResponse(BillingResult billingResult,
                                                          List<PurchaseHistoryRecord> purchasesList) {
                        int responseCode = billingResult.getResponseCode();
                        if (responseCode == BillingClient.BillingResponseCode.OK) {
                            if (purchasesList == null || purchasesList.size() == 0) {
                                textView1.setText("No History");
                            } else {
                                for (PurchaseHistoryRecord purchase : purchasesList) {
                                    // Process the result.
                                    textView1.setText("Purchase History="
                                            + purchase.toString() + "\n");
                                }
                            }
                        } else {
                            showResponseCode(responseCode);
                        }
                    }
        });
    }
    //Serverantwort anzeigen
    void showResponseCode(int responseCode){
        switch(responseCode){
            case BillingClient.BillingResponseCode.OK:
                textView1.setText("OK");break;
            case BillingClient.BillingResponseCode.USER_CANCELED:
                textView1.setText("USER_CANCELED");break;
            case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE:
                textView1.setText("SERVICE_UNAVAILABLE");break;
            case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE:
                textView1.setText("BILLING_UNAVAILABLE");break;
            case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE:
                textView1.setText("ITEM_UNAVAILABLE");break;
            case BillingClient.BillingResponseCode.DEVELOPER_ERROR:
                textView1.setText("DEVELOPER_ERROR");break;
            case BillingClient.BillingResponseCode.ERROR:
                textView1.setText("ERROR");break;
            case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
                textView1.setText("ITEM_ALREADY_OWNED");break;
            case BillingClient.BillingResponseCode.ITEM_NOT_OWNED:
                textView1.setText("ITEM_NOT_OWNED");break;
            case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
                textView1.setText("SERVICE_DISCONNECTED");break;
            case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
                textView1.setText("FEATURE_NOT_SUPPORTED");break;
        }
    }
}

Ändern Sie die Layoutdatei "activity_main.xml" wie folgt.

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <Button
            android:id="@+id/button_get_skus"
            android:text="Get Skus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/button_query_owned"
            android:text="Query Owned"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/button_purchase"
            android:text="Purchase"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/button_purchase_history"
            android:text="Purchase History"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:id="@+id/text_view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

Ändern Sie Build Valiant in Release.

Öffnen Sie Build Valiants auf der Registerkarte ganz links in Android Studio und ändern Sie Build Valiant von Debug in Release.

Wenn ich versuche, es auf dem tatsächlichen Computer zu installieren, wird der folgende Fehler angezeigt.

Error: The apk for your currently selected variant (app-release-unsigned.apk) is not signed.

Geben Sie das Passwort ein.

Klicken Sie auf "Fix" auf der rechten Seite der Fehleranzeige und geben Sie den in Google Play registrierten Schlüssel ein. Wenn Sie sich nicht registriert haben, müssen Sie sich registrieren. Die Tasteneingabe erfolgt wie folgt.

  1. Drücken Sie "Fix", um die Registerkarte "Projektstruktur> Module> Signierkonfigurationen" anzuzeigen.
  2. Drücken Sie "+", um die Taste hinzuzufügen. Der Name kann "config" bleiben.
  3. Geben Sie die in Google Play registrierten Schlüsselinformationen (storeFile, storePassword, keyAlias, keyPassword) ein.
  4. Wählen Sie die Registerkarte "Standardkonfiguration" und wählen Sie unter "Signierkonfiguration" den Namen (Konfiguration) des hinzugefügten Schlüssels aus der Auswahl ganz rechts aus ▼.

Test an der eigentlichen Maschine.

  1. Wenn Sie die App starten, wird "Setup Success" angezeigt.
  2. Klicken Sie auf die Schaltfläche "Skus abrufen", um Informationen zu den vier gewünschten Elementen anzuzeigen.
  3. Klicken Sie auf die Schaltfläche "Abfrage im Besitz", um die gekauften Artikel anzuzeigen. Zuerst gibt es keine.
  4. Klicken Sie auf die Schaltfläche "Kaufen", um den Dialog zum Kauf von "android.test.purchased" anzuzeigen. Klicken Sie zum Kauf auf OK.
  5. Klicken Sie erneut auf die Schaltfläche "Abfrage im Besitz", um den soeben gekauften Artikel "android.test.purchased" anzuzeigen.

Hinweis

Recommended Posts

In-App-Kauf für Android Google Play Billing Library 2.0 implementiert
Passen Sie infoWindow für Google Maps für Android an
[google-oauth] [python] Google APIs-Clientbibliothek für Python