【问题标题】:Multiple in-app products in android app that has implemented Firebase Realtime Database storage已实现 Firebase 实时数据库存储的 Android 应用中的多个应用内产品
【发布时间】:2020-12-26 11:53:36
【问题描述】:

我有一个应用程序可以向用户显示回收站视图,其中包含来自 Firebase 实时数据库的图像。当用户点击他们想要的图片时,它会将他们带到带有购买按钮的页面并显示他们选择的图片。

这是他们可以购买的活动代码

   package com.khumomashapa.notes.activities;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

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.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.khumomashapa.notes.R;
import com.khumomashapa.notes.billing.BillingActivity;
import com.khumomashapa.notes.billing.Security;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.android.billingclient.api.BillingClient.SkuType.INAPP;


public class PreviewActivity extends AppCompatActivity implements PurchasesUpdatedListener {

    TextView purchaseStatus;
    Button purchaseButton;
    Button DownloadBtn;

    ImageView Preview;

    public static final String PREF_FILE= "MyPref";
    public static final String PURCHASE_KEY= "purchase";
    public static final String PRODUCT_ID= "purchase";

    private BillingClient billingClient;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preview);

        Preview = findViewById(R.id.ImagePreview);
        byte[] bytes = getIntent().getByteArrayExtra("image");
        Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        Preview.setImageBitmap(bmp);

        purchaseStatus = (TextView) findViewById(R.id.purchase_status);
        purchaseButton = (Button) findViewById(R.id.PurchaseBtn);
        DownloadBtn = (Button) findViewById(R.id.DownloadBtn);



        // Establish connection to billing client
        //check purchase status from google play store cache
        //to check if item already Purchased previously or refunded
        billingClient = BillingClient.newBuilder(this)
                .enablePendingPurchases().setListener(this).build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
                    Purchase.PurchasesResult queryPurchase = billingClient.queryPurchases(INAPP);
                    List queryPurchases = queryPurchase.getPurchasesList();
                    if(queryPurchases!=null && queryPurchases.size()>0){
                        handlePurchases(queryPurchases);
                    }

                    //check which items are in purchase list and which are not in it
                    //if items that are found add them to purchaseFound
                    //check status of found items and save values to preference
                    //item which are not found simply save false values to their preference
                    //indexOf return index of item in purchase list from 0-3 (because we have 4 items) else returns -1 if not found

                    ArrayList purchaseFound =new ArrayList ();
                    if(queryPurchases!=null && queryPurchases.size()>0){
                        //check item in purchase list
                        for(Purchase p:queryPurchases){
                            int index=purchaseItemIDs.indexOf(p.getSku());
                            //if purchase found
                            if(index>-1)
                            {
                                purchaseFound.add(index);
                                if(p.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
                                {
                                    savePurchaseItemValueToPref(purchaseItemIDs.get(index),true);
                                }
                                else{
                                    savePurchaseItemValueToPref(purchaseItemIDs.get(index),false);
                                }
                            }
                        }
                        //items that are not found in purchase list mark false
                        //indexOf returns -1 when item is not in foundlist
                        for(int i=0;i < purchaseItemIDs.size(); i++){
                            if(purchaseFound.indexOf(i)==-1){
                                savePurchaseItemValueToPref(purchaseItemIDs.get(i),false);
                            }
                        }
                    }
                    //if purchase list is empty that means no item is not purchased
                    //Or purchase is refunded or canceled
                    //so mark them all false
                    else{
                        for( String purchaseItem: purchaseItemIDs ){
                            savePurchaseItemValueToPref(purchaseItem,false);
                        }
                    }

                }
            }

            @Override
            public void onBillingServiceDisconnected() {
            }
        });

        //item Purchased
        if(getPurchaseValueFromPref()){
            purchaseButton.setVisibility(View.GONE);
            purchaseStatus.setText("Purchase Status: Purchased");
            DownloadBtn.setVisibility(View.VISIBLE);
        }
        //item not Purchased
        else{
            purchaseButton.setVisibility(View.VISIBLE);
            purchaseStatus.setText("Purchase Status: Not Purchased");
        }
    }

    private static ArrayList purchaseItemIDs = new ArrayList() {{
        add("abduction");
        add("blocked_rainbow");
        add("blue_cinema");
        add("blue_clouds");
        add("blue_grey");
        add("lightmode_wallpaper");
        add("ic_cloudy_night");
        add("dark_blue");
        add("flowers");
        add("green_blue_plants");
        add("green_clouds");
        add("greeb_lights_with_stars");
        add("green_nightstreak");
        add("hexa_pattern");
        add("hexa_pattern_red");
        add("ic_logo");
        add("lollipop");
        add("moon_with_stars");
        add("ic_night");
        add("ic_clouds_night");
        add("orange_nightstreak");
        add("pink_dase");
        add("rain_drops");
        add("rainbow_ghost");
        add("sunset");
        add("tropical");
        add("white_clouds");
        add("yellow_gradient");
        add("yellow_orange_gradient");
    }};

    private boolean getPurchaseItemValueFromPref(String purchaseItem){
        return getPreferenceObject().getBoolean(purchaseItem,false);
    }
    private void savePurchaseItemValueToPref(String purchaseItem,boolean value){
        getPreferenceEditObject().putBoolean(purchaseItem,value).commit();
    }

    private SharedPreferences getPreferenceObject() {
        return getApplicationContext().getSharedPreferences(PREF_FILE, 0);
    }
    private SharedPreferences.Editor getPreferenceEditObject() {
        SharedPreferences pref = getApplicationContext().getSharedPreferences(PREF_FILE, 0);
        return pref.edit();
    }
    private boolean getPurchaseValueFromPref(){
        return getPreferenceObject().getBoolean( PURCHASE_KEY,false);
    }
    private void savePurchaseValueToPref(boolean value){
        getPreferenceEditObject().putBoolean(PURCHASE_KEY,value).commit();
    }

    //initiate purchase on button click
    public void purchase(View view) {
        //check if service is already connected
        if (billingClient.isReady()) {
            initiatePurchase();
        }
        //else reconnect service
        else{
            billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
            billingClient.startConnection(new BillingClientStateListener() {
                @Override
                public void onBillingSetupFinished(BillingResult billingResult) {
                    if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                        initiatePurchase();
                    } else {
                        Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
                    }
                }
                @Override
                public void onBillingServiceDisconnected() {
                }
            });
        }
    }
    private void initiatePurchase() {
        List<String> skuList = new ArrayList<>();
        skuList.add(PRODUCT_ID);


        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        params.setSkusList(skuList).setType(INAPP);
        billingClient.querySkuDetailsAsync(params.build(),
                new SkuDetailsResponseListener() {
                    @Override
                    public void onSkuDetailsResponse(BillingResult billingResult,
                                                     List<SkuDetails> skuDetailsList) {
                        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                            if (skuDetailsList != null && skuDetailsList.size() > 0) {
                                BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                                        .setSkuDetails(skuDetailsList.get(0))
                                        .build();
                                billingClient.launchBillingFlow(PreviewActivity.this, flowParams);
                            }
                            else{
                                //try to add item/product id "purchase" inside managed product in google play console
                                Toast.makeText(getApplicationContext(),"Purchase Item not Found",Toast.LENGTH_SHORT).show();
                            }
                        } else {
                            Toast.makeText(getApplicationContext(),
                                    " Error "+billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    @Override
    public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
        //if item newly purchased
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
            handlePurchases(purchases);
        }
        //if item already purchased then check and reflect changes
        else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
            Purchase.PurchasesResult queryAlreadyPurchasesResult = billingClient.queryPurchases(INAPP);
            List<Purchase> alreadyPurchases = queryAlreadyPurchasesResult.getPurchasesList();
            if(alreadyPurchases!=null){
                handlePurchases(alreadyPurchases);
            }
        }
        //if purchase cancelled
        else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
            Toast.makeText(getApplicationContext(),"Purchase Canceled",Toast.LENGTH_SHORT).show();
        }
        // Handle any other error msgs
        else {
            Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
        }
    }
    void handlePurchases(List<Purchase>  purchases) {
        for(Purchase purchase:purchases) {
            //if item is purchased
            if (PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
            {
                if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
                    // Invalid purchase
                    // show error to user
                    Toast.makeText(getApplicationContext(), "Error : Invalid Purchase", Toast.LENGTH_SHORT).show();
                    return;
                }
                // else purchase is valid
                //if item is purchased and not acknowledged
                if (!purchase.isAcknowledged()) {
                    AcknowledgePurchaseParams acknowledgePurchaseParams =
                            AcknowledgePurchaseParams.newBuilder()
                                    .setPurchaseToken(purchase.getPurchaseToken())
                                    .build();
                    billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase);
                }
                //else item is purchased and also acknowledged
                else {
                    // Grant entitlement to the user on item purchase
                    // restart activity
                    if(!getPurchaseValueFromPref()){
                        savePurchaseValueToPref(true);
                        Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
                        this.recreate();
                    }
                }
            }
            //if purchase is pending
            else if( PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PENDING)
            {
                Toast.makeText(getApplicationContext(),
                        "Purchase is Pending. Please complete Transaction", Toast.LENGTH_SHORT).show();
            }
            //if purchase is unknown
            else if(PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE)
            {
                savePurchaseValueToPref(false);
                purchaseStatus.setText("Purchase Status : Not Purchased");
                purchaseButton.setVisibility(View.VISIBLE);
                Toast.makeText(getApplicationContext(), "Purchase Status Unknown", Toast.LENGTH_SHORT).show();
            }
        }
    }
    AcknowledgePurchaseResponseListener ackPurchase = new AcknowledgePurchaseResponseListener() {
        @Override
        public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
            if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
                //if purchase is acknowledged
                //then it is auto saved in preference later during app restart
                Toast.makeText(getApplicationContext(), "Item Purchased. Please restart Activity", Toast.LENGTH_SHORT).show();
            }
        }
    };


    /**
     * Verifies that the purchase was signed correctly for this developer's public key.
     * <p>Note: It's strongly recommended to perform such check on your backend since hackers can
     * replace this method with "constant true" if they decompile/rebuild your app.
     * </p>
     */
    private boolean verifyValidSignature(String signedData, String signature) {
        try {
            // To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
            String base64Key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhBlajrk5nNjpRTPJjDtGxgtgAeKijz3Wc0KrRKKSCxxsViHl7DhsI+sUZfk4Y1jxSg4/3W1uRo/0UASM77XfJIq34bK9KYgoSAGYSuH8Z+4fK/MrPz7dHhsljkAi4GZkv8x9VhZdDdpn2GSHVFaxs8c+HBOFp9aWAErHrQhi9/7fYf39pQSTC3WkVcy9xNDZxiiKTfDN3dyEvS0XQ617ZJwqDuRdkU5Aw9+R8r+oXyURV/ekgCQkWfCUaTp/jWdySOIcR87Bde24lQAXbvJaL5uAYI4zPwO4sIP1AbXLuDtv3N2rFVmP/1cML/NHDcfI5FOoStz88jzJU26Ngpqu1QIDAQAB";
            return Security.verifyPurchase(base64Key, signedData, signature);
        } catch (IOException e) {
            return false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(billingClient!=null){
            billingClient.endConnection();
        }
    }
}

