*

Androidで課金アプリ作製 定期購読サンプルコード編

公開日: : 最終更新日:2014/04/05 Android, 開発

Androidで課金アプリ作製 定期購読編

In-app Billing v3を使用したアプリ開発に必要な最低限のソースコードになります。

シリーズ

Androidで課金アプリ作製 サンプルコード(BILLING V3) 起動編
Androidで課金アプリ作製 プロセス間通信予習編
・Androidで課金アプリ作製 定期購読サンプルコード編

開発、動作環境

Eclipse Juno Service Release 2
Android 4.1.2 SC-06D
sample code TRIVIAL DRIVE – SAMPLE FOR IN-APP BILLING VERSION 3
In-app Billing Version 3
OS Windows7

予備知識

Androidで課金アプリ作製 サンプルコード(BILLING V3) 起動編が動かせたらの話になります。

注意事項

今回は定期購読を例にしますが、テストだと思ってたらカード会社に請求が発生してしまって焦りました。
キャンセルしたので請求は来ないのかな?明細の確認をしてないので不明ですが、テストの際は自己責任でお願いします。
セキュリティー面や、正常に購入処理が行われなかった場合の処理が無いのであくまでも流れをざっくり把握出来る程度のサンプルコードです。

プロジェクト新規作成

今回はプロジェクト名をBillingにしました。
パッケージ名はnet.cochma.billing

以前TrivialDriveから必要なファイルプロジェクトに追加します。
インストール先を変更していないなら以下にあります。

C:\Users\[適宜変更]\AppData\Local\Android\android-sdk\extras\google\play_billing\samples

・aidlファイルを追加
/Billing/srcに「com.android.vending.billing」パッケージを追加。
サンプルコードのIInAppBillingService.aidlをドラッグ→ファイルをコピー
追加方法「/Billing/src」にカーソルを合わせて右クリック→新規→パッケージ
正常に追加された場合は「Billing/gen」以下にjavaファイルが生成されます。

・utilファイルを利用
今回はサンプルのutilをそのまま利用させて頂きます。Google先生に感謝。
utilを「/Billing/src」ドラッグで追加。
パッケージ名が変わるのでutil以下のファイルの「package」をutilに変更。

ファイル追加が正常にできていれば
Billing_project2

・Manifestファイルに以下を追加

uses-permission android:name=”com.android.vending.BILLING”

MainActivity.java書き換え

全体のソースコードは下の方に書きますが、先ずは実装に必要な処理を個別に抑えたいと思います。

・解説001

String base64EncodedPublicKey = "[playから取得したキー]";

Androidで課金アプリ作製 サンプルコード(BILLING V3) 起動編でキーの取得方法は書きましたが、playの管理画面より取得する必要があります。

・解説002
ヘルパークラスを生成します。サンプルコードのヘルパークラスをそのまま利用してます。便利です。便利でした。再利用性が高いのは良い事ですね。
引数にはキーを渡しています。

mHelper = new IabHelper(this, base64EncodedPublicKey);

・解説003
ヘルパークラスの中身で「ServiceConnection」をnewしているので課金サービスと接続をしているのが分かります。
ついでに「IBinder」を引数に渡してますが正確に理解してません。プロセス間通信に必要なものらしいのですが詳細は後の課題にします。

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            public void onIabSetupFinished(IabResult result) {

	// セットアップが終ったら呼び出される処理 省略
});

・解説004
セットアップ終了後に呼び出される処理の一つですが、購入情報照会してます。
引数にはリスナーが渡されてます 解説005がリスナーの中身になります。
コールバックとリスナーの区別が未だに出来てません。ややこしいです。同じではダメなのかと思ってしまうのは知識不足だからですね。

mHelper.queryInventoryAsync(mGotInventoryListener);

・解説005
リスナーの中身になります。リスナーの中ではアイテム名を渡して購入情報があるかどうか確認してます。引数がアイテム名です。

Purchase subscriptionPurchase = inventory.getPurchase(subscription001);

・解説006
レイアウトに下記を追加し、ボタンが押されたら呼び出される処理になってます。

android:onClick="onSubscriptionClicked"

下記の記述で購入処理を呼び出してます。

