2015-02-05

Amazon Appstore のアプリ内課金 (In-App Purchasing) の実装

Google Play にも「アプリ内課金」がありますが、Amazon Appstore にも当然のことながら「アプリ内課金」があります。

Google Play では IAB (In-App Billing)、Amazon Appstore では IAP (In-App Purchasing) とちょっぴり呼び名が違います。
まぁ、それはどうでもいいのですが。

現在、Amazon Appstore の API 関係のマニュアルがしっかりと日本語にローカライズされていないこと、IAP v1.x の情報が混在していること、情報が少ないことから、きっと私のように「ややこしっ」と思う人がいると思うので、こういうことを早め知りたかったと個人的に感じた内容を中心にまとめておきます。

というわけで、今回の投稿は IAP v2.x を対象としています。



基本的な流れは「Implementing IAP 2.0 - Amazon Apps & Services Developer Portal」に掲載されています。

書かれている流れの中で、必要最低限な部分だけをざっくりまとめると、こんな感じ。
  • App-SDK.zip をダウンロード (リンク)
  • ダウンロードした ZIP ファイルの中にある in-app-purchasing-<version>.jar をプロジェクトにインポート
  • AndroidManifest.xml に必要な内容を追記
  • PurchasingListener を実装 (implements)
  • PurchasingService.registerListener() を呼び、リスナーを登録
  • PurchasingService.purchase() で購入画面を開く

これだけ実装すれば、とりあえず動きます。


☆ PurchasingListener


テンプレっぽくなってますが、適当に削っているので、そのまま使うとエラーが出るところがあるかも。
使わない関数は、中身空っぽでも問題ないです。

/**
 * MyPurchasingListener
 * @author tag
 */
public class MyPurchasingListener implements PurchasingListener {

  private static final String TAG = "MyPurchasingListener";

  /**
   * MyPurchasingListener --- コンストラクタ
   *   - リスナーを onCreate() 辺りで登録してください。
   *     [code] PurchasingService.registerListener(getApplicationContext(), new MyPurchasingListener());
   */
  public MyPurchasingListener() {
    Log.d(TAG, "[IAB] " + TAG);
    // rvsProductionMode = !PurchasingService.IS_SANDBOX_MODE;
  }

  /**
   * onUserDataResponse --- アカウント情報の取得
   *   - PurchasingService.getUserData() のコールバック関数
   *   - アカウント情報を知る必要がなければ、適当な実装でOK。
   */
  @Override
  public void onUserDataResponse(final UserDataResponse response) {
    Log.d(TAG, "[IAB] onUserDataResponse");
    final UserDataResponse.RequestStatus status = response.getRequestStatus();
    switch(status) {
    case SUCCESSFUL:  // 取得に成功
      currentUserId = response.getUserData().getUserId();
      currentMarketplace = response.getUserData().getMarketplace();
      break;

    case FAILED:
    case NOT_SUPPORTED:
      break;
    }
  }

  /**
   * onProductDataResponse --- プロダクト情報の取得
   *   - PurchasingService.getProductData() のコールバック関数
   *   - プロダクト情報を取得する必要がなければ、適当な実装でOK。
   */
  @Override
  public void onProductDataResponse(final ProductDataResponse response) {
    Log.d(TAG, "[IAB] onProductDataResponse");
    final ProductDataResponse.RequestStatus status = response.getRequestStatus();
    switch (status) {
    case SUCCESSFUL:  // 成功
      /** 利用不能なプロダクト **/
      for (final String s : response.getUnavailableSkus()) {
        Log.v(TAG, "[IAB] Unavailable SKU:" + s);
      }
      /** 利用可能なプロダクト情報 **/
      final Map<String, Product> products = response.getProductData();
      for (final String key : products.keySet()) {
        Product product = products.get(key);
        Log.d(TAG, "[IAB] Title: " + product.getTitle());
        Log.d(TAG, "[IAB] ProductType: " + product.getProductType());
        Log.d(TAG, "[IAB] SKU: " + product.getSku());
        Log.d(TAG, "[IAB] Price: " + product.getPrice());
        Log.d(TAG, "[IAB] Description: " + product.getDescription());
      }
      break;

    case FAILED:
    case NOT_SUPPORTED:
      break;
    }
  }

