Flutter 调用Google内购支付最新教程

前言:

各位同学大家好, 之前看到有人在群里问flutter 怎么调用Google支付, 今天就准备整理写一篇文章。

效果图

实现方式:

我们是通过flutter和安卓交互 然后在原生安卓里面加入了内购支付结算库的依赖 最后调起的Google 支付 安卓原生内购支付教程

flutter 端代码

我们在flutter代码里面 现在写一个 Column里面包含一个 FlatButton 和一个Text

scss 复制代码
   body:Column(
        children: <Widget>[
          FlatButton(
            child: Text("调用native 接口"),
            onPressed: () async{
              String  result=await platform.invokeMethod("call_native_method");
              setState(() {
                _result=result;
                print("_result ---->"+_result);
            });
            },
          ),
          Text("result is:      "+_result)
        ],
      ),

flutter 端完整代码 flutter main.dart

scala 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);
  @override
  _MyHomePageState createState() {
    return _MyHomePageState();
  }
}
class _MyHomePageState extends State<MyHomePage> {
  static const String CHINAL_NAME="samples.flutter.study/call_native";
  static const platform=const MethodChannel(CHINAL_NAME);
  String  _result="";
  @override
  void initState() {
    super.initState();
  }
  @override
  void dispose() {
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
     appBar: AppBar(
       title: Text("flutter和native通信"),
     ),
      body:Column(
        children: <Widget>[
          FlatButton(
            child: Text("调用native 接口"),
            onPressed: () async{
              String  result=await platform.invokeMethod("call_native_method");
              setState(() {
                _result=result;
                print("_result ---->"+_result);
            });
            },
          ),
          Text("result is:      "+_result),
        ],
      ),
    );
  }
}

然后我们定义跟安卓原生交互的变量 CHINAL_NAME

ini 复制代码
  static const String CHINAL_NAME="samples.flutter.study/call_native";
  static const platform=const MethodChannel(CHINAL_NAME);
String  _result="";

这里的CHINAL_NAME要跟在安卓原生里面的对应上

然后我们在FlatButton 点击事件里面进行接收安卓原生的回传的消息

ini 复制代码
  FlatButton(
            child: Text("调用native 接口"),
            onPressed: () async{
              String  result=await platform.invokeMethod("call_native_method");
              setState(() {
                _result=result;
              //这里将我们拿到回传的值 赋值给我们自己定义的  _result  然后打
              //印到控制台观察 
                print("_result ---->"+_result);
            });
            },
          ),

安卓原生部分

我们需要导入flutter里面的安卓宿主工程

官方文档地址

Google结算库

需要的依赖

bash 复制代码
def billing_version = "6.0.0"
implementation "com.android.billingclient:billing:$billing_version"

flutter调用安卓通信

less 复制代码
String   C_NAME="samples.flutter.study/call_native";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
   super.configureFlutterEngine(flutterEngine);
    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),C_NAME)
            .setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                @Override
                public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {

                    toGooglePay("XXXX"); //这里换成你自己的商品ID
                    if(call.method.equals("call_native_method")){
                       result.success("我是安卓原生回传回来的数据");
                   }else{
                       result.success("I don't know what you say");
                   }

                }
            });


}

内购支付初始化

typescript 复制代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initgooglePlay();


}

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() {

            }
        });
    }
}

调用起内购支付

scss 复制代码
private  void  toGooglePay(String productId){
//  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();
                      }
                  }
              }
      );
  }
}

我们在flutter调用安卓原生的回调方法里面去调用我们的 toGooglePay 传入我们的商品ID 即可

支付回调

less 复制代码
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 信息之后可以去自己服务端进行验签 如果验签成功了才算是真正的成功支付成功 然后再进行消耗

商品消耗

scss 复制代码
   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);
        }

    }

支付完成后下次支付前查询消耗

scss 复制代码
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 声明周期方法里面调用

scss 复制代码
    @Override
    protected void onResume() {
        super.onResume();
        queryPurchasesAsync();

    }

完整原生内购支付代码

scss 复制代码
package com.testgame.demo;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
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 java.util.ArrayList;
import java.util.List;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

/**
*
*创建人:xuqing
* 创建时间:2024年03月13日 11:04:31
* 类说明:Flutter调起内购支付
*
*/

public class MainActivity extends FlutterActivity {

  private static final String TAG = "MainActivity";

  private Context context=MainActivity.this;
  private BillingClient billingClient;
  String   C_NAME="samples.flutter.study/call_native";
  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
      super.configureFlutterEngine(flutterEngine);
       new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),C_NAME)
               .setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                   @Override
                   public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {

                       toGooglePay("xxxxxx");
                       if(call.method.equals("call_native_method")){
                          result.success("我是安卓原生回传回来的数据");
                      }else{
                          result.success("I don't know what you say");
                      }

                   }
               });


  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      initgooglePlay();


  }

  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);
      }
  }




  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){
    //  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 -

最后总结:

其实调用内购支付也有插件, 我暂时没时间去研究 就先用这种方式实现 主要就是和安卓交互 然后再通过原生的方法去调用官方的api最后调起内购支付 如果你是第一次接入就按照次文档或者官方最新文档接入即可 最后呢 希望我都文章能帮助到各位同学工作和学习 如果你觉得文章还不错麻烦给我三连 关注点赞和转发 谢谢

相关推荐
AiFlutter15 小时前
Flutter之Package教程
flutter
Mingyueyixi19 小时前
Flutter Spacer引发的The ParentDataWidget Expanded(flex: 1) 惨案
前端·flutter
crasowas1 天前
Flutter问题记录 - 适配Xcode 16和iOS 18
flutter·ios·xcode
老田低代码2 天前
Dart自从引入null check后写Flutter App总有一种难受的感觉
前端·flutter
AiFlutter3 天前
Flutter Web首次加载时添加动画
前端·flutter
ZemanZhang4 天前
Flutter启动无法运行热重载
flutter
AiFlutter4 天前
Flutter-底部选择弹窗(showModalBottomSheet)
flutter
帅次5 天前
Android Studio:驱动高效开发的全方位智能平台
android·ide·flutter·kotlin·gradle·android studio·android jetpack
程序者王大川5 天前
【前端】Flutter vs uni-app:性能对比分析
前端·flutter·uni-app·安卓·全栈·性能分析·原生
yang2952423615 天前
使用 Vue.js 将数据对象的值放入另一个数据对象中
前端·vue.js·flutter