目录

一、创建空安卓库工程

二、Unity配置

三、注意事项

版本更新注意事项

服务器相关(相关文章如下)

支付相关错误码


https://developer.android.com/google/play/billing/integrate

一、创建空安卓库工程

应用gradle文件添加依赖

dependencies {def billing_version = "4.0.0"implementation "com.android.billingclient:billing:$billing_version"
}

源码:

package com.test.googlebillingandroidproj;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;import android.app.Activity;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;import com.android.billingclient.api.AcknowledgePurchaseParams;
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
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.Purchase;
import com.android.billingclient.api.PurchasesResponseListener;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.unity3d.player.UnityPlayer;import java.util.ArrayList;
import java.util.List;public class MainActivity extends UnityPlayerActivity {//初始化Google监听支付成功失败private PurchasesUpdatedListener purchasesUpdatedListener;private BillingClient billingClient;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//初始化Google监听支付成功失败purchasesUpdatedListener = new PurchasesUpdatedListener() {@Overridepublic void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) {//支付回调int responseCode = billingResult.getResponseCode();String debugMsg = billingResult.getDebugMessage();Log.d("GooglePay", "responseCode: " + responseCode + ", debugMsg: " + debugMsg);if(null != purchases && BillingClient.BillingResponseCode.OK == responseCode) {for(Purchase purchase : purchases) {// TODO 通知服务端发货,发货成功后,把订单关闭Log.d("Unity", "通知服务端发货,发货成功后,把订单关闭");ToaskMakeTest("订单成功支付 准备进行确认操作 服务器下发奖励" + purchase.getPurchaseToken());//测试用例 当做成功 正式需服务器通知再handlePurchasehandlePurchase(purchase);   // 注意,必须确保服务器发货成功后再执行handlePurchase}} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {// Handle an error caused by a user cancelling the purchase flow.Log.d("Unity", "11111111111111111 用户关闭订单");ToaskMakeTest("用户关闭订单");} else {// Handle any other error codes.//TODO  E Unity   : 支付其他错误码:4 (成乔测试)  ITEM_UNAVAILABLE = 4;  物品不可用?// 解决方法:测试人员必须通过链接登录邮箱点击接受测试..Log.e("Unity", "支付其他错误码:" + responseCode);ToaskMakeTest("支付其他错误码:" + responseCode);}}};billingClient = BillingClient.newBuilder(MainActivity.this).setListener(purchasesUpdatedListener).enablePendingPurchases().build();InitGooglePay();}public boolean IsConnectGoogleServer(){return billingClient.isReady();}//初始化Google支付public void InitGooglePay(){//连接google服务器billingClient.startConnection(new BillingClientStateListener() {@Overridepublic void onBillingSetupFinished(BillingResult billingResult) {if (billingResult.getResponseCode() ==  BillingClient.BillingResponseCode.OK) {// The BillingClient is ready. You can query purchases here.Log.i("Unity", "Google connect successfully!");ToaskMakeTest("Google connect successfully!");SearchGood(); //查询出所有现有的货品详情 //测试..}else{//TODO 问题一直无法进行连接谷歌服务器 返回3, 支付无效  可能是手机谷歌框架不匹配手机本身 或者 必须用release发布版并且等成功后//解决方法:VPN换美国线路 ***美国账号Google邮箱*** 测试人员需经过测试链接确认进入测试模式Log.e("Unity", "Google connect server fail!  code:" + billingResult.getResponseCode());ToaskMakeTest("Google connect server fail!  code:" + billingResult.getResponseCode());}}@Overridepublic void onBillingServiceDisconnected() {// Try to restart the connection on the next request to// Google Play by calling the startConnection() method.Log.i("Unity", "Google disconnect!!");ToaskMakeTest("Google disconnect!!");}});}//消耗型商品订单确认(订单关闭)//订单关闭(完成支付成功后 通知服务器发送货物成功后执行的行为  或者 客户端主动取消支付执行关闭订单)【消耗性商品确认流程】//purchase参数从PurchasesUpdatedListener支付成功回调拿到//或者 从玩家登陆服务器时发起补单BillingClient#queryPurchases行为获取到直接处理关闭订单(补单也是要确认服务器发货成功)void handlePurchase(Purchase purchase) {ConsumeParams consumeParams =ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();ConsumeResponseListener listener = new ConsumeResponseListener() {@Overridepublic void onConsumeResponse(BillingResult billingResult, String purchaseToken) {if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {// Handle the success of the consume operation.//订单关闭完毕Log.i("Unity", "订单确认完毕!");ToaskMakeTest("流程跑通 订单确认完毕!");}else{Log.e("Unity", "订单确认异常, code:" + billingResult.getResponseCode());ToaskMakeTest("订单确认异常, code:" + billingResult.getResponseCode());}}};billingClient.consumeAsync(consumeParams, listener);}private void ToaskMakeTest(String str) {
//        if (Looper.myLooper() == Looper.getMainLooper()) { // UI主线程
//            Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
//        } else { // 非UI主线程
//            Looper.prepare();
//            Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
//            Looper.loop();
//        }}//通过id查询并购买商品(可行)public void SearchAndPurchaseById(String id) {Log.i("Unity","准备购买商品:" + id + " 已连接Google服务器? " + IsConnectGoogleServer());List<String> skuList = new ArrayList<>();skuList.add(id);SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);billingClient.querySkuDetailsAsync(params.build(),new SkuDetailsResponseListener() {@Overridepublic void onSkuDetailsResponse(BillingResult billingResult,List<SkuDetails> skuDetailsList) {// Process the result.int resultCode = billingResult.getResponseCode();if (BillingClient.BillingResponseCode.OK != resultCode) {Log.e("Unity", String.format("购买商品时,查询失败。错误代码:%s", resultCode));ToaskMakeTest(String.format("购买商品时,查询失败。错误代码:%s", resultCode));} else if (skuDetailsList != null && skuDetailsList.size() > 0) {for (SkuDetails skuDetails : skuDetailsList) {Log.i("Unity", "查询出物品列表 物品id:" + skuDetails.getSku() + ", title:" + skuDetails.getTitle());if(id.equals(skuDetails.getSku())){Activity activity = MainActivity.this;BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();//                .setObfuscatedAccountId("10000001") //直接塞入用户IDint responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode();Log.i("Unity", "支付[" + id + "] 开始Start launchBillingFlow >> responseCode:" + responseCode);ToaskMakeTest("支付[" + id + "] 开始Start launchBillingFlow >> responseCode:" + responseCode);}}} else {Log.e("Unity", "购买商品异常失败, SkuDetailsList = " + skuDetailsList);ToaskMakeTest("购买商品异常失败, SkuDetailsList = " + skuDetailsList);}}});}///连上服务器时缓存所有商品并展览public void SearchGood() {Log.i("Unity", "开始查询所有物品!");List<String> skuList = new ArrayList<>();skuList.add("good_1004");skuList.add("good_10022");skuList.add("good_1003");SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);billingClient.querySkuDetailsAsync(params.build(),new SkuDetailsResponseListener() {@Overridepublic void onSkuDetailsResponse(BillingResult billingResult,List<SkuDetails> skuDetailsList) {// Process the result.int resultCode = billingResult.getResponseCode();if (BillingClient.BillingResponseCode.OK != resultCode) {Log.e("Unity", String.format("购买商品时,查询失败。错误代码:%s", resultCode));ToaskMakeTest(String.format("购买商品时,查询失败。错误代码:%s", resultCode));} else if (skuDetailsList != null && skuDetailsList.size() > 0) {for (SkuDetails skuDetails : skuDetailsList) {Log.i("Unity", "查询出物品列表 物品id:" + skuDetails.getSku() + ", title:" + skuDetails.getTitle());ToaskMakeTest("查询出物品列表 物品id:" + skuDetails.getSku() + ", title:" + skuDetails.getTitle());}} else {Log.e("Unity", "购买商品异常失败, SkuDetailsList = " + skuDetailsList);ToaskMakeTest("购买商品异常失败, SkuDetailsList = " + skuDetailsList);}}});}//客户端登陆服务器成功后,补发货物public void Replenishment(){ToaskMakeTest("补发货物");//异步请求补发订单billingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP, new PurchasesResponseListener() {@Overridepublic void onQueryPurchasesResponse(@NonNull BillingResult billingResult, @NonNull List<Purchase> list) {if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {for(Purchase purchase : list) {if (Purchase.PurchaseState.PURCHASED == purchase.getPurchaseState()) {// TODO 通知服务端补发货,发货完成后,客户端关闭订单。//purchase.getSkus() 订单货物ID列表?String strIds = "";for(String skuId : purchase.getSkus()){strIds += skuId + "|";Log.i("Unity", "skuId:" + skuId);}ToaskMakeTest("补发货物id:" + strIds + ", 订单token:" + purchase.getPurchaseToken() + ", order id:" + purchase.getOrderId());//TODO 需通知服务器补发货物,根据商品ID,若服务器已经发货,则直接回调客户端确认订单purchase, 否则发货 再确认,都是要通知客户端处理handlePurchase(purchase);   // 注意,必须确保服务器发货成功后再执行handlePurchase}}}}});}//非消耗型商品订单确认(订单关闭)void handleOtherPurchase(Purchase purchase) {BillingClient client = billingClient;if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {if (!purchase.isAcknowledged()) {AcknowledgePurchaseParams acknowledgePurchaseParams =AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();client.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {@Overridepublic void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {}});}}}
}

