Unity导出Android工程加支付逻辑
添加引用
//内购
implementation 'com.android.billingclient:billing:2.1.0'
AndroidMainfest文件添加权限
<uses-permission android:name="com.android.vending.BILLING"/>
GooglePayUtil.java 实现 PurchasesUpdatedListener 接口
执行顺序:
- 初始化:初始化完成后->查询未消耗订单(走正常支付成功后流程)
- 购买商品:购买商品->支付完成->传给服务器验证订单->成功:发送奖励并调用Google消耗方法
- 支付失败:购买商品->支付失败(BillingResponseCode = 7)存在未消耗订单->查询未消耗订单(走正常支付成功后流程)
IGooglePayUpdateListener主要监听事件
public interface IGooglePayUpdateListener{
//失败
void onFail(String tag, String msg);
//响应Code
void onResponseCode(int code);
//初始化成功
void onGooglePayClientSetupFinished();
//购买成功回调
void onGooglePayPurchaseSuccess(Purchase purchase);
//消耗成功
void onGooglePayConsumeSuccess(BillingResult result, String token);
}
private boolean _IsServiceConnected; //服务器连接状态
private Activity _activity;
private BillingClient _billingClient;
private IGooglePayUpdateListener _listener;
private Set<String> _tokensToBeConsumed = new HashSet<>();
private Map<String,Purchase> _mapTokenPurchase = new HashMap<>();
_tokensToBeConsumed 储存消耗token
_mapTokenPurchase token对应Purchase
构造方法
public GooglePayUtil(Activity activity, IGooglePayUpdateListener listener){
this._activity = activity;
this._listener = listener;
//构建client
_billingClient = BillingClient.newBuilder(_activity).setListener(this).enablePendingPurchases().build();
Log.i(TAG, "Starting setup.");
//连接GooglePay服务
startServiceConnection(new Runnable() {
@Override
public void run() {
//监听启动就绪
_listener.onGooglePayClientSetupFinished();
Log.i(TAG, "GooglePay服务已就位,进行查询订单.");
queryPurchases();
}
});
}
queryPurchases 查询订单方法
链接服务
//启动服务链接
private void startServiceConnection(final Runnable executeOnSuccess){
_billingClient.startConnection(new BillingClientStateListener() {
//启动完成
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
_listener.onResponseCode(billingResult.getResponseCode());
Log.i(TAG, "Setup finished. Response code: "+ billingResult.getResponseCode());
if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK){
Log.i(TAG, "Success 连接成功!");
_IsServiceConnected = true;
//可以执行扩展方法
if(executeOnSuccess != null) executeOnSuccess.run();
}
}
//连接断开
@Override
public void onBillingServiceDisconnected() {
_listener.onFail(TAG, "Disconnected 断开链接!");
_IsServiceConnected = false;
}
});
}
添加服务请求方法自动重连
private void executeServiceRequest(Runnable runnable){
if(_IsServiceConnected) runnable.run(); else startServiceConnection(runnable);
}
初始化完成查询未消耗订单
//查询
private void queryPurchases(){
Runnable queryToExecute = new Runnable() {
@Override
public void run() {
long time = System.currentTimeMillis();
Purchase.PurchasesResult purchasesResult = _billingClient.queryPurchases(BillingClient.SkuType.INAPP);
Log.i(TAG, String.format("查询内购消耗时间: %s ms",System.currentTimeMillis() - time));
Log.i(TAG, String.format("查询内购返回 code: %s size: %s",purchasesResult.getResponseCode(),purchasesResult.getPurchasesList().size()));
//检查是否支持订阅
if(checkAreSubscriptionsSupported()){
time = System.currentTimeMillis();
//查询订阅
Purchase.PurchasesResult subscriptionResult = _billingClient.queryPurchases(BillingClient.SkuType.SUBS);
Log.i(TAG, String.format("查询订阅消耗时间: %s ms",System.currentTimeMillis() - time));
Log.i(TAG, String.format("查询订阅返回 code: %s size: %s",subscriptionResult.getResponseCode(),subscriptionResult.getPurchasesList().size()));
if(subscriptionResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
purchasesResult.getPurchasesList().addAll(subscriptionResult.getPurchasesList());
else
_listener.onFail(TAG, String.format("查询订阅返回错误响应 code: %s",subscriptionResult.getResponseCode()));
} else if(purchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
Log.i(TAG, "未查询到订阅信息已跳过");
else
_listener.onFail(TAG, String.format("查询订阅返回错误响应 code: %s",purchasesResult.getResponseCode()));
//查询完成执行
onQueryPurchasesFinished(purchasesResult);
}
};
executeServiceRequest(queryToExecute);
}
//检查当前客户端是否支持订阅
private boolean checkAreSubscriptionsSupported(){
BillingResult featureSupported = _billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS);
if(featureSupported.getResponseCode() != BillingClient.BillingResponseCode.OK)
_listener.onFail(TAG, "checkAreSubscriptionsSupported() got an error response: " + featureSupported.getResponseCode());
return featureSupported.getResponseCode() == BillingClient.BillingResponseCode.OK;
}
//查询完成
private void onQueryPurchasesFinished(Purchase.PurchasesResult result){
if(_billingClient == null || result.getResponseCode() != BillingClient.BillingResponseCode.OK){
_listener.onResponseCode(result.getResponseCode());
_listener.onFail(TAG, String.format("billing == null 或 返回失败code: %s",result.getResponseCode()));
return;
}
Log.i(TAG, "查询成功!");
_mapTokenPurchase.clear();
//更新购买
onPurchasesUpdated(result.getBillingResult(),result.getPurchasesList());
}
购买回调
//购买回调
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
_listener.onResponseCode(billingResult.getResponseCode());
switch (billingResult.getResponseCode())
{
case BillingClient.BillingResponseCode.OK:
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
break;
case BillingClient.BillingResponseCode.USER_CANCELED:
Log.i(TAG, "onPurchasesUpdated() - 用户取消购买流程");
break;
case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
//TODO: 上次购买物品未消耗
_listener.onFail(TAG, String.format("onPurchasesUpdated() - 上次购买物品未消耗 code: %s 重新 queryPurchases",billingResult.getResponseCode()));
queryPurchases();
break;
default:
_listener.onFail(TAG, String.format("onPurchasesUpdated() - 返回未知错误 code: %s",billingResult.getResponseCode()));
break;
}
}
handlePurchase 处理购买方法
BillingResponseCode == BillingResponseCode.ITEM_ALREADY_OWNED 存在未消耗的订单这时需要执行查询订单方法,查询到未消耗的订单会自动走购买消耗流程
//处理购买
private void handlePurchase(Purchase purchase){
if(purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED){
//TODO: 购买完成
_mapTokenPurchase.put(purchase.getPurchaseToken(),purchase);
_listener.onGooglePayPurchaseSuccess(purchase);
} else if(purchase.getPurchaseState() == Purchase.PurchaseState.PENDING){
//TODO: 未完成-挂起
Log.i(TAG, "未完成购买-已挂起");
}
}
消耗商品(Google不调用消耗方法无法再次购买)
//消耗商品 只有消费成功之后,才能真正到账,否则3天之后,会执行退款处理 测试阶段只有5分钟
public void ConsumeAsync(final String token){
if(!CheckToken(token)){
_listener.onFail(TAG,String.format("检查订单未购买: %s",token));
return;
}
Purchase purchase = _mapTokenPurchase.get(token);
if(_tokensToBeConsumed.contains(purchase.getPurchaseToken())){
Log.i(TAG, "消耗商品已使用,正在跳过...");
return;
}
_tokensToBeConsumed.add(purchase.getPurchaseToken());
//监听
final ConsumeResponseListener onConsumeListener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
Log.i(TAG, "消耗成功.");
else
_listener.onFail(TAG, String.format("onConsumeResponse 消耗失败: code = %s , msg = %s , purchaseToken = %s", billingResult.getResponseCode(), billingResult.getDebugMessage(), purchaseToken));
_listener.onGooglePayConsumeSuccess(billingResult,purchaseToken);
}
};
//执行
Runnable consumeRequest = new Runnable() {
@Override
public void run() {
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setDeveloperPayload(purchase.getDeveloperPayload())
.setPurchaseToken(purchase.getPurchaseToken()).build();
_billingClient.consumeAsync(consumeParams,onConsumeListener);
}
};
executeServiceRequest(consumeRequest);
}
//*******
//查询是此账单是否已付款
public boolean CheckToken(String token){
return _mapTokenPurchase.containsKey(token);
}
//获取Sku
public String GetSku(String token){
return _mapTokenPurchase.get(token).getSku();
}
//*******
购买
//异步查询商品信息在后台是否配置过
public void QuerySkuDetailsAsync(@BillingClient.SkuType final String itemType, final List<String> skuList, final SkuDetailsResponseListener listener){
Runnable queryRequest = new Runnable() {
@Override
public void run() {
SkuDetailsParams.Builder parans = SkuDetailsParams.newBuilder().setType(itemType).setSkusList(skuList);
_billingClient.querySkuDetailsAsync(parans.build(), listener);
}
};
executeServiceRequest(queryRequest);
}
当查询到Sku后调用Google 支付窗口
//启动购买或订阅流程
public void Pay(final SkuDetails skuDetails ){
Runnable purchaseFlowRequest = new Runnable() {
@Override
public void run() {
BillingFlowParams flowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
_billingClient.launchBillingFlow(_activity,flowParams);
Log.i(TAG, "launchBillingFlow , 调用窗口...");
}
};
executeServiceRequest(purchaseFlowRequest);
}
GooglePayHelper 实现 IGooglePayUpdateListener 方法
主要与Unity通信
public GooglePayHelper(Activity activity){
_googlePayUtil = new GooglePayUtil(activity,this);
}
IGooglePayUpdateListener
@Override
public void onFail(String tag, String msg) {
UnityPlayer.UnitySendMessage("AndroidImpl","OnPayFail",String.format("%s : %s",tag,msg));
}
@Override
public void onResponseCode(int code){
UnityPlayer.UnitySendMessage("AndroidImpl","OnPayResponseCode",String.valueOf(code));
}
//初始化完成
@Override
public void onGooglePayClientSetupFinished() {
UnityPlayer.UnitySendMessage("AndroidImpl","OnPayClientSetupFinished","");
}
//购买回调 * 需要给服务器验证
@Override
public void onGooglePayPurchaseSuccess(Purchase purchase) {
//TODO:购买成功
Log.i(TAG, String.format("购买成功物品: %s , 订单: %s",purchase.getSku(),purchase.getOrderId()));
UnityPlayer.UnitySendMessage("AndroidImpl","OnPayPurchaseSuccess",purchase.getPurchaseToken());
}
//消耗完成
@Override
public void onGooglePayConsumeSuccess(BillingResult result, String token) {
Log.i(TAG, String.format("onGooglePayConsumeSuccess() : 消耗token - %s ,结果 - %s", token, result.getResponseCode()));
onResponseCode(result.getResponseCode());
if(result.getResponseCode() == BillingClient.BillingResponseCode.OK){
//TODO:消耗成功通知Unity
UnityPlayer.UnitySendMessage("AndroidImpl","OnPayConsumeSuccess","");
Log.i(TAG, "消耗成功物品");
}else {
Log.e(TAG, String.format("消耗物品返回error code: %s",result.getResponseCode()));
}
}
Unity 主要调用方法
public void PayItem(final String sku){
Log.i(TAG, String.format("PayItem 订单id: %s",sku));
List<String> skuList = new ArrayList<>();
skuList.add(sku);
_googlePayUtil.QuerySkuDetailsAsync(BillingClient.SkuType.INAPP, skuList, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Log.i(TAG,String.format("PayItem BillingResponseCode : %s",billingResult.getResponseCode()));
if(billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
onResponseCode(billingResult.getResponseCode());
Log.e(TAG, String.format("查询%s失败。错误代码:%s", sku, billingResult.getResponseCode()));
onFail(TAG, String.format("查询%s失败。错误代码:%s", sku, billingResult.getResponseCode()));
} else if(skuDetailsList != null && skuDetailsList.size() > 0){
//获取sku
for (SkuDetails details : skuDetailsList) {
if(sku.equals(details.getSku())){
_googlePayUtil.Pay(details);
}
}
}
}
});
}
//服务器验证通过 * 消耗物品
public void ConsumeItem(final String token){
_googlePayUtil.ConsumeAsync(token);
}
查询成功后调用Google 支付窗口
主要代码就这些,订阅我这边没有这个需求所以没处理
如果项目中有多个SDK 有的SDK需要v4 的支持可能会报 androidx 与 v4 冲突可以直接对支付api进行降级
//内购
implementation 'com.android.billingclient:billing:2.0.3'