前语
各位同学 咱们好有段时刻没有个咱们更新文章 ,最近在更新google的最新结算库 。所以就在这儿分享给废话不多说。
官方文档地址
Google结算库
需求的依靠
def billing_version = "6.0.0"
implementation "com.android.billingclient:billing:$billing_version"
请添加到build.gralde
效果图 :
具体接入
-
付出初始化
private BillingClient billingClient;
billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
if(billingClient!=null){
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
}
-
连接Google查询产品ID 信息
String productId ="xxxxx"; //请替换成自己的产品id
List<QueryProductDetailsParams.Product> productList= new ArrayList<>();
productList.add(QueryProductDetailsParams.Product.newBuilder()
.setProductId(productId)
.setProductType(BillingClient.ProductType.INAPP)
.build());
QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().
setProductList(productList)
.build();
if(billingClient!=null){
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult,
List<ProductDetails> productDetailsList) {
}
}
);
}
-
调起付出界面
ProductDetails productDetails=productDetailsList.get(0);
List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList= new ArrayList<>();
productDetailsParamsList.add(BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build());
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.build();
billingClient.launchBillingFlow((Activity) context, billingFlowParams);
-
付出回调
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener(){
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
Log.e(TAG, "getPurchaseToken: "+ purchase.getPurchaseToken() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
} else {
}
}
};
咱们拿到回调里边的 getPurchaseToken getSignature getSignature 信息之后能够去自己服务端进行验签 假如验签成功了才算是真实的成功付出成功 然后再进行耗费
-
产品耗费
private void handlePurchase(final Purchase purchase) {
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.e(TAG, "getPurchaseToken: "+ purchase.getPurchaseToken() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
}
}
};
if(billingClient!=null){
billingClient.consumeAsync(consumeParams, listener);
}
}
-
付出完成后下次付出前查询耗费
private void queryPurchasesAsync(){
if(billingClient!=null){
if(!billingClient.isReady()){
Toast.makeText(context,"BillingClient is not ready",Toast.LENGTH_SHORT).show();
}
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(),
new PurchasesResponseListener() {
public void onQueryPurchasesResponse(
BillingResult billingResult,
List<Purchase> purchases) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
if(purchases!=null&&purchases.size()>0){
for (Purchase purchase:purchases){
handlePurchase(purchase);
}
}
}
}
}
);
}
}
请再 onResume 声明周期方法里边调用
@Override
protected void onResume() {
super.onResume();
queryPurchasesAsync();
}
完整接入示例
package com.example.myapplication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
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.ConsumeParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.ProductDetails;
import com.android.billingclient.api.ProductDetailsResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesResponseListener;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.QueryProductDetailsParams;
import com.android.billingclient.api.QueryPurchasesParams;
import com.testgame.demo.R;
import java.util.ArrayList;
import java.util.List;
/**
* 创建人:xuqing
* 创建时刻:2023年5月18日14:53:03
* 类说明:google付出测试
*
*
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "-----MainActivity-----";
private Context context=MainActivity.this;
private BillingClient billingClient;
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
Log.e(TAG, "getPurchaseToken: "+ purchase.getPurchaseToken() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
} else {
}
}
};
private void handlePurchase(final Purchase purchase) {
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.e(TAG, "getPurchaseToken: "+ purchase.getPurchaseToken() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
Log.e(TAG, "getSignature: "+ purchase.getSignature() );
}
}
};
if(billingClient!=null){
billingClient.consumeAsync(consumeParams, listener);
}
}
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initgooglePlay();
findViewById(R.id.togooglepaybtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toGooglePay();
}
});
}
private void initgooglePlay() {
billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
if(billingClient!=null){
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
}
}
private void toGooglePay(){
String productId ="xxxx";
List<QueryProductDetailsParams.Product> productList= new ArrayList<>();
productList.add(QueryProductDetailsParams.Product.newBuilder()
.setProductId(productId)
.setProductType(BillingClient.ProductType.INAPP)
.build());
QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().
setProductList(productList)
.build();
if(billingClient!=null){
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult,
List<ProductDetails> productDetailsList) {
if(productDetailsList!=null&&productDetailsList.size()>0){
ProductDetails productDetails=productDetailsList.get(0);
List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList= new ArrayList<>();
productDetailsParamsList.add(BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build());
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.build();
billingClient.launchBillingFlow((Activity) context, billingFlowParams);
}else {
Toast.makeText(context,"产品ID无效",Toast.LENGTH_SHORT).show();
}
}
}
);
}
}
private void queryPurchasesAsync(){
if(billingClient!=null){
if(billingClient.isReady()){
Toast.makeText(context,"BillingClient is not ready",Toast.LENGTH_SHORT).show();
}
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(),
new PurchasesResponseListener() {
public void onQueryPurchasesResponse(
BillingResult billingResult,
List<Purchase> purchases) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
if(purchases!=null&&purchases.size()>0){
for (Purchase purchase:purchases){
handlePurchase(purchase);
}
}
}
}
}
);
}
}
@Override
protected void onResume() {
super.onResume();
queryPurchasesAsync();
}
@Override
public void onStop() {
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
}
}
需求注意的点
咱们的google 付出初始化不要多次初始化 否则会呈现多次回调的 会影响到咱们付出回调里边进行数据上报的逻辑 。付出耗费查询也能够放在每次付出之前先查询后再去进行下一笔付出 ,还有示例中部分底和官方写法不一样是我改过的 ImmutableList.of 这个其实Java里边的 声明一个非空集合 因为我这边一向导入不了 所以就改掉了 其他代码跟官方一毛一样
付出调不起来的几个原因
1 确保拿到产品ID 是正确的能够在google play后台检查到 2 确保手机里边 Google账号是能够付出的 3、安装的app包的versionName、versionCode 和 Google Play Console上传的不一样 Google Play Console – 一切使用 – 检查使用 (上传安装的app或者修改版别号) 4、安装的app包的签名和上传到Google Play Console的包签名不一致 Google Play Console – 一切使用 – 检查使用 -设置 – 使用完整性 (检查签名是否一致) 5 确保vpn 翻墙工具安稳
最终总结:
假如你是4.0.0版别的结算库 请服务检查 从 Google Play 结算库版别 4 或 5 迁移到版别 6 说明里边有些api 还是有改动 最好用最新方法 不要用过时的方法 假如你是第一次接入就依照次文档或者官方最新文档接入即可 最终呢 希望我都文章能帮助到各位同学工作和学习 假如你觉得文章还不错麻烦给我三连 关注点赞和转发 谢谢