mHelper.launchSubscriptionPurchaseFlow(this, subscription001, RC_REQUEST, mPurchaseFinishedListener, payload);

引数の「mPurchaseFinishedListener」は購入処理後に呼ばれるリスナーです。解説007がリスナーの中身になります。
引数の「payload」は購入の識別子に使用するので何か工夫が必要です。まだ考えてませんが。

・解説007
購入処理が終わった後に呼ばれる処理です。

result.isFailure()

購入の成否を判定します。おそらく失敗時はfalse返却される様です。

purchase.getSku().equals(subscription001)

引数にアイテム名を渡して購入商品の確認してます。期待した値が返却されない場合は商品が間違ってるなど疑うべきですね。

・解説008
購入のリクエストを受け取る処理です。これが無いと購入後の処理が行われません。
解説の位置が微妙でしたが、実際は解説006でリクエストを投げた後に呼ばれて、解説007へと処理が進みます。
onActivity自体何処から呼ばれ、何処へ行くのか良く分かってません。何処かで一歩踏み込んで調べたいと思います。

・解説009
サービスとの連結を解除するとありますが、切断する事の重要性が分かってません。
Httpで言うところのコネクションに近い概念でしょうか?やはり知識不足を感じます。

最後の方は解説が雑になってしまいましたがコードの全容となります。

サンプルコード

実際の所、実戦導入するにはセキュリティーの問題やリクエストが正常に行われなかった場合の処理等をクリアーしないといけません。あくまでも流れを把握する為のサンプルコードです。

public class MainActivity extends Activity {
    private final String TAG = "Billing sample";

    // リクエストコード
    static final int RC_REQUEST = 10001;
    
    // アイテム
    static final String subscription001 = "subsctiption001";
    
    // 定期購読アイテム購入フラグ
    boolean subScriptionFlag = false;
    
    // ヘルパーオブジェクト
    IabHelper mHelper;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Log.d(TAG, "ヘルパーオブジェクト生成");
        
        // playより取得したキーを入力 解説001 
        String base64EncodedPublicKey = "[playから取得したキー]";
        
        // ヘルパーオブジェクト生成 解説002 
        mHelper = new IabHelper(this, base64EncodedPublicKey);
        
        // セットアップ開始 解説003
        Log.d(TAG, "セットアップ開始");
        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            public void onIabSetupFinished(IabResult result) {
                Log.d(TAG, "Setup finished.");

                if (!result.isSuccess()) {
                    Log.d(TAG, "セットアップ失敗 結果: " + result);
                    return;
                }

                // オブジェクトが生成されていない
                    if (mHelper == null) return;

                Log.d(TAG, "セットアップ成功。 購入情報照会");
                // 解説004 購入情報照会
                mHelper.queryInventoryAsync(mGotInventoryListener);
            }
        });
    }

    // 解説005
    // Listener that's called when we finish querying the items and subscriptions we own
    IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {

            // オブジェクトが生成されていない
            if (mHelper == null) return;

            // 購入情報照会失敗
            if (result.isFailure()) {
                Log.d(TAG,"購入情報照会失敗 結果 : " + result);
                return;
            }

            Log.d(TAG, "Query inventory was successful.");


            // 購読情報があるかどうか?
            Purchase subscriptionPurchase = inventory.getPurchase(subscription001);
            
            // 購読購入が済んでいる場合はフラグを変更
            subScriptionFlag = (subscriptionPurchase != null && verifyDeveloperPayload(subscriptionPurchase));
            Log.d(TAG, "User is " + (subScriptionFlag ? "購読購入完了" : "購読未購入"));
                    
            Log.d(TAG, "購入情報チェック完了");
        }
    };
       
    // 解説006 定期購読ボタン
    public void onSubscriptionClicked(View arg0) {

        Log.d(TAG, "定期購入ボタンクリック");
        
        // 任意の識別子を与える事が出来る。購入後の正当性を確かめるために使用する
        // TODO 識別子の生成方法
        String payload = "subscription_sample0001";
       
        mHelper.launchSubscriptionPurchaseFlow(this, subscription001, RC_REQUEST, mPurchaseFinishedListener, payload);
        
    }

    // 解説007 購入処理が完了した後に呼ばれる処理
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);

            // オブジェクトが生成されていない場合は終了
            if (mHelper == null) return;
            
            // エラー時の処理
            if (result.isFailure()) {
                Log.d(TAG, "購入に失敗してます。");
                return;
            }

            // 購入商品がitem001の場合
            if (purchase.getSku().equals(subscription001)) {
                Log.d(TAG, "subscription001の購入が完了しました。");
                // 購入した商品に対応する処理
            }
            
        }
    };   

    // 解説008 課金リクエストの結果を受け取る
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
        if (mHelper == null) return;

        // Pass on the activity result to the helper for handling
        if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
            // not handled, so handle it ourselves (here's where you'd
            // perform any handling of activity results not related to in-app
            // billing...
            super.onActivityResult(requestCode, resultCode, data);
        }
        else {
            Log.d(TAG, "onActivityResult handled by IABUtil.");
        }
    }
    
    // 解説008 サービスをアンバインド(連結解除) 必須
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "Destroying helper.");
        if (mHelper != null) {
            mHelper.dispose();
            mHelper = null;
        } 
    }

    // 識別子をチェックする
    boolean verifyDeveloperPayload(Purchase p) {
        String payload = p.getDeveloperPayload();
        
        // TODO 002 どうやって識別子を保存確認するか
        
        return true;
    }   
}

