Google play內購 Iab

峻峰飛陽發表於2017-06-15

(原文地址:http://blog.csdn.net/yupu56/article/details/49452107)


Google Play 內購 In-App-Billing在Android專案或者Cocos2dx/Unity專案中的整合.

最近在做一個遊戲的海外版,需要加內購,碰到一些坑,這裡記錄下來,希望能對大家有個幫助。


參考教程:

Google Play In-app Billing官方教程
Google Play In-app Billing 踩過的那些坑
StackOverflow 論壇
Google Play 內購In-app-billing 總結~

開發者需要做的準備:


1.翻牆Android手機和電腦。

2.Google play 後臺應用,並且把內購專案建立好併發布成功。能夠得到內購產品的SKU即ProductID,和專案64位的祕鑰。

3.內購產品的說明:

a.產品的id是唯一的字串定義,比如com.engine.produce01,後臺新增產品後需要啟用。

b.In-app Billing 的 API 有個 v2 版本和 v3 版本,v2 版本已經不支援了,直接整 v3 版本的吧,Google Play 沒有可重購買商品這個概念,所有的“商品/充值檔”使用者成功購買過一次之後就不允許再次購買了。所以為了實現像應用內購買充值這種可重複購買的“商品/充值檔”,Google Play 提供了一個“消耗”藉口(Consuming In-app Products)。使用者購買完商品後,調一下“消耗”介面,這樣使用者下次就可以繼續購買了。


使用IAB的流程:


1.首先確定你的SKU和Request值(隨便填)


[java] view plain copy
  1. <pre name="code" class="java">static final String SKU_PACKAGE1 = "android.test.purchased";  
  2. static final String SKU_PACKAGE2 = "cinderella_product02";  
  3. static final String SKU_PACKAGE3 = "cinderella_infinite";  


[java] view plain copy
  1. // (arbitrary) request code for the purchase flow  
  2. static final int RC_REQUEST = 10001;  

2.IabHelper類初始化方法,這裡的base64EncodedPublicKey是googleplay後臺的釋出產品的時候生成提供的


[java] view plain copy
  1. mHelper=new IabHelper(this, base64EncodedPublicKey);  

3.startSetup 的操作是檢查是否有許可權和連線到Google Billing service是否成功.


這裡回撥的操作是如果成功,呼叫queryInventoryAsync檢視產品id是否可以使用,查詢完成後會呼叫IabHelper.QueryInventoryFinishedListener 這個回撥介面進行通知,在這個介面中可以獲取商品的詳細資訊SkuDetails和Purchase資訊。

[java] view plain copy
  1. mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {  
  2.     public void onIabSetupFinished(IabResult result) {  
  3.         Log.d(TAG, "Setup finished.");  
  4.   
  5.         if (!result.isSuccess()) {  
  6.             // Oh noes, there was a problem.  
  7.             complain("Problem setting up in-app billing: " + result);  
  8.             return;  
  9.         }  
  10.         // Have we been disposed of in the meantime? If so, quit.  
  11.         if (mHelper == null)  
  12.             return;  
  13.   
  14.         // IAB is fully set up. Now, let's get an inventory of stuff  
  15.         // we own.  
  16.         Log.d(TAG, "Setup successful. Querying inventory.");  
  17.         mHelper.queryInventoryAsync(mGotInventoryListener);  
  18.     }  
  19. });  

4.點選購買按鈕呼叫方法,主要是mHelper.launchPurchaseFlow()方法


[java] view plain copy
  1. // User clicked the "Buy Gas" button  
  2. public void onBuyGasButtonClicked(View arg0) {  
  3.     Log.d(TAG, "Buy gas button clicked.");  
  4.   
  5.     // launch the gas purchase UI flow.  
  6.     // We will be notified of completion via mPurchaseFinishedListener  
  7.     setWaitScreen(true);  
  8.     Log.d(TAG, "Launching purchase flow for gas.");  
  9.   
  10.     /* 
  11.      * TODO: for security, generate your payload here for verification. See 
  12.      * the comments on verifyDeveloperPayload() for more info. Since this is 
  13.      * a SAMPLE, we just use an empty string, but on a production app you 
  14.      * should carefully generate this. 
  15.      */  
  16.     String payload = "";  
  17.   
  18.     mHelper.launchPurchaseFlow(this, SKU_PACKAGE1, RC_REQUEST, mPurchaseFinishedListener, payload);  
  19. }  

5.verifyDeveloperPayload方法用來在伺服器做驗證的,起到確認訂單的作用,小遊戲就免了吧!


[java] view plain copy
  1. /** Verifies the developer payload of a purchase. */  
  2.     boolean verifyDeveloperPayload(Purchase p) {  
  3.         String payload = p.getDeveloperPayload();  
  4.   
  5.         /* 
  6.          * TODO: verify that the developer payload of the purchase is correct. 
  7.          * It will be the same one that you sent when initiating the purchase. 
  8.          * 
  9.          * WARNING: Locally generating a random string when starting a purchase 
  10.          * and verifying it here might seem like a good approach, but this will 
  11.          * fail in the case where the user purchases an item on one device and 
  12.          * then uses your app on a different device, because on the other device 
  13.          * you will not have access to the random string you originally 
  14.          * generated. 
  15.          * 
  16.          * So a good developer payload has these characteristics: 
  17.          * 
  18.          * 1. If two different users purchase an item, the payload is different 
  19.          * between them, so that one user's purchase can't be replayed to 
  20.          * another user. 
  21.          * 
  22.          * 2. The payload must be such that you can verify it even when the app 
  23.          * wasn't the one who initiated the purchase flow (so that items 
  24.          * purchased by the user on one device work on other devices owned by 
  25.          * the user). 
  26.          * 
  27.          * Using your own server to store and verify developer payloads across 
  28.          * app installations is recommended. 
  29.          */  
  30.   
  31.         return true;  
  32.     }  