主要Unity需调用

SearchAndPurchaseById函数

进行购买具体商品,传递商品ID,商品ID的配置在Google Console的内部应用商品里配置。

商品token验证上面源码是客户端进行的,正常要服务器进行验证是否有效token。

​​​​​​https://developer.android.com/google/play/billing/security

 AndroidManifest.xml配置

需添加uses-permission和meta-data如下所示。

    <uses-permission android:name="com.android.vending.BILLING" /><application><activityandroid:name="com.test.googlebillingandroidproj.MainActivity"android:exported="true" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter><meta-dataandroid:name="unityplayer.UnityActivity"android:value="true" /></activity></application>

最终打包成aar 获取里面的classes.jar和AndroidManifest 移动到Plugins/Android下。

注意:classes.jar 有一个文件 BuildConfig.classes删掉。

二、Unity配置

https://mvnrepository.com/

进入这个网址下载billing-4.0.0 arr包,导入Plugins/Android下。它是sdk的依赖包

紧接着Unity的C#代码

using UnityEngine;public class GoogleBillingComponent : MonoBehaviour
{AndroidJavaClass jc;AndroidJavaObject jo;// Start is called before the first frame updatevoid Start(){jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");jo = jc.GetStatic<AndroidJavaObject>("currentActivity");}private void OnDestroy(){jo.Dispose();jc.Dispose();jo = null;jc = null;}public void SearchAndPurchaseById(){Debug.Log("Unity ~~RequestAndroidGoogleLogin~~~~~~~~~~~~~~~~~");jo.Call("SearchAndPurchaseById", "good_1003");}
}

