已实现 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 实时数据库创建特定用户所有已保存帖子的列表?
SwiftUI - 将存储中图像文件的路径移动到 Firebase 上的实时数据库 [重复]
从浏览器手动将图像存储在 Firebase 存储中。如何使其反映在实时数据库中。这样我就可以在 Android 应用程序中获取