2012年12月30日日曜日

In-App Billing Version 3 翻訳(Implementing In-app Billing編)

EGGの曽川です。

アプリ内課金の実装方法について翻訳しました。
原文はこちらです。
誤訳等あればご指摘ください。
ドキュメントは文字のみなのでイメージを掴むたい方は、In-app Billing Version3 (サンプルを使う編)もご覧ください。

アプリ内課金の実装
GooglePlay上のアプリ内課金は、GooglePlayを使用したアプリ内課金のリクエストの送信や、アプリ内課金のトランザクション管理のための簡単でシンプルなインタフェースを提供します。
以下の情報はバージョン3のAPIを使用して、アプリからアプリ内課金サービスをどのように呼び出すかの基本をカバーしています。

注意:完成した実装を見て、アプリのテストする方法を学ぶためにSelling In-app Productsトレーニングクラスを見てください。
トレーニングクラスは完成しているアプリ内課金のサンプルアプリを提供します。
これは、重要なタスクの処理、接続のセットアップ、課金リクエストの送信、GooglePlayからのレスポンスの処理、メインアクティビティからアプリ内課金をコールするためのバックグラウンドスレッドの管理を含みます。

作業を開始する前に、アプリ内課金を実装する事が容易になる概念を理解するために、In-app Billing Overviewを読んでください。
アプリ内で課金を実行するために次の手順を実行してください。
  1. Billingライブラリプロジェクトを追加する。
  2. AndroidManifest.xmlの更新
  3. ServiceConnectionを作成し、IInAppBillingServiceをバインドします。
  4. アプリからIInAppBillingServiceにアプリ内課金のリクエストを送信します。
  5. GooglePlayからのアプリ内課金のレスポンスを処理します。

AIDLファイルをプロジェクトへ追加

サンプルアプリTrivialDriveは、GooglePlayのアプリ内課金サービスのインタフェースに定義されている
Android Definititon Language(AIDL)ファイルを含んでいます。
このファイルを追加すると、Androidのビルド環境はインタフェースファイル(IIAppBillingService.java)を作ります。
課金リクエストのIPCメソッド呼び出しを行うためにこのインタフェースを使います。
プロジェクトにアプリ内課金バージョン3のライブラリを追加するには:
  1. IInAppBillingService.aidlファイルをAndroidプロジェクトにコピーします。
    • もしEclipseを使用している場合は、IInAppBillingService.aidl/srcディレクトリにインポートします。
      Eclipseがプロジェクトをビルドした際に、インタフェースファイルを自動的に生成します。
    • もしEclipseプロジェクトを使用しない環境の場合は、
      /src/com/android/vending/billingディレクトリを作成し、IInAppBillingService.aidlをコピーします。
      AIDLファイルをプロジェクトに置き、IIAppBillingService.javaを生成するためにAntツールを使用します。
  2. アプリをビルドします。
    プロジェクトの/genディレクトリにIInAppBillingService.javaという名前で生成されたファイルがあるはずです。

AndroidManifestの更新

アプリ内課金は、アプリとGooglePlayサーバ間の全ての通信を処理するGooglePlayアプリに依存しています。
GooglePlayアプリを使用するには、適切なアクセス許可を要求しなければなりません。
AndroidManifest.xmlファイルにcom.android.vending.BILLINGパーミッションを追加する事でこれをおこなうことができます。
もし、アプリがアプリ課金のパーミッションを宣言せずに課金リクエストを試みる場合は、GooglePlayは要求を拒否しエラーを返します。

アプリに必要な権限を付与するには、AndroidManifest.xmlファイルに次の行を追加します。
<uses-permission android:name="com.android.vending.BILLING">


Service Connectionの作成

アプリは、GooglePlayとのメッセージのやり取りを容易にするServiceConnectionを持っていなければなりません。
少なくとも次のことを行ってください。

  • IInAppBillingServiceとのバインド
  • GooglePlayアプリへの課金リクエスト送信(IPCメソッド呼び出しなど)
  • 各課金リクエストで返される同期的なレスポンスメッセージの処理

