アプリ内課金の実装とテストの方法および注意点について

公式ドキュメントを見ながらAndroidの課金アプリを開発しましたが、少しわかりづらいと感じたのでまとめました。
[公式ドキュメント](https://developer.android.com/google/play/billing/index.html)を読めばすんなり進めることについては割愛します。

#目次
　1.実装方法について
　2.テストの方法について
　3.その他の注意点

#1.実装の方法について
In-app Billing Version 3を使います。詳細な使い方は[こちら](https://developer.android.com/google/play/billing/api.html)にありますが、実装するのが大変なので楽をしたい方はサンプルアプリについている[ヘルパークラス](https://developer.android.com/training/in-app-billing/index.html)を使うのがおすすめです。
また、新しいPlay Billing Libraryというものがありますが、執筆時点でプレビューリリースの状況なので、基本的にはIn-app Billing Version 3を使って実装します。

#2.テストの方法について
実装完了後はGoogleから提供されているテスト用の商品IDを使ってテストを行い、その後、自分が定義した商品IDを使ってテストを行う形で進めます。[商品ID](https://developer.android.com/google/play/billing/billing_overview.html#console)とは自分が定義した商品(アプリ内の広告を削除する等)を一意に特定する文字列であり、このIDを使って購入リクエストやそのユーザが何の商品を所有しているのかクエリをなげたりします。
###テスト用の商品IDを使う
[こちら](https://developer.android.com/google/play/billing/billing_testing.html#billing-testing-static)にテスト用の商品IDが４つ用意されているので、それを使ってテストします。なお、エミュレータ上ではテストが出来ないので、物理端末を用意する必要があります。
・android.test.purchased
・android.test.canceled
・android.test.refunded
・android.test.item_unavailable

###自分が定義した商品IDを使う
自分の商品IDを作成するのにひと手間必要です。
下記の項番１から実施し、項番４で作成できます。
自分の商品IDを使ったテストは、物理端末上でのテストが必要なことに加えて、使う端末にログインしているプライマリのGoogleアカウントが、項番１で登録したGoogleアカウント以外である必要があります。

1. [GooglePlayコンソールに登録する。](https://play.google.com/apps/publish/)
登録料金として２５ドル必要です。

2. [アプリケーションを登録する。](https://support.google.com/googleplay/android-developer/answer/113469)
コンソール上でアプリケーションを登録し、リリースできる状態にする必要があります。スクリーンショットやアプリの説明文章やアプリのコンテンツに関する質問など、テストとは関連がないことも登録する必要があります。
ただ、当然のことながら登録内容は一般リリースしなければ公開されないので、テスト時は適当な内容を登録しておいて、リリース前に編集するのでも問題はありません。

3. [リリース版でビルドをする。](https://developer.android.com/studio/publish/preparing.html#publishing-build)
リリース版でビルドし、apkファイルを作成する必要があります。また作成したapkを項番２で作成したアプリケーションに対してアップロードする必要があります。
なお、アプロードしたapkファイル(アプリケーション)を使ってテストする必要はありません。ここまでの作業は、項番４で商品IDを作るためだけに必要な作業です。

4. [課金対象の商品を作成する。](https://developer.android.com/google/play/billing/billing_admin.html#billing-list-setup)
これで商品IDができるので、作成された製品IDをソースに組み込みます。

5. [テスト用のGoogleアカウントを作成する。]
(https://developer.android.com/google/play/billing/billing_testing.html#testing-purchases)
普通のGoogleアカウントと同様の手順でアカウントを作成し、そのアカウントをテスト用としてコンソール上で登録します。ここで作成したアカウントをテスト端末のプライマリアカウントとしてください。(端末のプライマリアカウントを変更するには工場出荷状態にする必要があります。)
※項番１で登録したGoogleアカウントではテストができないため、別のアカウントを用意する必要があります。

6. 商品IDを使ったテスト
項番５まででテストを行う準備が整いましたのでテストが行えます。
購入テスト時に表示されるポップアップにテスト購入である旨が表示されていれば成功です。
また、項番３にも記載しましたが、アップロードをしたアプリケーションを使ってテストを行う必要はありません。テスト自体はデバッグビルドを行い、USB経由でアプリケーションをインストールするのでも問題はありません。

#3.その他の注意点
最後に３点ほどつまずきそうなポイントをまとめます。発生原因は割愛させてください。

###その１．onQueryInventoryFinishedでエラー(IabResult: Error refreshing inventory)が発生する。

#####＜事象＞

ヘルパークラスを使っている場合に発生します。
一度テスト用商品ID(android.test.purchased)を使って購入処理を行うと、以降はresultがエラーになってしまいます。次回以降は必ずエラーになり、また再購入もできない状態になってしまいます。(テストになりませんね。。)

```
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
    if (result.isFailure()) {
                Log.d(TAG, "Failed to query inventory: " + result);
　　　　　　　　　//ここで、Failed to query inventory: IabResult: Error refreshing inventory (querying owned items). (response: -1003:Purchase signature verification failed)
                return;
    }
}
```

#####＜対応方法＞

この手順でandroid.test.purchasedの購入前の状態に戻せます。
なお、あくまで復旧手順なので、手順内で修正や追加した箇所は戻す必要があります。

　手順１：ヘルプバークラスの一つであるSecurity.javaのverifyPurchaseメソッドが必ずtureを返すように修正する。return falseとなっている箇所をreturn trueにしてください。


　手順２：QueryInventoryFinished内の"if(result.isFailure()) {...}"のあとに以下のコードを追加して、android.test.purchasedが消費されたことにする。 

```
if (inventory.hasPurchase("android.test.purchased")) {
  mHelper.consumeAsync(inventory.getPurchase("android.test.purchased"),null);
}
```

　手順３：アプリを実行してconsunmeAsyncを実行する。

　手順４：手順１と手順２で追加修正した箇所を元に戻す。

###その２．onIabPurchaseFinishedがコールされない。

#####＜事象＞

こちらもヘルパークラスを使っていると発生します。
ドキュメント上は、「launchPurchaseFlowの処理が完了したときにonIabPurchaseFinishedへnotifyされ、購入処理の結果はonIabPurchaseFinished内で処理する」という記述がありますが、notifyされません。

#####＜対応方法＞

onActivityResult内でhandleActivityResultをコールする必要があります。

```
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        //課金処理とは関連がない場合の処理をここに記述する
        super.onActivityResult(requestCode, resultCode, data);
    }
}
```

###その３．apkファイルがアップロードに失敗する。

#####＜事象＞

apkファイルのアップロード時にERROR: JAR_SIG_NO_SIGNATURESが発生する。

#####＜対応方法＞

apkファイルに署名する際にsignature versionsのチェックボックスに両バージョン (v1とv2)ともチェックする必要があります。


#最後に

公式ドキュメントを読む前に私自身が知っておきたかったことを中心に記載しました。
誤りやわかりづらい箇所等ありましたら、ご指摘いただければ幸いです。