前言:
各位同学大家好, 之前看到有人在群里问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里面的安卓宿主工程
官方文档地址
需要的依赖
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最后调起内购支付 如果你是第一次接入就按照次文档或者官方最新文档接入即可 最后呢 希望我都文章能帮助到各位同学工作和学习 如果你觉得文章还不错麻烦给我三连 关注点赞和转发 谢谢