三、配置Google项目

https://play.google.com/console/

按步骤创建你的应用,要全部!全部!都填写好!比如 应用的图标 等各种无关紧要的东西 都要填写好,不然可能会出点奇奇怪怪的问题。

创建内部商品

测试时如果没有支付google的银行卡 可以用

创建免费的code去直接买商品(测试能创建100个)注意它的时间设定是在我们北京时间的-8小时。 即你想在早上10点  到 中午12点,开放code, 那应该是 填写 2点 到 4点。(格林尼治标准时间 (GMT))也就是说 北京时间-8小时 才是我们要填写的时间值,我们早上10点,就是格林尼治时间的2点,所以应该填2点!

三、注意事项

1、手机必须要能连外网

2、手机必须要有Google三件套

3、需要一个国外的Google邮箱(最好是漂亮国的)且不能是开发者账号。

4、需要通过邮箱测试验证。

要填写你的测试邮箱到测试列表里,反馈邮箱无所谓,然后复制下面的链接,传给测试人员去打开这个网页 进行验证邮箱 确认进行测试。(这一步很的重要!!!!

2022年2月11日更新:

正式测试支付功能需要添加许可测试,将你测试支付的google账号添加进去。

如果发现报错code:3 无法拉起支付窗口,则说明你的账号或网络有问题,需要换一个网络代理或换到美服账号。

版本更新注意事项

更新aab时若没有变更后台数据(例如:充值项)直接在下面这里提交最新的aab更新即可

服务器相关(相关文章如下)

http://www.wangwenyong.com/?p=306

支付相关错误码

错误码支付返回CODE(BillingResponseCode)
public @interface BillingResponseCode {undefined
    int SERVICE_TIMEOUT = -3;//服务超时
    int FEATURE_NOT_SUPPORTED = -2;//不支持功能
    int SERVICE_DISCONNECTED = -1;//服务单元已断开
    int OK = 0;//成功
    int USER_CANCELED = 1;//用户按上一步或取消对话框
    int SERVICE_UNAVAILABLE = 2;//网络连接断开
    int BILLING_UNAVAILABLE = 3;//所请求的类型不支持 Google Play 结算服务 AIDL 版本
    int ITEM_UNAVAILABLE = 4;//请求的商品已不再出售。
    int DEVELOPER_ERROR = 5;//提供给 API 的参数无效。此错误也可能说明应用未针对结算服务正确签名或设置,或者在其清单中缺少必要的权限。
    int ERROR = 6;//API 操作期间出现严重错误
    int ITEM_ALREADY_OWNED = 7;//未能购买,因为已经拥有此商品
    int ITEM_NOT_OWNED = 8;//未能消费,因为尚未拥有此商品
}

2022年11月10日更新

许可测试:加入该测试的邮箱账号都能在所有测试渠道都可免费购买内购商品
        开放式测试、内部测试邮箱列表和测试链接并无太多作用,如果内部测试时有报错可添加,尝试解决。

关于将aab包给谷歌发布后会修改密钥SHA的问题,其实是没有升级密钥操作导致的。
升级密钥操作:谷歌后台页面-选中你的应用-设置-应用完整性-升级应用签名密钥,按照文档提供的说明进行操作,执行一个命令即可完成操作,完成后应用签名密钥证书的SHA-1就会变回你的签名文件(keystore文件)对应的SHA-1了。

【Unity】Google内购相关推荐

  1. google内购-订阅模式

    1.订单有变化接收google推送的接口,据此可以实现续订订单 /*** 接收google推送接口* @param body* @param request* @param response* @re ...

  2. Python Google内购服务端验证

    Google内购完成后,服务端需要校验订单的状态是否正确(是否已经成功付款). 一.申请认证 参考https://developers.google.cn/android-publisher/gett ...

  3. Unity iOS内购

    前言:最近项目需要切换到iOS平台做一些提交审核和支付对接相关的工作,上一篇刚分享了最新的iOS10提交审核的一些坑,这篇分享一些内购相关的流程. Unity iOS内购 思路: Unity调用iOS ...

  4. Google 内购总结

    Google 内购坑之总结 最近项目中增加了 Google 内购的内容,接入并不难,在这里总结下接入过程中的细节和坑的地方. 内购接入过程 如何接入官方的教程写的很详细(传送门),并且官方也提供了一个 ...

  5. google内购In-App Billing

    本帖地址:http://blog.csdn.net/jinjian2009/article/details/9140891 这周做了google的内购,没搞过google的内购还是觉得比较繁琐的 go ...

  6. unity 配置内购_内购推荐 (IAP Promo)

    应用商店扩展 内购推荐 (IAP Promo) 集成 内购推荐 (IAP Promo) 概述 内购推荐 (IAP Promo) 让开发者可以轻松促进应用内购.使用内购推荐可以在游戏中的不同位置向不同玩 ...

  7. Google内购--封装版

    最近老大提出了一个需求,在应用里面加一个内购.由于之前没做过这块,所以百度一番.网上都是讲的使用一大堆的utils.还要加一个aidl文件.感觉挺麻烦的.最终让我找到了:com.android.bil ...

  8. Google 内购 - Android

    1. 添加依赖 implementation "com.android.billingclient:billing:5.0.0" 2.支付相关的代码 /*** 连接**/publi ...

  9. java集成Google Pay内购

    挺简单的直接上代码: api入参 @Data @ApiModel("google支付表单信息") public class GooglePayForm {/*** 包名*/@Api ...

最新文章

  1. 总结一下对buffer的学习体会
  2. 《深入剖析NGINX》学习记录
  3. 用GDB排查Python程序故障
  4. 网站运营需要注意什么?
  5. astr在python_python学习之初识字符串
  6. 超全面的JavaWeb笔记day11JSPSessionCookieHttpSession
  7. 使用阿里云接口进行银行卡三四要素实名认证(阿里云api接口java)
  8. HCIE学习笔记(2)之ISIS Overload
  9. 黑色星期五 问题描述   有些西方人比较迷信,如果某个月的13号正好是星期五,他们就会觉得不太吉利,用古人的说法,就是“诸事不宜”。请你编写一个程序,统计出在某个特定的年份中,出现了多少次既是13号又
  10. 【Java实验】文件中单词重复字母对的查找
  11. Distributing Ballot Boxes HDU - 4190【详细翻译】【贪心、二分】
  12. VS2010中水晶报表插件下载安装方法 详细出处参考:http://www.jb51.net/softjc/88860.html
  13. acme + acme-dns + google domains 签发泛域名证书
  14. 人工智能计算机技术图片,这是人工智能眼中它自己的样子
  15. 小程序商城制作一个需要多少钱?
  16. 迭代器(list迭代器的实现)
  17. WF2011 Chips Challenge
  18. 震惊!!十五天开发出一款安卓打卡app,并且成功发布!
  19. No core dump will be written. Minidumps are not enabled by default on client versions of Windows
  20. 地图切图工具:批量标注功能需要安装Firebird数据库

热门文章

  1. PROBOT_G603双臂GAZEBO+MoveIt!仿真中配置手眼相机和夹爪
  2. css如何将图片调成合适大小,如何利用CSS自动调整图片的大小
  3. 强化学习(一):概述
  4. 互联网如何再定义古老的眼镜行业?
  5. Oracle 语句大全,确实不错,赶紧转走!
  6. 小游戏“程序猿大战产品*那啥”
  7. echarts (二) 之canvas设置地图背景图
  8. 判断ssh远程命令是否执行结束
  9. 医院预约挂号系统(Java+SSM+MySQL+Maven)
  10. java强引用不会被回收_强引用(Strong Reference)-不回收