共用方式為


在 Xamarin.iOS 中購買消費性產品

消費性產品是最簡單的實作,因為沒有「還原」需求。 它們適用於遊戲內貨幣或單一使用功能等產品。 使用者可以再次重新購買消費性產品。

內建產品傳遞

本文件隨附的範例程式代碼會示範內建產品 – 產品標識符會硬式編碼至應用程式,因為它們與付款后「解除鎖定」功能的程式代碼緊密結合。 購買程式可以像這樣可視化:

購買程式視覺效果

基本工作流程為:

  1. 應用程式會將 新增 SKPayment 至佇列。 如果需要,系統會提示使用者輸入其 Apple ID,並要求確認付款。

  2. StoreKit 會將要求傳送至伺服器進行處理。

  3. 當交易完成時,伺服器會以交易收據回應。

  4. SKPaymentTransactionObserver 類別會接收收據並加以處理。

  5. 應用程式會啟用產品(藉由更新 NSUserDefaults 或其他機制),然後呼叫 StoreKit 的 FinishTransaction

檔稍後將討論另一種類型的工作流程 - 伺服器交付產品 (請參閱收據驗證和伺服器交付的產品一節)。

消費性產品範例

此範例包含名為 消費性 專案的專案,可實作基本的「遊戲內貨幣」(稱為「猴子點數」)。 此範例示範如何實作兩個應用程式內購買產品,讓使用者可以視需要購買盡可能多的「猴子點數」–在實際的應用程式中,也會有某種方式花費它們!

應用程式會顯示在這些螢幕快照中 – 每個購買都會在用戶的餘額中新增更多「猴子點數」:

每次購買都會增加更多猴子點數給用戶餘額

自訂類別、StoreKit 和 App Store 之間的互動如下所示:

自定義類別、StoreKit 和 App Store 之間的互動

ViewController 方法

除了擷取產品資訊所需的屬性和方法之外,檢視控制器還需要其他通知觀察者接聽購買相關通知。 這些只是NSObjects將分別在和 ViewWillDisappearViewWillAppear註冊和移除。

NSObject succeededObserver, failedObserver;

建構函式也會建立 SKProductsRequestDelegate 子類別 ( InAppPurchaseManager),進而建立並註冊 SKPaymentTransactionObserverCustomPaymentObserver

處理應用程式內購買交易的第一個部分是當使用者想要購買某個專案時,處理按鈕按下,如範例應用程式的下列程式代碼所示:

buy5Button.TouchUpInside += (sender, e) => {
   iap.PurchaseProduct (Buy5ProductId);
};​
buy10Button.TouchUpInside += (sender, e) => {
   iap.PurchaseProduct (Buy10ProductId);
};

使用者介面的第二個部分是藉由更新顯示的餘額來處理交易成功通知:

succeededObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionSucceededNotification,
(notification) => {
   balanceLabel.Text = CreditManager.Balance() + " monkey credits";
});

如果使用者介面的最後一個部分因為某些原因而取消交易,則會顯示訊息。 在範例程式代碼中,訊息只會寫入輸出視窗:

failedObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionFailedNotification,
(notification) => {
   Console.WriteLine ("Transaction Failed");
});

除了檢視控制器上的這些方法,消費性產品購買交易也需要 和 SKPaymentTransactionObserver上的SKProductsRequestDelegate程序代碼。

InAppPurchaseManager 方法

範例程式代碼會在 InAppPurchaseManager 類別上實作一些購買相關方法,包括 PurchaseProduct 建立 SKPayment 實例的方法,並將它新增至佇列進行處理:

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 通知 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 類別並設定 [數量] 字段:

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時無法使用,RequestFailed則會呼叫子類別 (InAppPurchaseManager) 的 SKProductsRequestDelegate 方法,如下所示:

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 類別會處於失敗狀態。

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.csCanMakePayments 方法會包裝 StoreKit 函式,如下所示:

public bool CanMakePayments()
{
   return SKPaymentQueue.CanMakePayments;​
}

若要測試此方法,請使用 iOS 的限制功能來停用應用程式內購買

使用 iOS 的限制功能來停用應用程式內購買

這個範例程式代碼會ConsumableViewController藉由在停用按鈕上顯示AppStore Disabled文字,CanMakePayments以回應傳回 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);
}

當應用程式內購買功能受到限制時,應用程式看起來會像這樣 – 已停用購買按鈕。

當應用程式內購買功能已限制購買按鈕已停用時,應用程式看起來會像這樣

當 為 false 時 CanMakePayments 仍可要求產品資訊,因此應用程式仍然可以擷取和顯示價格。 這表示,如果我們從程式代碼中移除CanMakePayments了檢查,購買按鈕仍會處於作用中狀態,但是當嘗試購買時,使用者會看到不允許應用程式內購買的訊息(在存取付款佇列時由 StoreKit 產生):

不允許應用程式內購買

真實世界的應用程式可能會採取不同的方法來處理限制,例如完全隱藏按鈕,並提供比 StoreKit 自動顯示的警示更詳細的訊息。