2014年6月27日金曜日

【iOS技術連載】In-App Purchase 第3回 - 購入処理と購入の持続

In-App Purchaseに関する連載3回目です。
今回は、利用者が購入操作を行った際の、支払いに関する処理ついてご紹介します。


まず、App Storeから取得したプロダクト情報(SKProduct)を利用して、支払い情報を生成し、支払いトランザクションに追加します。
プロダクトの取得については、連載2回目を参照してください。


  SKPayment *payment = [SKPayment paymentWithProduct:product];
  [[SKPaymentQueue defaultQueue] addPayment:payment];


支払いトランザクションからの通知を受け取るためには、あらかじめ(application: didFinishLaunchingWithOptions: などで)キューの監視を開始しておく必要があります:


  [[SKPaymentQueue defaultQueue] addTransactionObserver:singleton];


トランザクションに変化があると、SKPaymentTransactionObserverプロトコルに通知されます。
例えば、以下のようにトランザクションの状態に応じた処理を行います:


  - (void)paymentQueue:(SKPaymentQueue *)queue
   updatedTransactions:(NSArray *)transactions {

      // 通知された支払いトランザクションそれぞれについて:
      for (SKPaymentTransaction *transaction in transactions) {
          switch (transaction.transactionState) {
              case SKPaymentTransactionStatePurchased: {
                  // プロダクト購入が完了した。
                  // プロダクトを使えるようにする(★)。

                  [queue finishTransaction:transaction];
                  break;
              }
              case SKPaymentTransactionStateRestored: {
                  // 購入済みプロダクトの復元が完了した。
                  // 多くの場合、購入完了時と共用可能(★)。

                  [queue finishTransaction:transaction];
                  break;
              }
              case SKPaymentTransactionStateFailed: {
                  // 支払い処理が失敗した。
                  // エラー表示などを行う。

                  [queue finishTransaction:transaction];
                  break;
              }
          }
      }
  }


支払いトランザクションのエラーは、下記メソッドでハンドリングします:


  - (void)paymentQueue:(SKPaymentQueue *)queue
    restoreCompletedTransactionsFailedWithError:(NSError *)error {
      // 支払いトランザクションがエラーを引き起こした。
      // 利用者に対するメッセージ表示、画面の再描画などを行う。
  }


後の連載で別途ご紹介しますが、購入済みプロダクトの復元処理の完了のハンドリングには、以下のメソッドを使用します:

  - (void)paymentQueueRestoreCompletedTransactionsFinished:
          (SKPaymentQueue *)queue;


■購入の持続

支払いトランザクションが無事完了したら、購入されたプロダクトを利用者が使用できるようにする必要があります(上記コード例の「★」の部分)。

「非消耗型」プロダクトの場合、プロダクトIDなどをNSUserDefaultsやNSUbiquitousKeyValueStoreに保持するのが簡単です。

Speedy - ケータイ風短縮ダイヤル」では、NSUserDefaultsに記録しています。
Countil - あと何日で・・・」ではNSUserDefaultsに加えて(利用者がiCloudを使用していれば)NSUbiquitousKeyValueStoreにも記録します。

iCloudを利用することで、ある端末でプロダクトを購入すれば、同一利用者の他の端末でも自動的に購入済みとすることができます。



今回は、利用者が購入操作を行った場合の支払い処理についてご紹介しました。
次回は、購入済みプロダクトの復元についてご紹介する予定です。





2014年6月17日火曜日

【iOS技術連載】In-App Purchase 第2回 - プロダクト情報の取得

In-App Purchaseに関する連載2回目です。
In-App Purchaseの概要については、連載1回目を参照してください。


iTunes Connectでプロダクトを定義したら、いよいよ実装です。

アプリケーション内のストアにプロダクトを表示するには、プロダクト定義時に指定した識別子(以後プロダクトID)をApp Storeに送信し、プロダクト情報を取得する必要があります。

プロダクトの名前や価格はアプリケーションの外で変更可能なため、今現在の値を取得する必要があるのと、後の支払い処理に取得したプロダクト情報が必要なためです。

プロダクト情報の取得は、プロダクトIDの一覧を引数に指定して SKProductsRequest のインスタンスを生成し、リクエストを送信することで開始されます。


   SKProductsRequest *request = [[KSProductsRequest alloc]
           initWithProductIdentifiers:productIds];
   request.delegate = self;
   [request start];


送信するプロダクトIDの一覧は、アプリケーションに何らかの形で埋め込みます。

リクエスト時にIDを送信しなかったプロダクトは、iTunes Connectに登録があっても情報取得されません。

Speedy - ケータイ風短縮ダイヤル」では扱うプロダクトがひとつなので、プロダクトIDを管理クラスにハードコードしています。

Countil - あと何日で・・・」は多くのプロダクトを扱うため、付加情報とともにJSONファイルにまとめています。


■プロダクト情報の取得

リクエスト結果は、SKProductRequestDelegateの productRequest:didReceiveResponse: メソッドで受け取ります。

ここではプロダクト名や価格などを表示用に準備するとともに、無効なプロダクトを表示対象からはずす必要があります。

無効なプロダクトとは、送信したプロダクトIDのうちiTunes Connect側の設定などで現在販売対象となっていないプロダクトです。

これらは販売できないので、ストアUIへの表示を省略することが推奨されています。


  /*
   結果を受け取るデリゲートメソッド。
   */
  - (void)productRequest:(SKProductsRequest *)request
      didReceiveResponse:(SKProductsResponse *)response {
  
    // 無効なプロダクトIDを保持。
    _invalidProducIds = response.invalidProductIdentifiers;
  
    // ...
  }
  
  /*
   プロダクト一覧を返す。
   */
  - (NSArray *)products {
    // 返り値用の配列を初期化。
    NSMutableArray *products = [NSMutableArray array];
  
    // プロダクトの内部表現(Product*)の配列を走査。
    for (Product *product in _products) {
      // 無効なプロダクトIDとして記録されている場合はスキップ。
      if ([_invalidProductIds containsObject:product.productId]) 
        continue;
  
      // そうでなければ返り値に追加。
      [products addObject:product];
    }
  
    return products;
  }
   

プロダクト情報が取得できたら、ストアUIを表示します。

ストアUIにはプロダクトの名前や説明、価格、および購入の意思を示すためのボタンなどが含まれ、アプリケーションの他の画面と統合された表示が推奨されています。

Countilでは、プロダクトの販売を行う「ストア画面」を用意するとともに、壁紙選択画面でのシームレスな購入経路を用意しています。




神経衰弱 - Melancholy」の現在開発中の新バージョンでは、明示的なストア画面を廃止し、より統合されたストアUIを提供する予定です。


■プロダクト情報取得の頻度について

プロダクト情報取得リクエストの送信は、基本的にはストアUIの表示の度に毎回行うべきだと筆者は考えています。利用者の端末でアプリケーションが実行されている最中に、iTunes Connectで価格等の変更が行われ得るためです。

Countilの「ストア画面」では正確な価格等を表示するため、プロダクト情報の取得が完了するまで利用者の操作をブロックしています。一方、壁紙選択画面では、利用者の操作を妨げないよう先に画面を表示し、プロダクト情報が取得できた時点で画面を更新する工夫をしています。


今回は、プロダクト情報の取得とストアUIの表示について紹介しました。
次回は、利用者の操作をトリガーとする、支払い処理について紹介する予定です。





2014年6月10日火曜日

壁紙1点を追加した「Countil」新バージョンが公開されました

「Countil」をご愛顧いただき、大変ありがとうございます。

新しい壁紙を1点追加したバージョン「1.1.0」が、無事 App Store で公開 されましたのでお知らせします。

これからの応援シーズンにご活用いただけると嬉しいです。






「壁紙 - スポーツ」は通常100円ですが、日本応援キャンペーンと題しまして、6月22日までの期間、無料でプレゼントさせていただきます。

キャンペーン終了後も「壁紙 - スポーツ」をお使いいただくには、お手数ですが、購入の手順を踏んでいただく必要があります。

ショップ画面または壁紙選択画面で、「壁紙 - スポーツ」を0円でご購入下さい。






今現在、生活シーンにぴったりなテーマの壁紙も準備中ですので、今後のアップデートにもご期待下さい。

「Countil」は App Store で公開中です。





2014年6月9日月曜日

【iOS技術連載】In-App Purchase 第1回 - 概要

アプリケーション内で有料アイテムの販売を行う場合、In-App Purchaseを利用します。

開発者はアプリケーションの他の画面と統合された「ストア」画面のUIを用意する必要がありますが、それ以外の裏方の処理はフレームワークがサポートしてくれます。





In-App Purchaseを実現する大まかな手順は、以下のとおりです:
  1. iTunes Connectで「プロダクト」を定義する。
  2. プロダクト情報の取得処理を実装する。
  3. 取得したプロダクト情報を表示し利用者に購入を促す、ストアUIを実装する。
  4. 利用者の操作に応じて、支払いトランザクションを開始する処理を実装する。
  5. 支払い完了時に購入の事実を記録し、プロダクトを有効化する処理を実装する。
また上記以外に、利用者が(アプリケーション内の再インストール後などに)購入済みのプロダクトを復元できる手段を用意する必要があります。


■プロダクトの登録

In-App Purchaseを利用する開発者は、まずiTunes Connectで「プロダクト」を定義する必要があります。

プロダクトは販売しようとする有料アイテムに関するレコードで、アプリケーション内での販売は、プロダクトの販売の形で表現されます。

iTunes Connectでのプロダクト登録については、下記ドキュメントを参照してください:


以降の実装が必要な部分については、今後の連載で詳しくご紹介する予定です。

参考とすべきドキュメントは以下です:


なお、アイウィーヴの製品では、以下のアプリケーションがIn-App Purchaseに対応しています(2014.06.09現在):


今後の連載でもこれらのアプリケーションを例にとりながら説明する予定です。



今回は、In-App Purchaseの概要についてご紹介しました。
次回は、プロダクト情報の取得とストアUIの表示についてご紹介する予定です。