Here's one image example

Here's a second example

我还有 29 张图片要出售。如你所见,我没有 完全实现了应用内计费,因为我不知道该怎么做 现在。我已经设置了我的应用内产品。我会处理如何 用户获取图像,但我想知道的是我如何实现应用内 计费。

那么如何实现应用内计费,让用户可以购买图片 由他们选择。

更新

我找到了一个解释如何实现多个应用内购买的教程,但我对这些行有疑问:

 ArrayList purchaseFound =new ArrayList ();
                    if(queryPurchases!=null && queryPurchases.size()>0){
                        //check item in purchase list
                        for(Purchase p:queryPurchases){
                            int index=purchaseItemIDs.indexOf(p.getSku());
                            //if purchase found
                            if(index>-1)
                            {
                                purchaseFound.add(index);
                                if(p.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
                                {
                                    savePurchaseItemValueToPref(purchaseItemIDs.get(index),true);
                                }
                                else{
                                    savePurchaseItemValueToPref(purchaseItemIDs.get(index),false);
                                }
                            }
                        }
                        //items that are not found in purchase list mark false
                        //indexOf returns -1 when item is not in foundlist
                        for(int i=0;i < purchaseItemIDs.size(); i++){
                            if(purchaseFound.indexOf(i)==-1){
                                savePurchaseItemValueToPref(purchaseItemIDs.get(i),false);
                            }
                        }
                    }
                    //if purchase list is empty that means no item is not purchased
                    //Or purchase is refunded or canceled
                    //so mark them all false
                    else{
                        for( String purchaseItem: purchaseItemIDs ){
                            savePurchaseItemValueToPref(purchaseItem,false);
                        }
                    }

                }
          

我不断收到此错误。

错误:不兼容的类型:对象无法转换为购买 for(购买 p:queryPurchases){ ^

我该如何解决这个问题?

【问题讨论】:

    标签: java android-studio in-app-purchase in-app-billing google-play-console


    【解决方案1】:

    您可以在以下位置找到有关如何实施 Google Play 结算库的更多信息:https://developer.android.com/google/play/billing/integrate

    您可以在此处找到有关如何设置库以及如何实施购买的详细说明。

    【讨论】:

    • 我很难理解教程中的一些代码。你知道其他可以更好地解释这一点的网站吗?
    • 您能否提供更多有关您的问题的信息?我确信我们可以找到解决方案,但首先我们必须了解您的问题是什么。
    • 文字解释有点困难。我可以向您发送我的简短视频链接,解释我想要和不想要发生的事情吗?
    【解决方案2】:
    error: incompatible types: Object cannot be converted to Purchase for(Purchase p:queryPurchases){ ^
    

    您必须调用显示 void 的错误,其中参数必须如下所示

    List<Purchase> queryPurchases
    

    你也没有提交完整的代码

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-14
      • 1970-01-01
      • 2021-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 1970-01-01
      相关资源
      最近更新 更多