読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

AndroidにおけるConsumableタイプのアプリ内課金

アプリ内課金

今回のエンジニアブログを担当する村田です。

AndroidにおけるConsumableタイプ(消費型)のアプリ内課金についてです。

Androidでは In-App Billing API を使用してアプリ内課金を実装します。

大まかな手順は、以下のとおりです。

  1. In-app Billingライブラリをプロジェクトに追加
  2. AndroidManifest.xmlを更新
  3. ServiceConnectionを作り、IInAppBillingServiceとバインド
  4. IInAppBillingServiceに対してIn-app Billingリクエストを送付
  5. Google PlayからのIn-app Billingレスポンスをハンドル

1.In-app Billingライブラリをプロジェクトに追加

In-app Billingライブラリは、IInAppBillingService.aidlという名のAndroid Interface Definition Language (AIDL)ファイルで提供されております。

1-1.AIDLファイルの入手方法

  1. Android SDK Managerを開きます。
    Billing_01
  2. Extrasセクションを開きます。
  3. 「Google Play Billing Library」を選択します。
    Billing_02
  4. Install packagesを実行します。

インストールに成功しますと、下記ディレクトリに「IInAppBillingService.aidl」ファイルが置かれます。
<Android sdk ROOT>/extras/google/play_billing/

1-2.プロジェクトへの追加

IInAppBillingService.aidlは、com.android.vending.billingパッケージに属します。
使う際にディレクトリ構造を合わせる必要があります。
/src/com/android/vending/billing/
となるディレクトリにIInAppBillingService.aidlファイルをコピーします。
Eclipse上で見た場合は src/com.android.vending.billing/フォルダになります。

Billing_03

ファイルのコピーが終わりましたら、ビルドを行います。
ビルドに成功すると /genディレクトリにIInAppBillingService.javaファイルが生成されます。

Billing_04

2.AndroidManifest.xmlを更新

アプリ内課金には"com.android.vending.BILLING"権限を使います。
AndroidManifest.xmlに次の一行を追加します。

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

これで実装までの準備が整いました。

次に実装になりますが、3〜5の処理を自分で実装すると、使うまでの手続きやGoogle Playからのレスポンスハンドリングが大変です。

複雑な処理をお手伝いしてくれるライブラリがあります。
それは、DevelopersサイトのTrainigで使用されているライブラリです。

このライブラリは、IInAppBillingService.aidlと一緒に取得されます。
下記ディレクトリに存在するファイル達が該当のライブラリです。
/extras/google/play_billing/samples/TrivialDrive/src/com/example/android/trivialdrivesample/util
Billing_05
まず全ファイルをプロジェクトに追加します。パッケージ名が合わないと思いますので適宜修正します。

では、実装していきましょう!!

3.ServiceConnectionを作りIInAppBillingServiceとバインド

IabHelperクラスが主に操作するメインとなるクラスです。
IabHelperクラスは、onCreateメソッドで生成します。

IabHelper mHelper;  
  
@Override  
public void onCreate(Bundle savedInstanceState) {  
  // compute your public key and store it in base64EncodedPublicKey  
  mHelper = new IabHelper(this, base64EncodedPublicKey);  
  // デバッグを有効にする場合(デフォルトは無効です)  
  mHelper.enableDebugLogging(true);  
}  

コンストラクタの第二引数に渡しているbase64EncodedPublicKeyは、Developer Console上で確認できます。

Developer Console -> 該当アプリ -> サービスとAPI

このPublic Keyは、Google Playにて購入した結果が、不正なものではない事を確認するために使用します。
IabHelperクラスの生成が終わりましたら、次はstartSetupメソッドです。

IabHelperクラスのstartSetupメソッドを呼び出すことで

  • ServiceConnectionの生成
  • IInAppBillingServiceのバインド

までを自動で行ってくれます。

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {  
  public void onIabSetupFinished(IabResult result) {  
    if (!result.isSuccess()) {  
      // 失敗した時の処理  
    }  
  }  
});  

startSetupを実施した結果は、引数で渡すIabHelper.OnIabSetupFinishedListenerにて受け取ります。
成功時、失敗時のハンドリングはIabHelper.OnIabSetupFinishedListenerクラスのonIabSetupFinishedメソッドに記述します。

4.IInAppBillingServiceに対してIn-app Billingリクエストを送付

4-1.Developer Consoleで作成した「アプリ内アイテム」情報を取得