IInAppBillingServiceのバインド
GooglePlayとアプリ内課金サービスの接続を確立するには、IInAppBillingServiceにActivityをバインドするServiceConnectionを実装します。
接続が確立された後、IInAppBillingServiceインスタンスの参照を取得する、onServiceDisconnectedonServiceConnectedメソッドをオーバライドします。
IInAppBillingService mService;

ServiceConnection mServiceConn = new ServiceConnection() {
   @Override
   public void onServiceDisconnected(ComponentName name) {
       mService = null;
   }

   @Override
   public void onServiceConnected(ComponentName name, 
      IBinder service) {
       mService = IInAppBillingService.Stub.asInterface(service);
   }
};

ActivityのonCreateメソッドで、bindServiceメソッドを呼び出すことでバインドを実行します。
アプリ内課金サービスの参照と、作成したServiceConnectionインスタンスをIntentメソッドとして渡します。
@Override
public void onCreate(Bundle savedInstanceState) {    
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);        
   bindService(new 
      Intent("com.android.vending.billing.InAppBillingService.BIND"),
          mServiceConn, Context.BIND_AUTO_CREATE);
これでGooglePlayサービスとのやり取りのためにmServiceの参照を使用できます。

重要Activityが終了したらアプリ内課金サービスからアンバインドすることを忘れないでください。
もし、アンバインドをしなければ、開いているサービス接続がデバイスのパフォーマンスを低下させます。
この例では、ActivityのonDestroyメソッドをオーバライドすることにより、アプリ内課金サービス接続のmServiceConnをどのようにアンバインドを実行するか示しています。
@Override
public void onDestroy() {
   if (mServiceConn != null) {
      unbindService(mServiceConn);
   } 
}
IInAppBillingServiceとバインドするサービス接続の完全な実装については、トレーニングクラスのSelling In-app Productsを参照してください。


アプリ内課金のリクエストを作成する

一度アプリがGooglePlayに接続されたら、アプリ内サービスの購入リクエストを開始できます。
GooglePlayはユーザが支払方法を入力するチェックアウトインタフェースを提供するので、アプリは直接支払いのトランザクションを操作する必要はありません。
アイテムが購入されると、GooglePlayはユーザがアイテムの所有権を持っていることと、ユーザが同じサービスIDのアイテムを消費するまで、アイテムの購入が発生しないようにすること判別します。
アプリ内でアイテムがどのように消費されるか制御することができ、GooglePlayに再度購入ができるように通知します。
またユーザに関する購入リストをすぐに取得するために、GooglePlayに問い合わせることができます。
これは例えば、ユーザがアプリを起動した時にユーザの課金を復元したい時に非常に便利です。

購入可能なアイテムの問い合わせ
あなたのアプリ内で、アプリ内課金バージョン3を使ってGooglePlayからアイテムの詳細の問い合わせができます。
アプリ内課金サービスにリクエストを渡すために、まず"ITEM_ID_LIST"のキーでサービスIDを持つ、StringのArrayListを含むBundleを生成します。(それぞれの文字列は購入可能なアイテムのサービスIDです。)
ArrayList skuList = new ArrayList();
skuList.add("premiumUpgrade");
skuList.add("gas");
Bundle querySkus = new Bundle();
querySkus.putStringArrayList(“ITEM_ID_LIST”, skuList);
この情報をGooglePlayから取得するために、アプリ内課金バージョン3のgetSkuDetailsメソッドを呼び出すために、アプリ内課金バージョン("3")、アプリのパッケージ名、購入タイプ("inapp")と作成したBundleを渡します。
Bundle skuDetails = mService.getSkuDetails(3, getPackageName(), “inapp”, querySkus);
リクエストが成功した場合、返されたBundleは、BILLING_RESPONSE_RESULT_OK (0)のレスポンスコードを持ちます。

警告:メインスレッド上でgetSkuDetailsを呼んではいけません。このメソッドを呼び出すと、メインスレッドをロックするネットワークリクエストを行います。
代わりに別のスレッドを作成し、そのスレッド内からgetSkuDetailsメソッドを呼び出してください。

GooglePlayからのすべてのレスポンスコード見るには、In-app Billing Referenceを参照してください。

