目录
一、创建空安卓库工程
二、Unity配置
三、注意事项
https://developer.android.com/google/play/billing/getting-ready
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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//初始化Google监听支付成功失败
purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public 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());
//测试用例 当做成功 正式需服务器通知再handlePurchase
handlePurchase(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() {
@Override
public 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());
}
}
@Override
public 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() {
@Override
public 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() {
@Override
public 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") //直接塞入用户ID
int 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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>
<activity
android: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-data
android: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 update
void 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、需要通过邮箱测试验证。
?要填写你的测试邮箱到测试列表里,反馈邮箱无所谓,然后复制下面的链接,传给测试人员去打开这个网页 进行验证邮箱 确认进行测试。(这一步很的重要!!!!
这里只有服务器方面的东西我没有说明,因为哇不是服务器!
|