Unity3D Integration iOS In App Purchase
cocos2d-x에 iOS 인앱 연동 정리했던것과 내용은 거의 같습니다. 안드로이드 인앱 빌링이 V3로 되면서 많이 바뀐것에 비하면 iOS는 바뀌지 않은 것 같네요. 사실 cocos2d-x에서 작업한 내용을 그대로 가져와 유니티3d에 맞게 조금 수정해서하니 잘 되길래 iOS 인앱 관련 최신 레퍼런스는 살펴보지도 못했네요.
위 그림과 같이 iOS 플러그인을 만듭니다. 아래 소스 부분은 몇몇 유니티3D 관련 부분 빼고는 기존 cocos2d-x에서 했던 것과 비슷합니다.
// iOSInAppPlugin.h
// iOSInAppPlugin.mm
completeTransaction 에서 빌트인 모델이라고 주석된 부분은 기존에 cocos2d-x에 정리했던 것과 같은 클라이언트만 가지고 인앱을 처리하는 부분이고 서버와 인앱 영수증 처리를 하려면 transaction.transactionReceipt.bytes 를 Base64 Encode등의 처리를 거쳐서 애플 서버와 인증 확인을 해줘야합니다. 관련 소스는 몇몇 부분이 비워 있긴 하지만 애플이 만든게 있으니 링크를 통해 확인하시기 바랍니다. 추후 서버측 작업을 제가 하게되면 그때 또 정리하기로 하고 지금은 자체 서버에서 받아 처리하고 있어서 일단 유니티3D에 넘겨 주기만 하고 있습니다.
// iOSManager.cs
// TestGUI.cs
인앱 초기화 후 아이템 구매 시도중인 화면입니다.
- iOS 개발 인증서 만들어 요청 및 설치하기
- iOS 디바이스에서 테스트하기 위한 디바이스 등록 및 앱 ID, 개발 프로비저닝 프로파일 생성
- 애플 앱스토어에 앱 등록해보기 1. Prepare for Upload ~ Waiting for Uplod
- iOS 인앱 결제 코드 구현 전 웹 설정 작업
위 그림과 같이 iOS 플러그인을 만듭니다. 아래 소스 부분은 몇몇 유니티3D 관련 부분 빼고는 기존 cocos2d-x에서 했던 것과 비슷합니다.
// iOSInAppPlugin.h
#import <StoreKit/StoreKit.h>
@interface iOSInAppPlugin : NSObject<
///< 상품 정보를 얻어올 때 쓰는 딜리게이트
SKProductsRequestDelegate,
///< 상품 구매 관련 옵저버
SKPaymentTransactionObserver>
+ (iOSInAppPlugin*) sharediOSInAppPlugin;
- (BOOL) initInApp;
- (void) requestProductData:(NSString*)strProductId;
- (void) completeTransaction:(SKPaymentTransaction*)transaction;
- (void) restoreTransaction:(SKPaymentTransaction*)transaction;
- (void) failedTransaction:(SKPaymentTransaction*)transaction;
- (void) restoreCompletedTransactions;
@end
// iOSInAppPlugin.mm
#import "iOSInAppPlugin.h"
extern "C"
{
void iOSInAppInit()
{
[[iOSInAppPlugin sharediOSInAppPlugin] initInApp];
}
void iOSBuyItem(const char* pszProductId)
{
NSString* strProductId = [NSString stringWithUTF8String:pszProductId];
[[iOSInAppPlugin sharediOSInAppPlugin] requestProductData:strProductId];
}
void iOSRestoreCompletedTransactions()
{
[[iOSInAppPlugin sharediOSInAppPlugin] restoreCompletedTransactions];
}
}
@implementation iOSInAppPlugin
+ (iOSInAppPlugin*) sharediOSInAppPlugin
{
static iOSInAppPlugin* pInstance;
if(pInstance == NULL)
{
pInstance = [[iOSInAppPlugin alloc] init];
}
return pInstance;
}
- (BOOL) initInApp
{
///< 인앱 결제 시스템을 사용 가능한지 체크
if( [SKPaymentQueue canMakePayments] == NO )
return NO;
///< Product 결제 진행에 필요한 딜리게이트 등록
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSLog(@"InAppPurchase init OK");
return true;
}
///< 아이템 정보 요청
- (void) requestProductData:(NSString*)strProductId
{
///< iTunes Connect에 설정한 Product ID들
NSSet* productIdentifiers = [NSSet setWithObject:strProductId];
SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
request.delegate = self;
[request start];
NSLog(@"requestProductData %@", strProductId);
}
///< 아이템 정보 요청 결과 callback
- (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog( @"InAppPurchase didReceiveResponse" );
for( SKProduct* product in response.products )
{
if( product != nil )
{
NSLog(@"InAppPurchase Product title: %@", product.localizedTitle);
NSLog(@"InAppPurchase Product description: %@", product.localizedDescription);
NSLog(@"InAppPurchase Product price: %@", product.price);
//product.priceLocale
NSLog(@"InAppPurchase Product id: %@", product.productIdentifier);
///< 구매 요청
SKPayment* payment = [SKPayment paymentWithProduct:product];
//payment.quantity = 10;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
[request release];
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(@"InAppPurchase Invalid product id: %@", invalidProductId);
}
}
///< 새로운 거래가 발생하거나 갱신될 때 호출된다.
- (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
///< 서버에 거래 처리중
case SKPaymentTransactionStatePurchasing:
NSLog(@"InAppPurchase SKPaymentTransactionStatePurchasing");
break;
///< 구매 완료
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
///< 거래 실패 또는 취소
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
///< 재구매
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
}
}
}
- (void) completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"InAppPurchase completeTransaction");
NSLog(@"InAppPurchase Transaction Identifier : %@", transaction.transactionIdentifier );
NSLog(@"InAppPurchase Transaction Data : %@", transaction.transactionDate );
///< 구매 완료 후 아이템 인벤등 게임쪽 후 처리 진행
/* 빌트 인 모델
const char* pszProductId = [[[transaction payment] productIdentifier] UTF8String];
UnitySendMessage("iOSManager", "ResultBuyItem", pszProductId);
*/
NSString* strReceipt = [[NSString alloc] initWithBytes:transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length encoding:NSUTF8StringEncoding];
UnitySendMessage("iOSManager", "ResultBuyItem", [strReceipt UTF8String]);
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void) restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"InAppPurchase restoreTransaction");
const char* pszRestoreProductId = [transaction.originalTransaction.payment.productIdentifier UTF8String];
UnitySendMessage("iOSManager", "ResultRestoreItem", pszRestoreProductId);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void) failedTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"InAppPurchase failedTransaction.");
const char* pszResult = 0;
if( transaction.error.code != SKErrorPaymentCancelled )
{
pszResult = "faileIAP";
NSLog(@"InAppPurchase failedTransaction SKErrorDomain - %d", transaction.error.code );
}
else
{
pszResult = "cancelIAP";
NSLog(@"InAppPurchase failedTransaction SKErrorPaymentCancelled");
}
UnitySendMessage("iOSManager", "ResultBuyItem", pszResult);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
// 비소모성 아이템 복원 요청
- (void) restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
@end
completeTransaction 에서 빌트인 모델이라고 주석된 부분은 기존에 cocos2d-x에 정리했던 것과 같은 클라이언트만 가지고 인앱을 처리하는 부분이고 서버와 인앱 영수증 처리를 하려면 transaction.transactionReceipt.bytes 를 Base64 Encode등의 처리를 거쳐서 애플 서버와 인증 확인을 해줘야합니다. 관련 소스는 몇몇 부분이 비워 있긴 하지만 애플이 만든게 있으니 링크를 통해 확인하시기 바랍니다. 추후 서버측 작업을 제가 하게되면 그때 또 정리하기로 하고 지금은 자체 서버에서 받아 처리하고 있어서 일단 유니티3D에 넘겨 주기만 하고 있습니다.
// iOSManager.cs
using UnityEngine;
using System.Runtime.InteropServices;
public class iOSManager : MonoBehaviour
{
static iOSManager _instance;
private string strLog = "Unity3D iOS In App Purchase Sample";
public string strPostMsg = string.Empty;
[DllImport("__Internal")]
private static extern void iOSInAppInit();
[DllImport("__Internal")]
private static extern void iOSBuyItem(string strProductId);
[DllImport("__Internal")]
private static extern void iOSRestoreCompletedTransactions();
public static iOSManager GetInstance()
{
if( _instance == null )
{
_instance = new GameObject("iOSManager").AddComponent<iOSManager>();
}
return _instance;
}
public void InAppInit()
{
iOSInAppInit();
}
public void BuyItem(string strProductId)
{
iOSBuyItem(strProductId);
}
public void ResultBuyItem(string strResult)
{
// strResult is transaction.transactionReceipt or faileIAP or cancelIAP.
SetLog("ResultBuyItem " + strResult);
}
public void RestoreCompletedTransactions()
{
iOSRestoreCompletedTransactions();
}
public void ResultRestoreItem(string strRestoreItemId)
{
SetLog("ResultRestoreItem " + strRestoreItemId);
}
public void SetLog(string _strLog)
{
strLog = _strLog;
}
public string GetLog()
{
return strLog;
}
}
void OnGUI()
{
float fYpos = 0;
GUI.Label(new Rect(0, fYpos, 400, 50), iOSManager.GetInstance().GetLog());
fYpos += 50;
if (GUI.Button (new Rect(0, fYpos, 100, 50), "InApp Init") == true)
{
iOSManager.GetInstance().InAppInit();
}
fYpos += 50;
if (GUI.Button (new Rect(0, fYpos, 100, 50), "TestItem1") == true)
{
iOSManager.GetInstance().BuyItem("inapptestitem1");
}
fYpos += 50;
if (GUI.Button (new Rect(0, fYpos, 100, 50), "Restore") == true)
{
iOSManager.GetInstance().RestoreCompletedTransactions();
}
}
댓글
댓글 쓰기