Xamarin.iOS でのコンシューマブル製品の購入
コンシューマブル製品は、"復元" の要件がないため、実装するのが最も簡単です。 これらは、ゲーム内通貨や単一使用の機能などの製品に役立ちます。 ユーザーは、コンシューマブル製品を何度でも再購入できます。
組み込み製品デリバリー
このドキュメントに付属するサンプル コードは、組み込み製品を示しています。製品 ID は、支払い後に機能のロックを解除するコードと密接に結合されているため、アプリケーションにハードコーディングされています。 購入プロセスは次のように視覚化できます。
基本的なワークフローは次のとおりです。
アプリによって
SKPayment
がキューに追加されます。 必要に応じて、ユーザーは Apple ID の入力を求められ、支払いの確認を求められます。StoreKit によって、処理のために要求がサーバーに送信されます。
トランザクションが完了すると、サーバーによってトランザクション レシートが返されます。
SKPaymentTransactionObserver
サブクラスによってそのレシートが受信され、処理されます。アプリケーションは、(
NSUserDefaults
の更新またはその他のメカニズムによって) 製品を有効にし、StoreKit のFinishTransaction
を呼び出します。
別の種類のワークフロー (サーバー配信製品) もあります。このワークフローについては、このドキュメントの後半で説明します (「レシート検証とサーバー配信製品」セクションを参照してください)。
コンシューマブル製品の例
サンプルには、基本的な "ゲーム内通貨" ("モンキー クレジット" と呼ばれます) を実装する、"コンシューマブル" と呼ばれるプロジェクトが含まれます。 このサンプルでは、2 つのアプリ内購入製品を実装して、ユーザーが必要な数の "モンキー クレジット" を購入できるようにする方法を示します。実際のアプリケーションでは、それらを使用する方法もあります。
このアプリケーションを次のスクリーンショットに示します。購入するたびに、ユーザーの残高に "モンキー クレジット" が追加されます。
カスタム クラス、StoreKit、App Store の間の相互作用は次のようになります。
ViewController メソッド
ビュー コントローラーでは、製品情報を取得するために必要なプロパティとメソッドに加えて、購入関連の通知をリッスンするために追加の通知オブザーバーが必要です。 これらは、ViewWillAppear
と ViewWillDisappear
でそれぞれ登録および削除される単なる NSObjects
です。
NSObject succeededObserver, failedObserver;
コンストラクターは SKProductsRequestDelegate
サブクラス (InAppPurchaseManager
) も作成します。その後、このサブクラスが SKPaymentTransactionObserver
(CustomPaymentObserver
) を作成して登録します。
アプリ内購入トランザクションの処理の最初の部分では、サンプル アプリケーションの次のコードに示すように、ユーザーが何かを購入したいときのボタンの押下を処理します。
buy5Button.TouchUpInside += (sender, e) => {
iap.PurchaseProduct (Buy5ProductId);
};
buy10Button.TouchUpInside += (sender, e) => {
iap.PurchaseProduct (Buy10ProductId);
};
ユーザー インターフェイスの 2 番目の部分では、トランザクションが成功したことを示す通知を処理します。この場合は、表示されている残高を更新します。
succeededObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionSucceededNotification,
(notification) => {
balanceLabel.Text = CreditManager.Balance() + " monkey credits";
});
ユーザー インターフェイスの最後の部分では、何らかの理由でトランザクションが取り消された場合にメッセージを表示します。 このコード例では、単にメッセージが出力ウィンドウに書き込まれます。
failedObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionFailedNotification,
(notification) => {
Console.WriteLine ("Transaction Failed");
});
ビュー コントローラー上のこれらのメソッドに加えて、コンシューマブル製品の購入トランザクションには、SKProductsRequestDelegate
と SKPaymentTransactionObserver
に関するコードも必要です。
InAppPurchaseManager メソッド
このサンプル コードでは、InAppPurchaseManager クラスに購入関連のメソッドを多数実装します。これには、SKPayment
インスタンスを作成して処理のキューに追加する PurchaseProduct
メソッドが含まれます。
public void PurchaseProduct(string appStoreProductId)
{
SKPayment payment = SKPayment.PaymentWithProduct (appStoreProductId);
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
キューへの支払いの追加は非同期操作です。 StoreKit がトランザクションを処理し、これを Apple のサーバーに送信している間、アプリケーションは制御を回復します。 この時点で、iOS はユーザーが App Store にログインしていることを確認し、必要に応じて Apple ID とパスワードの入力をユーザーに求めます。
ユーザーが App Store で正常に認証され、トランザクションに同意したことを前提として、SKPaymentTransactionObserver
は StoreKit の応答を受け取り、次のメソッドを呼び出してトランザクションを実行し、完了します。
public void CompleteTransaction (SKPaymentTransaction transaction)
{
var productId = transaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
PhotoFilterManager.Purchase(productId);
FinishTransaction(transaction, true);
}
最後の手順では、FinishTransaction
を呼び出して、トランザクションが正常に完了したことを StoreKit に通知します。
public void FinishTransaction(SKPaymentTransaction transaction, bool wasSuccessful)
{
// remove the transaction from the payment queue.
SKPaymentQueue.DefaultQueue.FinishTransaction(transaction); // THIS IS IMPORTANT - LET'S APPLE KNOW WE'RE DONE !!!!
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {transaction},new NSObject[] {new NSString("transaction")});
if (wasSuccessful) {
// send out a notification that we've finished the transaction
NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerTransactionSucceededNotification, this, userInfo);
} else {
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerTransactionFailedNotification, this, userInfo);
}
}
}
製品が配信されたら、支払キューからトランザクションを削除するために SKPaymentQueue.DefaultQueue.FinishTransaction
を呼び出す必要があります。
SKPaymentTransactionObserver (CustomPaymentObserver) メソッド
StoreKit は、Apple のサーバーから応答を受け取ると UpdatedTransactions
メソッドを呼び出し、コードで検査するために SKPaymentTransaction
オブジェクトの配列を渡します。 このメソッドは、(次に示すように) 各トランザクションをループ処理し、トランザクションの状態に基づいて異なる関数を実行します。
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;
default:
break;
}
}
}
CompleteTransaction
メソッドについては、このセクションの前半で説明しました。このメソッドは、購入の詳細を NSUserDefaults
に保存し、StoreKit を使用してトランザクションを完了し、最後に更新するよう UI に通知します。
複数の製品の購入
アプリケーションで複数の製品を購入するのが理にかなっている場合は、SKMutablePayment
クラスを使用し、Quantity フィールドを設定します。
public void PurchaseProduct(string appStoreProductId)
{
SKMutablePayment payment = SKMutablePayment.PaymentWithProduct (appStoreProductId);
payment.Quantity = 4; // hardcoded as an example
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
完了したトランザクションを処理するコードは、購入を正しく実行するために Quantity プロパティのクエリも実行する必要があります。
public void CompleteTransaction (SKPaymentTransaction transaction)
{
var productId = transaction.Payment.ProductIdentifier;
var qty = transaction.Payment.Quantity;
if (productId == ConsumableViewController.Buy5ProductId)
CreditManager.Add(5 * qty);
else if (productId == ConsumableViewController.Buy10ProductId)
CreditManager.Add(10 * qty);
else
Console.WriteLine ("Shouldn't happen, there are only two products");
FinishTransaction(transaction, true);
}
ユーザーが複数の数量を購入すると、次のスクリーンショットに示すように、StoreKit の確認アラートに、課金される数量、単価、合計価格が反映されます。
ネットワーク障害の処理
アプリ内購入では、StoreKit が Apple のサーバーと通信するためのネットワーク接続が動作している必要があります。 ネットワーク接続が利用できない場合、アプリ内購入は使用できなくなります。
製品要求
SKProductRequest
の作成中にネットワークが使用できない場合は、次のように、SKProductsRequestDelegate
サブクラス (InAppPurchaseManager
) の RequestFailed
メソッドが呼び出されます。
public override void RequestFailed (SKRequest request, NSError error)
{
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {error},new NSObject[] {new NSString("error")});
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerRequestFailedNotification, this, userInfo);
}
}
その後、ViewController は通知をリッスンし、購入ボタンにメッセージを表示します。
requestObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerRequestFailedNotification,
(notification) => {
Console.WriteLine ("Request Failed");
buy5Button.SetTitle ("Network down?", UIControlState.Disabled);
buy10Button.SetTitle ("Network down?", UIControlState.Disabled);
});
ネットワーク接続はモバイル デバイスで一時的である可能性があるため、アプリケーションは SystemConfiguration フレームワークを使用してネットワークの状態を監視し、ネットワーク接続が使用可能になったら再試行できます。 Apple のガイドまたはそれを使用するガイドを参照してください。
購入トランザクション
StoreKit の支払いキューは、可能であれば購入要求を保存して転送するため、ネットワークの停止の影響は、購入プロセス中にネットワークが失敗したタイミングによって異なります。
トランザクション中にエラーが発生した場合、SKPaymentTransactionObserver
サブクラス (CustomPaymentObserver
) は UpdatedTransactions
メソッドを呼び出し、SKPaymentTransaction
クラスは Failed 状態になります。
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;
default:
break;
}
}
}
FailedTransaction
メソッドは、次に示すように、エラーがユーザーのキャンセルによるものであるかどうかを検出します。
public void FailedTransaction (SKPaymentTransaction transaction)
{
//SKErrorPaymentCancelled == 2
if (transaction.Error.Code == 2) // user cancelled
Console.WriteLine("User CANCELLED FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
else // error!
Console.WriteLine("FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
FinishTransaction(transaction,false);
}
トランザクションが失敗した場合でも、支払キューからトランザクションを削除するには、FinishTransaction
メソッドを呼び出す必要があります。
SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
次に、この例のコードは、ViewController がメッセージを表示できるように通知を送信します。 ユーザーがトランザクションをキャンセルした場合、アプリケーションに追加のメッセージを表示すべきではありません。 発生する可能性があるその他のエラー コードは、次のとおりです。
FailedTransaction Code=0 Cannot connect to iTunes Store
FailedTransaction Code=5002 An unknown error has occurred
FailedTransaction Code=5020 Forget Your Password?
Applications may detect and respond to specific error codes, or handle them in the same way.
処理の制限
iOS の [設定] > [一般] > [機能制限] を使用すると、ユーザーはデバイスの特定の機能をロックできます。
SKPaymentQueue.CanMakePayments
メソッドを使用して、ユーザーがアプリ内購入を許可されているかどうかを照会できます。 これによって false が返された場合、ユーザーはアプリ内購入にアクセスできません。 StoreKit は、購入が試行された場合にユーザーにエラー メッセージを自動的に表示します。 この値をチェックすることで、アプリケーションは代わりに購入ボタンを非表示にしたり、ユーザーの役に立つ他のアクションを実行したりできます。
InAppPurchaseManager.cs
ファイル内で、CanMakePayments
メソッドは、次のように StoreKit 関数をラップします。
public bool CanMakePayments()
{
return SKPaymentQueue.CanMakePayments;
}
この方法をテストするには、iOS の [制限機能] を使用して [App内での購入] を無効にします。
ConsumableViewController
のこのコード例は、無効になっているボタンに CanMakePayments
AppStore Disabled テキストを表示して false を返すことで に反応します。
// only if we can make payments, request the prices
if (iap.CanMakePayments()) {
// now go get prices, if we don't have them already
if (!pricesLoaded)
iap.RequestProductData(products); // async request via StoreKit -> App Store
} else {
// can't make payments (purchases turned off in Settings?)
// the buttons are disabled by default, and only enabled when prices are retrieved
buy5Button.SetTitle ("AppStore disabled", UIControlState.Disabled);
buy10Button.SetTitle ("AppStore disabled", UIControlState.Disabled);
}
App内での購入機能が制限されている場合、アプリケーションは次のようになります。購入ボタンは無効になります。
CanMakePayments
が false であっても製品情報は依然として要求できるため、アプリは引き続き価格を取得して表示できます。 つまり、コードからCanMakePayments
チェックを削除しても購入ボタンは依然としてアクティブですが、購入が試行されると、[App内課金アイテムの購入は許可されていません]というメッセージ (支払いキューにアクセスしたときに StoreKit によって生成されます) が表示されます。
実際のアプリケーションでは、ボタンを完全に非表示にしたり、場合によっては StoreKit によって自動的に表示されるアラートよりも詳細なメッセージを提供したりするなど、制限の処理に別のアプローチが取られる場合があります。