Wednesday, December 14, 2011

[Android in-app billing第2篇]Implementing In-app Billing實作應用程式內部金流機制(中文翻譯)

藉由一步一步的引導,將應用程式內金流機制整合進您的APP裡 。

原文連結︰Implementing In-app Billing
翻譯︰小鰻
翻譯版本號︰v.1.0
如需轉載請註明出處「小鰻的Android學習筆記
讓我參與最直接的討論以及錯誤修正, 謝謝!

Android Market In-app Billing provides a straightforward, simple interface for sending in-app billing requests and managing in-app billing transactions using Android Market. This document helps you implement in-app billing by stepping through the primary implementation tasks, using the in-app billing sample application as an example.
Android市集的應用程式內部金流(以下簡稱iap)機制透過Android市集提供了一個直接、簡單的介面,讓程式可以寄送iap請求或管理iap交易。這份文件將幫助你一步一步地透過主要的實作任務 - 也就是iap的範例程式來做範例教學。
Before you implement in-app billing in your own application, be sure that you read Overview of In-app Billing and Security and Design. These documents provide background information that will make it easier for you to implement in-app billing.
在你將你的APP實作iap之前,請確定你已讀過應用程式金流概述(Overview of In-app Billing)以及安全和設計(Security and Design)章節,這些文件提供了一些iap的背後原理,它們將會幫助你更輕鬆的實作完成iap機制。


To implement in-app billing in your application, you need to do the following:
如果你的APP要實作iap機制,你必須做好下列事項︰
  1. Download the in-app billing sample application.
  2. 下載iap範例程式
  3. Add the IMarketBillingService.aidl file to your project.
  4. 新增IMarketBillingService.aidl檔至你的專案夾
  5. Update your AndroidManifest.xml file.
  6. 更新你的AndroidManifest.xml檔
  7. Create a Service and bind it to the MarketBillingService so your application can send billing requests and receive billing responses from the Android Market application.
  8. 建立一個Service,並且將它綁定到MarketBillingService,你的APP才能夠透過Android市集軟體發送金流請求和接收金流回應。
  9. Create a BroadcastReceiver to handle broadcast intents from the Android Market application.
  10. 建立一個廣播監聽去處理從Android市集軟體來的廣播意圖(broadcast intents)。
  11. Create a security processing component to verify the integrity of the transaction messages that are sent by Android Market .
  12. 建立一套安全流程元件去驗証那些藉由Android市集發送的完整的交易訊息。
  13. Modify your application code to support in-app billing.
  14. 修改你的程式去對應iap機制。

 

Downloading the Sample Application下載範例程式

The in-app billing sample application shows you how to perform several tasks that are common to all Android Market in-app billing implementations, including:
範例程式將會告訴你一些執行Android市集iap機制很常見也很一般的實作概念,包括︰
  • Sending in-app billing requests to the Android Market application.
  • 發送IAP請求給Android市集
  • Handling synchronous responses from the Android Market application.
  • 處理從Android市集來的同步回應
  • Handling broadcast intents (asynchronous responses) from the Android Market application.
  • 處理從Android市集來的廣播意圖(非同步)
  • Using in-app billing security mechanisms to verify the integrity of billing responses.
  •  使用iap安全架構來確認金流回應的完整性
  • Creating a user interface that lets users select items for purchase.
  • 建立使用介面讓使用者能夠購買商品


File Description
IMarketBillingService.aidl Android Interface Definition Language (AIDL) file that defines the IPC interface to Android Market's in-app billing service (MarketBillingService).
Android接口定義語言檔。定義了到Android市集iap機制的內部進程溝通介面。
Dungeons.java Sample application file that provides a UI for making purchases and displaying purchase history.
提供購買的使用介面以及顯示購買記錄的範例程式檔。
PurchaseDatabase.java A local database for storing purchase information.
 一個用來儲存購買資訊的本地端資料庫。
BillingReceiver.java A BroadcastReceiver that receives asynchronous response messages (broadcast intents) from Android Market. Forwards all messages to the BillingService.
一個用來接收從Android市集來的異步訊息回應的廣播接收器。它會將所有傳來的訊息轉至BillingService去處理。
BillingService.java A Service that sends messages to Android Market on behalf of the application by connecting (binding) to the MarketBillingService.
一個有能力發送訊息至Android市集的Service(服務)。它也是APP能夠連線到市集金流服務的代表。
ResponseHandler.java A Handler that contains methods for updating the purchases database and the UI.
一個包含更新購買資料庫和更新畫面等函式的處理機制
PurchaseObserver.java An abstract class for observing changes related to purchases.
 一個觀察並監聽購買狀態相關資訊遭改變的抽象類別
Security.java Provides various security-related methods.
 提供各種安全相關的函式
Consts.java Defines various Android Market constants and sample application constants. All constants that are defined by Android Market must be defined the same way in your application.
定義了各種Android市集和範例程式會用到的常數。所有定義的常數,你的APP都應該照彷。
Base64.java and Base64DecoderException.java Provides conversion services from binary to Base64 encoding. The Security class relies on these utility classes.
 提供從二進位至Base64編碼轉換的服務。Security這個類別都是依賴這些類別工具在運作的。
The sample application includes an application file (Dungeons.java), the AIDL file for the MarketBillingService (IMarketBillingService.aidl), and several classes that demonstrate in-app billing messaging. It also includes a class that demonstrates basic security tasks, such as signature verification.
Table 1 lists the source files that are included with the sample application.
範例程式包含了程式檔(Dungeions.java)、市集金流服務專用的AIDL檔和數個用來說明iap機制傳遞訊息的類別檔。另外也解釋了基本安全認証的流程,像是數位簽章的驗証。

表1 列出範例程式裡所含蓋的資源檔檔名


Table 1. In-app billing sample application source files.
表1. iap機制範例程式的資源檔 

The in-app billing sample application is available as a downloadable component of the Android SDK. To download the sample application component, launch the Android SDK and AVD Manager and then select the "Google Market Billing package" component (see figure 1), and click Install Selected to begin the download.
Iap範例程式是在Android SDK裡,可供下載的內容之一。如果要下載範例程式這個內容,請執行Android SDK和AVD Manager,然後選擇"Google Market Billing package"這個內容(見圖1),然後點擊Install Selected來下載。


Figure 1. The Google Market Billing package contains the sample application and the AIDL file. 
圖1. Google市集金流套件包含了範例程式和一個AIDL(Android 定義接口語言)
When the download is complete, the Android SDK and AVD Manager saves the component into the following directory:
當下載完成後,Android SDK和AVD Manager會將該內容存至下載的資料夾中︰
<sdk>/extras/google/market_billing/
If you want to see an end-to-end demonstration of in-app billing before you integrate in-app billing into your own application, you can build and run the sample application. Building and running the sample application involves three tasks:

如果你想要看如何將iap機制與您的APP完全整合的完整說明,你可以建立並執行這個範例程式。建立和運行範例程式有3個工作要做︰
  • Configuring and building the sample application.
  • 設定並建立範例程式
  • Uploading the sample application to Android Market.
  • 上傳範例程式至Android Market(譯者註︰Android publisher)
  • Setting up test accounts and running the sample application.
  • 設定測試帳戶,並且同時執行範例程式

Note: Building and running the sample application is necessary only if you want to see a demonstration of in-app billing. If you do not want to run the sample application, you can skip to the next section, Adding the AIDL file to your project.
註︰如果你想要看關於iap機制的說明,才需要建立並運行範例程式。假如你不想要看這個說明,你可以直接跳到 Adding the AIDL file to your project這個篇幅。

Configuring and building the sample application設定並建立範例程式

Before you can run the sample application, you need to configure it and build it by doing the following:
在你開始執行範例程式以前,你需要先依照下列的說明做一些相關的設定︰

  1. Add your Android Market public key to the sample application code. This enables the application to verify the signature of the transaction information that is returned from Android Market. To add your public key to the sample application code, do the following:
  2. 將您Android市集的公鑰放進範例程式原始碼中。這麼做是為了要啟動從Android市集傳來交易資訊的簽章認証機制,請依下列方式新增您的公鑰至範例程式中︰
    1. Log in to your Android Market publisher account.
    2. 登入您的Android市集發佈帳戶
    3. On the upper left part of the page, under your name, click Edit Profile.
    4. 在頁面上方的左側,你的名字底下,點擊Edit Profile
    5. On the Edit Profile page, scroll down to the Licensing & In-app Billing panel.
    6. 在Edit Profile頁面下,往下滑到Licensing & In-app Billing這個區塊中
    7. Copy your public key.
    8. 複製區塊中的公鑰
    9. Open src/com/example/dungeons/Security.java in the editor of your choice. You can find this file in the sample application's project folder.
    10. 打開您使用的軟體開發工具,並打開範例程式專案並找到我們要設定的地方src/com/example/dungeons/Security.java
    11. Add your public key to the following line of code:  
    12.  將您的公鑰加入至下列的原始碼中   String base64EncodedPublicKey = "your public key here";
    13. Save the file.
    14. 存檔
  3. Change the package name of the sample application. The current package name is com.example.dungeons. Android Market does not let you upload applications with package names that contain com.example, so you must change the package name to something else.
  4. 更改範例程式的套件名稱(package name)。現在的套件名稱是com.example.dungeons。Android市集無法讓您上傳com.example開頭的套件名稱,因此你必須更改套件名稱。
  5. Build the sample application in release mode and sign it. To learn how to build and sign applications, see Building and Running.
  6. 建立範例程式並將它釋出,然後做簽署。如果想學習如何建立和替程式加入簽章,請參考Building and Running章節。 
     

