티스토리 뷰

iOS/IAP

[ IAP ] 2. Product Information

은조공주 2019. 12. 12. 16:51
반응형

안녕하세요, 은조공주🥰입니다.

드디어..? 아직...? 2편입니다. 이번 편에서는 상품 정보를 가져오는 단계에 대해서 설명할 것입니다.

( 2021-03-16 기준 Apple document 업데이트 내용 반영되었습니다.)

 

[ 1.Loading In-App Product Identifiers ]

앱스토어에서 상품 정보를 가져오기 위해 인앱 상품들의 고유 Id 를 로드

 

IAP 의 구매 플로우 구현은 3단계로 나뉠 수 있습니다.

먼저 첫번째 단계에서, 앱은 앱스토어로부터 상품 정보를 가져와서, 이를 스토어UI로 유저에게 보여주고, 유저가 상품을 고를 수 있게 합니다. 두번째로, 유저가 앱의 스토어에서 상품을 골랐을 때, 결제를 요청합니다. 그리고 마지막으로 앱이 상품을 제공해줍니다.

구매 프로세스를 시작하자면, 앱이 앱스토어에서 상품에 대한 정보를 가져오고, 이를 스토어UI 를 통해 유저에게 보여줄 수 있기 위해서, 앱의 상품 고유 ID 들을 가지고 있어야 합니다. 앱에서 판매되는 모든 상품은 고유한 identifier를 가집니다. AppStore Connect 에서 새로운 IAP 상품을 생성할 때 이 값도 함께 생성됩니다. (Create an In-App Purchase 참고)

앱은 이러한 상품ID 를 사용하여 앱스토어에서 판매할 수 있는 상품 정보를 가져오고 (ex: 가격 정보, 혹은 상품 이름), 또 이를 가지고 유저가 상품을 구매할 때 결제 요청을 submit 할 수 있습니다.

 

앱 번들에 저장하거나, 자체 서버에 저장하는 등 여러 방식으로 상품 ID 리스트를 저장할 수 있습니다. 그 후 이를 앱 번들에서 로컬로 읽거나, 자체 서버에서 가져와서 상품 정보를 받아올 수 있습니다. 개발하고 있는 앱의 구조나 설계 등 상황에 맞는 적절한 방법을 선택하면 됩니다. (참고 - 특정 앱에 대해 AppStore Connect에 있는 모든 상품 목록을 가져오는 런타임 매커니즘은 없음. 개발 측에서 앱 내 상품 목록을 관리하고 해당 정보를 앱에 제공해야 한다. - ID 를 param으로 해서 product request 를 해야하고, ID 목록을 가지고 있지 않으면 상품 정보를 요청할 방법이 없다는 얘기인 것 같습니다. - 많은 상품을 관리해야 하는 경우, AppStore Connect 의 대량 XML 업로드 / 다운로드 기능을 사용하는 것이 좋다.)

 

Retrieve Product IDs from the App Bundle

만약 다음 경우에 해당한다면, 상품 ID 리스트를 앱에 저장하세요 :

  • 앱 내 IAP 상품들의 리스트가 고정되어 있는 경우 (예를 들어서, 광고를 제거하거나 기능을 잠금 해제하기 위한 상품을 파는 앱 - 상품의 갯수가 한정적이고 정적)
  • 유저가 새로운 IAP 상품을 사용하려면 앱을 업데이트해야 하는 경우
  • 앱 / 상품이 서버를 필요로 하지 않는 경우

다음과 같은 상품 ID 배열이 포함된 plist 파일을 앱 번들에 포함해야 합니다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"

"http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<array>

    <string>com.example.level1</string>

    <string>com.example.level2</string>

    <string>com.example.rocket_car</string>

</array>

</plist>

 

plist 에서 상품 ID 들을 가져오려면, 이 파일을 앱 번들 안에 넣고 읽으면 됩니다.

