/*************************************************************************/ /* in_app_store.mm */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* 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. */ /*************************************************************************/ #ifdef STOREKIT_ENABLED #include "in_app_store.h" extern "C" { #import }; @interface SKProduct (LocalizedPrice) @property (nonatomic, readonly) NSString *localizedPrice; @end //----------------------------------// // SKProduct extension //----------------------------------// @implementation SKProduct (LocalizedPrice) - (NSString *)localizedPrice { NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; [numberFormatter setLocale:self.priceLocale]; NSString *formattedString = [numberFormatter stringFromNumber:self.price]; [numberFormatter release]; return formattedString; } @end InAppStore* InAppStore::instance = NULL; void InAppStore::_bind_methods() { ObjectTypeDB::bind_method(_MD("request_product_info"),&InAppStore::request_product_info); ObjectTypeDB::bind_method(_MD("purchase"),&InAppStore::purchase); ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&InAppStore::get_pending_event_count); ObjectTypeDB::bind_method(_MD("pop_pending_event"),&InAppStore::pop_pending_event); }; @interface ProductsDelegate : NSObject { }; @end @implementation ProductsDelegate - (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProductsResponse*)response { NSArray* products = response.products; Dictionary ret; ret["type"] = "product_info"; ret["result"] = "ok"; StringArray titles; StringArray descriptions; RealArray prices; StringArray ids; StringArray localized_prices; for (int i=0; i<[products count]; i++) { SKProduct* product = [products objectAtIndex:i]; const char* str = [product.localizedTitle UTF8String]; titles.push_back(String::utf8(str != NULL ? str : "")); str = [product.localizedDescription UTF8String]; descriptions.push_back(String::utf8(str != NULL ? str : "")); prices.push_back([product.price doubleValue]); ids.push_back(String::utf8([product.productIdentifier UTF8String])); localized_prices.push_back(String::utf8([product.localizedPrice UTF8String])); }; ret["titles"] = titles; ret["descriptions"] = descriptions; ret["prices"] = prices; ret["ids"] = ids; ret["localized_prices"] = localized_prices; StringArray invalid_ids; for (NSString* ipid in response.invalidProductIdentifiers) { invalid_ids.push_back(String::utf8([ipid UTF8String])); }; ret["invalid_ids"] = invalid_ids; InAppStore::get_singleton()->_post_event(ret); [request release]; }; @end Error InAppStore::request_product_info(Variant p_params) { Dictionary params = p_params; ERR_FAIL_COND_V(!params.has("product_ids"), ERR_INVALID_PARAMETER); StringArray pids = params["product_ids"]; printf("************ request product info! %i\n", pids.size()); NSMutableArray* array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease]; for (int i=0; i { }; @end @implementation TransObserver - (void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*) transactions { printf("transactions updated!\n"); for (SKPaymentTransaction* transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: { printf("status purchased!\n"); String pid = String::utf8([transaction.payment.productIdentifier UTF8String]); InAppStore::get_singleton()->_record_purchase(pid); Dictionary ret; ret["type"] = "purchase"; ret["result"] = "ok"; ret["product_id"] = pid; InAppStore::get_singleton()->_post_event(ret); [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } break; case SKPaymentTransactionStateFailed: { printf("status transaction failed!\n"); String pid = String::utf8([transaction.payment.productIdentifier UTF8String]); Dictionary ret; ret["type"] = "purchase"; ret["result"] = "error"; ret["product_id"] = pid; InAppStore::get_singleton()->_post_event(ret); [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } break; case SKPaymentTransactionStateRestored: { printf("status transaction restored!\n"); String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]); InAppStore::get_singleton()->_record_purchase(pid); [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } break; default: printf("status default %i!\n", (int)transaction.transactionState); break; }; }; }; @end Error InAppStore::purchase(Variant p_params) { ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE); if (![SKPaymentQueue canMakePayments]) return ERR_UNAVAILABLE; printf("purchasing!\n"); Dictionary params = p_params; ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER); NSString *pid = [[[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()] autorelease]; SKPayment *payment = [SKPayment paymentWithProductIdentifier:pid]; SKPaymentQueue* defq = [SKPaymentQueue defaultQueue]; [defq addPayment:payment]; printf("purchase sent!\n"); return OK; }; int InAppStore::get_pending_event_count() { return pending_events.size(); }; Variant InAppStore::pop_pending_event() { Variant front = pending_events.front()->get(); pending_events.pop_front(); return front; }; void InAppStore::_post_event(Variant p_event) { pending_events.push_back(p_event); }; void InAppStore::_record_purchase(String product_id) { String skey = "purchased/"+product_id; NSString* key = [[[NSString alloc] initWithUTF8String:skey.utf8().get_data()] autorelease]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key]; [[NSUserDefaults standardUserDefaults] synchronize]; }; InAppStore* InAppStore::get_singleton() { return instance; }; InAppStore::InAppStore() { ERR_FAIL_COND(instance != NULL); instance = this; TransObserver* observer = [[TransObserver alloc] init]; [[SKPaymentQueue defaultQueue] addTransactionObserver:observer]; }; InAppStore::~InAppStore() { }; #endif