Uploading the sample application上傳範例程式

After you build a release version of the sample application and sign it, you need to upload it as a draft to the Android Market publisher site. You also need to create a product list for the in-app items that are available for purchase in the sample application. The following instructions show you how to do this.
在您建立了範例程式的釋出版本,而且也簽署後,您必須將這個範例程式草稿apk上傳至Android市集發佈網站。您也需要在範例程式中建立一些內部消費的產品列表,下方的說明教告知您如何辦到︰

  1. Upload the release version of the sample application to Android Market. Do not publish the sample application; leave it as an unpublished draft application. The sample application is for demonstration purposes only and should not be made publicly available on Android Market. To learn how to upload an application to Android Market, see Uploading applications.
  2. 上傳範例程式的釋出版本至Android市集。請勿發佈此範例程式,請上傳並儲存就好。由於範例程式只是教學用,因此不需要真的發佈至Android市集。如果想學習如何上傳APP至Android市集,請參閱Uploading applications這篇。
  3. Create a product list for the sample application. The sample application lets you purchase two items: a two-handed sword (sword_001) and a potion (potion_001). We recommend that you set up your product list so that sword_001 has a purchase type of "Managed per user account" and potion_001 has a purchase type of "Unmanaged" so you can see how these two purchase types behave. To learn how to set up a product list, see Creating a Product List.
  4. 建立範例程式的產品列表。在範例程式中,我們提供您可以購買2種商品︰雙手劍(sword_001)和藥水(potion_001)。我們建議你將sword_001設定成「受管理(依使用帳戶)」,而potion_001則設成「不受管理」,設定完後,您就可以看到這2種商品類別的呈現方式了。如果想要學如何設定商品列表,請參見Creating a Product List 
    Note: You must publish the items in your product list (sword_001 and potion_001) even though you are not publishing the sample application. Also, you must have a Google Checkout Merchant account to add items to the sample application's product list.
    註︰即使您沒有真的將範例程式發佈出去,內部商品都需要真正發佈出去(sword_001和potion_001) 。並且,您必須擁有Google Checkout Merchant帳戶來新增範例程式商品列表的項目。(譯者註︰程式不發佈是因為不需要真的發佈,否則Android市集會大亂。即使之後你真的有APP要測試iap機制的可行性,都不需要將APP真正發佈,但是內部商品則需要發佈。Google會去檢查您Profile裡設定的測試帳戶,讓那些帳戶可以找到這些未被真實發佈、而又有內部商品的APP供您測試)

Running the sample application執行範例程式

You cannot run the sample application in the emulator. You must install the sample application onto a device to run it. To run the sample application, do the following:
您無法在模擬器中執行範例程式。您必須在裝置上運行它。請參照下列方式來運行︰

  1. Make sure you have at least one test account registered under your Android Market publisher account. You cannot purchase items from yourself (Google Checkout prohibits this), so you need to create at least one test account that you can use to purchase items in the sample application. To learn how to set up a test account, see Setting up Test Accounts.
  2. 請確定至少有一組測試帳號被註冊至您的Android publisher帳戶中。您無購買您自己的商品(會被Google付費禁止)。因此您必須至少加入一組可用的測試帳戶來購買範例程式中的商品。如果想要了解更多測試帳戶如何設定的資訊,請參見Setting up Test Accounts
  3. Verify that your device is running a supported version of the Android Market application or the MyApps application. If your device is running Android 3.0, in-app billing requires version 5.0.12 (or higher) of the MyApps application. If your device is running any other version of Android, in-app billing requires version 2.3.4 (or higher) of the Android Market application. To learn how to check the version of the Android Market application, see Updating Android Market.
  4. 請確認您裝置裡的Android市集軟體或MyApps軟體是能夠支援iap機制的。如果您的裝置在Android3.0運行,需要MyApps軟體在版號5.0.12(或更高)。假如您的裝置運行其它的Android版本,那麼Android市集的版號需求為2.3.4(或更高)。如果想要知道如何檢查Android市集軟體的版號,請參見Updating Android Market這篇文章的描述。
  5. Install the application onto your device. Even though you uploaded the application to Android Market, the application is not published, so you cannot download it from Android Market to a device. Instead, you must install the application onto your device. To learn how to install an application onto a device, see Running on a device.
  6. 將範例程式安裝至裝置上。即使您上傳了這個範例程式至Android市集,由於此APP未被發佈(譯者註︰請見上面的說明),所以您無法從Android市集下載此APP至裝置上。但是您仍然需要安裝這個APP!如果想知道要如何安裝,請參見Running on a device這篇文章。
  7. Make one of your test accounts the primary account on your device. The primary account on your device must be one of the test accounts that you registered on the Android Market site. If the primary account on your device is not a test account, you must do a factory reset of the device and then sign in with one of your test accounts. To perform a factory reset, do the following:
  8. 將測試帳戶設定至您裝置上成為主要帳戶。你裝置上的主要帳戶必須是一組您註冊至Android市集的測試帳戶。如果您裝置上的主要帳戶不是測試帳戶,您必須做原廠設定,然後用您的測試帳戶來登入。如果想知道如何做原廠設定,請照以下的方法執行︰
    1. Open Settings on your device.
    2. 開啟裝置上的設定
    3. Touch Privacy.
    4. 點擊隱私設定
    5. Touch Factory data reset.
    6. 點擊重設為原廠設定
    7. Touch Reset phone 
    8. 點擊重設手機按鈕
    9. After the phone resets, be sure to sign in with one of your test accounts during the device setup process.
    10. 手機重置之後, 請確認您已登入了一組測試帳戶。
  9. Run the application and purchase the sword or the potion. When you use a test account to purchase items, the test account is billed through Google Checkout and your Google Checkout Merchant account receives a payout for the purchase. Therefore, you may want to refund purchases that are made with test accounts, otherwise the purchases will show up as actual payouts to your merchant account.
  10. 執行範例程式,然後購買隻手劍和藥水。當你使用了測試帳戶來購買商品後,測試帳戶會透過Google Checkout的金流流程來執行購買動作。你的Google Checkout Merchant帳戶會接收到購買的花費記錄。因此,您也許會想要退費,因為您正在使用測試帳號來做測試。此外,實際花費的購買項目會顯示在您merchant帳戶裡。

Note: Debug log messages are turned off by default in the sample application. You can turn them on by setting the variable DEBUG to true in the Consts.java file.
註︰範例程式中,除錯用的log訊息預設是關閉的。你可以在Consts.java檔中,將DEBUG變數打開。

Adding the AIDL file to your project將AIDL檔加進您的專案夾中

The sample application contains an Android Interface Definition Language (AIDL) file, which defines the interface to Android Market's in-app billing service (MarketBillingService). When you add this file to your project, the Android build environment creates an interface file (IMarketBillingService.java). You can then use this interface to make billing requests by invoking IPC method calls.
範例程式裡已將Android接口定義語言加入進去了。這個檔定義了將請求發送至Android市集iap服務的接口。當您將此檔案加入至專案夾後,Android建構環境會建立一個接口檔(IMarketBillingService.java)。您可以使用這個接口,透過呼叫IPC函式的呼叫,來發送金流請求。
If you are using the ADT plug-in with Eclipse, you can just add this file to your /src directory. Eclipse will automatically generate the interface file when you build your project (which should happen immediately). If you are not using the ADT plug-in, you can put the AIDL file into your project and use the Ant tool to build your project so that the IMarketBillingService.java file gets generated.

如果您正在Eclipse裡使用ADT插件工具,您可以單純將此檔放進/src目錄下就好。Eclipse會在你建立專案時(應該會立即建立)自動產生接口檔 。如果您不是使用ADT插件工具,您仍可以將AIDL檔放至您的專案夾裡,並使用Ant工具來建立您的專案,這樣子IMarketBillingService.java檔仍會產生。

To add the IMarketBillingService.aidl file to your project, do the following:

如果您要將IMarketBillingService.aidl檔加進您的專案夾裡,請照著以下的說明︰

  1. Create the following directory in your application's /src directory: 
  2. 將下列的目錄加進您/src資料夾底下com/android/vending/billing/
  3. Copy the IMarketBillingService.aidl file into the sample/src/com/android/vending/billing/ directory.
  4. 將IMarketBillingService.aidl檔放進sample/src/com/android/vending/billing/資料夾下
  5. Build your application.
  6. 建立您的程式
You should now find a generated interface file named IMarketBillingService.java in the gen folder of your project.
現在您應該能在您專案裡gen資料夾下找到產生出來的這個IMarketBillingService.java接口檔。 