guard let url = Bundle.main.url(forResource: "product_ids", withExtension: "plist") else { fatalError("Unable to resolve url for in the bundle.") }

do {

    let data = try Data(contentsOf: url)

    let productIdentifiers = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: nil) as? [String]

   } catch let error as NSError {

    print("\(error.localizedDescription)")

}

Retrieve Product IDs from Your Server

만약 다음 경우에 해당한다면, 상품 ID 리스트를 서버에 저장하세요 :

  • 앱 업데이트 없이 IAP 상품 목록을 자주 업데이트하는 경우 (예를 들어서, 게임에서 추가 레벨 또는 캐릭터를 업데이트 없이 지원해야 할 때 이를 서버에서 가져와야 한다)
  • 상품이 컨텐츠일 경우
  • 앱 / 상품이 서버를 필요로 하는 경우

상품 ID 로 서버에 JSON 파일을 호스트합니다. 예를 들어 다음 JSON 파일에는 세가지 상품ID가 포함되어 있습니다.

[

    "com.example.level1",

    "com.example.level2",

    "com.example.rocket_car"

]

서버에서 상품 ID 들을 가져오려면, JSON 파일을 fetch 해서 읽습니다.

func fetchProductIdentifiers(from url: URL) {

    DispatchQueue.global(qos: .default).async {
        do {
            let jsonData = try Data(contentsOf: url)
            let identifiers = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String]

            guard let productIdentifiers = identifiers else {fatalError("Identifiers are not of type String.")}

            DispatchQueue.main.async {

           self.delegate?.display(products: productIdentifiers)
            }
      } catch let error as NSError {

            print("\(error.localizedDescription)")
      }
    }
}

앱의 이전 버전을 손상시키지 않고(?) 이후 버전의 앱 구조를 변경할 수 있도록, JSON 파일의 버전을 지정하는 것이 좋습니다.

예를 들어 이전제품_v1.json 을 사용하는 파일과 새제품_v2.json 을 사용하여 JSON 파일을 버저닝할 수 있습니다.

 

 

[ 2.Fetching Product Information from the App Store ]

앱에서 팔고자 하는 상품의 최신 정보를 가져와서 유저에게 보여주기

 

유저가 실제로 구매할 수 있는 상품만 노출하려면, 앱스토어로 쿼리를 해야합니다. 그리고 이를 위해 앱 내 모든 상품의 ID 가 필요합니다.

 

Request Product Information

앱스토어를 쿼리하려면, product request (SKProductsRequest) 를 생성하고 이를 상품ID 리스트로 초기화해야 합니다. (리퀘스트가 완료되기 전에 시스템에서 메모리 해제되지 않게 하기 위해, 리퀘스트 객체를 강한 참조로 유지해야 합니다. (Strong reference) - 반복해서 계속 나오네요...)

Product request 는 invalid product identifier 목록과 함께, 유효한 상품에 대한 정보를 가져온 다음 delegate 를 호출해서 결과를 처리합니다. Delegate 는 반드시 SKProductsRequestDelegate 프로토콜을 구현하여 앱스토어의 응답을 처리해야 합니다.

// Keep a strong reference to the product request.
var request: SKProductsRequest!

func validate(productIdentifiers: [String]) {
     let productIdentifiers = Set(productIdentifiers)

     request = SKProductsRequest(productIdentifiers: productIdentifiers)
     request.delegate = self 
     request.start()
}

var products = [SKProduct]()
// SKProductsRequestDelegate protocol method.
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    if !response.products.isEmpty {
       products = response.products
       // Custom method.
       displayStore(products)
    }

    for invalidIdentifier in response.invalidProductIdentifiers {
       // Handle any invalid product identifiers as appropriate.
    }
}

유저가 상품을 구입할 때 payment request 를 생성하기 위해 해당 상품 객체가 필요하므로, delegate 로 전달된 상품 객체 (SKProduct) 배열에 대한 참조를 유지해야 합니다. 상품을 매대에 넣거나 빼는 등 앱에서 판매되는 상품 목록이 변경될 경우, 이미지나 설명 텍스트같은, 서버에서 가져온 정보를 가지고 상품 객체에 대한 참조를 유지하는 커스텀 클래스를 만드는 것이 좋습니다.

