I try to make in app purchase on my app. But I have not result when I try to run it in device.
My code is following:
This is object needed for get info about products and purchase it.
.h
#import <Foundation/Foundation.h>
#import "StoreKit/StoreKit.h"
#define kProductsLoadedNotification #"ProductsLoaded"
#define kProductPurchasedNotification #"ProductPurchased"
#define kProductPurchaseFailedNotification #"ProductPurchaseFailed"
#interface StoreManager : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#property (strong, nonatomic) NSMutableSet * _purchasedProducts;
#property (strong, nonatomic) NSArray *products;
#property (strong, nonatomic) NSSet *_productIdentifiers;
#property (strong, nonatomic) SKProductsRequest *request;
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;
- (void)requestProducts;
- (void)buyProduct:(SKProduct *)product;
- (void)completeTransaction:(SKPaymentTransaction *)transaction;
- (void)restoreTransaction:(SKPaymentTransaction *)transaction;
- (void)failedTransaction:(SKPaymentTransaction *)transaction;
- (void)provideContent:(NSString *)productIdentifier;
- (void)recordTransaction:(SKPaymentTransaction *)transaction;
#end
.m
#import "StoreManager.h"
#implementation StoreManager
#synthesize _purchasedProducts;
#synthesize products;
#synthesize _productIdentifiers;
#synthesize request;
// Initializes the request object with the set of product identifiers.
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers{
self = [super init];
if(self){
self._productIdentifiers = productIdentifiers;
NSMutableSet *purchased = [NSMutableSet set];
for(NSString * productId in self._productIdentifiers){
BOOL flag = [[NSUserDefaults standardUserDefaults] boolForKey:productId];
if(flag){
[purchased addObject:productId];
NSLog(#"Previously purchased: %#", productId);
}
NSLog(#"Not purchased: %#", productId);
}
self._purchasedProducts = purchased;
}
return self;
}
// Request info for the product.
- (void)requestProducts{
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:self._productIdentifiers];
self.request.delegate = self;
[self.request start];
}
// Request fail.
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(#"Fail request! Error: %#", error);
}
// Delegate method - did receive responce.
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
self.products = response.products;
self.request = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:self.products];
}
// Buy product.
- (void)buyProduct:(SKProduct *)product{
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
// Payment transactions
- (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];
default:
break;
}
}
}
- (void)recordTransaction:(SKPaymentTransaction *)transaction {
// TODO: Record the transaction on the server side...
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
[self recordTransaction: transaction];
[self provideContent: transaction.payment.productIdentifier];
NSLog(#"completeTransaction...");
}
- (void)provideContent:(NSString *)productIdentifier {
NSLog(#"Toggling flag for: %#", productIdentifier);
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[self._purchasedProducts addObject:productIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchasedNotification object:productIdentifier];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self recordTransaction: transaction];
[self provideContent: transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseFailedNotification object:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
#end
Singleton object
.h
#import <Foundation/Foundation.h>
#import "StoreManager.h"
#interface StoreWrapper : StoreManager
+ (StoreWrapper *)sharedInstance;
#end
.m
#import "StoreWrapper.h"
#implementation StoreWrapper
static StoreWrapper *gInstance = NULL;
- (id)init{
NSSet *productId = [NSSet setWithObjects:#"com.company.sb.pack1", #"com.company.sb.pack5", nil];
self = [super initWithProductIdentifiers:productId];
if(self){
}
return self;
}
+ (StoreWrapper *)sharedInstance{
if(gInstance == NULL){
gInstance = [[self alloc] init];
}
return gInstance;
}
#end
In the my view controller i call next:
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productsLoaded:) name:kProductsLoadedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
} else {
NSLog(#"Internet connection available!");
// Request product
[[StoreWrapper sharedInstance] requestProducts];
[activIndicator startAnimating];
}
[super viewWillAppear:animated];
}
for this point I send an request for get product info from server with my product id
next if product loaded my class get notification about this (and call following method).
- (void)productsLoaded:(NSNotification *)notification{
[activIndicator stopAnimating];
[activIndicator setHidden:YES];
NSArray *arr = [notification object];
for(int i = 0; i < [arr count]; i++){
SKProduct *product = [[StoreWrapper sharedInstance].products objectAtIndex:i];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(0, i*55, 100, 50)];
[btn setTitle:product.localizedTitle forState:UIControlStateNormal];
[btn setTag:i];
[btn addTarget:self action:#selector(buyProduct:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
}
For the button i add selector buyProduct (as shown above)
- (void)buyProduct:(id)sender{
NSLog(#"sender tag %d", [sender tag]);
int tag = [sender tag];
SKProduct *product = [[StoreWrapper sharedInstance].products objectAtIndex:tag];
[[StoreWrapper sharedInstance] buyProduct:product];
}
For iPhone Simulator this work wonderful, but on device I don have any button, because response.products not download.
All method are called, but delegate method *- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse )response not return response.products.
Thanks for your time and thanks for answer!
For resolving, this problem I reset my device and rerun the application.
This helps for me.
Related
i am really confused, I dont know how can add In-app purchase to my existing app. When in 'Capabilities' i choose on In-app purchase it show me only error add the in app purchase entitlement to your app id. I added app id in dev center and in-app purchase in itunesconnect. My Bundle ID is sk.freetech.zatracenacestina.intro and in-app purchase i set up to sk.freetech.zatracenacestina.intro.package but I dont know where can I fill it?
Thank you
you can follow these tutorial
1. http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial
2. http://www.tutorialspoint.com/ios/ios_in_app_purchase.htm
3. http://code4app.net/ios/In-App-Purchase/4fc85be56803fa4a49000000
Create a class name it IAPHelper subclass NSObject
#import <StoreKit/StoreKit.h>
UIKIT_EXTERN NSString *const IAPHelperProductPurchasedNotification;
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
#protocol inAppPurchaseDelegate <NSObject>
#optional
-(void)transactionSucsess:(NSString *)transactionId;
-(void)transactionOnRestore:(NSString *)transactionId;
-(void)transactionOnFail;
#end
#interface IAPHelper : NSObject
#property(nonatomic,readwrite)id<inAppPurchaseDelegate> delegate;
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler;
- (void)buyProduct:(SKProduct *)product;
- (BOOL)productPurchased:(NSString *)productIdentifier;
- (void)restoreCompletedTransactions;
#end
in IAPHelper.m
import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper {
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
// Add self as transaction observer
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
_completionHandler = [completionHandler copy];
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
#pragma mark SKPaymentTransactionOBserver
- (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];
default:
break;
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if(_delegate)
[_delegate transactionSucsess:transaction.payment.productIdentifier];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if(_delegate)
[_delegate transactionOnRestore:transaction.payment.productIdentifier];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
if(_delegate)
[_delegate transactionOnFail];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Create a class RageIAPHelper subclass of IAPHelper
#import "IAPHelper.h"
#interface RageIAPHelper : IAPHelper
+ (RageIAPHelper *)sharedInstance;
#end
In RageIAPHelper.m
#import "RageIAPHelper.h"
#implementation RageIAPHelper
+ (RageIAPHelper *)sharedInstance {
static dispatch_once_t once;
static RageIAPHelper * sharedInstance;
dispatch_once(&once, ^{
NSSet * productIdentifiers = [NSSet setWithObjects:
#"your identifier",#"your identifier",
nil];
sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
});
return sharedInstance;
}
#end
[[RageIAPHelper sharedInstance] setDelegate:(id)self];
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
totalProduct_Arr =products;
for (SKProduct *s in totalProduct_Arr) {
NSLog(#"product Identifier %#",[s productIdentifier]);
if ([[s productIdentifier]isEqualToString:FULLVERSION]) {
[self.btnFullVersion setEnabled:YES];
}
else if ([[s productIdentifier]isEqualToString:REMOVE_ADS]) {
[self.btnRemoveAd setEnabled:YES];
}
}
}
else{
UIAlertView *alertFail=[[UIAlertView alloc] initWithTitle:#"Product Not Found" message:#"Fails to load product.." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alertFail show];
}
}];
and to buy product
[[RageIAPHelper sharedInstance] buyProduct:product];
This link will help you. Follow this link step by step.
You can use this code also.
Add the StoreKit.framework, Accounts.framework, MobileCoreServices.framework, QuartzCore.framework, CFNetwork.framework, SystemConfiguration.framework.
In .h file import 'StoreKit.h'
#import
Add these delegate on .h class
Declare a property for SKProductsRequest
#property(nonatomic,strong)SKProductsRequest *request;
Now In .m class copy this code for in-app purchase
-(void)startpurching
{
NSArray *arrPackageproductIdentifier=[NSArray arrayWithObjects:#"Your Product Id",nil];
NSString *strPrductIdentifier=[arrPackageproductIdentifier objectAtIndex:0];
request= [[SKProductsRequest alloc]
initWithProductIdentifiers: [NSSet setWithObject:strPrductIdentifier]];
NSLog(#"%#",request);
request.delegate = self;
[request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSArray *myProduct = response.products;
if ([myProduct count]==0) {
UIAlertView *alt=[[UIAlertView alloc]initWithTitle:nil message:#"Cannot connect !! Please check your internet connection." delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alt show];
} else {
NSLog(#"pro...%#",[[myProduct objectAtIndex:0] productIdentifier]);
SKPayment *newPayment = [SKPayment paymentWithProduct:[myProduct objectAtIndex:0]];
[[SKPaymentQueue defaultQueue] addPayment:newPayment];
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"Transaction....%#",transactions);
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
NSLog(#"%d",transaction.transactionState);
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
NSLog(#"=======%#",transactions);
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
NSLog(#"Transaction Completed");
NSUserDefaults *remvAddSttr = [NSUserDefaults standardUserDefaults];
// saving an NSString
NSString *chkRemVal=#"10";
[remvAddSttr setObject:chkRemVal forKey:#"revAdd"];
UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:#"" message:#"Transaction completed" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alrt show];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
NSLog(#"Transaction Restored");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Purchase Unsuccessful"
message:#"Your purchase failed. Please try again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
Now need to design a Restore button for non-consumable product.
For Restore button clicked copy this code.
//*****************************************************************************
//*****************************************************************************
-(void)restoreBtnClicked
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
if (error.code == SKErrorPaymentCancelled) {
NSLog(#"error.code");
}
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
// NSMutableArray* purchasableObjects = [[[NSMutableArray alloc] init] autorelease];
for (SKPaymentTransaction *transaction in queue.transactions) {
NSString *productID = transaction.payment.productIdentifier;
NSLog(#"productID...%#",productID);
if ([productID isEqualToString:#"Your Product Id"]) {
UIAlertView *alt12=[[UIAlertView alloc]initWithTitle:nil message:#"Product Restored" delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alt12 show];
}
}
//Block Call to access the products
}
For me this error appears because of having NO registered device in member center.
Here is the case that help for me.
Register device in member center.
Set your development team in "Your target" -> General -> "Team"
After that this error disappear for me.
I know this is really old, but I just had the same issue (I was following a Ray W. tutorial https://www.raywenderlich.com/122144/in-app-purchase-tutorial)...
When I added my signing team (Targets > General > Signing > Team) it was resolved.
This is not mentioned in the tutorial (no slight on Ray W. - they're generally great)
I have coded In App Purchasing into my app and have made it work so that a user can make a purchase by pressing a button.
I now want to create a button for the user to be able to Restore Purchase. I am using a Shared Instance in 'UpgradeViewController' to retrieve methods from 'IAHelper.h' so I have shown both pieces of code incase it helps
How do I add a 'Restore Purchase' method to my In App Purchase?
IAHelper.m
#import "IAPHelper.h"
// Add to top of file
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
#implementation IAPHelper
{
// 3
SKProductsRequest * _productsRequest;
// 4
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
NSArray *_products;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products... %#", response.products);
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (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];
default:
break;
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
// Add new method
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
- (NSArray*)getProductArrray {
return _products;
}
#end
UpgradeViewController
#import "UpgradeViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#import "RageIAPHelper.h"
#import <StoreKit/StoreKit.h>
#interface UpgradeViewController ()
{
NSMutableArray *_objects;
NSArray *_products;
NSNumberFormatter * _priceFormatter;
}
#property (retain, nonatomic) IBOutlet JSAnimatedImagesView *animatedImagesView;
#property (assign, nonatomic) IBOutlet UIButton *installFullAppButton;
#end
#implementation UpgradeViewController
#synthesize menuBtn, animatedImagesView = _animatedImagesView, scrolly, bannerView, labelPrice;
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (NSString *)publisherIdForAdSdkBannerView:(AdSdkBannerView *)banner {
return #"e0616d4190bff65279ed5c20de1b5653";
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[RageIAPHelper sharedInstance];
_products = nil;
[[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
_products = products;
}
}];
// Price New
SKProduct * product = (SKProduct *) [_products objectAtIndex:0];
([[RageIAPHelper sharedInstance] productPurchased:product.productIdentifier]);
// Unlock your features code comes here
UIButton *buyButton = [[UIButton alloc] initWithFrame:CGRectMake(-1, 370, 320, 60)];
UIImage *btnImage = [UIImage imageNamed:#"upgrade-new.png"];
[buyButton setImage:btnImage forState:UIControlStateNormal];
[buyButton.titleLabel setFont:[UIFont boldSystemFontOfSize:13.0]];
[buyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[buyButton.titleLabel setShadowColor:[UIColor colorWithWhite:0.1 alpha:1.0]];
[buyButton.titleLabel setShadowOffset:CGSizeMake(0, -1)];
[buyButton addTarget:self action:#selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
buyButton.tag = 0;
[[self view] addSubview:buyButton];
// Purchase Action End
[super viewDidLoad];
_priceFormatter = [[NSNumberFormatter alloc] init];
[_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_priceFormatter setLocale:product.priceLocale];
labelPrice.text = [_priceFormatter stringFromNumber:product.price];
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(8, 30, 34, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
myWebView.opaque = NO;
myWebView.backgroundColor = [UIColor clearColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.screenName = #"Upgrade";
}
#pragma mark - UI
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)buyButtonTapped:(id)sender {
UIButton *buyButton = (UIButton *)sender;
SKProduct *product = [_products objectAtIndex:buyButton.tag];
NSLog(#"Buying %#...", product.productIdentifier);
[[RageIAPHelper sharedInstance] buyProduct:product];
}
#end
Apple will refuse your app if you do not have a button to do this, well they did me even though they recognised the app automatically checked in background.
Any way if you add this to your helper;
- (void)restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
And call like this from VC;
- (IBAction)restoreAction:(id)sender
{
[[RageIAPHelper sharedInstance] restoreCompletedTransactions];
}
After you have doen this;
- (void)restoreCompletedTransactions
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
And call like this from VC;
- (IBAction)restoreAction:(id)sender
{
[[RageIAPHelper sharedInstance] restoreCompletedTransactions];
}
This methods needs to be changed to this;
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products... %#", response.products);
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
// Check is the Handler exists
if(_completionHandler)
{
_completionHandler(YES, skProducts);
}
_completionHandler = nil;
}
I have in app purchases set up in my app which work fine, however I want to call the 'Product Price' with local currency onto a viewController. I have posted this question before and have got the feedback:
"proUpgradeProduct is a private variable. Make it public by adding is as a method, or a property/sythesize pair. Your problems are not related to in-app purchasing, but rather a lack of basic objective-c knowledge."
I am quite new to this so would appreciate some help. basically, the productPrice variable is in InAppPurchaseSS.m file and I want to call it as a UILabel into a DIFFERENT viewController.
I have these 2 files:
InAppPurchaseSS.h
import
#import "StoreKit/StoreKit.h"
#define kProductPurchasedNotification #"ProductPurchased"
#define kProductPurchaseFailedNotification #"ProductPurchaseFailed"
#define kProductPurchaseCancelledNotification #"ProductPurchaseCancelled"
#interface InAppPurchaseSS : NSObject <SKProductsRequestDelegate,SKPaymentTransactionObserver,UIAlertViewDelegate>
{
SKProductsRequest* productsRequest;
SKProduct *proUpgradeProduct;
UIAlertView* waitingAlert;
BOOL isTransactionOngoing;
IBOutlet UILabel *productPriceLabel;
}
#property (retain) SKProductsRequest* productsRequest;
#property (retain) NSArray * products;
#property (retain) SKProductsRequest *request;
#property (assign) BOOL isTransactionOngoing;
+ (InAppPurchaseSS *) sharedHelper;
-(id)init;
- (void)buyProductIdentifier:(NSString *)productIdentifier;
- (BOOL)canMakePurchases;
-(void)restoreInAppPurchase;
#end
InAppPurchaseSS.m
//
#import "InAppPurchaseSS.h"
#import "Reachability.h"
#implementation InAppPurchaseSS
#synthesize products;
#synthesize request;
#synthesize productsRequest;
#synthesize isTransactionOngoing;
static InAppPurchaseSS * _sharedHelper;
+ (InAppPurchaseSS *) sharedHelper {
if (_sharedHelper != nil) {
return _sharedHelper;
}
_sharedHelper = [[InAppPurchaseSS alloc] init];
return _sharedHelper;
}
-(id)init {
if( (self=[super init]))
{
isTransactionOngoing=NO;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Received products results...");
self.products = response.products;
self.request = nil;
NSLog(#"Number of product:%i : %#",[response.products count],response.products);
NSArray *product = response.products;
proUpgradeProduct = [product count] == 1 ? [[product firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
[productPriceLabel setText:[NSString stringWithFormat:
#"Product Price: %#",proUpgradeProduct.price]];
}
-(void)restoreInAppPurchase
{
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Restoring..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)buyProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqual: #""]) {
NSLog(#"No IAP Product ID specified");
return;
}
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
isTransactionOngoing=YES;
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Purchasing..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
SKMutablePayment *payment = [[SKMutablePayment alloc] init];
payment.productIdentifier = productIdentifier;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
-(void)enableFeature
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"PurchaseSuccess"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"Restore completed transaction failed");
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
//
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
isTransactionOngoing=NO;
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if (wasSuccessful)
{
[self enableFeature];
[[[UIAlertView alloc] initWithTitle:#"Congratulations!!" message:#"You have succesfully Purchases." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
[[NSNotificationCenter defaultCenter] postNotificationName:#"successbuy" object:self];
}
else
{
[[[UIAlertView alloc] initWithTitle:#"Error!" message:transaction.error.localizedDescription delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"succesfull transaction");
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"transaction is restored");
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
isTransactionOngoing=NO;
NSLog(#"failed transaction called");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction failed called");
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
[self finishTransaction:transaction wasSuccessful:NO];
}
else
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"user cancel transaction");
// this is fine, the user just cancelled, so don’t notify
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
// called when the transaction status is updated
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"transaction status updated");
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;
}
}
}
- (void) dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[super dealloc];
}
#end
I have another viewController set up where I want to call the Local Product Price with UILabel. Let's assume this is called SecondViewController. I know that in InAppPurchaseSS.m I have a field for proUpgradeProduct.price but I need to call this as a UI label with the local currency
It may be something obvious Im overlooking but I'd appreciate some help with this
Thank you
In the method where you load the products each product should have a variable which contains the price as well as the label. Unfortunately, I don't know which one but I think it's the SKProduct. See if it contains any variables.
EDIT:
It's the proProduct.localizedLabel and proProduct.localizedPrice that you are looking for!
EDIT AGAIN:
I looked again and saw that you were more precise about your question. I would recommend you to save these as NSString.
For instance:
in your .h file you create a property.
#interface SomethingController : UIViewController {
//random stuff
}
#property NSString price;
#property NSString title;
#end
Now set the
self.price = proProduct.localizedPrice
and
self.title = proProduct.localizedLabel
somewhere in the .m file.
Now in your other viewcontroller where you want to show these you create an instance somewhere in the viewcontroller.m file.
First you import the SomethingController.h which contains the variables you are looking for.
At the top of your viewcontroller.m file of the viewcontroller you want to display the data in you write: #import SomethingController.h
Then you create an instance of it to get the data.
SomethingController * something = [SomethingController alloc];
Now you can access the data in your other viewcontroller by writing:
something.price
to access the price and
something.title
to access the label.
I have added InApp Purchasing to my app but when trying to complete a test transaction using a Test User account, the app keeps crashing at the following code saying that Unrecognised Selector sent to Instance.
I have read up about this and feel it may be something to do with the fact I am using Auto Renewal Subscription product.
The code that seems to be related to the crash is this line:
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
I have provided my InAppPurchase code incase anyone can please please help me with this!!
InAppPurchaseSS.h
#import <Foundation/Foundation.h>
#import "StoreKit/StoreKit.h"
#define kProductPurchasedNotification #"ProductPurchased"
#define kProductPurchaseFailedNotification #"ProductPurchaseFailed"
#define kProductPurchaseCancelledNotification #"ProductPurchaseCancelled"
#interface InAppPurchaseSS : NSObject <SKProductsRequestDelegate,SKPaymentTransactionObserver,UIAlertViewDelegate>
{
SKProductsRequest* productsRequest;
SKProduct *proUpgradeProduct;
UIAlertView* waitingAlert;
BOOL isTransactionOngoing;
}
#property (retain) SKProductsRequest* productsRequest;
#property (retain) NSArray * products;
#property (retain) SKProductsRequest *request;
#property (assign) BOOL isTransactionOngoing;
+ (InAppPurchaseSS *) sharedHelper;
-(id)init;
- (void)buyProductIdentifier:(NSString *)productIdentifier;
- (BOOL)canMakePurchases;
-(void)restoreInAppPurchase;
- (void)collectProducts;
#end
InAppPurchaseSS.m
#import "InAppPurchaseSS.h"
#import "Reachability.h"
#implementation InAppPurchaseSS
#synthesize products;
#synthesize request;
#synthesize productsRequest;
#synthesize isTransactionOngoing;
static InAppPurchaseSS * _sharedHelper;
+ (InAppPurchaseSS *) sharedHelper {
if (_sharedHelper != nil) {
return _sharedHelper;
}
_sharedHelper = [[InAppPurchaseSS alloc] init];
return _sharedHelper;
}
-(id)init {
if( (self=[super init]))
{
isTransactionOngoing=NO;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)collectProducts
{
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:#"my.inappads"]];
self.request.delegate = self;
[self.request start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(#"Received products results...");
self.products = response.products;
self.request = nil;
NSLog(#"Number of product:%i : %#",[response.products count],response.products);
NSArray *product = response.products;
proUpgradeProduct = [product count] == 1 ? [[product firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
}
-(void)restoreInAppPurchase
{
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Restoring..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)buyProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqual: #"my.inappads"]) {
NSLog(#"No IAP Product ID specified");
return;
}
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(#"No internet connection!");
[[[UIAlertView alloc] initWithTitle:#"No Internet" message:#"Sorry, no internet connection found" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
return;
}
isTransactionOngoing=YES;
waitingAlert = [[UIAlertView alloc] initWithTitle:#"Purchasing..." message:#"Please Wait...\n\n" delegate:nil cancelButtonTitle:nil otherButtonTitles: nil];
[waitingAlert show];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
SKMutablePayment *payment = [[SKMutablePayment alloc] init];
payment.productIdentifier = productIdentifier;
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
-(void)enableFeature
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"PurchaseSuccess"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"Restore completed transaction failed");
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
//
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful
{
isTransactionOngoing=NO;
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
// remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
if (wasSuccessful)
{
[self enableFeature];
[[[UIAlertView alloc] initWithTitle:#"Congratulations!!" message:#"You have succesfully Purchases." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil] show];
[[NSNotificationCenter defaultCenter] postNotificationName:#"successbuy" object:self];
}
else
{
[[[UIAlertView alloc] initWithTitle:#"Error!" message:transaction.error.localizedDescription delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"succesfull transaction");
[self finishTransaction:transaction wasSuccessful:YES];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(#"transaction is restored");
[self finishTransaction:transaction wasSuccessful:YES];
}
// called when a transaction has failed
- (void)failedTransaction:(SKPaymentTransaction *)transaction
{
isTransactionOngoing=NO;
NSLog(#"failed transaction called");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction failed called");
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
[self finishTransaction:transaction wasSuccessful:NO];
}
else
{
[waitingAlert dismissWithClickedButtonIndex:0 animated:YES];
NSLog(#"user cancel transaction");
// this is fine, the user just cancelled, so don’t notify
[[NSNotificationCenter defaultCenter] postNotificationName:#"TransCancel" object: self];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
#pragma mark -
#pragma mark SKPaymentTransactionObserver methods
// called when the transaction status is updated
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"transaction status updated");
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;
}
}
}
- (void) dealloc
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[super dealloc];
}
#end
I am using the #TransCancel in the view that the user makes the purchase in, the code is like this:
// Purchase
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransCancel" object: nil];
[center addObserver: self selector: #selector(TransactionComplete) name:#"successbuy" object: nil];
NSLog(#"%d",[[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"]);
if([[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"])
btn.hidden=YES;
else
btn.hidden=NO;
Error Log
2014-01-06 00:25:40.694 MyApp[2764:60b] transaction status updated
2014-01-06 00:25:40.695 MyApp[2764:60b] failed transaction called
2014-01-06 00:25:40.696 MyApp[2764:60b] Transaction failed called
2014-01-06 00:25:40.696 MyApp[2764:60b] Transaction error: Cannot connect to iTunes Store
2014-01-06 00:25:40.730 MyApp[2764:60b] -[MyViewController TransactionCancel]: unrecognized selector sent to instance 0x1666a350
MyViewController
#import "UpgradeViewController.h"
#import "ECSlidingViewController.h"
#import "MenuViewController.h"
#import "InAppPurchaseSS.h"
#define ProductIdentifier #"<my.inappads>"
#interface UpgradeViewController ()
#property (retain, nonatomic) IBOutlet JSAnimatedImagesView *animatedImagesView;
#property (weak, nonatomic) IBOutlet UIButton *installFullAppButton;
#end
#implementation UpgradeViewController
#synthesize menuBtn, animatedImagesView = _animatedImagesView, scrolly, bannerView, labelPrice;
- (NSString *)publisherIdForAdSdkBannerView:(AdSdkBannerView *)banner {
return #"e0616d4190bff65279ed5c20de1b5653";
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Price
SKProduct *product = [[[InAppPurchaseSS sharedHelper] products] lastObject];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
// [self.installFullAppButton setTitle:formattedString forState:UIControlStateNormal];
labelPrice.text = formattedString;
// Purchase
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransactionCancel" object: nil];
[center addObserver: self selector: #selector(TransactionComplete) name:#"successbuy" object: nil];
NSLog(#"%d",[[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"]);
if([[NSUserDefaults standardUserDefaults] boolForKey:#"PurchaseSuccess"])
btn.hidden=YES;
else
btn.hidden=NO;
// Do any additional setup after loading the view.
//UIScrollView
self.scrolly.contentSize = CGSizeMake(320, 600);
//Image Transition
// self.animatedImagesView.delegate = self;
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(8, 30, 34, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
myWebView.opaque = NO;
myWebView.backgroundColor = [UIColor clearColor];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.screenName = #"Upgrade";
}
#pragma mark - Memory Management
- (void)viewDidUnload
{
// [self setAnimatedImagesView:nil];
[self setScrolly:nil];
// [self setAnimatedImagesView:nil];
[super viewDidUnload];
[self setBannerView:nil];
[super viewDidUnload];
}
- (void)dealloc
{
bannerView.delegate = nil;
}
-(IBAction)but:(id)sender
{
[[InAppPurchaseSS sharedHelper] buyProductIdentifier:ProductIdentifier];
}
-(IBAction)restore:(id)sender
{
[[InAppPurchaseSS sharedHelper] restoreInAppPurchase];
}
- (IBAction)dismissView:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
}
-(void)TransactionComplete
{
btn.hidden=YES;
}
-(void)TransactionCancel
{
btn.hidden=NO;
}
You have this line:
[center addObserver: self selector: #selector(TransactionCancel) name:#"TransCancel" object: nil];
Which means that self must implement the TransactionCancel method. The error indicates that the method doesn't exist on the MyViewController class.
The solution is to add the TransactionCancel method to the MyViewController class.
i have carried out these several steps for five non consumable in app purchases to be available within my app but still nothing is showing up when i go to the TableViewController that i linked the IAP code to... (i would also like to give a big thanks to the tutorial i followed which has got me this far by raywenderlich)
-Made a new app ID
-Successfully followed all the steps for linking and downloading certificates
Changed the bundle ID on xcode for my project to the one i made for
my app on IOS developer portal
-Linked all the devices and made a test user account
-Made the non consumable IAP on itunes connect and used the Identifiers within the IAP coding
-Signed my phone out of my itunes account so tht its ready to be used with a test account
-Coding for the IAP looks to be correct with store kit imported and product identifiers implemented within the .m file
-Waited 24 hours for the newly created IAP to sync with itunes connect
-I DIDNT upload the binaries!
i have hosting with apple turned on but didnt upload anything - could
this be the issue?
i deleted the app on my phone and reinstalled for testing but still
nothing
All i get when i successfull run the build is a page that looks like its loading something but then nothing a blank page that i can pull down to refresh on but still nothing no IAP that i created.
Any suggestions of what else i can do or add and if you need me to upload all the codes for all the files i can do...
coding for IAP within the ViewController.h file
#import "Accounts/Accounts.h"
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
#interface ViewController19 : UITableViewController
#end
Coding for IAP within the ViewController.m file
#import "DetailViewController.h"
#import "SecretsIAPHelper.h"
#import <StoreKit/StoreKit.h>
#interface ViewController19 () {
NSArray *_products;
NSNumberFormatter * _priceFormatter;
}
#end
#implementation ViewController19
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"XXXXXXXXXXXXX";
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(reload) forControlEvents:UIControlEventValueChanged];
[self reload];
[self.refreshControl beginRefreshing];
_priceFormatter = [[NSNumberFormatter alloc] init];
[_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Restore" style:UIBarButtonItemStyleBordered target:self action:#selector(restoreTapped:)];
}
- (void)restoreTapped:(id)sender {
[[SecretsIAPHelper sharedInstance] restoreCompletedTransactions];
}
- (void)viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)productPurchased:(NSNotification *)notification {
NSString * productIdentifier = notification.object;
[_products enumerateObjectsUsingBlock:^(SKProduct * product, NSUInteger idx, BOOL *stop) {
if ([product.productIdentifier isEqualToString:productIdentifier]) {
[self.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:idx inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
*stop = YES;
}
}];
}
- (void)reload {
_products = nil;
[self.tableView reloadData];
[[SecretsIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
if (success) {
_products = products;
[self.tableView reloadData];
}
[self.refreshControl endRefreshing];
}];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _products.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
SKProduct * product = (SKProduct *) _products[indexPath.row];
cell.textLabel.text = product.localizedTitle;
[_priceFormatter setLocale:product.priceLocale];
cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];
if ([[SecretsIAPHelper sharedInstance] productPurchased:product.productIdentifier]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.accessoryView = nil;
} else {
UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buyButton.frame = CGRectMake(0, 0, 72, 37);
[buyButton setTitle:#"Buy" forState:UIControlStateNormal];
buyButton.tag = indexPath.row;
[buyButton addTarget:self action:#selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = buyButton;
}
return cell;
}
- (void)buyButtonTapped:(id)sender {
UIButton *buyButton = (UIButton *)sender;
SKProduct *product = _products[buyButton.tag];
NSLog(#"Buying %#...", product.productIdentifier);
[[SecretsIAPHelper sharedInstance] buyProduct:product];
}
#end
Coding for IAP within the IAPHelper.h file
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
UIKIT_EXTERN NSString *const IAPHelperProductPurchasedNotification;
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
#interface IAPHelper : NSObject
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler;
- (void)buyProduct:(SKProduct *)product;
- (BOOL)productPurchased:(NSString *)productIdentifier;
- (void)restoreCompletedTransactions;
#end
Coding for IAP within the IAPHelper.m file
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
NSString *const IAPHelperProductPurchasedNotification = #"IAPHelperProductPurchasedNotification";
// 2
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
// 3
#implementation IAPHelper {
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
// Add self as transaction observer
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
#pragma mark SKPaymentTransactionOBserver
- (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];
default:
break;
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
}
- (void)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Coding for IAP within the SecretsIAPHelper.h file
#import "IAPHelper.h"
#interface SecretsIAPHelper : IAPHelper
+ (SecretsIAPHelper *)sharedInstance;
#end
Coding for IAP within the SecretsIAPHelper.m file
#import "SecretsIAPHelper.h"
#implementation SecretsIAPHelper
+ (SecretsIAPHelper *)sharedInstance {
static dispatch_once_t once;
static SecretsIAPHelper * sharedInstance;
dispatch_once(&once, ^{
NSSet * productIdentifiers = [NSSet setWithObjects:
#"com.designsbydeondrae.XXXXXXX.remove_ads",
#"com.designsbydeondrae.XXXXXXX.FoundationSkills",
#"com.designsbydeondrae.XXXXXXX.IntermediateSkills",
#"com.designsbydeondrae.XXXXXXX.AllSkills",
#"com.designsbydeondrae.XXXXXXX.AdvancedSkills",
nil];
sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
});
return sharedInstance;
}
#end
Coding for IAP within the DetailViewController.h file
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
#end
Coding for IAP within the DetailViewController.h file
#import "DetailViewController.h"
#interface DetailViewController ()
- (void)configureView;
#end
#implementation DetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
So just to summarise...
Used StoreKit to access the In-App Purchase APIs and retrieve the
list which brings up nothing when i run the build
Specified the product identifiers for my app
Displaying the products which seems to be an issue though it does
show a screen like its about to load something then nothing just a
page with a pull to refresh on it
For any one that finds this post in the future, the reason no items where showing up when requesting IAP was due to content not being uploaded for hosted content.