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最后调起内购支付 如果你是第一次接入就按照次文档或者官方最新文档接入即可 最后呢 希望我都文章能帮助到各位同学工作和学习 如果你觉得文章还不错麻烦给我三连 关注点赞和转发 谢谢

相关推荐
钛态11 小时前
Flutter for OpenHarmony:mockito 单元测试的替身演员,轻松模拟复杂依赖(测试驱动开发必备) 深度解析与鸿蒙适配指南
服务器·驱动开发·安全·flutter·华为·单元测试·harmonyos
念格14 小时前
Flutter 弹窗 UI 不刷新?用 StatefulBuilder 解决
flutter
程序员老刘15 小时前
2026春招Flutter岗位为何变少?我看到的3个招聘逻辑变化
flutter·ai编程·客户端
念格16 小时前
Flutter 实现点击任意位置收起键盘的最佳实践
flutter
念格16 小时前
Flutter ListView Physics 滚动物理效果详解
flutter
国医中兴16 小时前
ClickHouse的数据模型设计:从理论到实践
flutter·harmonyos·鸿蒙·openharmony
国医中兴19 小时前
ClickHouse数据导入导出最佳实践:从性能到可靠性
flutter·harmonyos·鸿蒙·openharmony
国医中兴19 小时前
大数据处理的性能优化技巧:从理论到实践
flutter·harmonyos·鸿蒙·openharmony
●VON21 小时前
Flutter 入门指南:从基础组件到状态管理核心机制
前端·学习·flutter·von
西西学代码21 小时前
Flutter---SingleChildScrollView
前端·javascript·flutter