Troubleshoot Invalid Product IDs

앱 스토어 응답의 invalid product ID 리스트는 보통 앱 내 상품ID 목록에 오류가 있어서입니다.

잘못된 상품 ID 는 앱스토어 커넥트에서 상품 구성을 잘못 했음을 의미할 수도 있습니다. UI 와 로깅 구조를 잘 만들어서, 이러한 유형의 문제를 쉽게 해결할 수 있습니다.

  • 프로덕션 빌드에서는, 유효하지 않은 상품은 생략하고 나머지만 앱의 스토어 UI 에 노출해라.
  • 개발 빌드에서는, 이슈를 나타내는 오류를 노출해라.
  • 두 빌드 모두에서 NSLog 를 사용하여 콘솔에 메세지를 노출하고, 잘못된 id 에 대한 기록을 남겨라.
  • 앱이 서버에서 리스트를 가져오는 경우, invalid ID 리스트를 서버로 다시 보내서 로그를 남길 수도 있다.

 

[ 3.SKProductsRequest ]

특정 상품 리스트에 대해서 앱스토어에서 현지화된 정보를 가져올 수 있는 객체

class SKProductsRequest : SKRequest

앱은 SKProductsRequest 객체를 사용하여, 해당 상품 정보 리스트를 들고 있을 필요 없이 유저에게 현지화된 가격 및 기타 정보를 제공할 수 있습니다. SKProductsRequest 객체를 사용하려면 상품ID 리스트로 초기화해서, delegate 객체를 붙인 다음 request 의 start() 메소드를 호출하면 됩니다. 리퀘스트가 완료되면 앱의 delegate 객체가 SKProductResponse 객체를 수신합니다. (Request 객체에 대한 강한 참조를 유지해야 한다. 그렇지 않으면 request 가 완료되기 전에 시스템에서 dealloc될 수 있다. - 또 ...)

 

Initializing a Products Request

init(productIdentifiers: Set<String>) // 상품 ID Set 으로 리퀘스트를 초기화

Setting the Delegate

var delegate: SKProductsRequestDelegate? // Product request 의 응답을 받는 delegate

protocol SKProductsRequestDelegate // 요청한 상품 정보를 받기 위해 구현해야 하는 delegate

 

 

[ 4.SKProduct ]

앱스토어커넥트에 등록된 상품 정보

class SKProduct : NSObject

SKProductResponse 객체에 SKProduct 가 포함되어 리턴됩니다.

 

Getting the Product Identifier

var productIdentifier: String // 애플 앱스토어에서 상품을 구별할 수 있는 고유 ID

Getting Product Attributes

var localizedDescription: String // 상품의 상세 설명

var localizedTitle: String // 상품 타이틀

var contentVersion: String // 컨텐츠의 버전을 나타내는 스트링

var isFamilyShareable: Bool // 상품이 앱스토어 커넥트 상으로 가족 공유가 가능한지를 나타내는 값

var contentLengths: [NSNumber] // 컨텐츠 사이즈를 byte 로 나타낸 것 (Deprecated)

Getting Pricing Information

var price: NSDecimalNumber // 상품의 현지 가격 정보

var priceLocale: Locale // 상품 가격 포맷팅에 사용되는 locale

var introductoryPrice: SKProductDiscount? // 상품의 첫구독 할인 정보를 가지고 있는 객체

var discounts: [SKProductDiscount] // 현재 이용 가능한, 상품의 할인 정보 리스트

class SKProductDiscount // 구독 상품의 할인 정보 상세 객체

Getting Subscription Information

var subscriptionGroupIdentifier: String? // 구독 그룹 ID

var subscriptionPeriod: SKProductSubscriptionPeriod? // 구독 기간 정보

