前回、Androidのアプリ内課金の購入編を書きました。
今回は、リストア編です。
アプリ内課金の課金アイテムは2タイプあります。
- Googleアカウントに紐づいて1度だけ購入するタイプ
- 何度でも購入できるタイプ
WiFiCutterで実装したのは、1度だけ購入するタイプです。このタイプの場合、1度買っていると、また買おうとすると「もう購入済みだよ!」と怒られてしまいます。WiFiCutterの初回リリース時点では、全然気にしてなかったのですが、twitterのフォロワーさんから、他の端末に入れたら買えないって言われたよと指摘を受けたので、リストアに挑戦することにしました。
で、リストアってどのタイミングで、どんな風に処理するのかが全然わからない。テクブ本にも載ってない…。Googleのサンプルを見たら、アプリの初回起動時にリストア処理をしていたので、初回起動かどうかのフラグを持っておいて、そのときのみ行なうのがよさそうです。
今回はテクブ本のソースに追加する形で実装したので、ソースを載せておきます。
リストアの場合は、Android Marketアプリに対して、RESTORE_TRANSACTIONSのリクエストを送ります。これは、ユーザの今までのアプリ内の購入履歴を問い合わせるためのリクエストです。今までの購入履歴を受け取り、それを元に、復元したりするわけですね。
RESTORE_TRANSACTIONSの場合は、ノンスが必要なので、ノンスを設定します。
BillingService.java内で実装したRestoreTransactionsクラス
/*** * RESTORE_TRANSACTIONSリクエストを送信する */ class RestoreTransactions extends BillingRequest { long mNonce; @Override protected long run() throws RemoteException { // ノンスを生成する mNonce = Security.generateNonce(); // 基本Bundleを作成する Bundle request = makeRequestBundle("RESTORE_TRANSACTIONS"); request.putLong(Consts.BILLING_REQUEST_NONCE, mNonce); // MarketServiceに送信する Bundle response = mService.sendBillingRequest(request); // リクエストIDを返す return response.getLong(Consts.BILLING_RESPONSE_REQUEST_ID, Consts.BILLING_RESPONSE_INVALID_REQUEST_ID); } @Override protected void onRemoteException(RemoteException e) { super.onRemoteException(e); // 例外が発生したらノンスを削除 Security.removeNonce(mNonce); } } // RESTORE_TRANSACTIONSリクエストを処理する public boolean restoreTransactions() { return new RestoreTransactions().runRequest(); }
これを、Activity側のonCreateなど初回起動時に呼び出すわけですね。
適当なActivityのonCreateで初回だけ呼び出します。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); // 初回起動か確認 boolean isFirst = preferences.getBoolean("is_first", true); if (isFirst) { Editor editor = preferences.edit(); editor.putBoolean("is_first", false); editor.commit(); // まず、既にアプリを購入済みか確認する mBillingService = new BillingService(); mBillingService.setContext(this); mBillingService.restoreTransactions(); } }
これを呼び出したら、マーケットからPURCHASE_STATE_CHANGEDで購入履歴が送られてきます。形式は、購入時のJSONとほぼ同じですが、RESTORE_TRANSACTIONSの場合、notificationIdがありません。
notificationIdは、「購入によるアプリ側の処理がちゃんと行なわれた」ということをマーケットに通知するためのものなのですが(CONFIRM_NOTIFICATIONSで)、RESTORE_TRANSACTIONでは既に一度購入処理を行なっているので、マーケットに処理がうまくいった事を通知する必要がないので、ありません。
追記:実際に送られてきたJSONを書いときます。
(一部塗りつぶしてますが)
{ "nonce":**************************, "orders":[ { "orderId":"********************", "packageName":"com.okolabo.android.wificutter", "productId":"hoge", "purchaseTime":1321026274058, "purchaseState":0 } ] }
ordersがJSONArrayになっているので複数の購入履歴が一度に取れます。
PURCHASE_STATE_CHANGEDが送られた場合の処理は、既に実装済み(のはず)なので、あとは自動的に購入時の処理が行なわれます。何度でも購入できるアイテムなどの場合は、orderIdを比較するなりして、アイテムが増えないようにするべきでしょう。
テクブ本はタブレットアプリ開発ガイドと銘打っていますが、アプリ内課金などスマートフォン向けの実装も説明してあるので、ためになると思います(宣伝)!!