Updating Your Application's Manifest更新您APP裡Manifest裡的內容

In-app billing relies on the Android Market application, which handles all communication between your application and the Android Market server. To use the Android Market application, your application must request the proper permission. You can do this by adding the com.android.vending.BILLING permission to your AndroidManifest.xml file. If your application does not declare the in-app billing permission, but attempts to send billing requests, Android Market will refuse the requests and respond with a RESULT_DEVELOPER_ERROR response code.
Iap機制仰賴在Android市集軟體裡,因為能夠處理所有您的APP和Android市集伺服器之間的通。為了要使用Android市集軟體,您的APP必須做出合適的權限請求。您可以將com.android.vending.BILLING權限加進AndroidManifest.xml檔裡。如果您的APP沒有宣告此iap權限,並且嚐試發送金流請求,Androdi市集將會拒請您的請求,並且發出RESULT_DEVELOPER_ERROR這個回應值給您。
 
In addition to the billing permission, you need to declare the BroadcastReceiver that you will use to receive asynchronous response messages (broadcast intents) from Android Market, and you need to declare the Service that you will use to bind with the IMarketBillingService and send messages to Android Market. You must also declare intent filters for the BroadcastReceiver so that the Android system knows how to handle the broadcast intents that are sent from the Android Market application.

關於金流權限的宣告,這裡要附帶一提的是,您必須再宣告一個BroadcastReceiver來讓你能夠接收從Android市集傳來的異步回應訊息(廣播意圖),然後您需要宣告一個Service讓你能夠將IMarketBillingService綁定至您的程式,因而能夠發送訊息至Android市集。您也需要在BroadcastReceiver裡宣告intent filters讓Android系統了解如何處理來自Android市集軟體傳來的廣播意圖。

For example, here is how the in-app billing sample application declares the billing permission, the BroadcastReceiver, the Service, and the intent filters. In the sample application, BillingReceiver is the BroadcastReceiver that handles broadcast intents from the Android Market application and BillingService is the Service that sends requests to the Android Market application.
底下這些是範例程式裡宣告金流權限的範例,BroadcastReceiver(廣播接收機制)、Service(背景的服務)和intent filters。在範例程式裡,BillingReceiver就是這裡提的廣播接收機制,它幫助您處理從Android市集傳來的廣播意圖。而BillingService就是這個背景服務,負責發送請求至Android市集軟體。


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.dungeons"
  android:versionCode="1"
  android:versionName="1.0">

  <uses-permission android:name="com.android.vending.BILLING" />

  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".Dungeons" android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

    <service android:name="BillingService" />

    <receiver android:name="BillingReceiver">
      <intent-filter>
        <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
        <action android:name="com.android.vending.billing.RESPONSE_CODE" />
        <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
      </intent-filter>
    </receiver>

  </application>
</manifest>
 

Creating a Local Service建立一個本地端Service服務

Your application must have a local Service to facilitate messaging between your application and Android Market. At a minimum, this service must do the following:
您的APP必須有一個本地端的Service來有效率的處理您跟Android市集之間的訊息。最起碼,這個Service必須有一些下列列出來的功能︰

  • Bind to the MarketBillingService
  • 首先,這個Service要和MarketBillingService做綁定的動作
  • Send billing requests (as IPC method calls) to the Android Market application. The five types of billing requests include: 
  • 能夠發送金流請求(由IPC method來呼叫)到Android市集應用程式中。這5種請求包含︰
    • CHECK_BILLING_SUPPORTED requests 檢查金流是否支援的請求
    • REQUEST_PURCHASE requests 購買的請求
    • GET_PURCHASE_INFORMATION requests 取得購買資訊的請求
    • CONFIRM_NOTIFICATIONS requests 確認Android傳來的通知的請求
    • RESTORE_TRANSACTIONS requests 還原交易狀態的請求
  • Handle the synchronous response messages that are returned with each billing request.
  • 能夠處理每次我們發出金流請求後,Android市集同步回傳回來的同步反應訊息。
     

Binding to the MarketBillingService綁定到市集的金流服務

Binding to the MarketBillingService is relatively easy if you've already added the IMarketBillingService.aidl file to your project. The following code sample shows how to use the bindService() method to bind a service to the MarketBillingService. You could put this code in your service's onCreate() method.
如果您已經將IMarketBillingService.aidl檔加進您的專案夾裡,綁定到市集的金流服務應該就蠻簡單的了。跟著下面的範例程式碼,將會告訴您如何使用bindService()函式來綁定一個Service到市集金流服務中。您可以試著將這些程式碼放入您Service的onCreate()這個回呼函式中。

try {
  boolean bindResult = mContext.bindService(
    new Intent("com.android.vending.billing.MarketBillingService.BIND"), this,
    Context.BIND_AUTO_CREATE);
  if (bindResult) {
    Log.i(TAG, "Service bind successful.");
  } else {
    Log.e(TAG, "Could not bind to the MarketBillingService.");
  }
} catch (SecurityException e) {
  Log.e(TAG, "Security exception: " + e);
}
After you bind to the service, you need to create a reference to the IMarketBillingService interface so you can make billing requests via IPC method calls. The following code shows you how to do this using the onServiceConnected() callback method.
在您綁定到Service後,您需要建立一個參照到IMarketBillingService介面的接口。這樣您才可以透過IPC函式的呼叫來達到金流請求的能力。底下的程式碼告知您如何使用onServiceConnected()回呼函式來做到這件事。

/**
  * The Android system calls this when we are connected to the MarketBillingService.
  */
  public void onServiceConnected(ComponentName name, IBinder service) {
    Log.i(TAG, "MarketBillingService connected.");
    mService = IMarketBillingService.Stub.asInterface(service);
  }
You can now use the mService reference to invoke the sendBillingRequest() method.
For a complete implementation of a service that binds to the MarketBillingService, see the BillingService class in the sample application.

現在您可以使用參照到的這個mService來呼叫sendBillingRequest()函式了。想要了解完全實作綁定到市集金流服務的Service要怎麼做的話,可以參照範例程式裡BillingService這個類別檔案。


Sending billing requests to the MarketBillingService發送金流請求至市集金流服務

Now that your Service has a reference to the IMarketBillingService interface, you can use that reference to send billing requests (via IPC method calls) to the MarketBillingService. The MarketBillingService IPC interface exposes a single public method (sendBillingRequest()), which takes a single Bundle parameter. The Bundle that you deliver with this method specifies the type of request you want to perform, using various key-value pairs. For instance, one key indicates the type of request you are making, another indicates the item being purchased, and another identifies your application. The sendBillingRequest() method immediately returns a Bundle containing an initial response code. However, this is not the complete purchase response; the complete response is delivered with an asynchronous broadcast intent. For more information about the various Bundle keys that are supported by the MarketBillingService, see In-app Billing Service Interface.
現在您的Service已經有一個IMarketBillingService的接口了。您可以使用它來發送金流請求(透過IPC函式的呼叫)到市集金流服務中。市集金流服務這個IPC接口有一個public函式,可以拿它來裝一個Bundle參數。Bundle讓您傳送您指定的金流請求 - 使用key-value的方式。舉例來說,現在有一個key值,您拿來發出一個請求類型(如︰CHECK_BILLING_SUPPORTED)。而另一個key值傳送您想購買的商品為何,最後一個key值傳送辨識您APP的方式。此時,sendBillingRequest()函式會立刻返回一個含有初始回應值的Bundle給您。然而,這並非是完整購買的回應,完整的回應應該是會以一個異步的廣播意圖來傳達給您的。如果想知道市集金流服務裡,有被支援的各種Bundle的key的用法,請參見 In-app Billing Service Interface這個章節。

You can use the sendBillingRequest() method to send five types of billing requests. The five request types are specified using the BILLING_REQUEST Bundle key. This Bundle key can have the following five values:

您可以使用sendBillingRequest()函式去發送五種類型的金流請求。這五種金流請求皆被指定使用BILLING_REQUEST Bundle key。這些key如下︰
  • CHECK_BILLING_SUPPORTED—verifies that the Android Market application supports in-app billing.
  • CHECK BILLING_SUPPORTED(檢查是否支援金流服務) - 驗証裝置上的Android市集軟體是否支援iap機制。
  • REQUEST_PURCHASE—sends a purchase request for an in-app item.
  • REQUEST_PURCHASE(請求購買商品) - 發送購買商品的請求。
  • GET_PURCHASE_INFORMATION—retrieves transaction information for a purchase or refund.
  • GET_PURCHASE_INFORMATION(取得購買資訊) - 接收商品購買或退費的交易資訊。
  • CONFIRM_NOTIFICATIONS—acknowledges that you received the transaction information for a purchase or refund.
  • CONFIRM_NOTIFICATIONS(確認Andorid市集傳來的通知訊息) - 向市集做出交易資訊購買或退費而傳來的通知確認。
  • RESTORE_TRANSACTIONS—retrieves a user's transaction history for managed purchases.
  • RESTORE_TRANSACTIONS(還原交易狀態) - 接收使用者受管理類型的購買商品歷史交易資訊。