class SKProductSubscriptionPeriod // 구독 기간 정보를 담고 있는 객체

enum SKProduct.PeriodUnit // 구독 기간 타입의 정의

Getting Downloadable Content Information

var isDownloadable: Bool // 앱스토어에서 다운로드 가능한 상품인지의 여부

 

 

var downloadContentLengths: [NSNumber] // 상품의 다운로드 가능 파일의 길이

var downloadContentVersion: String // 컨텐츠 버전 중 다운로드 가능한 버전을 나타냄

 

 

[ 5.SKPRoductsResponse ]

상품 리스트에 관한 정보 요청에 앱스토어가 되돌려주는 응답

class SKProductsResponse : NSObject

Response Information

var products: [SKProduct] // Product request 의 상품 ID 각각에 매칭되는 상품들의 리스트

var invalidProductIdentifiers: [String] // 앱스토어가 인식하지 못한 상품 ID 들의 배열

 

 

[ 6.SKProductSubscriptionPeriod ]

구독 기간 정보에 관한 객체

class SKProductSubscriptionPeriod : NSObject

구독 기간은, SKProduct.PeriodUnit.day, .. .weak, .. .month, .. .year 중 하나로, 기간의 단위로 정의된 기간입니다.

예를 들어서, 구독 기간이 2주라면 unit은 SKProduct.PeriodUnit.week 이고 numberOfUnits 은 2입니다.

Getting Subscription Period Details

var numberOfUnits: Int // 구독 기간 단위의 수

var unit: SKProduct.PeriodUnit // 구독 기간의 단위

enum SKProduct.PeriodUnit // 기간 단위의 정의 (ex: .day, .month ..)

 

 

[ 7.SKProductDiscount ]

구독 객체의 할인 정보에 관한 객체

class SKProductDiscount : NSObject

App Store Connect Developer Help 에 설명된 대로, 앱스토어 커넥트에서 할인 정보를 세팅할 수 있습니다. SKProductDiscount 객체는 앱스토어에서 가져온 할인 정보를 포함합니다.

Identifying the Discount

var identifier: String? // 상품 할인 정보의 고유 ID

var type: SKProductDiscount.Type // 할인 타입

enum SKProductDiscount.Type // 할인 타입의 정의 (ex: .introductory, ..)

GettingPrice and Payment Mode

var price: NSDecimalNumber // 현지 기준 할인 금액

var priceLocale: Locale // 상품의 할인 금액을 포매팅하는 데에 쓰이는 locale

var paymentMode: SKProductDiscount.PaymentMode // 상품 할인의 결제 모드

enum SKProductDiscount.PaymentMode // 결제 모드의 정의 (ex: .payAsYouGo, .freeTrial, ..)

Getting the Discount Duration

var numberOfPeriods: Int // 할인 받을 수 있는 기간 단위의 수

var subscriptionPeriod: SKProductSubscriptionPeriod // 할인 기간 정보를 가지고 있는 객체

 

 

[ 참조 문서 ]

https://developer.apple.com/documentation/storekit/in-app_purchase/loading_in-app_product_identifiers

https://developer.apple.com/documentation/storekit/in-app_purchase/fetching_product_information_from_the_app_store https://developer.apple.com/documentation/storekit/skproductsrequest

https://developer.apple.com/documentation/storekit/skproduct

https://developer.apple.com/documentation/storekit/skproductsresponse

https://developer.apple.com/documentation/storekit/skproductsubscriptionperiod

https://developer.apple.com/documentation/storekit/skproductdiscount

 

 

반응형

'iOS > IAP' 카테고리의 다른 글

[ IAP ] 5. Purchase Validation  (7) 2019.12.16
[ IAP ] 4. Purchases  (3) 2019.12.12
[ IAP ] 3. Storefronts  (0) 2019.12.12
[ IAP ] 1. Essentials  (2) 2019.12.11
[ IAP ] Overview  (0) 2019.11.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함