IWT - in-app purchase code
by Scott Wilson-Billing · in iTorque 2D · 01/05/2011 (11:47 pm) · 14 replies
Guys,
Just a code dump - Johnny was looking for some code - hopefully helpful to someone ;-)
I can't take the credit as the code was written by my colleague An.
iWTIAPHandler.h
iWTIAPHandler.mm
Usage in Torque ...
iWTIAP.cs
Just a code dump - Johnny was looking for some code - hopefully helpful to someone ;-)
I can't take the credit as the code was written by my colleague An.
iWTIAPHandler.h
//
// iWTIAPHandler.h
// NeoDefender
//
// Created by An Nguyen on 28/05/2010.
// Copyright 2010 Xtremics. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
@interface iWTIAPHandler : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver> {
}
@endiWTIAPHandler.mm
//
// iWTIAPHandler.m
// DeoDefender
//
// Created by An Nguyen on 28/05/2010.
// Copyright 2010 Xtremics. All rights reserved.
//
#import "iWTIAPHandler.h"
#import "Reachability.h"
#include "console/console.h"
//#define FULL_FEATURE_IDENTIFIER @"com.meyume.iwtusa.20000"
@implementation iWTIAPHandler
- (void) initIAP {
NSLog(@"%s", _cmd);
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
- (void) requestProductData:(NSString*)productIdentifier {
NSLog(@"%s", _cmd);
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:productIdentifier]];
request.delegate = self;
[request start];
}
- (BOOL) checkProduct:(NSString*)identifier {
NSNumber *iap = [[NSUserDefaults standardUserDefaults] objectForKey:identifier];
return [iap boolValue];
}
- (BOOL) fullFeaturesEnabled:(NSString*)productIdentifier {
BOOL purchased = [self checkProduct:productIdentifier];
return purchased;
}
- (void) recordTransaction:(SKPaymentTransaction*)transaction {
//[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:transaction.originalTransaction.payment.productIdentifier];
}
- (void) provideContent:(NSString*)identifier {
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:identifier];
Con::executef(2, "oniWTIAPBoughtProduct", [identifier cStringUsingEncoding:NSUTF8StringEncoding]);
}
- (void) completeTransaction:(SKPaymentTransaction*)transaction {
[self recordTransaction:transaction];
[self provideContent:transaction.payment.productIdentifier];
[[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) {
// display error
Con::executef(1, "oniWTIAPFailedTransaction");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void) buyProduct:(NSString*)identifier {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:identifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark -
#pragma mark SKProductsRequestDelete methods
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(@"%s", _cmd);
NSArray *products = response.products;
NSLog(@"---- found [%d] products", [products count]);
int count = [products count];
NSString *param = @"";
for (SKProduct *product in products) {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
NSLog(@"------- [%@], [%@], [%@], [%@]", [product localizedTitle], [product localizedDescription], [product productIdentifier], formattedString);
param = [param stringByAppendingFormat:@"%@\t%@\t%@\t%@", [product productIdentifier], [product localizedTitle], [product localizedDescription], formattedString];
}
char param2[3];
param2[0] = '[[628778f975f0e]]';
snprintf(param2, sizeof(param2), "%d", count);
Con::executef(3, "oniWTIAPDidReceiveProducts", [param cStringUsingEncoding:NSUTF8StringEncoding], param2);
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
default:
break;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
Con::executef(1, "oniWTIAPRemovedTransactions");
}
@end
#pragma mark -
#pragma mark C++
iWTIAPHandler *iapHandler;
void initIAPHandler() {
if (!iapHandler) {
iapHandler = [[[iWTIAPHandler alloc] init] retain];
[iapHandler initIAP];
}
}
ConsoleFunction(iWTIAPInit, void, 1, 1, "Init the iWTIAPHandler") {
initIAPHandler();
}
ConsoleFunction(iWTIAPRequestProducts, void, 2, 2, "Request the products from server") {
initIAPHandler();
[iapHandler requestProductData:[NSString stringWithCString:argv[1]]];
}
ConsoleFunction(iWTIAPBuyProduct, void, 2, 2, "Buy a specified product") {
initIAPHandler();
[iapHandler buyProduct:[NSString stringWithCString:argv[1]]];
}
ConsoleFunction(iWTIAPFullFeaturesEnabled, bool, 2, 2, "Check if the full game game features has purchased") {
initIAPHandler();
return [iapHandler fullFeaturesEnabled:[NSString stringWithCString:argv[1]]];
}
ConsoleFunction(iWTIAPInternetConnectionStatus, bool, 1, 1, "Returns true if the internet connection status is available") {
NetworkStatus status = [[Reachability sharedReachability] internetConnectionStatus];
if (status == ReachableViaWiFiNetwork || status == ReachableViaCarrierDataNetwork) {
return true;
}
return false;
}Usage in Torque ...
iWTIAP.cs
//--------------------------------------------------------------------------
// iWT In App Purchase
//--------------------------------------------------------------------------
$IAP::fullFeaturesUnlocked = false;
// Initialize the IWT IAP and load the features that have been unlocked.
function iWTIAP::init(%this) {
iWTIAPInit();
if ($GAMECONFIG::liteVersion) {
$IAP::fullFeaturesUnlocked = iWTIAPFullFeaturesEnabled($iWT::fullGameIdentifier);
$GAMECONFIG::liteVersion = !$IAP::fullFeaturesUnlocked;
}
}
// Returns true if we should show the IAP button
function iWTIAP::showIAPButton(%this) {
return !$IAP::fullFeaturesUnlocked;
}
// Sends request to Store for products details.
function iWTIAP::requestProducts(%this) {
if (!iWTIAPInternetConnectionStatus()) {
networkErrorPanel.visible = true;
IAPGamePanel.visible = false;
return;
}
// the flag to indicate that IAP is launched by user, in case
// the payment resumed from previous transaction and we are not
// at the IAP screen, then this flag is used for determining
// not return to menu.
$iap.requested = true;
iWTIAPRequestProducts($iWT::fullGameIdentifier);
IAPBuyButton.setActive(false);
IAPCancelButton.setActive(true);
IAPStatusText.visible = true;
//IAPStatusText.setText("<just:center>Connecting to store, please wait.\n<color:FFFF00>Connecting...");
IAPText.setText("<just:center><color:FFFFFF>Get the FULL version of\nInvaders World Tour!\n\n<color:09EAF6>Price: <color:FFFF00>(Requesting...)");
//$iap.flashingId = %this.schedule(200, "flashConnectingText");
//$iap.flashingFlag = false;
}
function iWTIAP::flashConnectingText(%this) {
$iap.flashingFlag = !$iap.flashingFlag;
%color = "<color:09EAF6>";
if ($iap.flashingFlag) {
%color = "<color:FFFF00>";
}
IAPStatusText.setText("<just:center>Connecting to store, please wait.\n" @ %color @ "Connecting...");
$iap.flashingId = %this.schedule(200, "flashConnectingText");
}
function iWTIAP::cancelRequestProducts(%this) {
cancel($iap.flashingId);
}
function iWTIAP::payBuyFullGame(%this) {
IAPBuyButton.setActive(false);
IAPCancelButton.setActive(false);
iWTIAPBuyProduct($iap.fullGameProduct.id);
IAPStatusText.visible = true;
//IAPStatusText.setText("<just:center>Connecting to store, please wait...");
}
//--------------------------------------------------------------------------
// iWT in-app purchase - callback
//--------------------------------------------------------------------------
// Is called when received response from IAP store
function oniWTIAPDidReceiveProducts(%response, %count) {
cancel($iap.flashingId);
if (%count == 1) {
$iap.fullGameProduct = new ScriptObject();
$iap.fullGameProduct.id = getField(%response, 0);
$iap.fullGameProduct.title = getField(%response, 1);
$iap.fullGameProduct.desc = getField(%response, 2);
$iap.fullGameProduct.price = getField(%response, 3);
IAPBuyButton.setActive(true);
IAPStatusText.visible = false;
IAPText.setText("<just:center><color:FFFFFF>GET THE FULL VERSION OF INVADERS WORLD TOUR!\n<color:00FF00>Price: <color:FFFF00>" @ $iap.fullGameProduct.price @ "<color:00FF00>.");
}
}
// Is called when succeed a transaction.
function oniWTIAPBoughtProduct(%id) {
if (%id $= $iap.fullGameProduct.id) {
// unlock the game features
$IAP::fullFeaturesUnlocked = true;
$GAMECONFIG::liteVersion = false;
// resume to main menu
$mainMenu.hideIAPPanel();
// hide the buy buttons
buttonBuy.visible = false;
buyHorizontalDivider.visible = false;
// FIX ME - resume/continue the game
}
}
// Is called when one or more transactions removed from queue.
function oniWTIAPRemovedTransactions(%this) {
IAPBuyButton.setActive(true);
IAPCancelButton.setActive(true);
IAPStatusText.visible = false;
}
#2
01/06/2011 (6:27 am)
Very useful - pass the thanks :)
#3
is it for the user buy the app?
for example, when the user click the button, it will go to app store of your game?
01/10/2011 (2:43 pm)
hi scott, i don't know what is IWT :(is it for the user buy the app?
for example, when the user click the button, it will go to app store of your game?
#4
01/10/2011 (2:45 pm)
Hi Apppothk, yes that code will be used to purchase a copy of Invaders World Tour, however, you should be able to change it to work in-app purchase for another application.
#5
i would like to make a function like your (Asiad Puzzle) game, when i click more game,it will show your company game.
then, if i click App Store icon , it will change to apple store of your game
01/12/2011 (4:24 am)
still not understand : ( i would like to make a function like your (Asiad Puzzle) game, when i click more game,it will show your company game.
then, if i click App Store icon , it will change to apple store of your game
#6
I'm trying to make an easy game with DLC (downloadable content), and may be this could be my first approach.
Also, If you could help me on this topic I will be very pleased:
http://www.garagegames.com/community/forums/viewthread/124129
Thanks for the source code Scott, that's great!
02/03/2011 (10:27 am)
Wow, that's really strong code for me. Could you tell me an easy way to apply it to an tgb example Scott?.I'm trying to make an easy game with DLC (downloadable content), and may be this could be my first approach.
Also, If you could help me on this topic I will be very pleased:
http://www.garagegames.com/community/forums/viewthread/124129
Thanks for the source code Scott, that's great!
#7
02/03/2011 (2:06 pm)
You pretty much have a working example there, unfortunately, this is just for "unlocking" a game. To do downloadable content you will need external server and the code will be different, sorry. However, you can probably use that code as the starting point and combine with the Apple docs.
#8
One thing is just new for me is the "iWTIAPHandler.h" and "iWTIAPHandler.mm". Where do I need to put this files? I just have experience with behaviors and common ".cs" files.
Thank you so much for so useful code however!
02/04/2011 (8:36 am)
Yes I will give it a shot. May be when I understand this I will be able to generate alternative ways to make business with it.One thing is just new for me is the "iWTIAPHandler.h" and "iWTIAPHandler.mm". Where do I need to put this files? I just have experience with behaviors and common ".cs" files.
Thank you so much for so useful code however!
#9
06/30/2012 (6:05 am)
I'm sorry it's an old topic but can someone make a more user friendly manual?
#10
06/30/2012 (8:49 am)
@Rami, I believe the IAP stuff is built into 1.5 now, no need to use this code.
#11
06/30/2012 (9:13 am)
I still have 1.4
#12
Are there any docs on how the IAP is built into 1.5? I haven't seen anything in the iTorque2D documentation that shows me how to use it. Any pointers would be very much appreciated - thanks!
07/17/2012 (10:35 pm)
Hey Scott,Are there any docs on how the IAP is built into 1.5? I haven't seen anything in the iTorque2D documentation that shows me how to use it. Any pointers would be very much appreciated - thanks!
#13
07/18/2012 (12:45 am)
@Joe, we are still using our custom solution from 1.3/1.4 (above). I'm sure there must be plenty of devs on here who have used IAP in 1.5?
#14
07/18/2012 (7:04 am)
Thanks Scott. I seem to be building out a nice custom solution for 1.5 so I'll try to use your solution here.
Torque Owner Johnny Vo