《Android》『In-app Billing V3 API』- 應用程式內購買的基本觀念與相關實作用法
《Android Developers 參考文獻》
《簡單介紹》
In-app Billing (應用程式內購買),這個功能是現在幾乎所有免費的手機應用程式,都會使用到的一種金流服務,在 Android 中,Google Play 提供了一個稱為 In App Billing V3 的 API,讓程式開發者可以透過這個 API,不用直接處理任何交易,而是將訂單需求傳給 Google play,由 Google Play 幫忙處理所有交易結帳的細節並提供反饋,透過這種方式,可以確保使用者的購買體驗都是熟悉且一致的流程。
In App Billing V3 API 支援了所有 Android 2.2 以上的版本。我們可以透過 Google Play 所提供的內購服務來購買很多東西,像是可以提供下載的影音檔案或者是相片;虛擬道具像是遊戲裡的等級或點數等等。所有透過 Google Play 發布的程式都可以使用其所提供的應用程式內購買流程,只要有開發者帳戶以及商家電子錢包即可,這些都可以在開發者管理後台設定。
《特性》
在 Google Play 所提供的金流服務中,應用程式內購買的商品有分兩種類型 –
1. 一次性購買的產品 (消耗性商品、買斷型商品)
2. 經常性、自動計費的產品 (訂閱)
In-app Billing 只能用來販賣數位的內容,不能販賣實體商品、實體服務或者任何需透過寄送才能取得的商品。若是購買訂閱類型的商品,使用者可以在購買後的『試用時間』內由使用者自己取消購買;值得注意的一點是,當使用者付費購買內購商品時,是沒有退款的機制讓使用者自己取消購買的(但可寫信給開發者,由開發者決定是否取消該次付款)。
在 Google Play 的規範中,所有的購買行為都是受到管理的,意思是 Google Play 持續追蹤所有內購商品的擁有權,也就是說,當一件一次性購買的商品被使用者所購買以後,該商品儲存在 Google Play Server 中的狀態,會被改成『已被使用者擁有』的狀態,當商品處於這個狀態的時候,使用者是無法重複做購買的,因此,若是販賣一次性的商品,開發者必須額外去定義這個商品是消耗性的商品或者是買斷型的商品,若是消耗性的商品(像是神魔之塔裡面的魔法石),則在使用者完成購買此商品後,應用程式必須送出一個 consumePurchase() 的指令給 Google Play Server,告知這件商品已被消耗,Google Play 便會將該商品的擁有權改成為『未被擁有』的狀態,使用者方能再次購買該商品。
相關的流程圖如下 –
Consumable In-app Products
《In-app Billing V3 API 的購買商品流程》
在 In-app Billing V3 API (以下簡稱 IAB)的購買商品流程中,主要分為四個步驟,圖示如下 –
Purchasing Items Flow
Step1 – isBillingSupported()
在執行購買流程的時候,首先,應用程式會送出一個 isBillingSupported() 的請求,向 Google Play 確認目前這個程式所使用的 In-app Billing API 的版本是否支援付款。
Step2 – getPurchases()
接著,當應用程式被啟動或者使用者登入以後,就是檢查使用者擁有哪些購買項目的時候。要查詢使用者購買過哪些內購式商品,應用程式可以送出 getPurchases() 的請求。如果請求成功,會回傳一個 Bundle,此 Bundle 包含了以下資訊 :
- 已購買商品的ID清單
- 個人的購買明細清單
- 為購買簽名的清單。
Step3 – getSkuDetails()
檢查完成以後,通常來說,開發者會想要告訴使用者,目前有哪些商品可以購買,要查詢開發者在 Google Play 定義的可內購式商品的細節,應用程式可以送出 getSkuDetails() 的請求,在查詢請求中必須指定產品的SKU ID (也就是 Product ID),查詢成功則會回傳一個 Bundle,此 Bundle 包含了指定 ID 產品的相關細節,像是產品名稱、產品描述、產品定價…等等。
Step4.1 – getBuyIntent()、startIntentSenderForResult()
假如某個內購式商品尚未屬於使用者,開發者就可以對使用者顯示購買提示(像是購買按鈕),當使用者按下按鈕送出購買請求後,應用程式就會送出 getBuyIntent() 的請求,指定該項商品的 SKU ID及其他參數來購買。
當使用者送出購買請求後,Google Play 會回傳一個內含 PendingIntent 的 Bundle,可以讓使用者開始付款結帳的界面。應用程式會呼叫 startIntentSenderForResult 來啟動這個 PendingIntent。當整個結帳流程結束(指使用者成功購買或取消購買),Google Play 會回傳一個 Intent 到應用程式的 onActivityResult() 方法,onActivityResult() 帶有三個回傳參數,第一個是請求碼(request code),這個請求碼可以用來辨別是誰送出的購買請求,第二個是結果碼(result code),用來告知這次的購買是成功(-1)的或被取消(0),第三個參數則是資料(data),此資料中包含了所購買商品的相關資訊(此商品的SKU ID、此商品的購買明細、此商品的簽名)。
《程式實作》
在要使用 IAB 之前,首先我們必須透過 Android SDK Manager 下載安裝 Google Play Billing Library,接著在專案中加入 IInAppBillingService.aidl 檔,透過它來和 Google Play 服務溝通。這個檔案可以在所下載的 Library 中找到。在 Google 文件中的建議是,我們可以直接改寫它所提供的 Sample Code,或者是在已有的專案中,將所需的檔案從 Sample Code 抓過去用即可,很貼心的一點是,在 Sample Code 中,Google 不僅安置好了 IInAppBillingService.aidl,更寫了一個名為 IabHelper 的類別,來處理並控管 IInAppBillingService.aidl 所提供的介面服務,因此我們不用再去自己寫一個類別控管與 Google Play 溝通的介面,只要知道如何使用 Google 所提供的 IapHelper 類別即可。
設定好所需的檔案後,我們必須在 AndroidManifests.xml 加入以下權限 –
接著,我們首先透過 IabHelper 類別,宣告一個需要傳入在開發者後台所取得的金鑰的物件,此開發金鑰是開發者將應用程式上傳到 Google Play 後,Google Play 在後台所提供的專屬於這個應用程式的金鑰,要使用金流服務販賣內購式商品,必須用到此金鑰,且開發者必須先在後台設定好所要販賣的商品名稱以及 SKU ID,相關細節不在此篇討論範圍,可以上網搜尋關於內購式商品的上架流程查看。
宣告 mHelper 物件
Step1 – isBillingSupported()
呼應前面所提到的購買商品流程,這邊我們對照到 IabHelper 的方法,一個步驟一個步驟來說明,我們在宣告 mHelper 物件以後,透過其所提供的 startSetup() 方法,執行與 Google Play 綁定的初始化動作,在這個 startSetup() 方法中,便會去執行上面流程所提到的 isBillingSupported() 方法。
startSetup() 方法
在結束 Activity 時,要記得去釋放 mHelper 物件。
Step2 – getPurchases()
在 startSetup() 之後,我們首先必須檢查目前使用者擁有哪些商品,更進一步的,檢查是否有消耗性的商品尚未向 Google play 告知此商品已被消耗,將所有權從使用者身上歸還給 Google play。
透過 mHelper 物件所提供的 queryInventoryAsync(QueryInventoryFinishedListener) 方法,我們可以向 Google Play 發出 getPurchases() 的請求。
查詢使用者擁有哪些內購式商品
在此方法中,我們傳入了一個監聽器,叫做 QueryInventoryFinishedListener,透過這個監聽器,我們可以接收從 Google Play Server 所傳回來的結果。
QueryInventoryFinishedListener 監聽器
Step3 – getSkuDetails()
接著,當我們要找尋特定 ID 的內購式商品時,透過 mHelper 物件所提供的 queryInventoryAsync(boolean, List, QueryInventoryFinishedListener) 方法,我們可以向 Google Play 發出 getSkuDetails() 的請求。
查詢指定 ID 的產品資訊
第一個參數式告知是否可以檢索產品資料,基本都設為 true,第二個參數是一個存有 SKU ID 的清單,告知我要查詢哪些 SKU ID 的產品資訊,第三個參數則是監聽器,偵聽查詢完成以後回傳的事件。
QueryInventoryFinishedListener 監聽器
如果查詢成功,查詢結果會儲存在 Inventory 物件中回傳給偵聽器。以上程式碼展示如何從結果中取得產品的價格,我們可以透過 回傳的inventory 物件得知不同 SKU ID 的產品名稱、產品價格、產品描述…等資訊。
Step4.1 – getBuyIntent()、startIntentSenderForResult()
當使用者購買某件內購式商品時,我們透過 mHelper 物件所提供的 launchPurchaseFlow(Activity, String, int, OnIabPurchaseFinishedListener, String) 方法,向 Google Play 發出 getBuyIntent() 的請求,最後並會透過其內部的 startIntentSenderForResult() 將結果傳回到應用程式的 onActivityResult() 中。
(需注意的一點,此方法只能在 Activity 主執行緒中呼叫。)
執行購買請求
相關參數說明如下 –
- 參數1:目前這個 Activity 本身。
- 參數2:產品 ID (SKU ID)。
- 參數3:請求碼,任意正整數。Google Play 會回傳這個請求碼給 Activity 的 onActivityResult()。
- 參數4:監聽器。
- 參數5:用來附加其他資訊的字串,可以是空的。好的做法是傳送一組字串,增加安全性。我們可以透過 purchase.getDeveloperPayload() 來取得此字串。
OnIabPurchaseFinishedListener 監聽器
如果購買成功,Google Play 會將資料儲存在 Purchase 物件中回傳,然後監聽器就會收到,我們透過分辨 Purchase 中所帶的 SKU_ID 參數,就可以知道使用者購買了哪樣產品,進一步定義所要執行的操作。若是可以重複購買的消耗品(例如神魔之塔的魔法石),則必需在購買之後,將虛擬貨品提供給使用者之前,告知 Google Play 此商品已被消耗,將來使用者才可以再次執行購買的動作。
向 Google Play 告知商品已被消耗
透過 mHelper 物件所提供的 consumeAsync(Purchase, OnConsumeFinishedListener) 方法,我們可以將一件產品設定為已消耗的狀態。
執行取消所有權請求
第一個參數為所要設定的內購式商品,第二個參數則為監聽器。
當虛擬貨品消耗成功以後,我們就可以去執行我們要做的動作,像是為使用者增加汽油或者魔法石…等等。
《程式測試》
要測試透過 IAB 所寫的金流服務程式碼,有兩種方式,一種是透過 Google Play 所預留的 Product ID,在執行購買的時候,將這些 ID 傳進去,當 Google Play 收到這些 ID 後,就會根據所傳的 ID 來回傳假的流程訊息,我們可以透過這種方式,在程式還未正式上架前,測試整個程式的流程有沒有問題。
預留的 Product ID 如下:
- android.test.purchased
- android.test.canceled
- android.test.refunded
- android.test.item_unavailable
另一種方式,則是將應用程式發佈到 Alpha 版或 Beta 版,設定好測試人員後,進行內部測試,這種環境下的測試所花費的金錢都不會列入收費,要注意的是測試人員的帳號不能跟開發者的帳號一樣,否則會有商品無法購買的錯誤。
臉書留言
一般留言