先日、AndroidアプリのWiFiCutterをリリースしました。
https://market.android.com/details?id=com.okolabo.android.wificutter
今までアプリは基本的に無料アプリに広告を載せるという方法をとっていましたが、頻繁に使われないアプリは、ユーティリティ系のアプリ、ウィジェットなどの場合は広告を見せるという方法ができないので、今回はアプリ内課金(In-App Billing)を使ってシェアウェアとしてリリースしてみました。
In-App Billingについて参考にしたサイトは以下の通り。
あと、テクブ本です!7章のアプリ内課金の説明でだいぶわかりました(宣伝!!)
自分がアプリ内課金に使ったソースも、テクブ本のサンプルコードを修正して実装しました。
実装に関してグダグダ書くと、すごい長いので、詳細が知りたい人はテクブ本や参考URLを参考にしてもらうとして(ぇ、知りたいのは、実際にはどーするの?というところでしょう。まぁ実際にはどうするの?ってのはテクブ本がかなり詳しいので、それを読むと本当にいいんですが、読み終わって咀嚼して理解するまでに時間がかかったので、実際のフローについて書きます。(テクブ本ベースです。あと、はしょってる部分があります。あと、コードはここでは見せず。長いので。)
- Activity側でボタンなどを作り、クリックしたら、BillingService.javaのrequestPurchaseメソッドを呼び出します。購入依頼って意味ですね。そのときのrequestPurchaseメソッドの引数には、デベロッパーコンソールの各アプリのアプリ内サービスで定義するアプリ内サービスIDを渡します。アプリ内サービスIDが、ゲームとかだと各アイテム用のIDとかになりますね。タイプとしては、アカウント毎に紐つくタイプ(WiFiCutterで使ってるのはこっち。アプリ使用権とかはこれ)と、何度も買える制限なしタイプ(某SNSの壊れる釣り竿とかいうやつ?)の2種類があります。
- 早速、横道にそれました。戻します。
BillingService.requestPurchase(“hoge”)を呼ぶと、Android Marketアプリが課金サーバに対して非同期で接続を試みます。具体的には、抽象クラスのBillingRequestを継承したRequestPurchaseクラスがスレッド使ってマーケットに接続を試みます。うまくいくと、購入画面が出ます。
- 購入を押すと、購入処理が非同期で実行されます。結果はBroadcastされます。
何度かBroadcastされて、PURCHASE_STATE_CHANGEDがBroadcastされたところまで話をはしょります。途中経過を知りたい人は参考URLを。 - BillingReceiverがactionのPURCHASE_STATE_CHANGEDを受け取ったら、BillingService.purchaseStateChangedメソッドを呼び出します。購入状態が変わったので、正しい処理かどうかを確認する必要があります。
- PURCHASE_STATE_CHANGEDで受け取った値には、シグネチャ(署名)があるので、正しいものか確認します。テクブ本だと、Security.javaのverifyPurchaseメソッドです。公開鍵は、デベロッパーコンソールの開発者のプロフィールのページにありますから、それをコピーします。サンプルアプリでは、端末内でシグネチャと公開鍵を照らし合わせて正しいか確認しているのですが、実はこれはよくありません。
公開鍵なんだから別にいいんじゃないのか?と思っていたのですが、説明を見る限り、リバースエンジニアリングされて他の開発者の公開鍵と入れ替えられると簡単にアプリがパクられちゃうよということのようです。それは困る…。
Googleはサーバ側で署名チェックすることを推奨しているので、私はGAE側で署名チェックを実装しました。テクブ本のサンプルでいうと、Secirity.verifyメソッドで行なっている処理をサーバに移しました。そうすると、アプリ側に公開鍵を持たなくてもいいので、セキュリティ的にも向上します。(ただ、インターネット接続権限が必要になる…) - 署名が正しくなければ不正なので処理を中断してエラー扱い。署名が正しければ、署名と別に受け取っているJSONのデータがあります。テクブ本のサンプルソースのままだと、そのJSONが解析されてVerifiedPurchaseクラスのリストが生成されます。productIdが、アプリ内サービスIDです。あとノンスとかありますが、それはセキュリティトークンなんでしょうと理解。
- VerifiedPurchaseのリストを拡張for文で処理します。vp.productIdを比較して購入したブツを判定し、それぞれの処理を実装します。WiFiCutterの場合は、ここでPreferencesで購入フラグをtrueにしてます。
- BillingService.purchaseStateChangedメソッドの最後で、PurchaseController.purchaseCompleteメソッドを呼んでます。ここで、画面の更新を行なっています。PurchaseCallbackという抽象クラスがあるので、これをActivity内で継承したクラスを作り、画面の書き換え処理などを実装し、PurchaseController.regist(PurchaseCallbackを継承したクラス)を行なっておくと、非同期の購入処理が終わってから、ちゃんと購入済みに書き換えられます。
購入処理だけだと、以上です。キャンセル処理などは実装してません(汗)かなりアバウトな実装をしているので、後々アプデしていこうと思っています(ダメだろ…)
さらにちょっとTipsと気付いてることを書いておきます。
- アプリ内課金はAndroid 1.6以降で使うことができますが、テクブ本のサンプルだと、1.6をサポートしていません。使っているメソッドが2.0以上で使えるものなので、1.6でアプリ内課金を使う場合は、リフレクションを使う必要があります。しかしそのリフレクションのサンプルは、参考サイトにあります。
ローカルサービスの作成の、ペンディングインテントの起動のところを読んでください。 - 静的レスポンスを使ったテストができますから、最初は静的レスポンスで行ないましょう。予約済みのproductIdがあるのでそれを使います。参考URLとテクブ本に書いてあります。
固定のレスポンスを使ったアプリ内購入のテスト
Activityで渡すproductIdを”android.test.purchased”にするということになります。他のテストについてはサイトを参考にしてください。 - アプリ内課金を実装するアプリはマーケットに登録しなければなりませんが、非公開のままでよいです。アプリ内課金は動きます。
- デベロッパーコンソールのプロフィールから、テストアカウントを登録しておけば、そのアカウントで課金処理をテストすることができます。それ以外で行なうと、払い戻し処理ができなくなってしまう可能性が…
- Googleのサンプルのコードのほうが、綺麗な実装をしているのはわかる。が、いきなり読むにはわかりにくい…(俺のレベルが低いとも言える…)。
次回はアプリ内課金で購入済みのアプリのリストアの処理についてまた書こうと思います。
(再インストールとか、他の端末に同じアプリをインストールした場合の処理)
Pingback: Android:アプリ内課金を実装してみた(リストア編) | 自転車で通勤しましょ♪ブログ
Pingback: Android:アプリ内課金を実装してみた(キャンセル編) | 自転車で通勤しましょ♪ブログ