To make any of these billing requests, you first need to build an initial Bundle that contains the three keys that are required for all requests: BILLING_REQUEST, API_VERSION, and PACKAGE_NAME. The following code sample shows you how to create a helper method named makeRequestBundle() that does this.
如果要發送以上任一金流請求,首先你需要先建立一個初始化的Bundle用來夾帶3個key值。這3個key分別為︰BILLING_REQUEST, API_VERSION, PACKAGE_NAME。下列的程式教你如何建立一個助手函式(helper method) - makeRequestBundle()來達成這個任務︰

protected Bundle makeRequestBundle(String method) {
  Bundle request = new Bundle();
  request.putString(BILLING_REQUEST, method);
  request.putInt(API_VERSION, 1);
  request.putString(PACKAGE_NAME, getPackageName());
  return request;
 
To use this helper method, you pass in a String that corresponds to one of the five types of billing requests. The method returns a Bundle that has the three required keys defined. The following sections show you how to use this helper method when you send a billing request.
要使用這個助手函式,你需要先傳入一組String值,這個String值是上面列出的五種的其中一種。這個助手函式會返回一個Bundle,該Bundle含蓋了3個被定義的需求key(譯者註︰就是上面提到的BILLING_REQUEST, API_VERSION和PACKAGE_NAME)。下一個段落將會教你如何在需要發送金流請求時,使用這個助手函式來完成。

Important: You must make all in-app billing requests from your application's main thread.
重要︰你必須在你的應用程式主執行緒來發送iap金流請求,不可使用次執行緒來發送。


Verifying that in-app billing is supported (CHECK_BILLING_SUPPPORTED)驗証您裝置上的Android市集APP是否支援iap機制(使用CHECK_BILLING_SUPPORTED)

The following code sample shows how to verify whether the Android Market application supports in-app billing. In the sample, mService is an instance of the MarketBillingService interface.
下面的程式碼列出了如何驗証手上裝置的Android市集軟體是否有支援iap機制。在這範例中,mService是MarketBillingService介面的物件實體。


/**
* Request type is CHECK_BILLING_SUPPORTED
*/
  Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED");
  Bundle response = mService.sendBillingRequest(request);
  // Do something with this response.
}
 
The makeRequestBundle() method constructs an initial Bundle, which contains the three keys that are required for all requests: BILLING_REQUEST, API_VERSION, and PACKAGE_NAME. The request returns a synchronous Bundle response, which contains only a single key: RESPONSE_CODE. The RESPONSE_CODE key can have the following values:
makeRequesstBundle()函式會建構一個初始化的Bundle,這個Bundle會包含3個key值,也就是請求需要的BILLING_REQUEST, API_VERSION和PACKAGE_NAME。請求會回傳一個同步性質的Bundle回應,這個回應只會包含一個key值︰RESPONSE_CODE。RESPONSE_CODE可能會傳來︰

  • RESULT_OK—in-app billing is supported.
  • REQUEST_OK - 回覆您手機Android市集的軟體版號有支援iap機制
  • RESULT_BILLING_UNAVAILABLE—in-app billing is not available because the API version you specified is not recognized or the user is not eligible to make in-app purchases (for example, the user resides in a country that prohibits in-app purchases).
  • RESULT_BILLING_UNAVAILABLE - 由於您指定的API版本無法辨識或者使用者經由非法管道提出iap的請求(例如,使用者居住地禁止使用iap機制),因此無法支援iap服務。
  • RESULT_ERROR—there was an error connecting with the Android Market application.
  • RESULT_ERROR - 在連接Android市集軟體時,出了些問題。
  • RESULT_DEVELOPER_ERROR—the application is trying to make an in-app billing request but the application has not declared the com.android.vending.BILLING permission in its manifest. Can also indicate that an application is not properly signed, or that you sent a malformed request.
  • RESULT_DEVELOPER_ERROR - 您的APP試圖發出iap請求,但是由於Android Manifest.xml裡未宣告權限︰com.android.vending.BILLING,或者您的APP未做適當的簽署,也或者您的APP發出了一個未制定的請求,因此您收到了這個RESPONSE_CODE。
The CHECK_BILLING_SUPPORTED request does not trigger any asynchronous responses (broadcast intents).
CHECK_BILLING_SUPPORTED請求不會發出任何異步回應(指的是廣播意圖)。
We recommend that you invoke the CHECK_BILLING_SUPPORTED request within a RemoteException block. When your code throws a RemoteException it indicates that the remote method call failed, which means that the Android Market application is out of date and needs to be updated. In this case, you can provide users with an error message that contains a link to the Updating Android Market Help topic.

我們建議您在RemoteException區塊中呼叫CHECK_BILLING_SUPPORTED請求。當您的程式碼抛出了RemoteException這個例外,表示遠端呼叫函式出錯了,這意謂著Android市集軟體過期了、或者需要更新了。在這個狀況下,你可以提供一組連結至更新Android市集幫助文檔的連結,告知使用者哪裡出錯了。 
The sample application demonstrates how you can handle this error condition (see DIALOG_CANNOT_CONNECT_ID in Dungeons.java).
範例程式也說明了您能如何的處理此錯誤訊息(請看Dungeons.java裡DIALOG_CANNOT_CONNECT_ID)。


Making a purchase request (REQUEST_PURCHASE)發出購買請求(REQUEST_PURCHASE)

To make a purchase request you must do the following:
如果要發出購買請求,您必須遵照以下步驟︰

  • Send the REQUEST_PURCHASE request.
  • 發送REQUEST_PURCHASE請求
  • Launch the PendingIntent that is returned from the Android Market application.
  • 執行PendingIntent,這是由Android市集軟體回傳回來的值。
  • Handle the broadcast intents that are sent by the Android Market application.
  • 處理由Android市集軟體發送過來的廣播意圖。

