几年前,我制作了一个Android应用程序,但该项目被错误删除了。几年后,我决定再写一次。所以我已经到了我想添加应用程序购买(删除广告)的部分,但不知何故它没有按计划工作。我尝试按照文档并在线搜索,但没有成功。
我的应用程序 build.gradle 中有这些:
dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.test.espresso:espresso-core:3.5.1")
val billing_version = "6.1.0"
implementation("com.android.billingclient:billing:$billing_version")
implementation("com.android.billingclient:billing-ktx:$billing_version")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
这就是我的 Java 类的样子:
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
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 java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class InfoController extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
// Billing variables
private BillingClient billingClient;
private PurchasesUpdatedListener purchasesUpdatedListener;
private SkuDetails myProductSkuDetails; // Define SkuDetails field
List<String> skuList = Arrays.asList("com.xxxxxxx.yyyyyyyyyyyyy.pro");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_info);
upgradeButton = findViewById(R.id.upgradeButton);
billingClient = BillingClient.newBuilder(this)
.setListener(this)
.enablePendingPurchases()
.build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The billing client is ready, query SkuDetails here
querySkuDetails();
} else {
// Handle the error
Toast.makeText(InfoController.this, "Billing setup failed: " + billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onBillingServiceDisconnected() {
// Handle the case when the billing service is disconnected
Toast.makeText(InfoController.this, "Billing service disconnected", Toast.LENGTH_SHORT).show();
}
});
}
private void querySkuDetails() {
// Query SkuDetails for your product
List<String> skuList = Arrays.asList("com.xxxxxxx.yyyyyyyyyyyyy.pro");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
Log.d("BillingDebug", "Before querySkuDetailsAsync");
billingClient.querySkuDetailsAsync(params.build(), (billingResult, skuDetailsList) -> {
Log.d("BillingDebug", "Inside querySkuDetailsAsync callback");
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
// Use the first SkuDetails object
Toast.makeText(this, "Billing OK", Toast.LENGTH_SHORT).show();
myProductSkuDetails = skuDetailsList.get(0);
} else {
Toast.makeText(this, "Failed to retrieve SKU details", Toast.LENGTH_SHORT).show();
}
});
Log.d("BillingDebug", "After querySkuDetailsAsync");
}
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
// Implement your logic here when purchases are updated
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) {
// Handle the purchase
Toast.makeText(this, "OK", Toast.LENGTH_SHORT).show();
}
} else {
// Handle an error
Toast.makeText(this, "ERROR", Toast.LENGTH_SHORT).show();
}
}
public void onUpgradeButtonClick(View view) {
if (myProductSkuDetails != null) {
Toast.makeText(this, "Not null", Toast.LENGTH_SHORT).show();
// Create a BillingFlowParams object
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(myProductSkuDetails)
.build();
// Launch the billing flow
BillingResult result = billingClient.launchBillingFlow(this, billingFlowParams);
if (result.getResponseCode() != BillingClient.BillingResponseCode.OK) {
// Handle the error
}
} else {
// SkuDetails not available, handle accordingly
Toast.makeText(this, "SkuDetails not available", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (billingClient != null) {
billingClient.endConnection();
}
}
}
问题:
因此,当我加载此活动时,它打开正常,没有错误,也没有 Toast 消息。但是当我按
onUpgradeButtonClick
时,它会给我 Toast 消息:SkuDetails not available
。
我在支持 Google Play 商店的模拟器 Pixel 7 API 34 上运行此程序,并且我已登录到 Google Play 商店。
有什么建议吗?
根据官方 google-in-app-billing 文档,您应该
SkuDetailsParams
替换为 QueryProductDetailsParams
BillingClient.querySkuDetailsAsync()
呼叫以使用 BillingClient.queryProductDetailsAsync()
更新
querySkuDetails()
功能
private void querySkuDetails() {
// QueryProductDetailsParams for your product
QueryProductDetailsParams.Product inAppProduct = QueryProductDetailsParams.Product.newBuilder()
.setProductId("com.xxxxxxx.yyyyyyyyyyyyy.pro")
.setProductType(BillingClient.ProductType.INAPP)
.build();
List<QueryProductDetailsParams.Product> productList = Arrays.asList(inAppProduct);
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build();
billingClient.queryProductDetailsAsync(params, (billingResult, productDetailsList) -> {
// productDetailsList will be List<ProductDetails>
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && !productDetailsList.isEmpty()) {
// Use the first productDetailsList object
Toast.makeText(this, "Billing OK", Toast.LENGTH_SHORT).show();
myProductSkuDetails = productDetailsList.get(0);
} else {
Toast.makeText(this, "Failed to retrieve SKU details", Toast.LENGTH_SHORT).show();
}
});
}
更新
queryProductDetailsAsync
后,您的myProductSkuDetails
对象将更改为ProductDetails
private ProductDetails myProductSkuDetails; // Define SkuDetails field
请参阅 integrate-google-in-app-billing 和 Migration-to-goggle-play-billing-v6 官方文档了解详细信息。