(신)영수증 검증 + Notification V2 방식을 사용하려면 NHN Cloud SDK iOS v1.7.0 이상을 사용해야 합니다.
NHN Cloud SDK iOS v1.8.0부터 (신)영수증 검증 + Notification V2 방식과 (구)영수증 검증 + Notification V1 방식을 모두 지원합니다. SDK의 검증 방식은 IAP 콘솔에 설정된 방식에 따라 결정됩니다. - IAP 콘솔 가이드
iOS용 NHN Cloud IAP SDK의 구성은 다음과 같습니다.
Service | Cocoapods Pod Name | Framework | Dependency | Build Settings |
---|---|---|---|---|
IAP | NHNCloudIAP | NHNCloudIAP.framework | * StoreKit.framework [Optional] * libsqlite3.tdb |
|
Mandatory | NHNCloudCore NHNCloudCommon |
NHNCloudCore.framework NHNCloudCommon.framework |
OTHER_LDFLAGS = ( "-ObjC", "-lc++" ); |
platform :ios, '11.0'
use_frameworks!
target '{YOUR PROJECT TARGET NAME}' do
pod 'NHNCloudIAP'
end
사용자 아이디가 설정되지 않은 상태에서는 구매, 활성화된 상품 조회, 미소비 내역 조회 기능을 사용할 수 없습니다.
// 서비스 로그인 완료 후 사용자 아이디 설정
[NHNCloudSDK setUserID:@"INPUT_USER_ID"];
// 서비스 로그아웃 완료 후 사용자 아이디를 nil로 설정
[NHNCloudSDK setUserID:nil];
// 초기화
+ (void)initWithConfiguration:(NHNCloudIAPConfiguration *)configuration;
// Delegate 설정
+ (void)setDelegate:(nullable id<NHNCloudInAppPurchaseDelegate>)delegate;
// 초기화 및 Delegate 설정
+ (void)initWithConfiguration:(NHNCloudIAPConfiguration *)configuration
delegate:(nullable id<NHNCloudInAppPurchaseDelegate>)delegate;
결제 결과에 대한 통지를 받기 위해서는 상품 구매 전에 Delegate 가 설정되어 있어야만 합니다.
@protocol NHNCloudInAppPurchaseDelegate <NSObject>
// 구매 성공
- (void)didReceivePurchaseResult:(NHNCloudPurchaseResult *)purchase;
// 구매 실패
- (void)didFailPurchaseProduct:(NSString *)productIdentifier withError:(NSError *)error;
@optional
// 프로모션 결제 진행 방법 선택
- (BOOL)shouldAddStorePurchaseForProduct:(NHNCloudProduct *)product API_AVAILABLE(ios(11.0));
@end
#import <UIKit/UIKit.h>
#import <NHNCloudIAP/NHNCloudIAP.h>
@interface ViewController () <NHNCloudInAppPurchaseDelegate>
@end
@implementation ViewController
- (void)initializeNHNCloudIAP {
// 초기화 및 Delegate 설정
NHNCloudIAPConfiguration *configuration = [NHNCloudIAPConfiguration configurationWithAppKey:@"INPUT_YOUE_APPKEY"];
[NHNCloudIAP initWithConfiguration:configuration delegate:self];
}
// 구매 성공
- (void)didReceivePurchaseResult:(NHNCloudPurchaseResult *)purchase {
NSLog(@"Successfully purchased");
}
// 구매 실패
- (void)didFailPurchaseProduct:(NSString *)productIdentifier withError:(NSError *)error {
NSLog(@"Failed to purchase: %@", error);
}
// 프로모션 결제 진행 방법 선택
- (BOOL)shouldAddStorePurchaseForProduct:(NHNCloudProduct *)product {
/*
* return YES;
* 요청한 프로모션 결제를 SDK에서 수행하도록 합니다.
* 초기화 및 로그인 후 결제창이 출력됩니다.
*/
return YES;
/*
* return NO;
* 프로모션 결제가 종료됩니다.
* product 객체를 저장한뒤 이후 원하는 시점에 저장된 객체로 결제를 진행 할 수 있습니다.
*/
self.promotionProduct = product;
return NO;
}
@end
+ (void)requestProductsWithCompletionHandler:(nullable void (^)(NHNCloudProductsResponse * _Nullable response, NSError * _Nullable error))completionHandler;
[NHNCloudIAP requestProductsWithCompletionHandler:^(NHNCloudProductsResponse *response, NSError *error) {
if (error == nil) {
NSArray<NHNCloudProduct *> *products = response.products;
NSLog(@"Products : %@", products);
// 스토어로 부터 상품정보를 획득하지 못함
NSArray<NHNCloudProduct *> *invalidProducts = response.invalidProducts;
NSLog(@"Invalid Products : %@", invalidProducts);
} else {
NSLog(@"Failed to request products: %@", error);
}
}
상품명 | 상품타입 | 설명 |
---|---|---|
소비성 상품 | NHNCloudProductTypeConsumable | 소비 가능한 일회성 상품입니다. 게임내 재화, 코인, 반복 구입 가능한 상품등에 사용할 수 있습니다. |
자동 갱신형 구독 상품 | NHNCloudProductTypeAutoRenewableSubscription | 지정된 간격 및 가격으로 결제가 자동으로 반복되는 상품입니다, 잡지, 음악 스트리밍 접근 허용, 광고 제거등에 사용할 수 있습니다. |
자동 갱신형 소비성 구독 상품 | NHNCloudProductTypeConsumableSubscription | 지정된 간격 및 가격으로 결제가 자동으로 반복되는 상품입니다. 지정된 간격 및 가격으로 소비성 상품을 지급하고자 할 때 사용할 수 있습니다. |
자동 갱신형 구독 상품의 업그레이드, 다운그레이드, 수정 기능은 지원하지 않습니다.
하나의 구독 그룹에 하나의 상품만 등록해야 합니다.
typedef NS_ENUM(NSInteger, NHNCloudProductType) {
// 상품종류 획득 실패
NHNCloudProductTypeUnknown = 0,
// 소비성 상품
NHNCloudProductTypeConsumable = 1,
// 자동 갱신형 구독 상품
NHNCloudProductTypeAutoRenewableSubscription = 2,
// 자동 갱신형 소비성 구독 상품
NHNCloudProductTypeConsumableSubscription = 3
};
// 상품 구매 요청
+ (void)purchaseWithProduct:(NHNCloudProduct *)product;
// 상품 구매 요청시 사용자 데이터 추가
+ (void)purchaseWithProduct:(NHNCloudProduct *)product payload:(NSString *)payload;
// 상품 아이디로 구매 요청
+ (void)purchaseWithProductIdentifier:(NSString *)productIdentifier;
// 상품 아이디로 구매 요청시 사용자 데이터 추가
+ (void)purchaseWithProductIdentifier:(NSString *)productIdentifier payload:(NSString *)payload;
// 상품 구매 요청
[NHNCloudIAP purchaseWithProduct:self.products[0] payload:@"DEVELOPER_PAYLOAD"];
// or
// 상품 아이디로 구매 요청
[NHNCloudIAP purchaseWithProductIdentifier:@"PRODUCT_IDENTIFIER" payload:@"DEVELOPER_PAYLOAD"];
// 활성화된 앱스토어 구독 목록 조회하기
+ (void)requestActiveSubscriptionsWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
// 활성화된 모든 마켓(앱스토어, 구글플레이, 원스토어 등) 구독 목록 조회하기
+ (void)requestAllMarketsActiveSubscriptionsWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
[NHNCloudIAP requestActiveSubscriptionsWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *purchases, NSError *error) {
if (error == nil) {
for (NHNCloudPurchaseResult *purchase in purchases) {
// 구독 상품 접근 활성화
}
} else {
NSLog(@"Failed to request active purchases : %@", error);
}
}];
// 구매 복원
+ (void)restoreWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
[NHNCloudIAP restoreWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *purchases, NSError *error) {
if (error == nil) {
for (NHNCloudPurchaseResult *purchase in purchases) {
NSLog(@"Restored purchase : %@", purchase);
}
} else {
NSLog(@"Failed to request restore : %@", error);
}
}];
// 앱스토어 미소비 구매 내역 조회
+ (void)requestConsumablePurchasesWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
// 모든 마켓(앱스토어, 구글플레이, 원스토어 등)의 미소비 구매 내역 조회
+ (void)requestAllMarketsConsumablePurchasesWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
[NHNCloudIAP requestConsumablePurchasesWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *purchases, NSError *error) {
if (error == nil) {
NSLog(@"Consumable Purchases : %@", purchases);
} else {
NSLog(@"Failed to request consumable : %@", error);
}
}
+ (void)consumeWithPurchaseResult:(NHNCloudPurchaseResult *)result
completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
// 미소비 구매 내역 조회
[NHNCloudIAP requestConsumablePurchasesWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *purchases, NSError *error) {
if (error == nil) {
for (NHNCloudPurchaseResult *purchaseResult in purchases) {
// 상품 지급 처리
// ...
// 상품 지급 후 소비 처리
[NHNCloudIAP consumeWithPurchaseResult:purchaseResult
completionHandler:^(NSError *error) {
if (error == nil) {
NSLog(@"Successfully consumed");
} else {
NSLog(@"Failed to consume : %@", error);
// 상품 지급 회수
// ...
}
}];
}
} else {
NSLog(@"Failed to request consumable : %@", error);
}
}
자동 갱신형 구독 상품을 사용할 경우 사용자에게 구독 관리 페이지를 제공해야 합니다.
별도의 UI를 구성하지 않고 아래 URL을 호출해 구독 관리 페이지를 표시해야 합니다.
https://apps.apple.com/account/subscriptions
itms-apps://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/manageSubscription
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://apps.apple.com/account/subscriptions"] options: @{} completionHandler:nil];
또는
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"itms-apps://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/manageSubscriptions"] options: @{} completionHandler:nil];
App Store의 구독 관리 페이지로 연결됩니다.
iOS 기기의 왼쪽 상단의 이전 앱으로 돌아가기에
Service App
이 나타납니다.
sqlite3 Library(libsqlite3.tdb)
를 추가로 연결(link)해야 합니다.
+ (void)processesIncompletePurchasesWithCompletionHandler:(nullable void (^)(NSArray <NHNCloudPurchaseResult *> * _Nullable results, NSError * _Nullable error))completionHandler;
// 미완료 구매 재처리 요청
[NHNCloudIAP processesIncompletePurchasesWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *results, NSError *error) {
if (error == nil) {
for (NHNCloudPurchaseResult *purchaseResult in results) {
// 상품 지급 처리
// ...
// 상품 지급 후 소비 처리
[NHNCloudIAP consumeWithPurchaseResult:purchaseResult
completionHandler:^(NSError *error) {
if (error == nil) {
NSLog(@"Successfully consumed");
} else {
NSLog(@"Failed to consume : %@", error);
// 상품 지급 회수
// ...
}
}];
}
} else {
NSLog(@"Failed to process incomplete purchases : %@", error);
}
}];
NHN Cloud IAP 초기화 메소드의 파라미터로 사용되는 인앱 결제 설정 정보입니다.
@interface NHNCloudIAPConfiguration : NSObject <NSCoding, NSCopying>
// IAP 서비스 앱 키
@property (nonatomic, copy, readonly) NSString *appKey;
// 서비스 존
@property (nonatomic) NHNCloudServiceZone serviceZone;
+ (instancetype)configurationWithAppKey:(NSString *)appKey;
- (instancetype)initWithAppKey:(NSString *)appKey
NS_SWIFT_NAME(init(appKey:));
@end
결제 결과를 통지받고 프로모션 결제의 수행 방식을 설정 할 수 있습니다.
@protocol NHNCloudInAppPurchaseDelegate <NSObject>
// 결제 성공
- (void)didReceivePurchaseResult:(NHNCloudPurchaseResult *)purchase
NS_SWIFT_NAME(didReceivePurchase(purchase:));
// 결제 실패
- (void)didFailPurchaseProduct:(NSString *)productIdentifier withError:(NSError *)error
NS_SWIFT_NAME(didFailPurchase(productIdentifier:error:));
@optional
// 프로모션 결제 진행 방법 선택
- (BOOL)shouldAddStorePurchaseForProduct:(NHNCloudProduct *)product API_AVAILABLE(ios(11.0));
@end
상품 목록 정보를 확인 할 수 있습니다.
@interface NHNCloudProductsResponse : NSObject <NSCoding, NSCopying>
// IAP 콘솔과 스토어(Apple)에 등록되어 있는 결제에 사용할 수 있는 상품 목록
@property (nonatomic, copy, readonly) NSArray<NHNCloudProduct *> *products;
// 스토어(Apple)에서 상품 정보를 획득하지 못한 상품 목록
@property (nonatomic, copy, readonly) NSArray<NHNCloudProduct *> *invalidProducts;
@end
NHN Cloud IAP 콘솔에 등록된 상품의 정보를 확인할 수 있습니다.
@interface NHNCloudProduct : NSObject <NSCoding, NSCopying>
// 상품의 ID
@property (nonatomic, copy, readonly) NSString *productIdentifier;
// 상품 고유 번호
@property (nonatomic, readonly) long productSeq;
// 상품 이름 (IAP Console)
@property (nonatomic, copy, readonly, nullable) NSString *productName;
// 상품 유형
@property (nonatomic, readonly) NHNCloudProductType productType;
// 가격
@property (nonatomic, copy, readonly, nullable) NSDecimalNumber *price;
// 통화
@property (nonatomic, copy, readonly, nullable) NSString *currency;
// 현지 상품 이름 (AppStoreConnect)
@property (nonatomic, copy, readonly, nullable) NSString *localizedTitle;
// 현지 상품 설명 (AppStoreConnect)
@property (nonatomic, copy, readonly, nullable) NSString *localizedDescription;
// 현지 가격
@property (nonatomic, copy, readonly, nullable) NSString *localizedPrice;
// 상품 활성화 여부
@property (nonatomic, readonly, getter=isActive) BOOL active;
// 스토어 코드 "AS"
@property (nonatomic, copy, readonly) NSString *storeCode;
@end
결제 정보를 확인할 수 있습니다.
@interface NHNCloudPurchaseResult : NSObject <NSCoding, NSCopying>
// 사용자 ID
@property (nonatomic, copy, readonly) NSString *userID;
// 스토어 코드 "AS"
@property (nonatomic, copy, readonly) NSString *storeCode;
// 상품의 ID
@property (nonatomic, copy, readonly) NSString *productIdentifier;
// 상품 고유 번호
@property (nonatomic, readonly) long productSeq;
// 상품 유형
@property (nonatomic, readonly) NHNCloudProductType productType;
// 가격
@property (nonatomic, copy, readonly) NSDecimalNumber *price;
// 통화
@property (nonatomic, copy, readonly) NSString *currency;
// 결제 고유 번호결제 ID
@property (nonatomic, copy, readonly) NSString *paymentSeq;
// 소비에 사용되는 토큰
@property (nonatomic, copy, readonly) NSString *accessToken;
// 결제 ID
@property (nonatomic, copy, readonly) NSString *transactionIdentifier;
// 원본 결제 ID
@property (nonatomic, copy, readonly, nullable) NSString *originalTransactionIdentifier;
// 상품 구매 시간
@property (nonatomic, readonly) NSTimeInterval purchaseTime;
// 구독 상품의 만료 시간
@property (nonatomic, readonly) NSTimeInterval expiryTime;
// 프로모션 결제 여부
@property (nonatomic, readonly, getter=isStorePayment) BOOL storePayment;
// 샌드박스 결제 여부
@property (nonatomic, readonly, getter=isSandboxPayment) BOOL sandboxPayment;
// 사용자 데이터
@property (nonatomic, readonly, copy, nullable) NSString *payload;
@end
// IAP 기능 관련 에러 코드
static NSString *const NHNCloudIAPErrorDomain = @"com.nhncloud.iap";
typedef NS_ENUM(NSUInteger, NHNCloudIAPError) {
NHNCloudIAPErrorUnknown = 0, // 알 수 없음
NHNCloudIAPErrorNotInitialized = 1, // 초기화 하지 않음
NHNCloudIAPErrorStoreNotAvailable = 2, // 스토어 사용 불가
NHNCloudIAPErrorProductNotAvailable = 3, // 상품 정보 획득 실패
NHNCloudIAPErrorProductInvalid = 4, // 원결제의 상품 아이디와 현재 상품 아이디 불일치
NHNCloudIAPErrorAlreadyOwned = 5, // 이미 소유한 상품
NHNCloudIAPErrorAlreadyInProgress = 6, // 이미 진행중인 요청 있음
NHNCloudIAPErrorUserInvalid = 7, // 현재 사용자 아이디가 결제 사용자 아이디와 불일치
NHNCloudIAPErrorPaymentInvalid = 8, // 결제 추가정보(ApplicationUsername) 획득 실패
NHNCloudIAPErrorPaymentCancelled = 9, // 스토어 결제 취소
NHNCloudIAPErrorPaymentFailed = 10, // 스토어 결제 실패
NHNCloudIAPErrorVerifyFailed = 11, // 영수증 검증 실패
NHNCloudIAPErrorChangePurchaseStatusFailed = 12, // 구매 상태 변경 실패
NHNCloudIAPErrorPurchaseStatusInvalid = 13, // 구매 진행 불가 상태
NHNCloudIAPErrorExpired = 14, // 구독 만료
NHNCloudIAPErrorRenewalPaymentNotFound = 15, // 영수증내에 갱신 결제와 일치하는 결제 정보가 없음
NHNCloudIAPErrorRestoreFailed = 16, // 복원 실패
NHNCloudIAPErrorPaymentNotAvailable = 17, // 구매 진행 불가 상태 (e.g. 앱 내 구입 제한 설정)
NHNCloudIAPErrorPurchaseLimitExceeded = 18, // 월 구매 한도 초과
};
// 네트워크 관련 에러 코드
static NSString *const NHNCloudHttpErrorDomain = @"com.nhncloud.http";
typedef NS_ENUM(NSUInteger, NHNCloudHttpError) {
NHNCloudHttpErrorNetworkNotAvailable = 100, // 네트워크 사용 불가
NHNCloudHttpErrorRequestFailed = 101, // HTTP Status Code 가 200이 아니거나 서버에서 요청을 제대로 읽지 못함
NHNCloudHttpErrorRequestTimeout = 102, // 타임아웃
NHNCloudHttpErrorRequestInvalid = 103, // 잘못된 요청 (파라미터 오류 등)
NHNCloudHttpErrorURLInvalid = 104, // URL 오류
NHNCloudHttpErrorResponseInvalid = 105, // 서버 응답 오류
NHNCloudHttpErrorAlreadyInprogress = 106, // 동일 요청 이미 수행중
NHNCloudHttpErrorRequiresSecureConnection = 107, // Allow Arbitrary Loads 미설정
};