Making the request發出請求
You must specify four keys in the request Bundle. The following code sample shows how to set these keys and make a purchase request for a single in-app item. In the sample, mProductId is the Android Market product ID of an in-app item (which is listed in the application's product list), and mService is an instance of the MarketBillingService interface.
您必須在請求的Bundle中置入4組key。底下的程式碼範例將教您如何在一筆iap商品中,設定這4組key的購買請求。請範例裡,mProductId是Android市集的應用程式內商品的產品ID(被列表於軟體的產品項目中),而mService是MarketBillingService介面的實體。



/**
* Request type is REQUEST_PURCHASE
*/
  Bundle request = makeRequestBundle("REQUEST_PURCHASE");
  request.putString(ITEM_ID, mProductId);
  // Note that the developer payload is optional.
  if (mDeveloperPayload != null) {
    request.putString(DEVELOPER_PAYLOAD, mDeveloperPayload);
  }
  Bundle response = mService.sendBillingRequest(request);
  // Do something with this response.
 
The makeRequestBundle() method constructs an initial Bundle, which contains the three keys that are required for all requests: BILLING_REQUEST, API_VERSION, and PACKAGE_NAME. The ITEM_ID key is then added to the Bundle prior to invoking the sendBillingRequest() method.
The request returns a synchronous Bundle response, which contains three keys: RESPONSE_CODE, PURCHASE_INTENT, and REQUEST_ID. The RESPONSE_CODE key provides you with the status of the request and the REQUEST_ID key provides you with a unique request identifier for the request. The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI.

makeRequesstBundle()函式建構了一個初始化的Bundle,這個Bundle包含了3組key值,分別為︰BILLING_REQUEST, API_VERSION和PACKAGE_NAME,這些key值都是在使用這個函式時需要的。key ITEM_ID接著會加進Bundle中,拿來呼叫sendBillingRequest()函式。
此請求會回傳一個同步的Bundle回應,包含了3個key值︰
RESPONSE_CODE,PURCHASE_INTENT和REQUEST_ID 3組值。RESPONSE_CODE值提供了請求的狀態,REQUEST_ID值提供您單一的請求識別碼,PURCHASE_INTENT值提供您一組PendingIntent,可以拿它啟動付費使用介面。

Using the pending intent使用pending intent(即將發生的意圖)
How you use the pending intent depends on which version of Android a device is running. On Android 1.6, you must use the pending intent to launch the checkout UI in its own separate task instead of your application's activity stack. On Android 2.0 and higher, you can use the pending intent to launch the checkout UI on your application's activity stack. The following code shows you how to do this. You can find this code in the PurchaseObserver.java file in the sample application.
如何使用pending intent(即將發生的意圖)完全仰賴於裝置運行的Android版本。如果是Androdi1.6,您必須在另外的執行緒中運行付費使用介面,而非應用程式主執行緒來完成工作。在Android2.0或更高的版本,您可以在您應用軟體的activity stack中使用pending intent。底下的式碼將教導您如何使用pending intent。您可以在範例檔中,PruchaseObserver.java檔裡找到這段式碼。


void startBuyPageActivity(PendingIntent pendingIntent, Intent intent) {
  if (mStartIntentSender != null) {
    // This is on Android 2.0 and beyond.  The in-app checkout page activity
    // will be on the activity stack of the application.
    try {
      // This implements the method call:
      // mActivity.startIntentSender(pendingIntent.getIntentSender(),
      //     intent, 0, 0, 0);
      mStartIntentSenderArgs[0] = pendingIntent.getIntentSender();
      mStartIntentSenderArgs[1] = intent;
      mStartIntentSenderArgs[2] = Integer.valueOf(0);
      mStartIntentSenderArgs[3] = Integer.valueOf(0);
      mStartIntentSenderArgs[4] = Integer.valueOf(0);
      mStartIntentSender.invoke(mActivity, mStartIntentSenderArgs);
    } catch (Exception e) {
      Log.e(TAG, "error starting activity", e);
      }
  } else {
    // This is on Android 1.6. The in-app checkout page activity will be on its
    // own separate activity stack instead of on the activity stack of
    // the application.
    try {
      pendingIntent.send(mActivity, 0 /* code */, intent);
    } catch (CanceledException e) {
      Log.e(TAG, "error starting activity", e);
      }
  }
}
 

Important: You must launch the pending intent from an activity context and not an application context. Also, you cannot use the singleTop launch mode to launch the pending intent. If you do either of these, the Android system will not attach the pending intent to your application process. Instead, it will bring Android Market to the foreground, disrupting your application.

重要︰您必須在Activity的context下運行pending intent而非使用application的context。您不能使用singleTop這個launch mode(執行模式)來執行pending intent。如果您都這樣做的話,Android系統將無法將pending intent附著於您的應用程式進程中。反之,它還會將Android市集帶至前景,擾亂了您的應用程式運行。
 
Handling broadcast intents處理Android市集傳來的廣播意圖
A REQUEST_PURCHASE request also triggers two asynchronous responses (broadcast intents). First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides error information about the request. If the request does not generate an error, the RESPONSE_CODE broadcast intent returns RESULT_OK, which indicates that the request was successfully sent. (To be clear, a RESULT_OK response does not indicate that the requested purchase was successful; it indicates that the request was sent successfully to Android Market.)
當我們發送出REQUEST_PURCHASE請求時,Android市集會以異步的方式傳來2個回應(皆為廣播意圖)。第1個是RESPONSE_CODE,該廣播意圖可能會傳來關關於請求的錯誤資訊。倘若請求未發生任何錯誤,REPONSE_CODE原則上應該是會回傳RESULT_OK,這個回應指出你的請求已經成功被發送。(這裡要釐清一點︰RESULT_OK這個回應並不代表請求購買成功,而是指我們發送請求購買至Android市集這個動作是成功的。)
Next, when the requested transaction changes state (for example, the purchase is successfully charged to a credit card or the user cancels the purchase), the Android Market application sends an IN_APP_NOTIFY broadcast intent. This message contains a notification ID, which you can use to retrieve the transaction details for the REQUEST_PURCHASE request.

接著,當請求過的交易資訊遭改變(舉例︰購買行為真的使用了信用卡消費或者使用者取消購買),Android市集軟體會發送IN_APP_NOTIFY這個廣播意圖給您。這個訊息會夾雜訊息通知ID(notification ID),可以用這個ID來接收我們發送出REQUEST_PURCHASE請求的交易詳細資訊。

Note: The Android Market application also sends an IN_APP_NOTIFY for refunds. For more information, see Handling IN_APP_NOTIFY messages.
註︰Android市集軟體也會發送使用者退費的IN_APP_NOTIFY廣播意圖。更多資訊,請參閱Handling IN_APP_NOTIFY messages這個篇幅。
Because the purchase process is not instantaneous and can take several seconds (or more), you must assume that a purchase request is pending from the time you receive a RESULT_OK message until you receive an IN_APP_NOTIFY message for the transaction. While the transaction is pending, the Android Market checkout UI displays an "Authorizing purchase..." notification; however, this notification is dismissed after 60 seconds and you should not rely on this notification as your primary means of conveying transaction status to users. Instead, we recommend that you do the following:
由於購買流程不會馬上完成,這可能會花上數分鐘的時間(甚至更多時間),您必須假設會有一段要等待接收該交易RESULT_OK訊息至接收IN_APP_NOTIFY的過程時間。當交易機制在背後運作時,Android市集提供的付費使用畫面會顯示"購買驗証中..."訊息;然而,當背景在傳送購買的這段過間,付費使用畫面上的訊息可能會在60秒後被關掉,因此您不應該等待這個訊息的完成。反之,在這段時間,我們會建議您做下列幾件事︰

  • Add an Activity to your application that shows users the status of pending and completed in-app purchases.
    新增一個Activity到您的APP中,告知使用者正在等待完成購買應用程式內商品。
  • Use a status bar notification to keep users informed about the progress of a purchase.
  • 用訊息列通知(status bar norification)來通知使用者現在的交易狀況。
To use these two UI elements, you could invoke a status bar notification with a ticker-text message that says "Purchase pending" when your application receives a RESULT_OK message. Then, when your application receives an IN_APP_NOTIFY message, you could update the notification with a new message that says "Purchase succeeded" or "Purchase failed." When a user touches the expanded status bar notification, you could launch the activity that shows the status of pending and completed in-app purchases.
若 要用到上述的這2個使用介面元件,您應該在您APP接收到RESULT_OK訊息時,呼叫訊息通知欄,並顯示一段文字︰"購買等待中"。然後,當您的 APP接收到了IN_APP_NOTIFY訊息後,您應該更新訊息通知的文字內容,改成"購買成功"或"購買失敗了"。當使用者點擊了該訊息通知欄位,您 應該要啟動一個Activity,顯示出"正在等待購買流程"或"已完成iap購買"的狀態。
If you use some other UI technique to inform users about the state of a pending transaction, be sure that your pending status UI does not block your application. For example, you should avoid using a hovering progress wheel to convey the status of a pending transaction because a pending transaction could last a long time, particularly if a device loses network connectivity and cannot receive transaction updates from Android Market.
若您要使用一些其它的使用介面技巧來通知您的user「交易正在處理中,請稍待」時,請確定您的通知使用介面不會將您原本的APP鎖死。舉例來説,您應該避免使用旋轉等待輪之類的東西,告知使用者您正在等待交易的進行。因為等待交易可能會花上一段很長的時間,特別是裝置在無連線能力或者無法接收從Android市集傳來的交易狀態更新時。

Important: If a user purchases a managed item, you must prevent the user from purchasing the item again while the original transaction is pending. If a user attempts to purchase a managed item twice, and the first transaction is still pending, Android Market will display an error to the user; however, Android Market will not send an error to your application notifying you that the second purchase request was canceled. This might cause your application to get stuck in a pending state while it waits for an IN_APP_NOTIFY message for the second purchase request.
重要︰如果使用者購買的是受管理性質的商品,您必須預防使用者在既有交易等待狀態下再次購買商品。假如使用者試圖購買第2次受管理性質的商品,而同時第1筆交易又仍在等待中,Andorid市集會顯示錯誤給使用者;然而,Andorid市集不會發送錯誤給您的APP知道,您的APP也因此不會知道第2次請求購買的動作被取消了。這可能會造成您的APP在等待第2次購買請求要傳來的IN_APP_NOTIFY訊息時,整個程式卡住了。 

Retrieving transaction information for a purchase or refund (GET_PURCHASE_INFORMATION)接收購買成功或退費的交易訊息(GET_PURCHASE_INFORMATION)

You retrieve transaction information in response to an IN_APP_NOTIFY broadcast intent. The IN_APP_NOTIFY message contains a notification ID, which you can use to retrieve transaction information.
您會在Androkd市集傳來的IN_APP_NOTIFY廣播意圖裡收到交易資訊。這個訊息包含了訊息通知ID(notification ID),您可以拿它來接收交易資訊。
To retrieve transaction information for a purchase or refund you must specify five keys in the request Bundle. The following code sample shows how to set these keys and make the request. In the sample, mService is an instance of the MarketBillingService interface.

如果想要知道購買成功或者被退費,您必須在請求Bundle裡指定5個key值。底下的程式範例將會教您如何設定這5個key值並且發出詢問的請求。在這個範例中,mService是MarketBillingService介面的實體元件。


/**
* Request type is GET_PURCHASE_INFORMATION
*/
  Bundle request = makeRequestBundle("GET_PURCHASE_INFORMATION");
  request.putLong(REQUEST_NONCE, mNonce);
  request.putStringArray(NOTIFY_IDS, mNotifyIds);
  Bundle response = mService.sendBillingRequest(request);
  // Do something with this response.
}
 