  /**
   * onPurchaseUpdatesResponse --- 過去の購入状況の確認
   *   - PurchasingService.getPurchaseUpdates() のコールバック関数
   *   - ユーザーが購入した端末とは別の端末にインストールしたり、再インストールしたりしたときに、購入情報からアイテムを復元するときはココで。
   */
  @Override
  public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse response) {
    Log.d(TAG, "[IAB] onPurchaseUpdatesResponse");
    final PurchaseUpdatesResponse.RequestStatus status = response.getRequestStatus();
    switch (status) {
    case SUCCESSFUL:
      /** 購入履歴を参照 **/
      for (final Receipt receipt : response.getReceipts()) {
        final String receiptId = receipt.getReceiptId();
        final String sku = receipt.getSku();
        final ProductType productType = receipt.getProductType();
        final Date purchaseDate = receipt.getPurchaseDate();
        final Date cancelDate = receipt.getCancelDate();
        
        Log.d(TAG, "[IAB] ReceiptId: " + receiptId);
        Log.d(TAG, "[IAB] SKU: " + sku);
        Log.d(TAG, "[IAB] ProductType: " + productType.toString());
        Log.d(TAG, "[IAB] PurchaseDate: " + purchaseDate);
        Log.d(TAG, "[IAB] CancelDate: " + cancelDate);
        }
      }

      // 読み込みきれていない購入履歴があれば、更に読み込む(?)
      if (response.hasMore()) {
        PurchasingService.getPurchaseUpdates(false);
      }
      break;

    case FAILED:
    case NOT_SUPPORTED:
      break;
    }
  }

  /**
   * onPurchaseResponse --- 購入情報の取得
   *   - 購入後に必要な処理があるならココで処理。
   *   - 自前サーバなどで処理させたりする場合もココで。
   */
  @Override
  public void onPurchaseResponse(final PurchaseResponse response) {
    Log.d(TAG, "[IAB] onPurchaseResponse");
    Receipt receipt = null;
    switch(response.getRequestStatus()) {
    case SUCCESSFUL:  // 購入完了
      break;

    case ALREADY_PURCHASED:  // 購入済み
      break;
      
    case FAILED:
    case INVALID_SKU:
    case NOT_SUPPORTED:
      // 購入に失敗したときだけでなく、購入画面でキャンセルを押した場合にも呼ばれる。
      break;
    }
  }

}


☆ PurchasingService.getPurchaseUpdates() の引数


getPurchaseUpdates() 自体は、過去の購入情報を取得する関数。
引数は boolean 値なのですが、これによって挙動が異なるので簡単に。

違いは、「どの時点からの情報を取得するか」です。もう少し詳しく言うと、

true の場合 … 初めから
false の場合 … 最後に getPurchaseUpdates() を呼んだ時点以降

という違い。サンプルでの変数名が reset なので、そこからもわからんでもないですね。

「あれ、購入履歴が全然読み込まれないな……」と思ったら確認してみてください。(サンプルでは false なのが……)
基本的に true で読み込んで、追加読み込みさせるときに false でいいのではないでしょうか。


☆ ProGuard 関係


リリースする場合には、一応 ProGuard を使っているかなと思うのですが、リリース用の署名 APK を作成するときに、大量のエラーが出ると思うので、proguard-project.txt に下記内容の追記が必要です。<version> は組み込んだ jar ファイルに合わせて変更してください。
# Amazon IAB
-libraryjars ./libs/in-app-purchasing-<version>.jar
-keep class com.amazon.** { *; }
-keepattributes *Annotation*
-dontwarn com.amazon.**


☆ Amazon App Tester でのテスト


IAB v1.x では、App-SDK.zip 同梱の AmazonSDKTester.apk (Apps-SDK.zip\Android\InAppPurchasing\PreviousVersions\1.0\tools 内) を使うようですが、IAB v2.x では Amazon Appstore で公開されている「Amazon App Tester」を利用します。

amazon.sdktester.json (アプリ内課金情報記述ファイル、Apps-SDK.zip\Android\InAppPurchasing\PreviousVersions\1.0\tools 内) は IAB v1.x 同様使うことができます。amazon.sdktester.json を適当なテキストファイルで開いたりして、上手く書き換えてみてください。
# 参考: Amazon In-App Purchasing API (アプリ内課金) テスト方法 | Androidアプリ開発の紆余曲折


☆ Amazon Appstore でのテスト


Live App Testing を利用してください。
別の投稿「Amazon Appstore の Live App Testing の話。」にて、非常に簡単な流れについてまとめてあります。

購入画面が Amazon Appstore によるものに変わっているかを確認してみてください。
# Amazon App Tester でのテスト時には Sandbox モードとなりますが、Live App Testing では勝手に解除されているみたいです。(同じ APK ファイルだけど、挙動が変わったので、多分……)



とりあえず、こんなところで。

0 件のコメント :

コメントを投稿