Xamarin.iOS에서 소모성 제품 구매

소모성 제품은 '복원' 요구 사항이 없으므로 구현하는 가장 간단한 제품입니다. 게임 내 통화 또는 일회용 기능과 같은 제품에 유용합니다. 사용자는 소모성 제품을 반복해서 다시 구매할 수 있습니다.

기본 제공 제품 배달

이 문서에 포함된 샘플 코드는 기본 제공 제품을 보여 줍니다. 제품 ID는 결제 후 기능을 '잠금 해제'하는 코드와 긴밀하게 결합되어 있으므로 애플리케이션에 하드 코딩됩니다. 구매 프로세스는 다음과 같이 시각화할 수 있습니다.

구매 프로세스 시각화

기본 워크플로는 다음과 같습니다.

  1. 앱은 큐에 SKPayment 추가합니다. 필요한 경우 사용자에게 Apple ID를 묻는 메시지가 표시되고 결제를 확인하라는 메시지가 표시됩니다.

  2. StoreKit는 처리를 위해 서버에 요청을 보냅니다.

  3. 트랜잭션이 완료되면 서버가 트랜잭션 영수증으로 응답합니다.

  4. SKPaymentTransactionObserver 서브클래스는 영수증을 받고 처리합니다.

  5. 애플리케이션은 제품을 사용하도록 설정한 다음(업데이트 NSUserDefaults 또는 다른 메커니즘을 통해) StoreKit를 FinishTransaction호출합니다.

이 문서의 뒷부분에서 설명하는 다른 유형의 워크플로 인 서버 배달 제품 (영수증 확인 및 서버 배달 제품 섹션 참조)이 있습니다.

소모성 제품 예제

샘플에는 기본 '게임 내 통화'("원숭이 크레딧"이라고 함)를 구현하는 소모품이라는 프로젝트가 포함되어 있습니다. 이 샘플에서는 사용자가 원하는 만큼 "원숭이 크레딧"을 구매할 수 있도록 앱에서 바로 구매 제품을 두 개 구현하는 방법을 보여 줍니다. 실제 애플리케이션에서는 이를 지출하는 방법도 있습니다.

애플리케이션은 다음 스크린샷에 표시됩니다. 각 구매는 사용자의 잔액에 더 많은 "원숭이 크레딧"을 추가합니다.

각 구매는 사용자 잔액에 더 많은 원숭이 크레딧을 추가합니다.

사용자 지정 클래스, StoreKit 및 App Store 간의 상호 작용은 다음과 같습니다.

사용자 지정 클래스, StoreKit 및 App Store 간의 상호 작용

ViewController 메서드

제품 정보를 검색하는 데 필요한 속성 및 방법 외에도 보기 컨트롤러에는 구매 관련 알림을 수신 대기하는 추가 알림 관찰자가 필요합니다. 이들은 등록 NSObjects 및 각각에 제거 ViewWillAppearViewWillDisappear 됩니다.

NSObject succeededObserver, failedObserver;

또한 생성자는 ()를 만들고 등록하는 서브클래스(InAppPurchaseManagerCustomPaymentObserver)도 만듭니 SKProductsRequestDelegateSKPaymentTransactionObserver .

앱에서 바로 구매 트랜잭션을 처리하는 첫 번째 부분은 샘플 애플리케이션의 다음 코드와 같이 사용자가 구매하려는 경우 단추 누름을 처리하는 것입니다.

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 메서드

샘플 코드는 인스턴스를 만들고 SKPayment 처리를 위해 큐에 추가하는 메서드를 포함하여 PurchaseProduct InAppPurchaseManager 클래스에서 다양한 구매 관련 메서드를 구현합니다.

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 이 섹션의 앞부분에서 설명했습니다. 즉, 구매 세부 정보를 저장하고 NSUserDefaultsStoreKit로 트랜잭션을 완료하고 마지막으로 업데이트하도록 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 서버와 통신하기 위해 작동하는 네트워크 연결이 필요합니다. 네트워크 연결을 사용할 수 없는 경우 앱에서 바로 구매를 사용할 수 없습니다.

제품 요청

네트워크를 만드는 SKProductRequestRequestFailed 동안 네트워크를 사용할 수 없는 경우 아래와 같이 서브클래스(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 또는 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.cs 메서드는 CanMakePayments 다음과 같이 StoreKit 함수를 래핑합니다.

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

이 방법을 테스트하려면 iOS의 제한 기능을 사용하여 앱에서 바로 구매를 사용하지 않도록 설정합니다.

iOS의 제한 기능을 사용하여 앱 내 구매를 사용하지 않도록 설정

다음은 사용 안 함 단추에 ConsumableViewController AppStore 사용 안 함 텍스트를 표시하여 false를 반환하는 예제 코드 CanMakePayments 입니다.

// 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에서 자동으로 표시하는 경고보다 더 자세한 메시지를 제공하는 등 제한을 처리하는 다른 접근 방식을 사용할 수 있습니다.