The makeRequestBundle() method constructs an initial Bundle, which contains the three keys that are required for all requests: BILLING_REQUEST, API_VERSION, and PACKAGE_NAME. The additional keys are then added to the bundle prior to invoking the sendBillingRequest() method. The REQUEST_NONCE key contains a cryptographically secure nonce (number used once) that you must generate. The Android Market application returns this nonce with the PURCHASE_STATE_CHANGED broadcast intent so you can verify the integrity of the transaction information. The NOTIFY_IDS key contains an array of notification IDs, which you received in the IN_APP_NOTIFY broadcast intent.
makeRequestBundle()這個函式建立了一個初始化的Bundle,包含了3個key值,提供發出請求之需,這3個key值分別為︰BILLING_REQUEST, API_VERSION和PACKAGE_NAME,加入後用來呼叫sendBillingRequest()函式。而REQUEST_NONCE這個key值則包含一組您必須產生的密碼安全隨機數(僅用一次)。Android市集軟體就會回傳夾雜這個隨機數的PURCHASE_STATE_CHANGED廣播意圖給您,因此您完全可以做好交易資訊的驗証工作。NOTIFY_IDS這個key值包含了一組訊息通知IDs的陣列,可以在IN_APP_NOTIFY這個廣播意圖中接收到。

The request returns a synchronous Bundle response, which contains two keys: RESPONSE_CODE and REQUEST_ID. The RESPONSE_CODE key provides you with the status of the request and the REQUEST_ID key provides you with a unique request identifier for the request.
A GET_PURCHASE_INFORMATION request also triggers two asynchronous responses (broadcast intents). First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides status and error information about the request. Next, if the request was successful, the Android Market application sends a PURCHASE_STATE_CHANGED broadcast intent. This message contains detailed transaction information. The transaction information is contained in a signed JSON string (unencrypted). The message includes the signature so you can verify the integrity of the signed string.

該請求會回傳一組同步的Bundle回應,並且包含了2個key值︰RESPONSE_CODE和REQUEST_ID。RESPONSE_CODE此key提供給您請求的狀態,REQUEST_ID則提供您一組用來請求的單一請求識別。
GET_PURCHASE_INFORMATION這個請求也會發送出2組異步的回應(廣播意圖)。首先,
Android市集軟體會發送RESPONSE_CODE這個廣播意圖,提供您關於請求的狀態或錯誤資訊。接著,如果請求是成功的,Android市集軟體會發送PURCHASE_STATE_CHANGE這個廣播意圖過來。這個訊息包含了交易資訊的詳細內容,而交易訊又是被簽署在JSON字串裡(以未加密的方式)。該訊息包含了數位簽章,因此您可以對簽章的字串做出高完整度的驗証。 

Acknowledging transaction information (CONFIRM_NOTIFICATIONS)承認該筆交易資訊(CONFIRM_NOTIFICATIONS)

To acknowledge that you received transaction information you send a CONFIRM_NOTIFICATIONS request. You must specify four keys in the request Bundle. The following code sample shows how to set these keys and make the request. In the sample, mService is an instance of the MarketBillingService interface.
如果要承認您已收到交易資訊,那就請您發送一組CONFIRM_NOTIFICATIONS請求。在這個請求的Bundle裡,您必須指定4組key值。底下的範例程式將會教導您如何設定這些key值,並且發送出請求。在範例中,mService是MarketBillingService介面的實體元件。


/**
* Request type is CONFIRM_NOTIFICATIONS
*/
  Bundle request = makeRequestBundle("CONFIRM_NOTIFICATIONS");
  request.putStringArray(NOTIFY_IDS, mNotifyIds);
  Bundle response = mService.sendBillingRequest(request);
  // Do something with this response.
}
 
The makeRequestBundle() method constructs an initial Bundle, which contains the three keys that are required for all requests: BILLING_REQUEST, API_VERSION, and PACKAGE_NAME. The additional NOTIFY_IDS key is then added to the bundle prior to invoking the sendBillingRequest() method. The NOTIFY_IDS key contains an array of notification IDs, which you received in an IN_APP_NOTIFY broadcast intent and also used in a GET_PURCHASE_INFORMATION request.
makeRequestBundle()函式建構了一個初始化的Bundle值,該Bundle值包含了3組key值,用來提供給Android市集,這3組key值分別為BILLING_REQUEST, API_VERSION, PACKAGE_NAME。接著要將NOTIFY_IDS加進這個Bundle中去呼叫sendBillingRequesst()函式。NOTIFY_IDS這個key值包含了一組訊息通知ID的陣列,您可以拿來接收IN_APP_NOTIFY廣播意圖,也可以拿來發出GET_PURCHASE_INFORMATION的請求。
The request returns a synchronous Bundle response, which contains two keys: RESPONSE_CODE and REQUEST_ID. The RESPONSE_CODE key provides you with the status of the request and the REQUEST_ID key provides you with a unique request identifier for the request.
該請求會回傳一個同步的Bundle回應,並包含2個key︰RESPONSE_CODE和REQUEST_ID。RESPONSE_CODE key提供了請求的狀態以及單一請求識別碼的REQUEST_ID key。
A CONFIRM_NOTIFICATIONS request triggers a single asynchronous response—a RESPONSE_CODE broadcast intent. This broadcast intent provides status and error information about the request.

CONFIRM_NOTIFICATIONS請求會回傳一個異步的回應 - 一個RESPONSE_CODE廣播意圖。這個廣播意圖提供了請求的狀態與錯誤的相關資訊。

You must send a confirmation when you receive transaction information from Android Market. If you don't send a confirmation message, Android Market will continue sending IN_APP_NOTIFY messages for the transactions you have not confirmed. Also, your application must be able to handle IN_APP_NOTIFY messages that contain multiple orders.
In addition, as a best practice, you should not send a CONFIRM_NOTIFICATIONS request for a purchased item until you have delivered the item to the user. This way, if your application crashes or something else prevents your application from delivering the product, your application will still receive an IN_APP_NOTIFY broadcast intent from Android Market indicating that you need to deliver the product.

當您在接收到來至於Android市集傳來的交易資訊之後,您必發出確認動作。假如您未發送確認訊息出去,Android市集將會不斷地發送IN_APP_NOTIFY訊息給您,直至您確認了這筆交易。而且,您的APP必須能夠處理包含多筆訂單的IN_APP_NOTIFY訊息。附帶一提,在最佳的狀況下,在您尚未傳遞商品給user以前,都不應該發出該商品的CONFIRM_NOTIFICATIONS請求。假如您的應用程式當了,或者其它的原因造成您的APP無法傳遞商品,您都還可以繼續接收到來至Androkd市集傳來的IN_APP_NOTIFY廣播意圖來傳遞您需要傳遞的商品。


Restoring transaction information (RESTORE_TRANSACTIONS)還原交易資訊(RESTORE_TRANSACTIONS)

To restore a user's transaction information, you send a RESTORE_TRANSACTIONS request. You must specify four keys in the request Bundle. The following code sample shows how to set these keys and make the request. In the sample, mService is an instance of the MarketBillingService interface.
如果要還原使用者的交易資訊,您必須發送RESTORE_TRANSACTIONS請求至Androkd市集。您必須使用4組指定請求的Bundle key。底下這段程式範例將會教導您如何設定這些key並發出請求。在範例程式中,mService是MarketBillingService介面的物件實體。


/**
* Request type is RESTORE_TRANSACTIONS
*/
  Bundle request = makeRequestBundle("RESTORE_TRANSACTIONS");
  request.putLong(REQUEST_NONCE, mNonce);
  Bundle response = mService.sendBillingRequest(request);
  // Do something with this response.
}
 
The makeRequestBundle() method constructs an initial Bundle, which contains the three keys that are required for all requests: BILLING_REQUEST, API_VERSION, and PACKAGE_NAME. The additional REQUEST_NONCE key is then added to the bundle prior to invoking the sendBillingRequest() method. The REQUEST_NONCE key contains a cryptographically secure nonce (number used once) that you must generate. The Android Market application returns this nonce with the transactions information contained in the PURCHASE_STATE_CHANGED broadcast intent so you can verify the integrity of the transaction information.
makeRequestBundle()函式建構了一個初始化的Bundle,用來夾帶這3組請求需要的key︰BILLING_REQUEST, API_VERSION和PACKAGE_NAME。附帶的REQUEST_NONCE key加入Bundle裡主要是用來呼叫sendBillingRequest()函式用的。REQUEST_NONCE key包含了您產生的一組加密安全隨機數(僅用一次)。Android市集軟體會透過PURCHASE_STATE_CHANGED這個廣播意圖來返回這串隨機數和交易資訊,因此您可以用來做完整交易資訊的驗証工作。
The request returns a synchronous Bundle response, which contains two keys: RESPONSE_CODE and REQUEST_ID. The RESPONSE_CODE key provides you with the status of the request and the REQUEST_ID key provides you with a unique request identifier for the request.

該請求回傳了一組同步的Bundle回應,該Bundle包含了2個key︰RESPONSE_CODE和REQUEST_ID。RESPONSE_CODE key提供您請求的狀態,REQUEST_ID key提供您該請求的單一請求識別碼。
A RESTORE_TRANSACTIONS request also triggers two asynchronous responses (broadcast intents). First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides status and error information about the request. Next, if the request was successful, the Android Market application sends a PURCHASE_STATE_CHANGED broadcast intent. This message contains the detailed transaction information. The transaction information is contained in a signed JSON string (unencrypted). The message includes the signature so you can verify the integrity of the signed string.