今までどれだけ表面的な事しか知らないでAndroidの開発をしてたのか、知ってしまう良い機会となってしまいました。お恥ずかしい限りです。

次回

andoridに関して理解を深めたいと思います。

関連記事

Androidで課金アプリ作製 プロセス間通信予習編

Androidで課金アプリ作製 プロセス間通予習編 課金アプリを作成の際にaidlファイルを利用す

記事を読む

jQuery 水平スクロール&cssでカスタム可能なスクロールバーなプラグイン

水平スクロール&cssでカスタム可能なスクロールバーなプラグイン jQueryを使ったプラグインは

記事を読む

Macで ローカルサーバー構築 ローカルネットワーク参加編

Macで ローカルサーバー環境を構築するまで Apache設定編 ネットでの情報は必要最低限しか載

記事を読む

Androidで課金アプリ作製 サンプルコード(BILLING V3) 起動編

Androidで課金アプリ作製 サンプルコード起動編 課金アプリの制作の機会が来てしまいました。

記事を読む

jQuery Googleカレンダー風スケジュール表 タイムテーブル

jQueryでスケジュール表もしくはタイムテーブルっぽいもの 仕事でスケジュール管理のシステム開発

記事を読む

PHPで指定日の祝日を取得する方法 GoogleAPI利用

google APIを利用して指定日が祝日かどうか取得する 技術的な内容を書く場合は載せてるコード

記事を読む

jQuery 要素の相対位置

jQueryを使って要素の相対位置を調べる Google先生に「jquery offsetLeft

記事を読む

jQuery draggable,resizable 要素のサイズがずれる

uiプロパティ以外で要素のサイズ取得を試みる draggableとresizableを使用し要素の

記事を読む

C言語 バッファオーバーランまで ポインタ考察編

趣旨 ただC言語を勉強してもモチベーションがあがり辛いので、 取り合えずバッファオーバーランを目

記事を読む

Androidで課金アプリ作製 エラー編(-1008:Unknown error)

Androidで課金アプリ作製 エラー編 In-app Billing v3を使用したアプリ開発で

記事を読む

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

jQuery 水平スクロール&cssでカスタム可能なスクロールバーなプラグイン

水平スクロール&cssでカスタム可能なスクロールバーなプラグイン j

C言語 バッファオーバーランまで 関数の引数ポインタ

関数引数としてのポインタ 今回は関数引数としてのポインタに関して。

C言語 バッファオーバーランまで ポインタ考察編

趣旨 ただC言語を勉強してもモチベーションがあがり辛いので、 取り

Google Cloud Messaging プロジェクトナンバー API Key取得編 2014/07最新版

Google Cloud Messaging プロジェクトナンバー A

jQuery removeClass 特定のクラスを全て削除

特定のクラスを全て削除 フォームを作成した時に、入力項目に不備があっ

→もっと見る

PAGE TOP ↑