Skip to content

Instantly share code, notes, and snippets.

Created April 4, 2012 19:00
Show Gist options
  • Save anonymous/2304762 to your computer and use it in GitHub Desktop.
Save anonymous/2304762 to your computer and use it in GitHub Desktop.
InApp Manager
//
// InAppPurchaseManager.m
//
// Created by Jiří Třečák.
//
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
// Add a couple notifications sent out when the transaction completes
#define INAPP_PURCHASE_SUCCEEDED @"inappPurchaseSucceeded"
#define INAPP_PURCHASE_FAILED @"inappPurchaseFailed"
#define INAPP_STORE_FETCHED_DATA @"inappStoreFetchedData"
@interface InAppPurchaseManager : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver> {
NSArray *products;
SKProductsRequest *productsRequest;
}
+ (InAppPurchaseManager *)sharedInAppPurchaseManager;
// public methods
- (void)loadStore;
- (BOOL)canMakePurchases;
- (void)requestProductData;
- (void)kickOffPurchaseForTier:(int)aTier ;
@end
//
// InAppPurchaseManager.m
//
// Created by Jiří Třečák.
//
#import "InAppPurchaseManager.h"
#import "SynthesizeSingleton.h"
// All payment keys
#define INAPP_MAIN_IDENTIFIER @"com.myCompanyIdentifier.issueT%d"
#define INAPP_MAX_TIER 3
@implementation InAppPurchaseManager
SYNTHESIZE_SINGLETON_FOR_CLASS(InAppPurchaseManager)
// InAppPurchaseManager.m
- (void)requestProductData {
NSMutableSet *productIdentifiers = [[NSMutableSet alloc] initWithCapacity:INAPP_MAX_TIER];
// Create all identifiers
for (int i = 1; i <= INAPP_MAX_TIER; i++) {
[productIdentifiers addObject:[NSString stringWithFormat:INAPP_MAIN_IDENTIFIER,i]];
}
// Request all products
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
// Start request
[productsRequest start];
[productIdentifiers release];
}
#pragma mark -
#pragma mark SKProductsRequestDelegate methods
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
products = [[NSArray alloc] initWithArray:[response products]];
// Test valid product ID's (approved for sale)
if (products != nil && [products count] > 0) {
int i = 1;
for (SKProduct *product in products) {
NSLog(@"%d. product title: %@", i, product.localizedTitle);
NSLog(@"%d. product description: %@", i, product.localizedDescription);
NSLog(@"%d. product price: %@", i, product.price);
NSLog(@"%d. store item initialization successful, identifier: %@", i, product.productIdentifier);
i++;
}
}
// Invalid product ID's (proly not approved yet?)
for (NSString *invalidProductId in response.invalidProductIdentifiers) {
NSLog(@"Invalid product id: %@" , invalidProductId);
}
}
// Store initialization
- (void)loadStore {
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestProductData];
}
// call this before making a purchase
- (BOOL)canMakePurchases {
return [SKPaymentQueue canMakePayments];
}
- (void)kickOffPurchaseForTier:(int)aTier {
NSString *productIdentifier = [NSString stringWithFormat:INAPP_MAIN_IDENTIFIER,aTier];
for (SKProduct *product in products) {
// Make payment if identifier is valid
if ([product.productIdentifier compare:productIdentifier] == NSOrderedSame) {
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
return;
}
}
// send out a notification for the failed transaction
[[NSNotificationCenter defaultCenter] postNotificationName:INAPP_PURCHASE_FAILED object:aIssue userInfo:nil];
}
#pragma -
#pragma Purchase helpers
// saves a record of the transaction -> remote?
- (void)recordTransaction:(SKPaymentTransaction *)transaction {
NSError *error = nil;
// Write own save code here (core data manipulation etc.
if (error != nil) {
NSLog(@"Error while recording transaction %@",[error localizedDescription]);
}
}
// Remove transaction from the queue and posts a notification with the transaction result
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful {
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:transaction, @"transaction" , nil];
if (wasSuccessful) {
// send out a notification that we’ve finished the transaction
[[NSNotificationCenter defaultCenter] postNotificationName:INAPP_PURCHASE_SUCCEEDED object:nil userInfo:userInfo];
NSLog(@"transaction successful");
} else {
// send out a notification for the failed transaction
[[NSNotificationCenter defaultCenter] postNotificationName:INAPP_PURCHASE_FAILED object:nil userInfo:userInfo];
NSLog(@"transaction failed");
}
}
// called when the transaction was successful
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
[self recordTransaction:transaction];
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has been restored and and successfully completed
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
[self recordTransaction:transaction.originalTransaction];
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled) {
// Unknown error
[self finishTransaction:transaction wasSuccessful:NO];
} else {
// User cancelled
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
[[NSNotificationCenter defaultCenter] postNotificationName:INAPP_PURCHASE_FAILED object:lastKnownIssue userInfo:nil];
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
// called when the transaction status is updated
- (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];
break;
default:
break;
}
}
}
@end
//
// SynthesizeSingleton.h
// CocoaWithLove
//
// Created by Matt Gallagher on 20/10/08.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
\
static classname *shared##classname = nil; \
\
+ (classname *)shared##classname { \
\
@synchronized(self) { \
\
if (shared##classname == nil) { \
shared##classname = [[self alloc] init]; \
} \
} \
\
return shared##classname; \
} \
\
+ (id)alloc { \
\
@synchronized(self) { \
\
if (shared##classname == nil) { \
shared##classname = [super alloc]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
\
- (id)retain { \
\
return self; \
} \
\
\
- (oneway void)release { \
\
if (shared##classname != nil) { \
\
shared##classname = nil; \
[shared##classname release]; \
} \
if (self != nil) { \
\
[self dealloc]; \
self = nil; \
[super dealloc]; \
} \
} \
\
- (id)autorelease { \
\
return self; \
}
// Usage is pretty easy:
- (void)use {
// Create shared instance
manager = [InAppPurchaseManager sharedInAppPurchaseManager];
// Load store
[manager loadStore];
// Make purchase (this has to be done AFTER store is initialized, which can take about 2 - 3 sec (async))
[manager kickOffPurchaseForTier:1];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment