Xamarin.iOS でのトランザクションと検証
過去のトランザクションの復元
お使いのアプリケーションで復元可能な製品の種類がサポートされている場合は、ユーザーがそれらの購入を復元できるように、いくつかのユーザー インターフェイス要素を含める必要があります。 この機能により、顧客は製品を追加のデバイスに追加したり、デバイスを初期化した後、またはアプリを削除して再インストールした後に製品を同じデバイスに復元したりすることができます。 復元可能な製品の種類は次のとおりです。
- 非コンシューマブル製品
- 自動更新サブスクリプション
- 無料サブスクリプション
復元プロセスでは、製品を提供するためにデバイスに保存されている記録を更新する必要があります。 顧客は、任意のデバイスで、いつでも復元を選択できます。 復元プロセスでは、そのユーザーの以前のすべてのトランザクションが再送信されます。その後、アプリケーション コードは、その情報に対してどのようなアクションを実行するかを決定する必要があります (たとえば、そのデバイスにその購入の記録が既にあるかどうかをチェックし、ない場合は、その購入の記録を作成し、ユーザーに対してその製品を有効にします)。
復元の実装
ユーザー インターフェイスの [復元] ボタンを使用すると、次のメソッドが呼び出されます。これは SKPaymentQueue
で RestoreCompletedTransactions をトリガーします。
public void Restore()
{
// theObserver will be notified of when the restored transactions start arriving <- AppStore
SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();
}
StoreKit によって、復元要求が Apple のサーバーに非同期的に送信されます。
CustomPaymentObserver
はトランザクション オブザーバーとして登録されているため、Apple のサーバーが応答するとメッセージを受信します。 応答には、このユーザーがこのアプリケーションで (すべてのデバイスにわたって) これまでに実行したすべてのトランザクションが含まれます。 以下に示すように、コードは各トランザクションをループし、復元された状態を検出し、UpdatedTransactions
メソッドを呼び出してそれを処理します。
// called when the transaction status is updated
public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
foreach (SKPaymentTransaction transaction in transactions)
{
switch (transaction.TransactionState)
{
case SKPaymentTransactionState.Purchased:
theManager.CompleteTransaction(transaction);
break;
case SKPaymentTransactionState.Failed:
theManager.FailedTransaction(transaction);
break;
case SKPaymentTransactionState.Restored:
theManager.RestoreTransaction(transaction);
break;
default:
break;
}
}
}
ユーザーに復元可能な製品がない場合、UpdatedTransactions
は呼び出されません。
サンプル内の特定のトランザクションを復元する最も単純なコードは、購入が行われた場合と同じアクションを実行します。ただし、OriginalTransaction
プロパティを使用して製品 ID にアクセスする点が異なります。
public void RestoreTransaction (SKPaymentTransaction transaction)
{
// Restored Transactions always have an 'original transaction' attached
var productId = transaction.OriginalTransaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
PhotoFilterManager.Purchase(productId); // it's as though it was purchased again
FinishTransaction(transaction, true);
}
より高度な実装では、元の日付やレシート番号など、他の transaction.OriginalTransaction
プロパティをチェックする場合があります。 この情報は、一部の製品の種類 (サブスクリプションなど) に役立ちます。
復元の完了
CustomPaymentObserver
には、次に示すように、復元プロセスが完了 (成功または失敗) したときに StoreKit によって呼び出される 2 つの追加メソッドがあります。
public override void PaymentQueueRestoreCompletedTransactionsFinished (SKPaymentQueue queue)
{
Console.WriteLine(" ** RESTORE Finished ");
}
public override void RestoreCompletedTransactionsFailedWithError (SKPaymentQueue queue, NSError error)
{
Console.WriteLine(" ** RESTORE FailedWithError " + error.LocalizedDescription);
}
この例では、これらのメソッドは何も行いませんが、実際のアプリケーションでは、ユーザーへのメッセージまたはその他の機能を実装することを選択する場合があります。
購入のセキュリティ保護
このドキュメントの 2 つの例では、NSUserDefaults
を使用して購入を追跡します。
コンシューマブル – クレジット購入の "残高" は、購入のたびにインクリメントされる単純な NSUserDefaults
整数値です。
非コンシューマブル – 各写真フィルターの購入は、キーと値のペアとして NSUserDefaults
に保存されます。
NSUserDefaults
を使用するとコード例はシンプルになりますが、技術的な知識のあるユーザーが (支払いメカニズムをバイパスして) 設定を更新できる可能性があるため、あまり安全なソリューションとはなりません。
注: 実際のアプリケーションでは、ユーザーの改ざんの影響を受けず、購入したコンテンツを保存するための安全なメカニズムを採用する必要があります。 これには、暗号化や、リモート サーバー認証などの他の手法が含まれる場合があります。
このメカニズムは、iOS、iTunes、iCloud に組み込まれたバックアップと回復機能を利用するように設計する必要もあります。 これにより、ユーザーがバックアップを復元するとすぐに、以前に購入したものが利用できるようになります。
iOS 固有のガイドラインについては、Apple のセキュア コーディング ガイドを参照してください。
レシート検証とサーバー配信製品
このドキュメントのこれまでの例は、App Store サーバーと直接通信して購入トランザクションを実行するアプリケーションのみで構成されており、これにより、アプリに既にコード化されている機能のロックが解除されます。
Apple は、購入時のレシートを別のサーバーで個別に検証できるようにして、購入のセキュリティ レベルをさらに高めています。これは、購入の一部としてデジタル コンテンツ (電子書籍や雑誌など) を配信する前に要求を検証するのに役立ちます。
組み込み製品 – このドキュメントの例と同様に、購入する製品は、アプリケーションに付属する機能として存在します。 アプリ内購入により、ユーザーは機能にアクセスできるようになります。 製品 ID はハードコードされます。
サーバー配信製品 – この製品は、トランザクションが成功してコンテンツがダウンロードされるまでリモート サーバーに保存されるダウンロード可能なコンテンツで構成されます。 書籍や雑誌の号などが例として挙げられます。 製品 ID は通常、(製品コンテンツもホストされている) 外部サーバーから取得されます。 アプリケーションは、コンテンツのダウンロードが失敗した場合に、ユーザーを混乱させることなく再試行できるように、トランザクションの完了時に記録する堅牢な方法を実装する必要があります。
サーバー配信製品
書籍や雑誌 (またはゲーム レベル) などの一部の製品のコンテンツは、購入プロセス中にリモート サーバーからダウンロードする必要があります。 これは、製品の購入後に製品コンテンツを保存して配信するために追加のサーバーが必要なことを意味します。
サーバー配信製品の価格の取得
製品はリモートで配信されるため、書籍や雑誌の新しい号の追加など、時間の経過とともに(アプリのコードを更新せずに) さらに製品を追加することも可能です。 アプリケーションがこれらの新製品を検出してユーザーに表示できるように、追加のサーバーにこの情報を保存して配信する必要があります。
製品情報は、サーバーと iTunes Connect の複数の場所に保存する必要があります。 さらに、各製品には関連付けられたコンテンツ ファイルがあります。 これらのファイルは、購入が成功した後に配信されます。
ユーザーが製品の購入を希望する場合、アプリケーションは利用可能な製品を決定する必要があります。 この情報はキャッシュされる可能性がありますが、製品のマスター リストが保存されているリモート サーバーから配信される必要があります。
このサーバーから、アプリケーションが解析する製品 ID のリストが返されます。
その後、価格と説明を取得するために StoreKit に送信する製品 ID がアプリケーションによって決定されます。
StoreKit によって、製品 ID のリストが Apple のサーバーに送信されます。
iTunesサーバーによって、有効な製品情報 (説明と現在の価格) が返されます。
アプリケーションの
SKProductsRequestDelegate
に、ユーザーに表示する製品情報が渡されます。
サーバー配信製品の購入
リモート サーバーには、コンテンツ要求が有効である (つまり、支払いが行われた) ことを検証する何らかの方法が必要なため、認証のためにレシート情報が一緒に渡されます。 リモート サーバーによってそのデータが検証のために iTunes に転送され、成功した場合は、アプリケーションへの応答に製品コンテンツが含まれます。
アプリによって
SKPayment
がキューに追加されます。 必要に応じて、ユーザーは Apple ID の入力を求められ、支払いの確認を求められます。StoreKit によって、処理のために要求がサーバーに送信されます。
トランザクションが完了すると、サーバーによってトランザクション レシートが返されます。
SKPaymentTransactionObserver
サブクラスによってそのレシートが受信され、処理されます。 製品はサーバーからダウンロードする必要があるため、アプリケーションによってリモート サーバーへのネットワーク要求が開始されます。ダウンロード要求にはレシート データが添付されるため、リモート サーバーで、その要求にコンテンツへのアクセスが許可されていることを確認できます。 アプリケーションのネットワーク クライアントは、この要求への応答を待機します。
サーバーは、コンテンツの要求を受信すると、レシート データを解析し、iTunes サーバーに直接要求を送信して、そのレシートが有効なトランザクションであることを確認します。 サーバーは、運用 URL またはサンドボックス URL に要求を送信するかどうかを決定するために、いくつかのロジックを使用する必要があります。 Apple は、常に運用 URL を使用し、状態 21007 (サンドボックス レシートが運用サーバーに送信された) を受信した場合はサンドボックスに切り替えることを提案しています。 詳細については、Apple のレシート検証プログラミング ガイドを参照してください。
iTunes によってレシートがチェックされ、有効な場合は 0 の状態が返されます。
サーバーは iTunes の応答を待機します。 有効な応答を受信した場合、コードは関連する製品コンテンツ ファイルを見つけて、アプリケーションへの応答に含める必要があります。
アプリケーションは応答を受信して解析し、製品コンテンツをデバイスのファイルシステムに保存します。
アプリケーションによって製品が有効になり、StoreKit の
FinishTransaction
が呼び出されます。 その後は、必要に応じて購入したコンテンツをアプリケーションで表示することができます (たとえば、購入した書籍や雑誌の号の最初のページを表示します)。
非常に大きな製品コンテンツ ファイルの代替実装には、トランザクションを迅速に完了できるように、手順 9 でトランザクション レシートを保存だけして、後でユーザーが実際の製品コンテンツをダウンロードするためのユーザー インターフェイスを提供することが考えられます。 後続のダウンロード要求で、保存されているレシートを再送信して、必要な製品コンテンツ ファイルにアクセスできます。
サーバー側レシート検証コードの記述
サーバー側コードでレシートを検証するには、ワークフロー図の手順 #5 から #8 を含む単純な HTTP POST 要求/応答を使用します。
アプリで SKPaymentTansaction.TransactionReceipt
プロパティを抽出します。 これは、検証のために iTunes に送信する必要があるデータです (手順 #5)。
トランザクション レシート データを Base64 でエンコードします (手順 #5 または #6)。
次のような単純な JSON ペイロードを作成します。
{
"receipt-data" : "(base-64 encoded receipt here)"
}
運用環境の場合は JSON を https://buy.itunes.apple.com/verifyReceipt に、テストの場合は https://sandbox.itunes.apple.com/verifyReceipt に HTTP POST します。
JSON 応答には、次のキーが含まれます。
{
"status" : 0,
"receipt" : { (receipt repeated here) }
}
状態が 0 の場合は、有効なレシートを示しています。 サーバーは、購入した製品のコンテンツを提供を続行できます。 レシート キーには、アプリが受信した SKPaymentTransaction
オブジェクトと同じプロパティを持つ JSON 辞書が含まれているため、サーバー コードはこの辞書に対してクエリを実行して、product_id や購入数量などの情報を取得できます。
詳細については、Apple のレシート検証プログラミング ガイドのドキュメントを参照してください。