RESTORE_TRANSACTIONS請求也會傳來2組異步的回應(廣播意圖)。首先,Android市集軟體會發送一組RESPONSE_CODE廣播意圖,用來提供請求的狀態以及錯誤資訊。接著,如果請求是成功的,Android市集軟體會發送PURCHASE_STATE_CHANGED廣播意圖過來。這個訊息包含了交易資訊的詳細內容。此交易資訊被包含在一個被簽署的JSON字串裡(非加密狀態)。這個JSON訊息包含了數位簽章,因此您可以用這個簽章來做完整的簽章字串驗證。

Note: You should use the RESTORE_TRANSACTIONS request type only when your application is installed for the first time on a device or when your application has been removed from a device and reinstalled.
註︰使用RESTORE_TRANSACTIONS請求的時機應該只會發生在當您的APP首次安裝在裝置上,或者是被移除後又安裝回來的狀況底下。 

Other service tasks

Service的其它工作

You may also want your Service to receive intent messages from your BroadcastReceiver. You can use these intent messages to convey the information that was sent asynchronously from the Android Market application to your BroadcastReceiver. To see an example of how you can send and receive these intent messages, see the BillingReceiver.java and BillingService.java files in the sample application. You can use these samples as a basis for your own implementation. However, if you use any of the code from the sample application, be sure you follow the guidelines in Security and Design.
您也許也想要使用您的Service來接收從您BroadcastReceiver傳來的意圖訊息。您可以將那些從Android市集軟體以異步方式傳至您BroadcastReceiver裡的意圖訊息來傳遞資訊。若想看範例説明該如何發送和接收這些意圖訊息,請參見歸檔在範例程式中的BillingReceiver.java和BillingService.java檔。您可以像基本實作自己類別的方式來使用它們。然而,如果您真的採用了範例程式裡的任何原始碼來編寫您的iap機制,我們希望您已經確定讀過Security and Design這篇文章。


Creating a BroadcastReceiver建立一個廣播接收機制

The Android Market application uses broadcast intents to send asynchronous billing responses to your application. To receive these intent messages, you need to create a BroadcastReceiver that can handle the following intents:
Androkd市集使用了一個廣播意圖來傳送異步的金流回應到您的APP裡。如果要接收這些意圖訊,您必須建立一個BroadReceiver來處理底下的這些意圖︰

  • com.android.vending.billing.RESPONSE_CODE This broadcast intent contains an Android Market response code, and is sent after you make an in-app billing request. For more information about the response codes that are sent with this response, see Android Market Response Codes for In-app Billing.
  • com.android.vending.billing.RESPONSE_CODE 這個廣播意圖包含了一個Android市集回應碼,會在您接著要執行iap機制再被發送出去。如果想知道更多關於這個回應將被發送出去的回應碼的相關訊息,請見 Android Market Response Codes for In-app Billing這個篇幅。
  • com.android.vending.billing.IN_APP_NOTIFY This response indicates that a purchase has changed state, which means a purchase succeeded, was canceled, or was refunded. For more information about notification messages, see In-app Billing Broadcast Intents
  • com.android.vending.billing.IN_APP_NOTIFY 這個回應指出了購買狀態遭到變更了。這意謂著購買成功了、取消了、甚至是退費了。更多關於訊息通知的相關資訊,請參見In-app Billing Broadcast Intents篇幅。
  • com.android.vending.billing.PURCHASE_STATE_CHANGED This broadcast intent contains detailed information about one or more transactions. For more information about purchase state messages, see In-app Billing Broadcast Intents
  • com.android.vending.billing.PURCHASE_STATE_CHANGED 這個廣播意圖包含了一次或多次交易的詳細資訊。更多關於購買狀態訊息的更多資訊,請參閱In-app Billing Broadcast Intents這個篇幅。
Each of these broadcast intents provide intent extras, which your BroadcastReceiver must handle. 
The intent extras are listed in the following table (see table 1).
每個廣播意圖都提供了intent extras,因此您的BroadcastReceiver必須能夠處理這個東西。

Table 1. Description of broadcast intent extras that are sent in response to billing requests.
表1. 描述出當我們在做金流請求而傳來回應時,我們會用到的相關廣播intent extras。

Intent Extra Description
com.android.vending.billing.RESPONSE_CODE request_id A long representing a request ID. A request ID identifies a specific billing request and is returned by Android Market at the time a request is made.
以Long型態來表示request ID。一個request ID即為一個向Android市集提出金流請求時間所返回的識別值。
com.android.vending.billing.RESPONSE_CODE response_code An int representing the actual Android Market server response code.
以int型態來表示Android市集伺服器的回應碼。
com.android.vending.billing.IN_APP_NOTIFY notification_id A String representing the notification ID for a given purchase state change. Android Market notifies you when there is a purchase state change and the notification includes a unique notification ID. To get the details of the purchase state change, you send the notification ID with the GET_PURCHASE_INFORMATION request.
以String型態來表示當購買狀態遭改變時的訊息通知ID。當購買狀態遭改變,Android市集會通知您,回傳的通知會包含一組特定訊息通知ID。如果要取得更多詳細的購買狀態改變資料,請您使用GET_PURCHASE_INFORMATION這個請求,並夾帶這裡提到的訊息通知ID過來。
com.android.vending.billing.PURCHASE_STATE_CHANGED inapp_signed_data A String representing the signed JSON string. The JSON string contains information about the billing transaction, such as order number, amount, and the item that was purchased or refunded.
一組String代表著已簽署的JSON字串。JSON字串包含了有關金流交易的資訊,像是訂單編號、訂單數量以及購買的商品是否購買成功或遭退費。
com.android.vending.billing.PURCHASE_STATE_CHANGED inapp_signature A String representing the signature of the JSON string.
一組String代表JSON字串型態的數位簽章。

The following code sample shows how to handle these broadcast intents and intent extras within a BroadcastReceiver. The BroadcastReceiver in this case is named BillingReceiver, just as it is in the sample application.
底下的程式碼教您如何在BroadcastReceiver底下處理這些廣播意圖和intent extras。BroadcastReceiver在範例裡命名為BillingReceiver。



public class BillingReceiver extends BroadcastReceiver {

  private static final String TAG = "BillingReceiver";

  // Intent actions that we receive in the BillingReceiver from Android Market.
  // These are defined by Android Market and cannot be changed.
  // The sample application defines these in the Consts.java file.
  public static final String ACTION_NOTIFY = "com.android.vending.billing.IN_APP_NOTIFY";
  public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE";
  public static final String ACTION_PURCHASE_STATE_CHANGED =
    "com.android.vending.billing.PURCHASE_STATE_CHANGED";

  // The intent extras that are passed in an intent from Android Market.
  // These are defined by Android Market and cannot be changed.
  // The sample application defines these in the Consts.java file.
  public static final String NOTIFICATION_ID = "notification_id";
  public static final String INAPP_SIGNED_DATA = "inapp_signed_data";
  public static final String INAPP_SIGNATURE = "inapp_signature";
  public static final String INAPP_REQUEST_ID = "request_id";
  public static final String INAPP_RESPONSE_CODE = "response_code";


  @Override
  public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (ACTION_PURCHASE_STATE_CHANGED.equals(action)) {
      String signedData = intent.getStringExtra(INAPP_SIGNED_DATA);
      String signature = intent.getStringExtra(INAPP_SIGNATURE);
      // Do something with the signedData and the signature.
    } else if (ACTION_NOTIFY.equals(action)) {
      String notifyId = intent.getStringExtra(NOTIFICATION_ID);
      // Do something with the notifyId.
    } else if (ACTION_RESPONSE_CODE.equals(action)) {
      long requestId = intent.getLongExtra(INAPP_REQUEST_ID, -1);
      int responseCodeIndex = intent.getIntExtra(INAPP_RESPONSE_CODE,
        ResponseCode.RESULT_ERROR.ordinal());
      // Do something with the requestId and the responseCodeIndex.
    } else {
      Log.w(TAG, "unexpected action: " + action);
    }
  }
  // Perform other processing here, such as forwarding intent messages to your local service.
}
 
In addition to receiving broadcast intents from the Android Market application, your BroadcastReceiver must handle the information it received in the broadcast intents. Usually, your BroadcastReceiver does this by sending the information to a local service (discussed in the next section). The BillingReceiver.java file in the sample application shows you how to do this. You can use this sample as a basis for your own BroadcastReceiver. However, if you use any of the code from the sample application, be sure you follow the guidelines that are discussed in Security and Design .
關於從Android市集軟體傳來的廣播意圖,這裡還要稍做說明,您的BroadcastReceiver必須處理在廣播意圖裡收到的資訊。通常,您的BroadcastReceiver會以發送資訊至本地端的Service來完成這個工作(會在下一個段落做討論)。在範例程式裡的BillingReceiver.java檔將會教您如何做到。您可以使用這個範例當成是您的基本BroadcastReceiver。然而,假如您使用了任何從範例程式出來的原始碼,請確定您遵照Security and Design這個導引章節。 