クエリの結果はDETAILS_LISTキーのStringのArrayListに格納されます。
購入情報はJSON形式の文字列に格納されます。
返されたサービス詳細情報の種類を確認するには、In-app Billing Referenceを参照してください。
この例では、先程のコードスニペットから返されたskuDetailsのBundleからアプリ内アイテムの価格を取得しています。
int response = skuDetails.getInt("RESPONSE_CODE");
if (response == 0) {
   ArrayList responseList 
      = skuDetails.getStringArrayList("DETAILS_LIST");
   
   for (String thisResponse : responseList) {
      JSONObject object = new JSONObject(thisResponse);
      String sku = object.getString("productId");
      String price = object.getString("price");
      if (sku.equals(“premiumUpgrade”)) mPremiumUpgradePrice = price;
      else if (sku.equals(“gas”)) mGasPrice = price;
   }
}

アイテムの購入
アプリから課金リクエストを開始するには、アプリ内課金サービスのgetBuyIntentメソッドを呼び出します。
アプリ内課金APIバージョン("3")、アプリパッケージ名、アイテムを購入するためのサービスID、購入タイプ(inapp)、developerPayload文字列をメソッドに渡します。
developerPayload文字列は、購入情報と一緒にGooglePlayに返送して欲しい、追加の引数を指定するために使用されます。
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
リクエストが成功した場合、返されたBundleBILLING_RESPONSE_RESULT_OK (0)のレスポンスコードと、購入フローを開始するために使用出来るPendingIntentを持っています。
GooglePlayからのすべてのレスポンスコードを見るには、In-app Billing Referenceを参照してください。
次に、返されたBundleBUY_INTENTキーを使用して、PendingIntentを取得します。
PendingIntent pendingIntent = buyIntentBundle.getParcelable(“BUY_INTENT”);
購入トランザクションを完了するために、startIntentSenderForResultメソッドを呼び出し、作成したPendingIntentを使用します。
この例では、リクエストコードとしての1001という任意の値を使用しています。
startIntentSenderForResult(pendingIntent.getIntentSender(),1001, new Intent(), Integer.valueOf(0),Integer.valueOf(0),Integer.valueOf(0));
GooglePlayは、PendingIntentからアプリのonActivityResultメソッドにレスポンスを送信します。
onActivityResultメソッドはActivity.RESULT_OK (1)またはActivity.RESULT_CANCELED (0)の結果コードを持ちます。
レスポンスIntentに返された注文情報の種類を見るには、In-app Billing Referenceを参照してください。

注文の購入データは、レスポンスIntentINAPP_PURCHASE_DATAキーにマップされているJSON形式の文字列です。
例えば以下のとおりです。
'{ 
"orderId":"12999763169054705758.1371079406387615", 
"packageName":"com.example.app",
"productId":"exampleSku",
"purchaseTime":1345678900000,
"purchaseState":0,
"developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
“purchaseToken”:“rojeslcdyyiapnqcynkjyyjh”
}'
前の例の続きは、レスポンスIntentからレスポンスコード、購入データ、署名を取得します。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
   if (requestCode == 1001) {     
      int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
      String purchaseData = data.getStringExtra(“INAPP_PURCHASE_DATA”);
      String dataSignature = data.getStringExtra(“INAPP_DATA_SIGNATURE”);
        
      if (resultCode == RESULT_OK) {
         try {
            JSONObject jo = new JSONObject(purchaseData);
            String sku = jo.getString("productId");
            alert("You have bought the " + sku + ". Excellent choice, 
               adventurer!");
          }
          catch (JSONException e) {
             alert("Failed to parse purchase data.");
             e.printStackTrace();
          }
      }
   }
}
セキュリティの推奨事項:購入リクエストを送信すると、購入リクエストを一意に識別する文字列トークンを作成し、developerPayloadにこのトークンを含めます。
トークンとしてランダムに生成された文字列を使用することができます。
GooglePlayから購入レスポンスを受け取った時、返されたデータ署名、orderIddeveloperPayloadを確認してください。
セキュリティを強化するために、自身のセキュアなサーバ上でチェックを実行してください。
orderIdが以前に処理されていないユニークな値であり、developerPayload文字列が購入リクエスト以前送信したトークンと一致することを必ず確認してください。

