iOS InApp Purchase - Sizuha/devdog GitHub Wiki
OS In-App Purchase
- ํ์ํ ํ๋ ์์ํฌ: StoreKit.framework
- ์ถ์ฒ: http://blog.naver.com/PostView.nhn?blogId=kimysaptec&logNo=130095894390
- ์ํ๊ณ์ข๊ฐ ์ฌ์ ์ ๋ฑ๋ก ํ์ --- ์์ผ๋ฉด ํ ์คํธ ํ์ธ๋ถ๊ฐ
- ์์ดํฐ ์ค์ /store์ ๊ณ์ ๋ก๊ทธ์์(๋ฐ๋์) ---- ํ ์คํธ ๊ณ์ ์ฌ์ฉ์ ์ํจ
- ์ํ ํ๋งค ์ง์ญ ์ธ์ด ์ผ์น ํ์ --- ๊ฐ๋ฐ์ดํ๊ณผ ์ ํ iTunes Connect ์ฌ์ดํธ์ Manage Your Applications์์
- ์ ํ ๊ฒฐ์ ์ผ๋ฟ์ฐฝ์ ์ง์ฐ์๊ฐ์ด ๋ฐ์ํ์ฌ ๋๋ฆฌ๊ฒ ํ์ ๋ ์ ์์. (์ด์ ๋ ๋คํธ์ํฌ์ ์ ํ ์๋ฒ์ ์ํฅ์ธ๋ฏ ํจ) ์ต๋ 15์ด๊น์ง ์ง์ฐ ๋ฐ์ ๊ฐ๋ฅ ํ์ธ ๋จ.
iTunes Connect > Manage Your Apps์์ ํด๋น ์ฑ์ ์ ํ.
Manage In-App Purchases ๋ฒํผ ํด๋ฆญ.
โป ์ํ ๊ฐ๊ฒฉ์ ํ๋ฒ ์ ํ๋ฉด ๋ฐ๊พธ๊ธฐ ์ด๋ ต๋ค. ๊ธฐ๊ฐ ํ์ ์ผ๋ก๋ง ๋ฐ๊ฟ ์ ์๋ ๋ฏ.
Create New ๋ฒํผ ํด๋ฆญ.
ํ๋งค ์ํ ์ ํ์ ์๋ 3๊ฐ์ง ์ ํ์ค ํ๋ ์ ํ
- ๋ณด์ถฉ ๊ฐ๋ฅ(Consumable): ์ด ์์ฉ ํ๋ก๊ทธ๋จ ๋ด ๊ตฌ์
๊ธฐ๋ฅ์ ๋งค๋ฒ ๊ตฌ์
ํด์ผ ํ๊ณ ๋ฌด๋ฃ๋ก ๋ค์ ๋ค์ด๋ก๋ํ ์ ์๋ ํญ๋ชฉ์
๋๋ค.
- ์ถ๊ฐ ์ฒด๋ ฅ
- ์ถ๊ฐ ๊ฒฝํ์น
- ๋ณด์ถฉ ๋ถ๊ฐ๋ฅ(Non-Consumable): ์ด ์์ฉ ํ๋ก๊ทธ๋จ ๋ด ๊ตฌ์
๊ธฐ๋ฅ์ ํ ๋ฒ๋ง ๊ตฌ์
ํด์ผ ํ๊ณ , ๋์ผํ iTunes Store ๊ณ์ ์ผ๋ก ์ธ์ฆ๋ ์ฌ๋ฌ ์ฅ๋น์ ์ ์กํ ์ ์๋ ํญ๋ชฉ์
๋๋ค.
- ๋ณด๋์ค ๊ฒ์ ๋ ๋ฒจ
- ๋์ ์๋ด ์ง๋
- ๊ตฌ๋
(Subscriptions): ๊ตฌ๋
๊ธฐ๊ฐ์ด ๋ง๋ฃ๋ ๊ฒฝ์ฐ ๋ค์ ๊ตฌ์
ํด์ผ ํ๋ ์ผํ์ฑ ์๋น์ค์
๋๋ค.
- ํ ๋ฌ ๊ตฌ๋
- ์์น ์๋น์ค ๊ตฌ๋
์ํ ์ ํ์ ์ ํํ๋ฉด, In-app ์ํ์ ๋ํ ์์ธ ์ค์ ํ์ด์ง๊ฐ ๋์จ๋ค.
- Reference Name: iTunes Connect ์ฌ์ดํธ ๋ด์์ ํ์๋๋ ์ด๋ฆ. ๊ด๋ฆฌ์๊ฐ ์์๋ณผ ์ ์๊ฒ ์์ฑ.
- Product ID: ์ํ ID. ์ ๋ํฌํ ์ด๋ฆ์ด ๋๋๋ก ์์ฑ.
- Cleared for Sale: ํ๋งคํด์ผ๋ ์ํ์ด๋ฏ๋ก Yes๋ก ์ค์ ํด ๋์.
- In-App Purchase Details: ''Add Language'' ๋ฒํผ์ ํด๋ฆญํด์ ํด๋น ์ธ์ด์ ๋ง๊ฒ ์ถ๊ฐ.
- Language: ์ธ์ด
- Display Name: ์ฌ์ฉ์์๊ฒ ํ์๋๋ ์ํ ์ด๋ฆ.
- Description: ์ํ ์ค๋ช . ์ค์ ์ฌ์ฉ์์๊ฒ๋ ๋ณด์ด์ง ์๋ ๋ฏ.
- Publication Name: ๊ตฌ๋ ์ํ์ธ ๊ฒฝ์ฐ, ๋งค๊ฑฐ์ง์ด๋ ๋ด์ค ๋งค์ฒด์ ํ์ดํ.
- Review Notes (Optional)
- Screenshot for Review : ๊ฒ์ ๊ณผ์ ์์, ๊ฒ์์๊ฐ ์ฐธ๊ณ ํ ์ ์๋ ์คํฌ๋ฆฐ ์ท.{{ note | ์ฐธ๊ณ | In-app ๊ฒฐ์ฌ์, ๊ฒฐ์ฌ๋ฅผ ์งํํ๊ธฐ ์ ์ ์ฌ์ฉ์์๊ฒ ํ์ธ์ ์๊ตฌํ๋ ๊ณผ์ ์ ํ๋ฒ ๋ฐ์์ฃผ๋๊ฒ ์ข๋ค.
๋ณดํต yes/no ํ์ ์ ํ๋ ๋์์ฃผ๋ฉด ๋ ๊ฒ์ด๋ค. }}
์์ดํฐ์์ ๊ฒฐ์ ํ๋ ๋ฐฉ์์ด๋ฏ๋ก ๋ก๊ทธ์ธ ์ด๋ฉ์ผ ๊ณ์ ์ผ๋ก ์์๋ก ์์ฑ ํจ. ๋จ์ง, ์ด๋ฏธ iTunes ๊ณ์ ์ผ๋ก ๋ฑ๋ก ๋ ๊ณ์ ์ ์์ฑ ํ๋ฉด ์๋จ. ์ค์ ๊ฒฐ์ ๋ ์ ์์.
- iTunes Connect > Manage Users
- Test User ํด๋ฆญ.
- Add New user ํด๋ฆญ.
- ํญ๋ชฉ๋ค์ ์ ์ฑ์ฐ๊ณ Save ํด๋ฆญ.
- ์ด๋ ๋ฑ๋กํ E-mail์ด ์ฌ์ฉ์ ID๊ฐ ๋๋ค.
- In-App ๊ฒฐ์ฌ ํ ์คํธ์ ์์, App Store๋ฅผ ๋ก๊ทธ ์์ ์ํ๋ก ๋ง๋ค์ด ๋๋๋ค.
- App์ ์ค์นํ๋ค.
- App์์ ๊ฒฐ์ฌ๋ฅผ ์๋ํ๋ค. ์ด๋ ์๋๋ฐ์ค ํ ์คํธ ์์ด๋๋ก ๋ก๊ทธ์ธ ํ๋ค.
- ์ฐธ๊ณ : In App Purchase Programming Guide์์ Adding a Store to Your Application ๋ด์ฉ
#import <StoreKit/StoreKit.h>
@interface XXXXX : NSObject <SKPaymentTransactionObserver, KProductsRequestDelegate> {
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
- ์ค์ ์ํ๊ฒฐ์ ์งํ์ ๋ฐ๋ฅธ ๊ฒฐ๊ณผ ๋ธ๋ฆฌ๊ฒ์ดํธ ๋ฐ์ ์ํด. (ํ๋ก๊ทธ๋จ ์ด๊ธฐํ๋ ๋ก๋์, ๋๋ ๊ฒฐ์ ๋ฒํผ ํด๋ฆญ์ ์ฒ๋ฆฌ)
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
if ([SKPaymentQueue canMakePayments])
{
... // Display a store to the user.
}
else
{
... // Warn the user that purchases are disabled.
}
- (void)requestProductData
{
SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject: @"com.testman.inappdemo.001"]];
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *myProduct = response.products;
// populate UI
[request autorelease];
}
SKPayment *payment = [SKPayment paymentWithProductIdentifier:@"com.testman.inappdemo.001"];
[[SKPaymentQueue defaultQueue] addPayment:payment];
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
// Your application should implement these two methods.
//์๋ต๊ฐ๋ฅ [self recordTransaction: transaction];
//์๋ต๊ฐ๋ฅ [self provideContent: transaction.payment.productIdentifier];
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
//์๋ต ๊ฐ๋ฅ[self recordTransaction: transaction];
//์๋ต๊ฐ๋ฅ [self provideContent: transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
๋ค์์ Unity ์ฐ๋์ ์ํด ๋ง๋ ๊ตฌํ ์ํ ์ฝ๋์ด๋ค.
//
// iOSPlugins.h
//
#ifndef Unity_iPhone_iOSPlugins_h
#define Unity_iPhone_iOSPlugins_h
#import <StoreKit/StoreKit.h>
@interface InappObserver : NSObject<SKPaymentTransactionObserver, SKPaymentTransactionObserver> {
}
#pragma mark Store Kit
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
-(void)completeTransaction: (SKPaymentTransaction *)transaction;
-(void)failedTransaction: (SKPaymentTransaction *)transaction;
@end
#endif
//
// iOSPlugins.m
//
#import <Foundation/Foundation.h>
#include "iOSPlugins.h"
InappObserver* g_inapp;
void initPlugins()
{
g_inapp = [[InappObserver alloc] init];
if ([SKPaymentQueue canMakePayments]) {
[[SKPaymentQueue defaultQueue] addTransactionObserver: g_inapp];
}
}
void purchase_ios(int product_id, const char* market_pid)
{
NSLog(@"purchase_ios: %d, %s", product_id, market_pid);
NSString* pid = [NSString stringWithUTF8String: market_pid];
//-- 1.์ํ์์ฒญ
NSSet *set = [NSSet setWithObjects:pid, nil];
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
productsRequest.delegate = g_inapp;
[productsRequest start];
}
@implementation InappObserver
#pragma mark Store Kit
//-- 2.์ํ์๋ต
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
if ([response.products count] > 0) {
SKProduct *product = [response.products objectAtIndex:0];
NSLog(@"Title:%@", product.localizedTitle);
NSLog(@"Description:%@", product.localizedDescription);
NSLog(@"Price:%@", product.price);
//-- 3.๊ตฌ๋งค์์ฒญ
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment: payment];
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
//-- 4.๊ตฌ๋งค์๋ต
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
// Call the appropriate custom method for the transaction state.
case SKPaymentTransactionStateFailed:
NSLog(@"่ณผๅ
ฅๅคฑๆ: %@, %@", transaction.transactionIdentifier, transaction.error);
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStatePurchased:
NSLog(@"่ณผๅ
ฅๆๅ");
[self completeTransaction:transaction];
const char* product_id = [[transaction.payment productIdentifier] UTF8String];
UnitySendMessage("ShopList", "storeBuyCallBack", product_id);
break;
case SKPaymentTransactionStateRestored:
// ใชในใใขๅฆ็
// NSLog(@"ไปฅๅใซ่ณผๅ
ฅใใๆฉ่ฝใๅพฉๅ
");
//[queue finishTransaction:transaction];
// TODO: ใขใคใใ ่ณผๅ
ฅใใๅฆ็๏ผใขใใใฐใฌใผใ็ใฎๆฉ่ฝๅถ้่งฃ้คๅฆ็็ญ๏ผ
break;
default:
// For debugging
NSLog(@"Unexpected transaction state %@", @(transaction.transactionState));
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
NSString *receiptStr = [transaction.transactionReceipt base64Encoding];
NSLog(@"completeTransaction");
NSLog(@"Trasaction Identifier : %@", transaction.transactionIdentifier);
NSLog(@"Trasaction Date : %@", [transaction.transactionDate description]);
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
//LOG(@"Trasaction transactionReceipt : %@", receiptStr);
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
NSLog(@"failedTransaction");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
const char* msg = [[transaction.error localizedDescription] UTF8String];
UnitySendMessage("ShopList", "storeFailCallBack", msg);
}
@end