Verifying Signatures and Nonces驗証數位簽章和隨機數

Android Market's in-app billing service uses two mechanisms to help verify the integrity of the transaction information you receive from Android Market: nonces and signatures. A nonce (number used once) is a cryptographically secure number that your application generates and sends with every GET_PURCHASE_INFORMATION and RESTORE_TRANSACTIONS request. The nonce is returned with the PURCHASE_STATE_CHANGED broadcast intent, enabling you to verify that any given PURCHASE_STATE_CHANGED response corresponds to an actual request that you made. Every PURCHASE_STATE_CHANGED broadcast intent also includes a signed JSON string and a signature, which you can use to verify the integrity of the response.
Androidy市集的iap服務使用了2種架構來幫助您完整的驗証從Android市集來的交易資訊,這2個架構分別為︰隨機數(nonces)和數位簽章(signatures)。一個隨機數(僅用一次的值值)是一個加密地安全數值,它是由您的APP產生的,並且每次使用GET_PURCHASE_INFORMATION和RESTORE_TRANSACTOINS請求時,都會將此值發送出去。隨機數接著會尾隨PURCHASE_STATE_CHANGED這個廣播意圖被回傳回來,讓您可以去驗証PURCHASE_STATE_CHANGED傳來的值和您用來發請求所產生的值是否相符。每次的PURCHASE_STATE_CHANGED廣播意圖也會包含一組被簽署過的JSON字串和數位簽章,您可以拿來做完整的回應的驗証動作。

Your application must provide a way to generate, manage, and verify nonces. The following sample code shows some simple methods you can use to do this
您的APP必須提供一個能產生、管理和驗証隨機數的管道。底下的範例程式將教您一些簡單的方式來完成這個需求︰
.

  private static final SecureRandom RANDOM = new SecureRandom();
  private static HashSet<Long> sKnownNonces = new HashSet<Long>();

  public static long generateNonce() {
    long nonce = RANDOM.nextLong();
    sKnownNonces.add(nonce);
    return nonce;
  }

  public static void removeNonce(long nonce) {
    sKnownNonces.remove(nonce);
  }

  public static boolean isNonceKnown(long nonce) {
    return sKnownNonces.contains(nonce);
  }

 
Your application must also provide a way to verify the signatures that accompany every PURCHASE_STATE_CHANGED broadcast intent. The Security.java file in the sample application shows you how to do this. If you use this file as a basis for your own security implementation, be sure to follow the guidelines in Security and Design and obfuscate your code.
You will need to use your Android Market public key to perform the signature verification. The following procedure shows you how to retrieve Base64-encoded public key from the Android Market publisher site.
您的APP必須提供一個方式來替每次傳來的PURCHASE_STATE_CHANGED廣播意圖伴隨的數位簽章做出驗証,範例程式裡的Security.java檔有教您如何做到。如果您使用了這個檔當成是您實作安全性考量的基礎,請確認已遵照 Security and Design這份導引,並且您也模糊過您的程式碼。您將會需要使用Android市集的公鑰來執行數位簽章驗証。底下流程將會教您如何接收從Android市集發佈網站傳來的Base64編碼公鑰。

  1. Log in to your publisher account.
  2. 首先請登入您的發佈者帳戶
  3. On the upper left part of the page, under your name, click Edit profile.
  4. 請在上方左半部,您的名字底下,點擊Edit profile
  5. On the Edit Profile page, scroll down to the Licensing & In-app Billing panel (see figure 2).
  6. 在Edit Profile頁面下,下滑到Licensing & In-app Billing區塊(見圖2)
  7. Copy your public key.
  8. 拷貝您的公鑰

Important: To keep your public key safe from malicious users and hackers, do not embed your public key as an entire literal string. Instead, construct the string at runtime from pieces or use bit manipulation (for example, XOR with some other string) to hide the actual key. The key itself is not secret information, but you do not want to make it easy for a hacker or malicious user to replace the public key with another key.
重要︰為了您公鑰的安全,並防止惡意使用者或黑客的攻擊,請物將完整的String全數嵌入您的程式碼中,相反的,在執行期間建構出String或者使用bit manipulation(舉例,XOR和其它一些字串)來隱藏真實的key都是比較好的做法。雖然說鑰匙本身並非是一個秘密資訊,但是您並不會想讓黑客或惡意使用者那麼輕易的就將您的公鑰被換成另外一組吧?


Figure 2. The Licensing and In-app Billing panel of your account's Edit Profile page lets you see your public key. 
圖2.  在您帳戶的編輯資料裡「許可與應用程式內部金流機制」的區塊。您可以看見您的公鑰就擺放於此。

Modifying Your Application Code修改您的應用程式原始碼

After you finish adding in-app billing components to your project, you are ready to modify your application's code. For a typical implementation, like the one that is demonstrated in the sample application, this means you need to write code to do the following: 
在您專案完成了iap元件的添加以後,您應該準備開始修改您的程式碼了。在典型的實作行為底下,就如同範例程式說明的一樣,這意謂著您需要照著以下的方式開始攢寫程式碼︰

  • Create a storage mechanism for storing users' purchase information.
  • 替儲存使用者的購買資訊建立一個儲存的架構
  • Create a user interface that lets users select items for purchase.
  • 建立一個能夠讓使用者購買商品的使用介面
The sample code in Dungeons.java shows you how to do both of these tasks.
範例程式的Dungeons.java教導您如何做到這2件事。 

Creating a storage mechanism for storing purchase information


建立一個儲存購買資訊的儲存架構

You must set up a database or some other mechanism for storing users' purchase information. The sample application provides an example database (PurchaseDatabase.java); however, the example database has been simplified for clarity and does not exhibit the security best practices that we recommend. If you have a remote server, we recommend that you store purchase information on your server instead of in a local database on a device. For more information about security best practices, see Security and Design.
您必須設定一組資料庫和其它的架構來儲存使用者的購買資訊。範例程式提供了一個簡單的資料庫(PurchaseDatabase.java)來做儲存。然而,範例資料庫為了表達地更清楚,因此寫的非常簡單,因而也並無法展示出我們所建議您的的安全性的架構應該為何。如果您有遠端伺服器,那麼我們會建議您將購買資訊儲存至您的伺服器,而非裝置裡的本地端資料庫。更多關於最佳安全性實例,請見Security and Design這篇。

Note: If you store any purchase information on a device, be sure to encrypt the data and use a device-specific encryption key. Also, if the purchase type for any of your items is "unmanaged," we recommend that you back up the purchase information for these items to a remote server or use Android's data backup framework to back up the purchase information. Backing up purchase information for unmanaged items is important because unmanaged items cannot be restored by using the RESTORE_TRANSACTIONS request type.
註︰如果您在裝置上儲存了任何的購買資訊,請確定您已將資料加密,並且使用裝置特殊的加密金鑰。並且,假如您「不受管理」性質的商品被銷售出去,我們建議您將這些商品的購買資訊也備份至我們提供給您的Android資料備份架構的這個遠端伺服器裡。備份這些不受管理的購買商品是非常重要的,因為不受管理的商品無法被儲存,因此也無法使用RESTORE_TRANSACTIONS請求類型來還原商品的購買數量或狀態。

Creating a user interface for selecting items


建立一個能讓使用者選擇商品的使用介面

You must provide users with a means for selecting items that they want to purchase. Android Market provides the checkout user interface (which is where the user provides a form of payment and approves the purchase), but your application must provide a control (widget) that invokes the sendBillingRequest() method when a user selects an item for purchase.
You can render the control and trigger the sendBillingRequest() method any way you want. The sample application uses a spinner widget and a button to present items to a user and trigger a billing request (see Dungeons.java). The user interface also shows a list of recently purchased items.
您必須提供使用者一個選擇購買商品的環境。Android市集雖然提供您付費使用介面(這個介面是當使用者點選了付款後,Android吐出來的一個標準付費畫面),但是您的APP還是必須去控制(元件)當使用者選擇了商品後呼叫sendBillingRequest()函式的方式。您可以用任何方式使用控制元件,並發發送sendBillingRequest()函式 。範例程式使用了下拉式的元件(spinner widget)和按鈕來呈現使用者發送金流請求的畫面(參見Dungeons.java)。使用介面也會顯示出最近最購買過的商品列表。

相關文章︰
1.[Android in-app billing前言]In-app Billing應用程式內部付費機制(中文翻譯)
2.[Android in-app billing第1篇]In-app Billing Overview應用程式內部付費機制概述(中文翻譯)
3.[Android in-app billing第2篇]Implementing In-app Billing實作應用程式內部金流機制(中文翻譯)
4.[Android in-app billing第3篇]Security and Design安全與設計(中文翻譯)
5.[Android in-app billing第4篇]Testing In-app Billing測試應用程式內金流機制(中文翻譯)
6.[Android in-app billing第5篇]Administering In-app Billing應用程式內金流機制的管理(中文翻譯)
7.[Android in-app billing第6篇(末)]In-app Billing Reference應用程式金流的相關API(中文翻譯)

1 comment: