Xamarin.iOS에서 StoreKit 개요 및 제품 정보 검색

앱에서 바로 구매에 대한 사용자 인터페이스는 아래 스크린샷에 나와 있습니다. 트랜잭션이 발생하기 전에 애플리케이션은 제품의 가격과 설명을 검색하여 표시해야 합니다. 그런 다음, 사용자가 Buy누르면 애플리케이션은 확인 대화 상자와 Apple ID 로그인을 관리하는 StoreKit에 요청을 합니다. 트랜잭션이 성공한다고 가정하면 StoreKit는 애플리케이션 코드에 알리며, 이 코드는 트랜잭션 결과를 저장하고 사용자에게 구매에 대한 액세스 권한을 제공해야 합니다.

StoreKit notifies the application code, which must store the transaction result and provide the user with access to their purchase

클래스

앱 내 구매를 구현하려면 StoreKit 프레임워크에서 다음 클래스가 필요합니다.

SKProductsRequest – 승인된 제품 판매 요청(App Store)에 대한 StoreKit입니다. 여러 제품 ID로 구성할 수 있습니다.

  • SKProductsRequestDelegate – 제품 요청 및 응답을 처리하는 메서드를 선언합니다.
  • SKProductsResponse – StoreKit(App Store)에서 대리자로 다시 전송됩니다. 요청과 함께 보낸 제품 ID와 일치하는 SKProducts를 포함합니다.
  • SKProduct – StoreKit에서 검색된 제품입니다(iTunes 커넥트 구성한 제품). 제품 ID, 타이틀, 설명 및 가격과 같은 제품에 대한 정보를 포함합니다.
  • SKPayment – 제품 ID를 사용하여 만들고 결제 큐에 추가하여 구매를 수행합니다.
  • SKPaymentQueue – Apple로 전송할 지연된 결제 요청입니다. 각 결제가 처리되면 알림이 트리거됩니다.
  • SKPaymentTransaction – 완료된 트랜잭션을 나타냅니다(App Store에서 처리하고 StoreKit를 통해 애플리케이션으로 다시 전송된 구매 요청). 트랜잭션을 구매, 복원 또는 실패할 수 있습니다.
  • SKPaymentTransactionObserver – StoreKit 결제 큐에서 생성된 이벤트에 응답하는 사용자 지정 하위 클래스입니다.
  • StoreKit 작업은 비동기적입니다 . SKProductRequest가 시작되거나 SKPayment가 큐에 추가되면 컨트롤이 코드로 반환됩니다. StoreKit는 Apple 서버에서 데이터를 받을 때 SKProductsRequestDelegate 또는 SKPaymentTransactionObserver 하위 클래스에서 메서드를 호출합니다.

다음 다이어그램은 다양한 StoreKit 클래스 간의 관계를 보여 줍니다(추상 클래스는 애플리케이션에서 구현해야 합니다.)

The relationships between the various StoreKit classes abstract classes must be implemented in the app

이러한 클래스는 이 문서의 뒷부분에 자세히 설명되어 있습니다.

테스팅

대부분의 StoreKit 작업에는 테스트를 위한 실제 디바이스가 필요합니다. 제품 정보(예: 가격 및 설명)를 검색하면 시뮬레이터에서 작동하지만 구매 및 복원 작업에서 오류가 반환됩니다(예: FailedTransaction Code=5002 알 수 없는 오류가 발생했습니다).

참고: StoreKit은 iOS 시뮬레이터에서 작동하지 않습니다. iOS 시뮬레이터에서 애플리케이션을 실행할 때 애플리케이션이 결제 큐를 검색하려고 하면 StoreKit에서 경고를 기록합니다. 저장소 테스트는 실제 디바이스에서 수행해야 합니다.

중요: 설정 애플리케이션에서 테스트 계정으로 로그인하지 마세요. 설정 애플리케이션을 사용하여 기존 Apple ID 계정에서 로그아웃한 다음 앱에서 구매 시퀀스 내에서 테스트 Apple ID를 사용하여 로그인하라는 메시지가 표시될 때까지 기다려야 합니다.

테스트 계정으로 실제 저장소에 로그인하려고 하면 자동으로 실제 Apple ID로 변환됩니다. 해당 계정은 더 이상 테스트에 사용할 수 없습니다.

StoreKit 코드를 테스트하려면 일반 iTunes 테스트 계정을 로그아웃하고 테스트 저장소에 연결된 특수 테스트 계정(iTunes 커넥트 생성됨)으로 로그인해야 합니다. 현재 계정에서 로그아웃하려면 다음과 같이 설정 > iTunes 및 App Store를 방문하세요.

To sign out of the current account visit Settings iTunes and App Store

그런 다음 앱 내에서 StoreKit에서 요청하는 경우 테스트 계정으로 로그인합니다.

iTunes에서 테스트 사용자를 만들려면 커넥트 기본 페이지에서 사용자 및 역할을 클릭합니다.

To create test users in iTunes Connect click on Users and Roles on the main page

샌드박스 테스터 선택

Selecting Sandbox Testers

기존 사용자 목록이 표시됩니다. 새 사용자를 추가하거나 기존 레코드를 삭제할 수 있습니다. 포털에서는 (현재) 기존 테스트 사용자를 보거나 편집할 수 없으므로 생성된 각 테스트 사용자(특히 할당한 암호)에 대한 좋은 레코드를 유지하는 것이 좋습니다. 테스트 사용자를 삭제하면 다른 테스트 계정에 전자 메일 주소를 다시 사용할 수 없습니다.

The list of existing users is displayed

새 테스트 사용자는 실제 Apple ID(예: 이름, 암호, 비밀 질문 및 답변)와 유사한 특성을 가집니다. 여기에 입력된 모든 세부 정보의 레코드를 유지합니다. iTunes 스토어 선택 필드에는 앱에서 바로 구매한 사용자가 해당 사용자로 로그인할 때 사용할 통화와 언어가 결정됩니다.

The Select iTunes Store field will determine the user's currency and language for their in-app purchases

제품 정보 검색

앱에서 바로 구매 제품을 판매하는 첫 번째 단계는 표시를 위해 앱 스토어에서 현재 가격 및 설명을 검색하는 것입니다.

앱에서 판매하는 제품 유형(소모성, 소모성 제품 또는 구독 유형)에 관계없이 표시할 제품 정보를 검색하는 프로세스는 동일합니다. 이 문서와 함께 제공되는 InAppPurchaseSample 코드에는 표시를 위해 프로덕션 정보를 검색하는 방법을 보여 주는 Consumables라는 프로젝트가 포함되어 있습니다. 다음을 수행하는 방법을 보여 줍니다.

  • 추상 메서드의 SKProductsRequestDelegate 구현을 만들고 구현합니다 ReceivedResponse . 예제 코드는 이를 클래스라고 InAppPurchaseManager 부릅니다.
  • StoreKit에 문의하여 결제가 허용되는지 확인합니다(사용 SKPaymentQueue.CanMakePayments ).
  • iTunes 커넥트 정의된 제품 ID를 사용하여 인스턴스화 SKProductsRequest 합니다. 이 작업은 예제의 InAppPurchaseManager.RequestProductData 메서드에서 수행됩니다.
  • 에서 Start 메서드를 호출합니다 SKProductsRequest . 그러면 App Store 서버에 대한 비동기 호출이 트리거됩니다. 대리자( InAppPurchaseManager )는 결과와 함께 다시 호출됩니다.
  • 대리자의 ( InAppPurchaseManager ) ReceivedResponse 메서드는 앱 스토어에서 반환된 데이터(제품 가격 및 설명 또는 잘못된 제품에 대한 메시지)로 UI를 업데이트합니다.

전체 상호 작용은 다음과 같습니다. StoreKit 은 iOS에 기본 제공되고 앱 스토어 는 Apple의 서버를 나타냅니다.

Retrieving Product Information graph

제품 정보 표시 예제

InAppPurchaseSample소모품 샘플 코드는 제품 정보를 검색하는 방법을 보여 줍니다. 샘플의 기본 화면에는 App Store에서 검색된 두 제품에 대한 정보가 표시됩니다.

The main screen displays information products retrieved from the App Store

제품 정보를 검색하고 표시하는 샘플 코드는 아래에서 자세히 설명합니다.

ViewController 메서드

클래스는 ConsumableViewController 제품 ID가 클래스에서 하드 코딩된 두 제품의 가격 표시를 관리합니다.

public static string Buy5ProductId = "com.xamarin.storekit.testing.consume5credits",
   Buy10ProductId = "com.xamarin.storekit.testing.consume10credits";
List<string> products;
InAppPurchaseManager iap;
public ConsumableViewController () : base()
{
   // two products for sale on this page
   products = new List<string>() {Buy5ProductId, Buy10ProductId};
   iap = new InAppPurchaseManager();
}

클래스 수준에서 관찰자를 설정하는 NSNotificationCenter 데 사용할 NSObject 선언도 있어야 합니다.

NSObject priceObserver;

ViewWillAppear 메서드에서 관찰자는 기본 알림 센터를 사용하여 생성되고 할당됩니다.

priceObserver = NSNotificationCenter.DefaultCenter.AddObserver (
  InAppPurchaseManager.InAppPurchaseManagerProductsFetchedNotification,
(notification) => {
   // display code goes here, to handle the response from the App Store
}

메서드의 끝에서 메서드를 ViewWillAppearRequestProductData 호출하여 StoreKit 요청을 시작합니다. 이 요청이 수행되면 StoreKit는 Apple의 서버에 비동기적으로 연결하여 정보를 가져오고 앱에 다시 공급합니다. 이 작업은 다음 섹션에서 설명하는 하위 클래스(InAppPurchaseManager)에 의해 SKProductsRequestDelegate 수행됩니다.

iap.RequestProductData(products);

가격 및 설명을 표시하는 코드는 단순히 SKProduct에서 정보를 검색하고 UIKit 컨트롤에 할당합니다( LocalizedTitleLocalizedDescription Microsoft는 사용자의 계정 설정에 따라 올바른 텍스트와 가격을 자동으로 확인합니다.). 다음 코드는 위에서 만든 알림에 속합니다.

priceObserver = NSNotificationCenter.DefaultCenter.AddObserver (
  InAppPurchaseManager.InAppPurchaseManagerProductsFetchedNotification,
(notification) => {
   // display code goes here, to handle the response from the App Store
   var info = notification.UserInfo;
   if (info.ContainsKey(NSBuy5ProductId)) {
       var product = (SKProduct) info.ObjectForKey(NSBuy5ProductId);
       buy5Button.Enabled = true;
       buy5Title.Text = product.LocalizedTitle;
       buy5Description.Text = product.LocalizedDescription;
       buy5Button.SetTitle("Buy " + product.Price, UIControlState.Normal); // price display should be localized
   }
}

마지막으로, 메서드는 ViewWillDisappear 관찰자가 제거되었는지 확인해야 합니다.

NSNotificationCenter.DefaultCenter.RemoveObserver (priceObserver);

SKProductRequestDelegate(InAppPurchaseManager) 메서드

애플리케이션 RequestProductData 제품 가격 및 기타 정보를 검색 하려는 경우 메서드를 호출 합니다. 제품 ID 컬렉션을 올바른 데이터 형식으로 구문 분석한 다음 해당 정보를 사용하여 SKProductsRequest 만듭니다. Start 메서드를 호출하면 Apple 서버에 대한 네트워크 요청이 발생합니다. 요청이 비동기적으로 실행되고 성공적으로 완료되면 대리자의 메서드를 호출 ReceivedResponse 합니다.

public void RequestProductData (List<string> productIds)
{
   var array = new NSString[productIds.Count];
   for (var i = 0; i < productIds.Count; i++) {
       array[i] = new NSString(productIds[i]);
   }
   NSSet productIdentifiers = NSSet.MakeNSObjectSet<NSString>(array);​​​
   productsRequest = new SKProductsRequest(productIdentifiers);
   productsRequest.Delegate = this; // for SKProductsRequestDelegate.ReceivedResponse
   productsRequest.Start();
}

iOS는 애플리케이션이 실행 중인 프로비저닝 프로필에 따라 App Store의 '샌드박스' 또는 '프로덕션' 버전으로 요청을 자동으로 라우팅하므로 앱을 개발하거나 테스트할 때 요청은 iTunes 커넥트 구성된 모든 제품(Apple에서 아직 제출 또는 승인하지 않은 제품)에 액세스할 수 있습니다. 애플리케이션이 프로덕션 환경에 있는 경우 StoreKit 요청은 승인된 제품에 대한 정보만 반환합니다.

ReceivedResponse 재정의된 메서드는 Apple 서버가 데이터로 응답한 후에 호출됩니다. 백그라운드에서 호출되므로 코드는 유효한 데이터를 구문 분석하고 알림을 사용하여 해당 알림에 대해 '수신 대기'하는 모든 ViewControllers에 제품 정보를 보내야 합니다. 유효한 제품 정보를 수집하고 알림을 보내는 코드는 다음과 같습니다.

public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
   SKProduct[] products = response.Products;
   NSDictionary userInfo = null;
   if (products.Length > 0) {
       NSObject[] productIdsArray = new NSObject[response.Products.Length];
       NSObject[] productsArray = new NSObject[response.Products.Length];
       for (int i = 0; i < response.Products.Length; i++) {
           productIdsArray[i] = new NSString(response.Products[i].ProductIdentifier);
           productsArray[i] = response.Products[i];
       }
       userInfo = NSDictionary.FromObjectsAndKeys (productsArray, productIdsArray);
   }
   NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerProductsFetchedNotification, this, userInfo);
}

다이어그램 RequestFailed 에는 표시되지 않지만 App Store 서버에 연결할 수 없거나 다른 오류가 발생할 경우 사용자에게 피드백을 제공할 수 있도록 메서드를 재정의해야 합니다. 예제 코드는 콘솔에만 쓰지만 실제 애플리케이션은 속성에 error.Code 쿼리하고 사용자 지정 동작(예: 사용자에게 경고)을 구현하도록 선택할 수 있습니다.

public override void RequestFailed (SKRequest request, NSError error)
{
   Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription);
}

이 스크린샷은 로드 직후(제품 정보를 사용할 수 없는 경우) 샘플 애플리케이션을 보여줍니다.

The sample app immediately after loading when no product information is available

잘못된 제품

또한 잘못된 SKProductsRequest 제품 ID 목록을 반환할 수도 있습니다. 잘못된 제품은 일반적으로 다음 중 하나로 인해 반환됩니다.

제품 ID가 잘못 입력 되었습니다. 유효한 제품 ID만 허용됩니다.

제품이 승인 되지 않았습니다. 테스트하는 동안 판매를 위해 허가된 모든 제품은 해당 제품에서 반환 SKProductsRequest해야 하지만 프로덕션에서는 승인된 제품만 반환됩니다.

앱 ID는 명시적이지 않습니다. Wild카드 앱 ID(별표 포함)는 앱에서 바로 구매를 허용하지 않습니다.

잘못된 프로비저닝 프로필 – 프로비저닝 포털에서 애플리케이션 구성을 변경하는 경우(예: 앱에서 바로 구매 사용) 앱을 빌드할 때 올바른 프로비전 프로필을 다시 생성하고 사용해야 합니다.

iOS 유료 애플리케이션 계약이 없습니다 . Apple 개발자 계정에 대한 유효한 계약이 없으면 StoreKit 기능이 전혀 작동하지 않습니다.

이진 파일은 거부됨 상태입니다 . 이전에 제출된 이진 파일이 거부됨 상태(App Store 팀 또는 개발자)에 있는 경우 StoreKit 기능이 작동하지 않습니다.

샘플 코드의 메서드는 ReceivedResponse 잘못된 제품을 콘솔에 출력합니다.

public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
   // code removed for clarity
   foreach (string invalidProductId in response.InvalidProducts) {
       Console.WriteLine("Invalid product id: " + invalidProductId );
   }
}

지역화된 가격 표시

가격 계층은 모든 국제 앱 스토어에서 각 제품에 대한 특정 가격을 지정합니다. 각 통화에 대해 가격이 올바르게 표시되도록 하려면 각 SKProduct통화의 Price 속성 대신 다음 확장 메서드(정의SKProductExtension.cs됨)를 사용합니다.

public static class SKProductExtension {
   public static string LocalizedPrice (this SKProduct product)
   {
       var formatter = new NSNumberFormatter ();
       formatter.FormatterBehavior = NSNumberFormatterBehavior.Version_10_4;  
       formatter.NumberStyle = NSNumberFormatterStyle.Currency;
       formatter.Locale = product.PriceLocale;
       var formattedString = formatter.StringFromNumber(product.Price);
       return formattedString;
   }
}

단추의 제목을 설정하는 코드는 다음과 같은 확장 메서드를 사용합니다.

string Buy = "Buy {0}"; // or a localizable string
buy5Button.SetTitle(String.Format(Buy, product.LocalizedPrice()), UIControlState.Normal);

두 개의 서로 다른 iTunes 테스트 계정(미국 스토어용 계정과 일본 스토어용 계정)을 사용하면 다음 스크린샷이 표시됩니다.

Two different iTunes test accounts showing language specific results

스토어는 제품 정보 및 가격 통화에 사용되는 언어에 영향을 주지만 디바이스의 언어 설정은 레이블 및 기타 지역화된 콘텐츠에 영향을 줍니다.

다른 스토어 테스트 계정을 사용하려면 설정 > iTunes 및 App Store에서 로그아웃하고 애플리케이션을 다시 시작하여 다른 계정으로 로그인해야 합니다. 디바이스의 언어를 변경하려면 설정 > 일반 > 국제 > 언어이동합니다.