已实现 Firebase 实时数据库存储的 Android 应用中的多个应用内产品

Posted

技术标签:

【中文标题】已实现 Firebase 实时数据库存储的 Android 应用中的多个应用内产品【英文标题】:Multiple in-app products in android app that has implemented Firebase Realtime Database storage 【发布时间】: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) ^

我该如何解决这个问题?

【问题讨论】:

【参考方案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

你也没有提交完整的代码

【讨论】:

以上是关于已实现 Firebase 实时数据库存储的 Android 应用中的多个应用内产品的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Firebase 实时数据库创建特定用户所有已保存帖子的列表?

Firebase 数据库存储大小限制

SwiftUI - 将存储中图像文件的路径移动到 Firebase 上的实时数据库 [重复]

从浏览器手动将图像存储在 Firebase 存储中。如何使其反映在实时数据库中。这样我就可以在 Android 应用程序中获取

从 Firebase 实时数据库中获取需要几秒钟 [已修改]

Firebase:实时数据库和文件存储之间的差异