6.下面是執行完購買後的監聽方法


[java] view plain copy
  1. // Callback for when a purchase is finished  
  2. IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {  
  3.     public void onIabPurchaseFinished(IabResult result, Purchase purchase) {  
  4.         Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);  
  5.         // if we were disposed of in the meantime, quit.  
  6.         if (mHelper == null)  
  7.             return;  
  8.         if (result.isFailure()) {  
  9.             complain("Error purchasing: " + result);  
  10.             setWaitScreen(false);  
  11.             return;  
  12.         }  
  13.         if (!verifyDeveloperPayload(purchase)) {  
  14.             complain("Error purchasing. Authenticity verification failed.");  
  15.             setWaitScreen(false);  
  16.             return;  
  17.         }  
  18.         Log.d(TAG, "Purchase successful.");  
  19.         if (purchase.getSku().equals(SKU_PACKAGE1)) {  
  20.             // bought 1/4 tank of gas. So consume it.  
  21.             Log.d(TAG, "Purchase1 is gas. Starting gas consumption.");  
  22.             mHelper.consumeAsync(purchase, mConsumeFinishedListener);  
  23.         } else if (purchase.getSku().equals(SKU_PACKAGE2)) {  
  24.             // bought the premium upgrade!  
  25.             Log.d(TAG, "Purchase2 is premium upgrade. Congratulating user.");  
  26.             mHelper.consumeAsync(purchase, mConsumeFinishedListener);  
  27.         } else if (purchase.getSku().equals(SKU_PACKAGE3)) {  
  28.             // bought the premium upgrade!  
  29.             Log.d(TAG, "Purchase3 is premium upgrade. Congratulating user.");  
  30.         }  
  31.     }  
  32. };  

7.執行完購買回撥後,消耗型商品需要呼叫消耗方法


IabHelper.OnConsumeFinishedListenermConsumeFinishedListener =new IabHelper.OnConsumeFinishedListener()

這句的意思就是消耗掉你剛買的商品,消耗是指在googleplay上的消耗,為什麼呢?因為GooglePlay 的In-app-Billing V3.0版本,已經沒有管理,非管理的商品,或者像蘋果iOS那邊消耗性和非消耗性的商品了,在後臺新建商品的時候,你會發現全部是受管理的商品,所以在我們購買了消耗型的商品後,在程式碼中執行mHelper.consumeAsync(purchase,mConsumeFinishedListener);就行了,代表這個商品被消耗了,你還可以購買。下面是消耗後的回撥方法:

[java] view plain copy
  1. // Called when consumption is complete  
  2.     IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {  
  3.         public void onConsumeFinished(Purchase purchase, IabResult result) {  
  4.             Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);  
  5.   
  6.             // if we were disposed of in the meantime, quit.  
  7.             if (mHelper == null)  
  8.                 return;  
  9.   
  10.             // We know this is the "gas" sku because it's the only one we  
  11.             // consume,  
  12.             // so we don't check which sku was consumed. If you have more than  
  13.             // one  
  14.             // sku, you probably should check...  
  15.             if (result.isSuccess()) {  
  16.                 // successfully consumed, so we apply the effects of the item in  
  17.                 // our  
  18.                 // game world's logic, which in our case means filling the gas  
  19.                 // tank a bit  
  20.                 Log.d(TAG, "Consumption successful. Provisioning.");  
  21.                 //saveData();  
  22.             } else {  
  23.                 complain("Error while consuming: " + result);  
  24.             }  
  25.             updateUi();  
  26.             setWaitScreen(false);  
  27.             Log.d(TAG, "End consumption flow.");  
  28.         }  
  29.     };  

8.最後補充一點,官方例子的方法設計非常合理,一些輔助方法的書寫和使用,很經典,值得我們的借鑑。


[java] view plain copy
  1. // updates UI to reflect model  
  2.     public void updateUi() {  
  3.           
  4.     }  
  5.   
  6.     // Enables or disables the "please wait" screen.  
  7.     void setWaitScreen(boolean set) {  
  8.         findViewById(R.id.screen_main).setVisibility(set ? View.GONE : View.VISIBLE);  
  9.         findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE : View.GONE);  
  10.     }  
  11.   
  12.     void complain(String message) {  
  13.         Log.e(TAG, "**** TrivialDrive Error: " + message);  
  14.         alert("Error: " + message);  
  15.     }  
  16.   
  17.     void alert(String message) {  
  18.         AlertDialog.Builder bld = new AlertDialog.Builder(this);  
  19.         bld.setMessage(message);  
  20.         bld.setNeutralButton("OK"null);  
  21.         Log.d(TAG, "Showing alert dialog: " + message);  
  22.         bld.create().show();  

  1.     }  

相關文章