/**************************************************************************** Copyright (c) 2013 cocos2d-x.org http://www.cocos2d-x.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ #import "IOSIAP.h" #import "ParseUtils.h" #define OUTPUT_LOG(...) if (self.debug) NSLog(__VA_ARGS__); @implementation IOSIAP @synthesize debug; NSMutableSet * _productIdentifiers; NSMutableArray *_productArray; //productsRequest; NSMutableArray* _productsRequest; //SKProductsRequest * _productsRequest; //productTransation NSArray * _transactionArray; - (id)init { if ( self = [super init] ){ // NSLog(@"addTransactionObserver"); [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; } return self; } - (void) restoreProducts { [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } -(void) configDeveloperInfo: (NSMutableDictionary*) cpInfo{ } - (void) payForProduct: (NSMutableDictionary*) cpInfo{ NSString * pid = [cpInfo objectForKey:@"productId"]; SKProduct *skProduct = [self getProductById:pid]; if(skProduct){ SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:skProduct]; [[SKPaymentQueue defaultQueue] addPayment:payment]; // OUTPUT_LOG(@"add PaymentQueue"); } } - (void) payForProductWithStr: (NSString*) pid{ SKProduct *skProduct = [self getProductById:pid]; if(skProduct){ SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:skProduct]; [[SKPaymentQueue defaultQueue] addPayment:payment]; // OUTPUT_LOG(@"add PaymentQueue"); } } - (void) setDebugMode: (id) _debug{ bool mode = [_debug boolValue]; // NSLog(@"set debug mode %i", mode); [self setDebug:mode]; } - (NSString*) getSDKVersion{ return @"1.0"; } - (NSString*) getPluginVersion{ return @"1.0"; } /*------------------------IAP functions-------------------------------*/ -(void)setServerMode{ //do not support client mode from 2016-8-19 } -(void)requestProducts:(NSString*) paramMap{ NSArray *producIdArray = [paramMap componentsSeparatedByString:@","]; if (!_productIdentifiers) { _productIdentifiers = [[NSMutableSet alloc] init]; } NSMutableSet* set = [[NSMutableSet alloc] initWithArray:producIdArray]; for (NSString* identifier in producIdArray) { if ([_productIdentifiers containsObject:identifier]) [set removeObject:identifier]; else{ [_productIdentifiers addObject:identifier]; } } // OUTPUT_LOG(@"param is %@, %@, %@",paramMap, _productIdentifiers, set); if ([set count] > 0 ) { // OUTPUT_LOG(@"param is %@",set); if (!_productsRequest) _productsRequest = [[NSMutableArray alloc] init]; SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set]; productsRequest.delegate = self; [productsRequest start]; [_productsRequest addObject:productsRequest]; } } -(NSString *)parseProductToString:(NSArray *) products{ return @"1"; } -(SKProduct *)getProductById:(NSString *)productid{ for (SKProduct * skProduct in _productArray) { if([skProduct.productIdentifier isEqualToString:productid]){ return skProduct; } } return NULL; } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { // OUTPUT_LOG(@"Failed to load list of products."); [IAPWrapper onPayResult:self withRet:RequestFail withMsg:error.localizedDescription]; [_productIdentifiers removeAllObjects]; [_productsRequest removeObject:request]; // _productsRequest = nil; } //SKProductsRequestDelegate needed - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ if (!_productArray) _productArray = [[NSMutableArray alloc] init]; // _productArray = response.products; NSMutableArray* append = [[NSMutableArray alloc] init]; NSArray * skProducts = response.products; for (SKProduct * skProduct in skProducts) { if (![self getProductById:skProduct.productIdentifier]) [append addObject:skProduct]; } [_productArray addObjectsFromArray:append]; [IAPWrapper onRequestProduct:self withRet:RequestSuccees withProducts:skProducts]; // if(skProducts.count == 1) // { // NSLog(@"pay search product"); // [IAPWrapper onRequestProduct:self withRet:RequestSuccees withProducts:skProducts]; // } // else // { // NSLog(@"get products"); // [IAPWrapper onRequestProduct:self withRet:RequestProductsSuccess withProducts:skProducts]; // } [_productsRequest removeObject:request]; } //SKPaymentTransactionObserver needed - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ #if TARGET_OS_OSX _transactionArray = [transactions copy]; #else _transactionArray = transactions; #endif // NSLog(@"updatedTransactions"); for (SKPaymentTransaction * transaction in transactions) { // NSLog(@"transaction %@ - state :%i",transaction.payment.productIdentifier, (int)(transaction.transactionState)); switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: [self completeTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: [self restoreTransaction:transaction]; break; case SKPaymentTransactionStatePurchasing: break; default: [self failedTransaction:transaction]; break; } }; } - (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error NS_AVAILABLE_IOS(3_0) { NSLog(@"paymentQueue error:"); } - (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads NS_AVAILABLE_IOS(6_0) { NSLog(@"paymentQueue updatedDownloads:"); } - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions NS_AVAILABLE_IOS(3_0) { NSLog(@"paymentQueue removedTransactions:"); } - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue NS_AVAILABLE_IOS(3_0) { // NSLog(@"paymentQueueRestoreCompletedTransactionsFinished"); [self paymentQueue:queue updatedTransactions:[queue transactions]]; } - (NSString*) getMsg:(SKPaymentTransaction*) transaction receipt:(NSString*) receipt { NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; [dict setObject:receipt forKey:@"receipt"]; if(transaction.payment.productIdentifier != nil) { [dict setObject:transaction.payment.productIdentifier forKey:@"identifier"]; } if(transaction.transactionIdentifier != nil) { [dict setObject:transaction.transactionIdentifier forKey:@"transaction_id"]; } NSString *info = [ParseUtils NSDictionaryToNSString:dict]; return info; } - (void) consume:(NSString*) pid { NSLog(@"consume %@",pid); if (![self finishTransaction: pid withState:SKPaymentTransactionStatePurchased]){ NSLog(@"consume failed"); [self finishTransaction: pid withState:SKPaymentTransactionStateFailed]; } } - (void)completeTransaction:(SKPaymentTransaction *)transaction { NSString *receipt = nil; #if TARGET_OS_OSX NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *recData = [[NSData dataWithContentsOfURL:receiptURL] base64EncodedDataWithOptions:0]; receipt = [[NSString alloc] initWithData:recData encoding:NSUTF8StringEncoding]; if (!receipt) { // receipt = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length]; } #else if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { // iOS 6.1 or earlier. // Use SKPaymentTransaction's transactionReceipt. receipt = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length]; } else { // iOS 7 or later. NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *recData = [[NSData dataWithContentsOfURL:receiptURL] base64EncodedDataWithOptions:0]; receipt = [[NSString alloc] initWithData:recData encoding:NSUTF8StringEncoding]; if (!receipt) { receipt = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length]; } } #endif NSString* info = [self getMsg:transaction receipt:receipt]; [IAPWrapper onPayResult:self withRet:PaymentTransactionStatePurchased withMsg:info]; } - (void)restoreTransaction:(SKPaymentTransaction *)transaction { // [self finishTransaction:transaction.payment.productIdentifier withState:SKPaymentTransactionStateRestored]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; NSString* info = [self getMsg:transaction receipt:@""]; [IAPWrapper onPayResult:self withRet:PaymentTransactionStateRestored withMsg:info]; } - (void)failedTransaction:(SKPaymentTransaction *)transaction { // OUTPUT_LOG(@"failedTransaction..."); if (transaction.error.code != SKErrorPaymentCancelled) { // OUTPUT_LOG(@"Transaction error: %@", transaction.error.localizedDescription); #if TARGET_OS_OSX dispatch_async(dispatch_get_main_queue(), ^(void){ NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText:@"Unsuccessful Purchase"]; [alert setInformativeText:transaction.error.localizedDescription]; [alert addButtonWithTitle:@"OK"]; [alert setAlertStyle:NSAlertStyleWarning]; NSWindow *mainWindow = [[NSApplication sharedApplication] keyWindow]; if (mainWindow == nil) { mainWindow = [[[NSApplication sharedApplication] windows] firstObject]; } [alert beginSheetModalForWindow:mainWindow completionHandler:^(NSModalResponse returnCode) { // Handle button click event if needed }]; }); #else [[[UIAlertView alloc] initWithTitle:@"Unsuccessful Purchase" message:transaction.error.localizedDescription delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil] show]; #endif } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; // NSString* info = [self getMsg:transaction receipt:@""]; // [IAPWrapper onPayResult:self withRet:PaymentTransactionStateFailed withMsg:info]; [IAPWrapper onPayError:self withRet:PaymentTransactionStateFailed withError:transaction.error]; } - (void)restoreCompletedTransactions { [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; } - (void) finishAllTransactions { for(SKPaymentTransaction *transaction in _transactionArray){ [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } } - (int) getTransactionCount { int count = 0; for(SKPaymentTransaction *transaction in _transactionArray){ count = count + 1; } return count; } - (BOOL) finishTransaction:(NSString *)productId withState:(SKPaymentTransactionState)state{ SKPaymentTransaction *transaction = [self getTranscationByProductId:productId state:state]; if(transaction){ NSString* info = [self getMsg:transaction receipt:@""]; [IAPWrapper onPayResult:self withRet:PaymentTransactionConsumeSucess withMsg:info]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; return true; } else { NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; [dict setObject:@"Get Transaction Failed" forKey:@"error"]; [dict setObject:productId forKey:@"identifier"]; NSString *info = [ParseUtils NSDictionaryToNSString:dict]; [IAPWrapper onPayResult:self withRet:PaymentTransactionConsumeFailed withMsg:info]; return false; } } -(SKPaymentTransaction *) getTranscationByProductId:(NSString *)productId state:(SKPaymentTransactionState) state{ #if TARGET_OS_OSX if (_transactionArray != nil && _transactionArray.count > 0) { for(SKPaymentTransaction *tran in _transactionArray){ if([tran.payment.productIdentifier isEqualToString:productId]){ return tran; } } } #else for(SKPaymentTransaction *tran in _transactionArray){ if([tran.payment.productIdentifier isEqualToString:productId]){ return tran; } } #endif return NULL; } - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length { static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; uint8_t *output = (uint8_t *)data.mutableBytes; for (NSInteger i = 0; i < length; i += 3) { NSInteger value = 0; for (NSInteger j = i; j < (i + 3); j++) { value <<= 8; if (j < length) { value |= (0xFF & input[j]); } } NSInteger index = (i / 3) * 4; output[index + 0] = table[(value >> 18) & 0x3F]; output[index + 1] = table[(value >> 12) & 0x3F]; output[index + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '='; output[index + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '='; } return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] ; } @end