連載
» 2011年05月13日 00時00分 公開

Androidアプリにアプリ内課金を実装してみようAndroid Marketアプリ内課金サービス徹底解説(2)(3/4 ページ)

[緒方聡,株式会社イーフロー]

ステップ5:BroadcastReceiverを作成

 Android Marketアプリは、アプリに非同期課金レスポンスを送信するためにブロードキャストインテントを使用します。それらのインテントメッセージを受信するために、以下のインテントをハンドリングすることが可能なBroadcastReceiverを作成する必要があります。

com.android.vending.billing.RESPONSE_CODE

 このブロードバンドインテントはAndroid Marketレスポンスコードを含み、アプリ内課金リクエストを作成した後に送信されます。このレスポンスと一緒に送信されるレスポンスコードに関する詳細については、「アプリ内課金のためのAndroid Marketレスポンスコード」を参照してください。

com.android.vending.billing.IN_APP_NOTIFY

 このレスポンスコードは購入完了、キャンセル、払い戻しといった購入の状態変更を含みます。通知メッセージに関する詳細については、「アプリ内課金ブロードキャストインテント」を参照してください。

com.android.vending.billing.PURCHASE_STATE_CHANGED

 このブロードキャストインテントは1つ以上のトランザクション情報の詳細を含みます。購入状態メッセージに関する詳細な情報は、「アプリ内課金ブロードキャストインテント」を参照してください。

ブロードキャストインテントのエクストラ

 これらのブロードキャストインテントが提供するエクストラ(付加情報)のいずれも、BroadcastReceiverでハンドリングされなければなりません。以下はインテントエクストラのリストです。

表1 ブロードキャストインテントエクストラの詳細
インテント エクストラ 詳細
com.android.vending.billing.
RESPONSE_CODE
request_id リクエストIDを表すlong。リクエストIDは課金リクエストを特定し、リクエストが生成された際にAndroid Marketから戻される
response_code Android Marketサーバの実際のレスポンスコードを表すint
com.android.vending.billing.
IN_APP_NOTIFY
notification_id 与えられた購入状態変更のための通知IDを表すString。Android Marketは購入状態変更とユニークな通知IDを通知。購入状態変更の詳細を取得するために、通知IDをGET_PURCHASE_INFORMATIONリクエストで送信
com.android.vending.billing.
PURCHASE_STATE_CHANGED
inapp_signed_data 署名されたJSON文字列を表すString。JSON文字列は注文番号、金額、購入または払い戻しされた商品などの課金トランザクションに関する情報を含む
inapp_signature JSON文字列の署名を表すString

 以下のサンプルコードは、これらのブロードキャストインテントをハンドルする方法と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.
}

 ブロードキャストインテントをAndroid Marketアプリから受信することに加えて、BroadcastReceiverはブロードキャストインテントで受信した情報を処理しなければいけません。大抵、BroadcastReceiverはローカルサービスに情報を転送することによって、これを実現します(後ほど説明します)。

 サンプルアプリのBillingReceiver.javaは、この方法で実装されています。開発者は自身の実装のベースとして、これらのサンプルを使えます。サンプルアプリのコードを流用するのであれば、次回記事の「設計・実装における8つのセキュリティ対策ポイント」を参照してください。

ステップ6:シグネチャとナンスの検証

 Android Marketアプリ内課金サービスは開発者がAndroid Marketから受け取るトランザクション情報の完全性を検証する補助として、ナンスとシグネチャの2つのメカニズムを使用します。

ナンス

 ナンスはPURCHASE_STATE_CHANGEDブロードキャストインテントで戻され、アプリが生成した実際のリクエストに対するレスポンスであるかどうか検証できます。すべてのPURCHASE_STATE_CHANGEDブロードキャストインテントはまた、署名されたJSON文字列とレスポンスの完全性を検証可能なシグネチャを含みます。

 アプリは作成や管理、ナンス検証を行う方法を提供しなければいけません。以下のサンプルコードは、これらを行うシンプルな方法です。


    private static final SecureRandom RANDOM = new SecureRandom();
    private static HashSet sKnownNonces = new HashSet();
  
    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);
    }

シグネチャ

 またアプリは、すべてのPURCHASE_STATE_CHANGEDブロードキャストインテントに付随するシグネチャを検証する方法を提供しなければいけません。サンプルアプリのSecurity.javaファイルは、これらが実装されています。サンプルアプリのコードを流用するのであれば、次回の「設計・実装における8つのセキュリティ対策ポイント」に従ってください。

 Android Market公開鍵をシグネチャ検証に使用する必要があります。以下の手順はAndroid Market開発者サイトのBase64エンコードされた公開鍵を取得する方法です。

  1. Android Marketの開発者アカウントにログイン
  2. ページ左上にある名前の下の[Edit Profile]をクリック
  3. [Edit Profile]ページで[Licensing]と[In-app Billing]パネルまでスクロール
  4. 公開鍵をコピー

 重要なのは、公開鍵を悪意あるユーザーとクラッカーから守るには、公開鍵をアプリに文字列として組み込まないことです。詳細は次回の「Android Market公開鍵の保護」を参照してください。

図2 Edit Profileページの開発者の公開鍵 図2 Edit Profileページの開発者の公開鍵

ステップ7:アプリのコードの修正

 アプリ内課金のコンポーネントをアプリに組み込み終えたら、アプリのコードを修正する準備をします。サンプルアプリのような典型的な実装は、以下のコードを書く必要があります。

  • ユーザー購入情報を保存するための仕組みの作成
  • 購入アイテム選択用のUIを作成

 サンプル内のDungeons.javaは上記の両方を行っています。

購入情報を保存するためのストレージメカニズムの構築

 ユーザー購入情報の保存のために、データベースまたはその他の仕組みを用意する必要があります。サンプルアプリは(PurchaseDatabase.javaで)サンプルデータベースを提供します。しかし、このサンプルデータベースは分かりやすさのためにシンプルに作られており、推奨するセキュリティ手法を満たしていません。

 もし開発者がリモートサーバを持っているなら、実機上のローカルデータベースの代わりにサーバ上に購入情報を保存することを推奨します。詳細は、次回の「設計・実装における8つのセキュリティ対策ポイント」を参照してください。

 もしも、いくつかの購入情報を実機上に保存したなら、データを実機固有の暗号キーによって暗号化することを忘れないでください。

商品選択のためのUIの構築

 アプリはユーザーが購入したいアイテムを選択する方法を提供する必要があります。Android MarketはチェックアウトのUIを提供しますが、アプリ側は購入時に商品がユーザーに選択された際にsendBillingRequest()メソッドを呼び出す画面を提供する必要があります。

 アプリは画面およびsendBillingRequest()メソッドの呼び出しのトリガーを、どのように実装しても構いません。サンプルアプリはスピナーウィジェットとボタンを使って、これを行っています(Dungeons.javaを参照)。また、サンプルは最近購入した商品を表示します。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。