1.
2.implementation("com.android.billingclient:billing:5.0.0")
3.谷歌内购代码
//google内购// private BillingClient billingClient; private static final long RECONNECT_TIMER_START_MILLISECONDS = 1L * 1000L; private static final long RECONNECT_TIMER_MAX_TIME_MILLISECONDS = 1000L * 60L * 15L; // 15 mins private long reconnectMilliseconds = RECONNECT_TIMER_START_MILLISECONDS; private boolean billingSetupComplete = false; private static final Handler handler = new Handler(Looper.getMainLooper()); protected static Handler uiHandler = new Handler(Looper.getMainLooper()); private static MapskuDetailsLiveDataMap = new HashMap<>(); private String[] cacheRequestList; private String buyProductId; public interface Action { public void Invoke(); } //----------------------1.谷歌连接服务器------------------------------- protected void BillingClientInit() { System.out.print("-------1.连接服务器-----" + "\n"); billingClient = BillingClient.newBuilder(this).setListener(this).enablePendingPurchases().build(); billingClient.startConnection(this); } @Override public void onBillingServiceDisconnected() { // PrintLog("onBillingServiceDisconnected"); billingSetupComplete = false; retryBillingServiceConnectionWithExponentialBackoff(); } //----------------------1.谷歌连接服务器,状态------------------------------- @Override public void onBillingSetupFinished(@NonNull BillingResult billingResult) { int responseCode = billingResult.getResponseCode(); String debugMessage = billingResult.getDebugMessage(); System.out.print("-------1.连接服务器成功:onBillingSetupFinished " + debugMessage + "(" + GetResponseText(responseCode) + ")" + "\n"); switch (responseCode) { case BillingClient.BillingResponseCode.OK: // The billing client is ready. You can query purchases here. // This doesn't mean that your app is set up correctly in the console -- it just // means that you have a connection to the Billing service. reconnectMilliseconds = RECONNECT_TIMER_START_MILLISECONDS; billingSetupComplete = true; System.out.print("-------1.连接服务器成功 onBillingSetupFinished " + debugMessage + "(" + GetResponseText(responseCode) + ")" + "\n"); break; case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE: case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE: System.out.print("-------1.连接服务器失败onBillingSetupFinished " + debugMessage + "(" + GetResponseText(responseCode) + ")" + "\n"); break; default: retryBillingServiceConnectionWithExponentialBackoff(); break; } } private void retryBillingServiceConnectionWithExponentialBackoff() { handler.postDelayed(() -> billingClient.startConnection(this), reconnectMilliseconds); reconnectMilliseconds = Math.min(reconnectMilliseconds * 2, RECONNECT_TIMER_MAX_TIME_MILLISECONDS); } //----------------------------------------------------2.开始购买---------------------------------------------------------------------------- public static void OnBuyProduct(String productId, String isConsumable1) { System.out.print("-------2.开始购买111 OnBuyProduct------ productId:" + productId + ",isConsumable:" + isConsumable1 + "\n"); uiHandler.post(new Runnable() { @Override public void run() { try { activity.OnBuyProduct1(productId, true); } catch (Exception e) { System.out.print("-------2.开始购买111 BuyProduct数据传输错误:" + e.getMessage() + "\n"); } } }); } public void OnBuyProduct1(String productId, boolean isConsumable) { System.out.print("-------2.开始购买222 OnBuyProduct1------ productId:" + productId + ",isConsumable:" + isConsumable + "\n"); SkuDetails skuDetails = skuDetailsLiveDataMap.get(productId); if (skuDetails == null) { System.out.print("-------2.开始购买333 OnBuyProduct1------(skuDetails == null) productId:" + productId + ",isConsumable:" + isConsumable + "\n"); RequstProducts(new String[]{productId}, new Action() { @Override public void Invoke() { SkuDetails skuDetails = skuDetailsLiveDataMap.get(productId); System.out.print("-------6.查詢商品id -成功-返回商品信息 开始购买 Invoke RequstProducts:" + productId + ",isConsumable:" + isConsumable + "\n"); Purchase(productId, isConsumable, skuDetails); } }); } else { System.out.print("-------2.开始购买333 OnBuyProduct1------ productId:" + productId + ",isConsumable:" + isConsumable + "\n"); Purchase(productId, isConsumable, skuDetails); } } //3.查詢商品id private void RequstProducts(String[] productId, Action callBack) { System.out.print("-------3.查詢商品id111 RequstProducts------productId:" + productId + "\n"); List skuList = new ArrayList<>(); skuList.addAll(Arrays.asList(productId)); cacheRequestList = productId; SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP); billingClient.querySkuDetailsAsync(params.build(), (billingResult, skuDetailsList) -> { int responseCode = billingResult.getResponseCode(); System.out.print("------3.查詢商品id222 RequstProducts 查询商品列表:responseCode:" + responseCode + ",skuDetailsList.size()=" + skuDetailsList.size() + "\n"); switch (responseCode) { case BillingClient.BillingResponseCode.OK: System.out.print("------3.查詢商品id222 RequstProducts 成功 responseCode:" + responseCode + "\n"); RecieveProducts(skuDetailsList); break; default: System.out.print("------3.查詢商品id222 RequstProducts 失败:" + billingResult.getDebugMessage() + "\n"); RequestProductsFail("Failed to query inventory: " + billingResult.getDebugMessage()); break; } if (callBack != null) { System.out.print("------3.查詢商品id333 RequstProducts callBack:callBack" + "\n"); callBack.Invoke(); } }); } //4.查詢商品id -成功 private void RecieveProducts(List skuDetailsList) { System.out.print("------4.查詢商品id-成功111 RecieveProducts skuDetailsList.size():" + skuDetailsList.size() + "\n"); ArrayList skuItems = new ArrayList (); ArrayList invaildIds = new ArrayList (); int length = cacheRequestList != null ? cacheRequestList.length : 0; if (length > 0) { for (int i = 0; i < length; i++) { String productId = cacheRequestList[i]; if (!TextUtils.isEmpty(productId)) { SkuDetails detail = null; for (SkuDetails skuDetails : skuDetailsList) { if (skuDetails.getSku().equals(productId)) { detail = skuDetails; break; } } if (detail == null) { // PrintLog("未找到该产品信息:"+productId); invaildIds.add(productId); continue; } skuDetailsLiveDataMap.put(productId, detail); String price = detail.getPrice(); String formatPrice = price; SkuItem skuItem = new SkuItem(); skuItem.productId = productId; skuItem.title = detail.getTitle(); skuItem.desc = detail.getDescription(); skuItem.price = price; skuItem.formatPrice = formatPrice; skuItem.priceCurrencyCode = detail.getPriceCurrencyCode(); skuItem.skuType = detail.getType(); skuItems.add(skuItem); } } } RecieveProductInfo(skuItems, invaildIds); } //5.查詢商品id -成功-返回商品信息 protected void RecieveProductInfo(ArrayList skuItems, ArrayList invalidProductIds) { JSONObject jsonObject = new JSONObject(); try { JSONArray skuArray = new JSONArray(); JSONObject tmpObj = null; for (int i = 0; i < skuItems.size(); i++) { SkuItem skuItem = skuItems.get(i); tmpObj = new JSONObject(); tmpObj.put("productId", skuItem.productId); tmpObj.put("title", skuItem.title); tmpObj.put("desc", skuItem.desc); tmpObj.put("price", skuItem.price); tmpObj.put("formatPrice", skuItem.formatPrice); tmpObj.put("priceCurrencyCode", skuItem.priceCurrencyCode); tmpObj.put("skuType", skuItem.skuType); skuArray.put(tmpObj); } JSONArray invalidArray = new JSONArray(); for (int i = 0; i < invalidProductIds.size(); i++) { invalidArray.put(invalidProductIds.get(i)); } jsonObject.put("skuItems", skuArray); jsonObject.put("invalidIds", invalidArray); } catch (JSONException e) { // PrintLog("Json数据错误:"+e.getMessage()); } String info = jsonObject.toString(); System.out.print("------5.查詢商品id -成功-返回商品信息111 RecieveProducts info:" + info + "\n"); } private boolean isConsumable; //----------------------------------------------------3.开始购买--------------------------------------------------------------------------- private void Purchase(String productId, boolean isConsumable, SkuDetails skuDetails) { System.out.print("-------7.开始购买Purchase productId:" + productId + ",isConsumable:" + isConsumable + "\n"); if (null != skuDetails) { buyProductId = productId; this.isConsumable = isConsumable; BillingFlowParams purchaseParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .build(); System.out.print("-------7.开始购买Purchase buyProductId:" + buyProductId + "\n"); billingClient.launchBillingFlow(this, purchaseParams); } else { System.out.print("-------7.开始购买Purchase Can not find SkuDetails productId:" + buyProductId + "\n"); BuyFail(productId, "Can not find SkuDetails:" + productId); } } public String ArrayList2String(ArrayList arrayList) { String result = ""; if (arrayList != null && arrayList.size() > 0) { for (String item : arrayList) { result += item + ","; } result = result.substring(0, result.length() - 1); } return result; } private void handlePurchase(Purchase purchase1) { ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase1.getPurchaseToken()).build(); ConsumeResponseListener listener = new ConsumeResponseListener() { @Override public void onConsumeResponse(BillingResult billingResult, String purchaseToken) { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { try { String signedData = purchase1.getOriginalJson(); String signature = purchase1.getSignature(); JSONObject jObject = new JSONObject(); jObject.put("signedData", signedData); jObject.put("signature", signature); System.out.print("------8.开始购买-上次购买物品未消耗-ITEM_ALREADY_OWNED signedData:" + signedData + "\n"); System.out.print("------8.开始购买-上次购买物品未消耗-ITEM_ALREADY_OWNED signature:" + signature + "\n"); System.out.print("------8.开始购买-上次购买物品未消耗-ITEM_ALREADY_OWNED jObject.toString():" + jObject.toString() + "\n"); JavaGoJS("ProductBuyComplete", jObject.toString()); } catch (JSONException e) { e.printStackTrace(); } } } }; billingClient.consumeAsync(consumeParams, listener); //去消耗道具 } //3.开始购买--结果 @Override public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List list) { int responseCode = billingResult.getResponseCode(); System.out.print("-------8.开始购买--onPurchasesUpdated-- BillingResult [" + GetResponseText(responseCode) + "]: " + billingResult.getDebugMessage() + ",responseCode" + responseCode + "\n"); switch (responseCode) { case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED: System.out.print("-------8.开始购买-上次购买物品未消耗-ITEM_ALREADY_OWNED 00000 " + "\n"); //上次购买物品未消耗 if (billingClient != null && billingClient.isReady()) { billingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP, new PurchasesResponseListener() { @Override public void onQueryPurchasesResponse(@NonNull BillingResult billingResult, @NonNull List purchasesList) { if (purchasesList != null) { System.out.print("-------8.开始购买-上次购买物品未消耗-ITEM_ALREADY_OWNED purchasesList.size():" + purchasesList.size() + "\n"); for (int i = 0; i < purchasesList.size(); i++) { Purchase purchase = purchasesList.get(i); handlePurchase(purchase); } } else { // LogUtils.i(TAG, "processPurchases: with no purchases"); } } }); } break; case BillingClient.BillingResponseCode.OK: System.out.print("-------8.开始购买--onPurchasesUpdated 成功 responseCode:" + responseCode + "\n"); FlowFinish(true, null, list); break; case BillingClient.BillingResponseCode.USER_CANCELED: String productId = buyProductId; buyProductId = null; BuyCancle(productId); break; default: System.out.print("-------8.开始购买--onPurchasesUpdated 失败 responseCode:" + responseCode + "\n"); FlowFinish(false, billingResult.getDebugMessage(), list); break; } } //3.开始购买--结果 private void FlowFinish(Boolean isSuccess, String message, List purchases) { System.out.print("------ 9.FlowFinish isSuccess:" + isSuccess + ",message:" + message + ",buyProductId:" + buyProductId + ",isConsumable:" + isConsumable + "\n"); if (isSuccess) { if (buyProductId != null) { String productId = buyProductId; buyProductId = null; if (isConsumable) { String purchaseToken = null; String signedData = ""; String signature = ""; if (purchases != null) { for (Purchase purchase : purchases) { for (String skus : purchase.getSkus()) { //需要校验付款状态 if (skus.contains(productId) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { purchaseToken = purchase.getPurchaseToken(); signedData = purchase.getOriginalJson(); signature = purchase.getSignature(); System.out.print("------9.FlowFinish-purchaseToken" + purchaseToken + ",signedData:" + signedData + ",signature:" + signature + "\n"); break; } if (purchaseToken != null) break; } } } System.out.print("------9.FlowFinish-purchaseToken" + purchaseToken + ",productId:" + productId + ",signature:" + signature + "\n"); if (purchaseToken == null) { CallBackBuyFail("unknown purchaseToken:" + productId); } else { //验证购买。确保此purchaseToken的授权尚未被授予。授予用户权限。 ConsumeParams consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchaseToken) .build(); String finalSignedData = signedData; String finalSignature = signature; //consumeAsync()是处理消耗性商品 billingClient.consumeAsync(consumeParams, (billingResult, token) -> { System.out.print("------9.FlowFinish-billingResult.getResponseCode()" + billingResult.getResponseCode() + ",finalSignedData:" + finalSignedData + ",finalSignature:" + finalSignature + "\n"); if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { System.out.print("------9.FlowFinish-OKOKOKOK" + "\n"); BuyComplete(productId, finalSignedData, finalSignature); } else { System.out.print("------9.FlowFinish-FailFailFail" + "\n"); CallBackBuyFail(billingResult.getDebugMessage()); } }); } } else { BuyComplete(productId, "", ""); } } } else { if (buyProductId != null) { CallBackBuyFail(message); } } } //----------------------------------------------------4.开始购买结果--完成--------------------------------------------------------------------------- protected void BuyComplete(String productId, String signedData, String signature) { System.out.print("------10.BuyComplete productId:" + productId + ",signedData:" + signedData + ",signature:" + signature + "\n"); try { JSONObject jObject = new JSONObject(); // jObject.put("productId", productId); jObject.put("signedData", signedData); jObject.put("signature", signature); System.out.print("------10.BuyComplete productId:" + productId + "\n"); System.out.print("------10.BuyComplete signedData:" + signedData + "\n"); System.out.print("------10.BuyComplete signature:" + signature + "\n"); //{"signedData":"{\"orderId\":\"GPA.3309-4457-0847-02874\",\"packageName\":\"org.cocos2d.anzhan1\",\"productId\":\"80000\",\"purchaseTime\":1704802700986,\"purchaseState\":0,\"purchaseToken\":\"lcidfngdoakkjmcklgnjbmno.AO-J1OxyCG1tBKRiAtWsDijnTxR_aFwrbmBvPNXDca_xA_bhin10V0KIpzAEfQmhfBJmIrXOUIHo0MqensVlyYjVtYLCMBatGA\",\"quantity\":1,\"acknowledged\":false}","signature":"NHtMrmG0X5h4LTSfqgSQ57\/zQxUSpu9s2aOQ1C8jCDLTfRBS7kA2BtM0VGQSFR6Qf6hMd+k56pbZzzKVrJpL3N9\/fu23exI8\/UlHfmvovWB2m4a5Ukktgya4X1YOIIKGDjMPhxumKrarH+HZElFyvle1BG+BKOsXFLAF+vJ4FR9ONCDPmS2ljM9Sh8lOaPX4sNkYAVuAxsJ7TqBqTA+xJMLGkGIp\/7XKK82NW+Ge0moiGd02b2s0+3xjd28MsKqjLy58CoXCzCxf+uEZVY+6cipDdf049M0XjpV7C+rBlhPWGnr2aZBMXrK6nX+u2z3yMydy6SqNiOOYyJzezNzV\/A=="} System.out.print("------10.BuyComplete jObject.toString():" + jObject.toString() + "\n"); JavaGoJS("ProductBuyComplete", jObject.toString()); //System.out.print("------10.BuyComplete ---ProductBuyCompleteTest---" + "\n"); //GoogleUtils.getInstance().JavaGoJSfu("ProductBuyCompleteTest", signedData, signature); // SendUnityMessage("ProductBuyComplete", jObject.toString()); } catch (JSONException e) { System.out.print("------10.BuyComplete BuyComplete数据错误:" + e.getMessage() + "\n"); } } protected void RequestProductsFail(String message) { System.out.print("------11.RequestProductsFail message:" + message + "\n"); JavaGoJS("ProductRequestFail", message); } protected void BuyCancle(String productId) { System.out.print("------11.ProductBuyCancled productId:" + productId + "\n"); JavaGoJS("ProductBuyCancled", productId); } private void CallBackBuyFail(String message) { String productId = buyProductId; buyProductId = null; BuyFail(productId, message); // PrintLog("Error purchasing: " + message); } protected void BuyFail(String productId, String error) { // PrintLog("购买失败:"+productId+"原因:"+error); try { JSONObject jObject = new JSONObject(); jObject.put("productId", productId); jObject.put("error", error); System.out.print("------11.ProductBuyFailed jObject.toString():" + jObject.toString() + "\n"); JavaGoJS("ProductBuyFailed", jObject.toString()); } catch (JSONException e) { // PrintLog("BuyFail数据错误:"+e.getMessage()); } } private String GetResponseText(int responseCode) { switch (responseCode) { case BillingClient.BillingResponseCode.OK: return "OK"; case BillingClient.BillingResponseCode.SERVICE_TIMEOUT: return "SERVICE_TIMEOUT"; case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED: return "FEATURE_NOT_SUPPORTED"; case BillingClient.BillingResponseCode.USER_CANCELED: return "USER_CANCELED"; case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED: return "SERVICE_DISCONNECTED"; case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE: return "SERVICE_UNAVAILABLE"; case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE: return "BILLING_UNAVAILABLE"; case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE: return "ITEM_UNAVAILABLE"; case BillingClient.BillingResponseCode.DEVELOPER_ERROR: return "DEVELOPER_ERROR"; case BillingClient.BillingResponseCode.ERROR: return "ERROR"; case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED: return "ITEM_ALREADY_OWNED"; case BillingClient.BillingResponseCode.ITEM_NOT_OWNED: return "ITEM_NOT_OWNED"; default: return "UnKnown"; } }
5.测试总结经验
1.不要从本地studio安装手机包去测试充值,大部分情况是拉不起来充值,会包billingresult.getresponsecode() code 3问题
2.上线内部测试版本就可以测试充值了。但是上传到google商店需要等20分钟左右测试最佳,从谷歌商店下载内部测试版本的时候一定要看版本号,大部分情况是上次的版本号,需要等20分钟左右才改变版本号。
3.查看谷歌商店付费栏目里面有没有产品,如果有产品是可以测试充值的,无产品那就是账号的问题了,被锁区。需要更换账号,不需要更换手机。