To use the (New) Receipt verification + Notification V2 method, use NHN Cloud SDK iOS v1.7.0 or later.
NHN Cloud SDK iOS v1.8.0 and higher supports both (New) Receipt Verification + Notification V2 method and (Old) Receipt Verification + Notification V1 method. How the SDK is verified depends on the IAP console settings. - IAP Console Guide
NHN Cloud IAP SDK for iOS consists of the following:
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
Without setting the user ID, features such as purchase, query of activated products, or query of unconsumed details are not available.
// Set user ID after service login is completed
[NHNCloudSDK setUserID:@"INPUT_USER_ID"];
// Set user ID to nil after service logout is completed
[NHNCloudSDK setUserID:nil];
// Initialize
+ (void)initWithConfiguration:(NHNCloudIAPConfiguration *)configuration;
// Set delegate
+ (void)setDelegate:(nullable id<NHNCloudInAppPurchaseDelegate>)delegate;
// Initialize and set delegate
+ (void)initWithConfiguration:(NHNCloudIAPConfiguration *)configuration
delegate:(nullable id<NHNCloudInAppPurchaseDelegate>)delegate;
To receive notifications on payment result, Delegate must be set before purchase of a product.
@protocol NHNCloudInAppPurchaseDelegate <NSObject>
// Purchase succeeded
- (void)didReceivePurchaseResult:(NHNCloudPurchaseResult *)purchase;
// Purchase failed
- (void)didFailPurchaseProduct:(NSString *)productIdentifier withError:(NSError *)error;
@optional
// Select how to proceed with the promotion payment
- (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 {
// Initialize and set delegate
NHNCloudIAPConfiguration *configuration = [NHNCloudIAPConfiguration configurationWithAppKey:@"INPUT_YOUE_APPKEY"];
[NHNCloudIAP initWithConfiguration:configuration delegate:self];
}
// Purchase succeeded
- (void)didReceivePurchaseResult:(NHNCloudPurchaseResult *)purchase {
NSLog(@"Successfully purchased");
}
// Purchase failed
- (void)didFailPurchaseProduct:(NSString *)productIdentifier withError:(NSError *)error {
NSLog(@"Failed to purchase: %@", error);
}
// Select how to proceed with the promotion payment
- (BOOL)shouldAddStorePurchaseForProduct:(NHNCloudProduct *)product {
/*
* return YES;
* Make the requested promotion payment performed in SDK.
* Payment window will show up after initialization and login.
*/
return YES;
/*
* return NO;
* Promotion payment will be terminated.
* After storing the product object, you can proceed with payment using the stored object at a later time.
*/
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);
// Failed to obtain product information from store
NSArray<NHNCloudProduct *> *invalidProducts = response.invalidProducts;
NSLog(@"Invalid Products : %@", invalidProducts);
} else {
NSLog(@"Failed to request products: %@", error);
}
}
Product Name | Product Type | Description |
---|---|---|
Consumable product | NHNCloudProductTypeConsumable | Consumable one-time products. This can be used for in-game goods, coins, or products that can be purchased repeatedly. |
Auto-renewable subscription product | NHNCloudProductTypeAutoRenewableSubscription | Products that are automatically purchased at specific interval and price. This can be used for access to magazines and music streaming services, and advertisement removal. |
Auto-renewable consumable subscription product | NHNCloudProductTypeConsumableSubscription | Products that are automatically purchased at specific interval and price. This can be used to provide consumable products at specific interval and price. |
Upgrade, downgrade, and modification of auto-renewable subscription products are not supported.
Only one product must be registered to one subscription group.
typedef NS_ENUM(NSInteger, NHNCloudProductType) {
// Failed to obtain product types
NHNCloudProductTypeUnknown = 0,
// Consumable products
NHNCloudProductTypeConsumable = 1,
// Auto-renewable subscription products
NHNCloudProductTypeAutoRenewableSubscription = 2,
// Auto-renewable consumable subscription products
NHNCloudProductTypeConsumableSubscription = 3
};
// Request product purchase
+ (void)purchaseWithProduct:(NHNCloudProduct *)product;
// Add user data when requesting product purchase
+ (void)purchaseWithProduct:(NHNCloudProduct *)product payload:(NSString *)payload;
// Request purchase with a product ID
+ (void)purchaseWithProductIdentifier:(NSString *)productIdentifier;
// Add user data when requesting purchase with a product ID
+ (void)purchaseWithProductIdentifier:(NSString *)productIdentifier payload:(NSString *)payload;
// Request product purchase
[NHNCloudIAP purchaseWithProduct:self.products[0] payload:@"DEVELOPER_PAYLOAD"];
// or
// Request purchase with a product ID
[NHNCloudIAP purchaseWithProductIdentifier:@"PRODUCT_IDENTIFIER" payload:@"DEVELOPER_PAYLOAD"];
// Query an activated subscription list in App Store
+ (void)requestActiveSubscriptionsWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
// Query the list of activated subscriptions in all markets (such as App Store, Google Play, and ONE Store)
+ (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) {
// Activate access for subscription products
}
} else {
NSLog(@"Failed to request active purchases : %@", error);
}
}];
// Restore purchase
+ (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);
}
}];
// Query unconsumed purchases for App Store
+ (void)requestConsumablePurchasesWithCompletionHandler:(nullable void (^)(NSArray<NHNCloudPurchaseResult *> * _Nullable purchases, NSError * _Nullable error))completionHandler;
// Query unconsumed purchases for all markets (such as App Store, Google Play, and ONE Store)
+ (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;
// Query Unconsumed Purchases
[NHNCloudIAP requestConsumablePurchasesWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *purchases, NSError *error) {
if (error == nil) {
for (NHNCloudPurchaseResult *purchaseResult in purchases) {
// Process as Product Provided
// ...
// Process as Consumed after Product is Provided
[NHNCloudIAP consumeWithPurchaseResult:purchaseResult
completionHandler:^(NSError *error) {
if (error == nil) {
NSLog(@"Successfully consumed");
} else {
NSLog(@"Failed to consume : %@", error);
// Retreive Product Provided
// ...
}
}];
}
} else {
NSLog(@"Failed to request consumable : %@", error);
}
}
When auto-renewable subscription products are used, the Manage Subscriptions page must be provided to users.
Without configuring a separate UI, you must display the Manage Subscriptions page by calling the URL below.
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];
Or
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"itms-apps://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/manageSubscriptions"] options: @{} completionHandler:nil];
The Manage Subscriptions page of App Store is connected.
Service App
appears in the return to previous app button in the top left corner of an iOS device.
To enable compatibility with (old) IAP SDK, additionally link
sqlite3 Library(libsqlite3.tdb)
.
+ (void)processesIncompletePurchasesWithCompletionHandler:(nullable void (^)(NSArray <NHNCloudPurchaseResult *> * _Nullable results, NSError * _Nullable error))completionHandler;
// Request for Reprocessing Incomplete Purchase
[NHNCloudIAP processesIncompletePurchasesWithCompletionHandler:^(NSArray<NHNCloudPurchaseResult *> *results, NSError *error) {
if (error == nil) {
for (NHNCloudPurchaseResult *purchaseResult in results) {
// Process as Product Provided
// ...
// Process as Consumed after Product Provided
[NHNCloudIAP consumeWithPurchaseResult:purchaseResult
completionHandler:^(NSError *error) {
if (error == nil) {
NSLog(@"Successfully consumed");
} else {
NSLog(@"Failed to consume : %@", error);
// Retrieve Product Provided
// ...
}
}];
}
} else {
NSLog(@"Failed to process incomplete purchases : %@", error);
}
}];
IAP configuration information which is used as a parameter for the NHN Cloud IAP initialization method.
@interface NHNCloudIAPConfiguration : NSObject <NSCoding, NSCopying>
// IAP service Appkey
@property (nonatomic, copy, readonly) NSString *appKey;
// Service zone
@property (nonatomic) NHNCloudServiceZone serviceZone;
+ (instancetype)configurationWithAppKey:(NSString *)appKey;
- (instancetype)initWithAppKey:(NSString *)appKey
NS_SWIFT_NAME(init(appKey:));
@end
Lets you be notified of the purchase result and set how to perform a promotion purchase.
@protocol NHNCloudInAppPurchaseDelegate <NSObject>
// Purchase succeeded
- (void)didReceivePurchaseResult:(NHNCloudPurchaseResult *)purchase
NS_SWIFT_NAME(didReceivePurchase(purchase:));
// Purchase failed
- (void)didFailPurchaseProduct:(NSString *)productIdentifier withError:(NSError *)error
NS_SWIFT_NAME(didFailPurchase(productIdentifier:error:));
@optional
// Select how to proceed with the promotion payment
- (BOOL)shouldAddStorePurchaseForProduct:(NHNCloudProduct *)product API_AVAILABLE(ios(11.0));
@end
Lets you check the product list information.
@interface NHNCloudProductsResponse : NSObject <NSCoding, NSCopying>
// List of products that can be used for purchase, which are registered in IAP console and Apple Store
@property (nonatomic, copy, readonly) NSArray<NHNCloudProduct *> *products;
// List of products for which product information could not be obtained from Apple Store
@property (nonatomic, copy, readonly) NSArray<NHNCloudProduct *> *invalidProducts;
@end
Lets you check information of a product registered in NHN Cloud IAP console.
@interface NHNCloudProduct : NSObject <NSCoding, NSCopying>
// Product ID
@property (nonatomic, copy, readonly) NSString *productIdentifier;
// Product sequence number
@property (nonatomic, readonly) long productSeq;
// Product name (IAP console)
@property (nonatomic, copy, readonly, nullable) NSString *productName;
// Product type
@property (nonatomic, readonly) NHNCloudProductType productType;
// Price
@property (nonatomic, copy, readonly, nullable) NSDecimalNumber *price;
// Currency
@property (nonatomic, copy, readonly, nullable) NSString *currency;
// Local product name (AppStoreConnect)
@property (nonatomic, copy, readonly, nullable) NSString *localizedTitle;
// Local product description (AppStoreConnect)
@property (nonatomic, copy, readonly, nullable) NSString *localizedDescription;
// Local price
@property (nonatomic, copy, readonly, nullable) NSString *localizedPrice;
// Whether the product is activated or not
@property (nonatomic, readonly, getter=isActive) BOOL active;
// Store code "AS"
@property (nonatomic, copy, readonly) NSString *storeCode;
@end
Lets you check the purchase information.
@interface NHNCloudPurchaseResult : NSObject <NSCoding, NSCopying>
// User ID
@property (nonatomic, copy, readonly) NSString *userID;
// Store code "AS"
@property (nonatomic, copy, readonly) NSString *storeCode;
// Product ID
@property (nonatomic, copy, readonly) NSString *productIdentifier;
// Product sequence number
@property (nonatomic, readonly) long productSeq;
// Product type
@property (nonatomic, readonly) NHNCloudProductType productType;
// Price
@property (nonatomic, copy, readonly) NSDecimalNumber *price;
// Currency
@property (nonatomic, copy, readonly) NSString *currency;
// Payment sequence number payment ID
@property (nonatomic, copy, readonly) NSString *paymentSeq;
// Token used for consumption
@property (nonatomic, copy, readonly) NSString *accessToken;
// Transaction ID
@property (nonatomic, copy, readonly) NSString *transactionIdentifier;
// Original transaction ID
@property (nonatomic, copy, readonly, nullable) NSString *originalTransactionIdentifier;
// Product purchase time
@property (nonatomic, readonly) NSTimeInterval purchaseTime;
// Expiry time of a subscription product
@property (nonatomic, readonly) NSTimeInterval expiryTime;
// Whether it is promotion payment or not
@property (nonatomic, readonly, getter=isStorePayment) BOOL storePayment;
// Whether it is sandbox payment or not
@property (nonatomic, readonly, getter=isSandboxPayment) BOOL sandboxPayment;
// User data
@property (nonatomic, readonly, copy, nullable) NSString *payload;
@end
// IAP Error
static NSString *const NHNCloudIAPErrorDomain = @"com.nhncloud.iap";
typedef NS_ENUM(NSUInteger, NHNCloudIAPError) {
NHNCloudIAPErrorUnknown = 0, // Unknown
NHNCloudIAPErrorNotInitialized = 1, // Not Initialized
NHNCloudIAPErrorStoreNotAvailable = 2, // Store is unavailable
NHNCloudIAPErrorProductNotAvailable = 3, // Failed to get product information
NHNCloudIAPErrorProductInvalid = 4, // Inconsistency of IDs between original payment and current product
NHNCloudIAPErrorAlreadyOwned = 5, // Product is already owned
NHNCloudIAPErrorAlreadyInProgress = 6, // Request is already processing
NHNCloudIAPErrorUserInvalid = 7, // Inconsistency of IDs between current user and paid user
NHNCloudIAPErrorPaymentInvalid = 8, // Failed to get further payment information (ApplicationUsername)
NHNCloudIAPErrorPaymentCancelled = 9, // Store payment cancelled
NHNCloudIAPErrorPaymentFailed = 10, // Store payment failed
NHNCloudIAPErrorVerifyFailed = 11, // Receipt verification failed
NHNCloudIAPErrorChangePurchaseStatusFailed = 12, // Change of purchase status failed
NHNCloudIAPErrorPurchaseStatusInvalid = 13, // Unavailable to purchase
NHNCloudIAPErrorExpired = 14, // Subscription expired
NHNCloudIAPErrorRenewalPaymentNotFound = 15, // Renewal payment information not found in receipt
NHNCloudIAPErrorRestoreFailed = 16, // Failed to restore
NHNCloudIAPErrorPaymentNotAvailable = 17, // Status of purchase inoperative (e.g. setting purchase restrictions in app)
NHNCloudIAPErrorPurchaseLimitExceeded = 18, // Monthly purchase limit exceeded
};
// Network Error
static NSString *const NHNCloudHttpErrorDomain = @"com.nhncloud.http";
typedef NS_ENUM(NSUInteger, NHNCloudHttpError) {
NHNCloudHttpErrorNetworkNotAvailable = 100, // Network is unavailable
NHNCloudHttpErrorRequestFailed = 101, // HTTP Status Code is not 200 or can not read request
NHNCloudHttpErrorRequestTimeout = 102, // Timeout
NHNCloudHttpErrorRequestInvalid = 103, // Request is invalid
NHNCloudHttpErrorURLInvalid = 104, // URL is invalid
NHNCloudHttpErrorResponseInvalid = 105, // Response is invalid
NHNCloudHttpErrorAlreadyInprogress = 106, // Request is already in progress
NHNCloudHttpErrorRequiresSecureConnection = 107, // Do not set Allow Arbitrary Loads
};