取得したいアイテムのサービスIDをListで渡します。

// 取得したいアイテムのサービスID一覧を生成  
List additionalSkuList = new ArrayList();  
additionalSkuList.add(サービスID_A); // プロダクトAのサービスID  
additionalSkuList.add(サービスID_B); // プロダクトBのサービスID  
■ 非同期で取得する場合

queryInventoryAsyncメソッドを使用します。
処理の結果は、IabHelper.QueryInventoryFinishedListenerクラスで受け取ります。

mHelper.queryInventoryAsync(true, additionalSkuList, new IabHelper.QueryInventoryFinishedListener() {  
  public void onQueryInventoryFinished(IabResult result, Inventory inventory) {  
    if (result.isFailure()) {  
      // エラー時のハンドリング  
      return;  
    }  
    // 成功時の処理  
    // inventoryから情報の取得が可能です  
    String aPrice =  
      inventory.getSkuDetails(サービスID_A).getPrice();  
    String bPrice =  
      inventory.getSkuDetails(サービスID_B).getPrice();  
  }  
});  
■ 同期で取得する場合

queryInventoryメソッドを使用

Inventory inventory = mHelper.queryInventory(true, additionalSkuList);  
// 戻り値のInventoryから情報の取得が可能です  
inventory.getSkuDetails(サービスID_A).getPrice();  

4-2.In-app Billingリクエストを送付

実際に購入処理を行います。
Consumableタイプの購入処理はlaunchPurchaseFlowメソッドで行います。

mHelper.launchPurchaseFlow(this,  
                           購入アイテムのサービスID,  
                           10001,  
                           mPurchaseFinishedListener,  
                           一意となる文字列);  

第2引数に購入したいアイテムのサービスIDを渡します。
第3引数の10001はリクエストコードです。onActivityResultメソッド内でのハンドリングに使うので、他のリクエストコードと重複しないコードを指定します。
第4引数は、処理結果を受け取るIabHelper.OnIabPurchaseFinishedListenerになります。
第5引数の一意となる文字列は購入処理の結果が不正なものではないかに使用します。その都度作成し利用するとセキュリティが向上します。

5.Google PlayからのIn-app Billingレスポンスをハンドル

購入処理の結果は、先ほど述べたとおりIabHelper.OnIabPurchaseFinishedListeneで受け取ります。
onActivityResultを経由されますので、まずonActivityResultメソッドをオーバーライドし、次のように実装する必要があります。

@Override  
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  
  // IabHelperのhandleActivityResultに渡す  
  if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {  
    // falseの場合はハンドリング対象外なので親のonActivityResultメソッドを呼び出す  
    super.onActivityResult(requestCode, resultCode, data);  
  } else {  
    // IabHelperにハンドリングしていただいたので他に何もしない  
    Log.d(TAG, "onActivityResult handled by IABUtil.");  
  }  
}  

Google Playへのリクエストが終わった時、つまりIabHelper.OnIabPurchaseFinishedListenerの実装です。

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener  
    = new IabHelper.OnIabPurchaseFinishedListener() {  
  public void onIabPurchaseFinished(IabResult result, Purchase purchase) {  
    if (result.isFailure()) {  
      // 失敗時のエラー処理  
      return;  
    }  
  
    // consumableタイプのアイテムを消費  
    // これを行わないとGoogle Play側で消費されていないと見なされますので同じアイテムを購入できなくなります  
    mHelper.consumeAsync(purchase, mConsumeFinishedListener);  
  }  
};  

消費処理の結果は、consumeAsyncメソッドの第二引数で渡したIabHelper.OnConsumeFinishedListenerで受け取ります。

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =  
    new IabHelper.OnConsumeFinishedListener() {  
  public void onConsumeFinished(Purchase purchase, IabResult result) {  
    if (result.isSuccess()) {  
      // 成功時の処理  
      // サーバーへ必要な情報を投げ  
    } else {  
      // handle error  
    }  
  }  
};  

サーバー側にデータを反映したい場合は、成功時に必要な情報をサーバー側に渡します。

以上がAndroidにおけるConsumableタイプのアプリ内課金になります。

今回記述した内容では、base64EncodedPublicKeyをアプリ内に持ち、Google Playからの結果の妥当性をアプリ側で行っております。
セキュリティを考慮すると、よろしい構造ではありません。
この辺りをサーバーで行う場合、どういった処理になるかは、別途サーバーエンジニアが記載します。