購入アイテムの問い合わせ
アプリからユーザが行った購入に関する情報を取得するためには、アプリ内課金APIバージョン3サービスのgetPurchasesを呼び出します。
アプリ内課金APIバージョン("3")、アプリのパッケージ名、購入タイプ("inapp")をメソッドに渡します。
Bundle ownedItems = mService.getPurchases(3, getPackageName(), “inapp”, null);
GooglePlayサービスは、現在端末にログインしているユーザアカウントの購入のみ返します。
リクエストが成功した場合、返されたBundleはレスポンスコードとして0を持っています。
返されたBundleはサービスID、それぞれの購入の注文詳細リスト、それぞれの購入の署名が含まれています。

パフォーマンスを改善するために、getPurchasesが最初に呼び出された時、アプリ内課金サービスはユーザが所有している700の商品までしか返しません。
ユーザが多数の商品を所有している場合、GooglePlayは更に多くの商品を検索できることを示すために、返されたBundle内で文字列トークンにマップされたINAPP_CONTINUATION_TOKENキーを含んでいます。
アプリはそれ以降のgetPurchasesの呼び出しを行うために、引数としてこのトークンを渡します。
GooglePlayは、ユーザが所有するすべての商品がアプリに送られるまで、返されたBundle内に継続トークンを返し続けます。

getPurchasesによって返されるデータの詳細については、 In-app Billing Referenceを参照してください。
次の例はレスポンスからデータを取得する方法を示します。
int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
   ArrayList ownedSkus = 
      ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
   ArrayList purchaseDataList = 
      ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
   ArrayList signatureList = 
      ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE");
   String continuationToken = 
      ownedItems.getString("INAPP_CONTINUATION_TOKEN");
   
   for (int i = 0; i < purchaseDataList.size(); ++i) {
      String purchaseData = purchaseDataList.get(i);
      String signature = signatureList.get(i);
      String sku = ownedSkus.get(i);
  
      // 購入情報を使って何かします。
      // 例えば、ユーザが所有している商品の最新のリストの表示
   } 

   // continuationToken != nullの場合、getPurchasesを再度呼んで 
   // 更にアイテムを検索するためにトークンを渡します。
}

購入の消費
GooglePlayで購入したアイテムの所有権を追跡するために、アプリ内課金バージョン3のAPIを使用することができます。
アイテムが購入されると、「所有」であるとみなされ、GooglePlayから購入することはできません。
GooglePlayで再度購入可能な状態にする前に、消費リクエストを送らなければいけません。
すべての管理対象の商品は消耗可能です。
消費のメカニズムをどのように使用するかはあなた次第です。
一般的にユーザが複数回購入したいと思うであろう、一時的な利点を持つ商品のために消費を実装します。(例:ゲーム内通貨や装備)
一般的に、一度の購入で恒久的な効果を提供する商品のために消費は実装しません。(例:プレミアムアップグレード)

購入の消費を記録するためには、アプリ内課金サービスにcomsumePurchaseメソッドをを送り、削除するための購入識別の値であるpurchaseToken文字列を渡します。
purchaseTokenは、購入リクエストの成功後にGooglePlayサービスによってINAPP_PURCHASE_DATA文字列で返されたデータの一部です。
この例では、token変数にpurchaseTokenで識別された商品の消費を記録しています。
int response = mService.consumePurchase(3, getPackageName(), token);
警告:メインスレッドでconsumePurchaseメソッドを呼び出してはいけません。
のメソッドを呼び出すと、メインスレッドをロックするネットワークリクエストを行います。
代わりに別のスレッドを作成し、そのスレッド内からconsumePurchaseメソッドを呼び出してください。

ユーザに提供されたアプリ内商品のどのように制御と追跡するかはあなたの責任です。
例えば、ユーザがゲーム内通貨を購入した場合、プレイヤーの購入した通貨の量のインベントリを更新する必要があります。

セキュリティの推奨事項:ユーザへ消費可能なアプリ内購入の利益を提供する前に、消費リクエストを送らなければいけません。
アイテムの提供前にGooglePlayから消費成功レスポンスの受信を確認してください。

Except as noted, this content is licensed under Creative Commons Attribution 2.5.

0 件のコメント:

コメントを投稿