When I run the app with the code below, i buy one of the products using my test account and I get this in my console:
queue
queue
unlocked:
buy20
Transaction Failed
Somehow the purchase manages to be successful in the - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions method then immediately after, fails. Do you see any problems with the code below or have you heard of this problem before?
#import "BuyWindowViewController.h"
#interface BuyWindowViewController ()
#end
#implementation BuyWindowViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[_closeButton setTitle:#"Close" forState:UIControlStateNormal];
[_closeButton.titleLabel setFont:[UIFont fontWithName:#"Helvetica" size:16.0f]];
[_titleBar setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"Button 400x50 Yellow.png"]]];
_productIDs = [iAPBundleIDs returnAllIDs];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[self getProductID];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)closeButtonPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTitleBar:nil];
[super viewDidUnload];
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotate {
return YES;
}
- (IBAction)infoButtonPressed:(UIButton *)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Serpentine!" message:#"" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
if (sender.tag == 1) {
//buy 20
alert.title = #"Instantly get 20 Upgrade Points to spend in the Shop!";
} else if (sender.tag == 2) {
//buy 45
alert.title = #"Instantly get 45 Upgrade Points to spend in the Shop!";
} else if (sender.tag == 3) {
//buy 100
alert.title = #"Instantly get 100 Upgrade Points to spend in the Shop!";
} else if (sender.tag == 4) {
//double
alert.title = #"Permanently double the upgrade points you receive at the end of each game!";
} else if (sender.tag == 5) {
//remove ads + 20
alert.title = #"Remove those annoying iAds and get 20 upgrade points with one purchase!";
}
[alert show];
}
- (IBAction)purchaseButtonPressed:(UIButton *)sender {
_whichProduct = 0;
if (sender.tag == 1) {
//buy 20
_whichProduct = 1;
} else if (sender.tag == 2) {
//buy 45
_whichProduct = 2;
} else if (sender.tag == 3) {
//buy 100
_whichProduct = 0;
} else if (sender.tag == 4) {
//double
_whichProduct = 3;
} else if (sender.tag == 5) {
//remove ads + 20
_whichProduct = 4;
}
SKPayment *payment = [SKPayment paymentWithProduct:_products[_whichProduct]];
[[SKPaymentQueue defaultQueue] addPayment:payment];
[self showLoadingWithText:#"Purchasing item..."];
}
#pragma mark - Loading Stuff
- (void)couldNotConnectToStoreWithText:(NSString *)text {
[_spinner removeFromSuperview];
[_spinner startAnimating];
[_spinnerLabel setText:text];
}
- (void)hideLoading {
[UIView animateWithDuration:0.5f animations:^{
_spinnerBackground.alpha = 0;
} completion:^(BOOL finished) {
[_spinner removeFromSuperview];
[_spinnerLabel removeFromSuperview];
[_spinnerBackground removeFromSuperview];
}];
}
- (void)showLoadingWithText:(NSString *)text {
[_spinnerBackground addSubview:_spinner];
[_spinnerBackground addSubview:_spinnerLabel];
_spinnerLabel.text = text;
[self.view addSubview:_spinnerBackground];
[self.view bringSubviewToFront:_spinnerBackground];
[UIView animateWithDuration:0.5f animations:^{
_spinnerBackground.alpha = 0;
}];
}
#pragma mark - iAP
- (void)getProductID {
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIDs];
request.delegate = self;
[request start];
} else {
[self couldNotConnectToStoreWithText:#"Error: Could not connect to store. Try enabling in-app-purchases in settings"];
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
if (products.count != 0) {
_products = products;
[self hideLoading];
} else {
[self couldNotConnectToStoreWithText:#"Error: Could not connect to iTunes"];
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products) {
NSLog(#"Product not found: %#", product);
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"queue");
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");//[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Error: Transaction Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:break;
}
}
}
- (void)unlockPurchase {
NSLog(#"unlocked:");
if (_whichProduct == 1) {
//buy 20
NSLog(#" buy20");
} else if (_whichProduct == 2) {
//buy 45
NSLog(#" buy45");
} else if (_whichProduct == 0) {
//buy 100
NSLog(#" buy100");
} else if (_whichProduct == 3) {
//double
NSLog(#" buydouble");
} else if (_whichProduct == 4) {
//remove ads + 20
NSLog(#" buyads");
}
[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Purchase successful!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[self hideLoading];
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
[self unlockPurchase];
NSLog(#"restore");
}
- (void)restore {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Thanks
In your code :
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"queue");
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");//[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Error: Transaction Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:break;
}
}
}
please add break; statement as below
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"queue");
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:[self unlockPurchase];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// we want skip next case
break; // skipping next "case"
case SKPaymentTransactionStateFailed:NSLog(#"Transaction Failed");//[[[UIAlertView alloc] initWithTitle:#"Serpentine" message:#"Error: Transaction Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil] show];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:break;
}
}
}
The problem is next case is executed after first. It should be skipped explicitly. That is common problem in c-like languages.
languages. We sometimes forget about this.
Related
I did everything right, here is the iTunesConnect integrated shopping screenshot and my simple code. Only he tells me "no integrated purchase".
I've done it hundreds of times, but I still do not understand it.
My productID fits well on itunes as in the code. So where's the problem ?
Help me please guys :)
Code :
#import ....
#import ....
#define kTutorialPointProductID #"com.mathrack.SocialFame.CoinsPlus"
..........
..........
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self fetchAvailableProducts];
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault]; //UIImageNamed:#"transparent.png"
self.navigationController.navigationBar.shadowImage = [UIImage new];////UIImageNamed:#"transparent.png"
self.navigationController.navigationBar.translucent = YES;
self.navigationController.view.backgroundColor = [UIColor clearColor];
}
-(void)fetchAvailableProducts{
NSSet *productIdentifiers = [NSSet
setWithObjects:kTutorialPointProductID,nil];
productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
- (void)purchaseMyProduct:(SKProduct*)product{
if ([self canMakePurchases]) {
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Les achats intégrés sont désactiver sur votre appareil" message:nil delegate:
self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}
}
- (IBAction)buy50coins:(id)sender {
[self purchaseMyProduct:[validProducts objectAtIndex:0]];
}
#pragma mark StoreKit Delegate
-(void)paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"Purchasing");
break;
case SKPaymentTransactionStatePurchased:
if ([transaction.payment.productIdentifier
isEqualToString:kTutorialPointProductID]) {
NSLog(#"Purchased ");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Restored ");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self purchaseFailed];
NSLog(#"Purchase failed ");
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:kTutorialPointProductID]) {
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Non disponible"
message:#"Aucun produit à acheter"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil];
[tmp show];
}
}
- (void)purchaseFailed {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Achat impossible" message:#"Un problème est survenue lors de votre achat, veuillez réessayer ulterieurement" delegate:
self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}
ItunesConnect picture :
I have been receiving from some of my clients that even they do in app purchase, the product is not loaded. But most of my clients do not have this issue. I was instructing them to do restore operation but that didn't help. The thing is they can restore correctly in other device with same Apple id.
There is no specific device or IOS version and App is not crashing. Just it says restored correctly or purchased correctly, but its not doing it.
It makes me crazy because I tried in many devices and it works correctly for me.
I want to share the code I use for that which may help you to understand issue which I can't.
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
SKProduct *validProduct = nil;
NSUInteger count = [response.products count];
if( count > 0 ) {
validProduct = [response.products objectAtIndex:0];
NSLog(#"Products Available!");
[self purchase:validProduct];
}
else if(!validProduct){
NSLog(#"No products available");
[self hideProgressHud];
isProgress = NO;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:LocalizedString(#"Notice") message:LocalizedString(#"Product is not available now.") preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:LocalizedString(#"OK") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}];
[alert addAction:cancelBtn];
[self presentViewController:alert animated:true completion:nil];
}
}
- (void)purchase:(SKProduct *)product {
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(#"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for(SKPaymentTransaction *transaction in queue.transactions){
if(transaction.transactionState == SKPaymentTransactionStateRestored || transaction.transactionState == SKPaymentTransactionStateRestored){
NSLog(#"Transaction state -> Restored");
NSString *productID = transaction.payment.productIdentifier;
[[InAppPurchaseManager sharedManager] savePurchaseItem:productID];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
[self hideProgressHud];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
[self hideProgressHud];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
static bool isOnce = false;
for(SKPaymentTransaction *transaction in transactions){
if (transaction.transactionState == SKPaymentTransactionStatePurchasing) {
//called when the user is in the process of purchasing, do not add any of your own code here.
NSLog(#"Transaction state -> Purchasing");
}
else if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self refresh_purchaseBtn];
return;
}
else if (transaction.transactionState == SKPaymentTransactionStateRestored) {
NSLog(#"Transaction state -> Restored");
[self hideProgressHud];
isProgress = NO;
//add the same code as you did from SKPaymentTransactionStatePurchased here
NSString *productID = transaction.payment.productIdentifier;
if ([productID isEqualToString:kPURCHASE_ITEM_TYPE_A] || [productID isEqualToString:kPURCHASE_ITEM_TYPE_B] || [productID isEqualToString:kPURCHASE_ITEM_TYPE_C]) {
[[InAppPurchaseManager sharedManager] savePurchaseItem:productID];
}
else {
NSArray *arr = [Workout MR_findByAttribute:#"productId" withValue:productID inContext:ApplicationDelegate.managedObjectContext];
if (arr.count > 0) {
Workout *workout = (Workout *)[arr firstObject];
workout.state = #"purchased";
[ApplicationDelegate.managedObjectContext MR_saveOnlySelfAndWait];
}
[[InAppPurchaseManager sharedManager] savePurchasedReadyProgram:productID];
}
if (!isOnce) {
// UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:LocalizedString(#"Notice") message:LocalizedString(#"Purchased Item restored succesfully") delegate:self cancelButtonTitle:LocalizedString(#"Ok") otherButtonTitles: nil];
// [alertView show];
isOnce = true;
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[self refresh_purchaseBtn];
}
else if (transaction.transactionState == SKPaymentTransactionStateFailed) {
//called when the transaction does not finish
if(transaction.error.code == SKErrorPaymentCancelled){
NSLog(#"Transaction state -> Cancelled");
//the user cancelled the payment
}
[self hideProgressHud];
isProgress = NO;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
}
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 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 2 files set up with the InAppPurchase code in there which look like 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;
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
//
// InAppPurchaseSS.m
// Pharaonia
//
// Created by iMac on 4/16/13.
// Copyright (c) 2013 Apple. All rights reserved.
//
#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
SKProduct has a priceLocale attribute and price attribute you can use to create the text for your label.
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
My app has been rejected by itunes.
Their reason is:
After we tap the buy button the app does not initiate the password
window to finish the In App Purchase process and only displays a
message "waiting" then nothing happens.
They mean there is purchase dialog,but when tap the buy button,their is no the password window.But it is OK when I test it.
The password window initialization is not under my control, what should I do ?
Here is my code:
-(void)buy:(NSString*)type
{
buyType = type;
//judge whether can buy product
if ([SKPaymentQueue canMakePayments]) {
NSLog(#"can buy");
[self RequestProductData];
}else{
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:#"Alert"
message:#"You can‘t purchase in app store(不允许应用程序内购买)"
delegate:nil cancelButtonTitle:NSLocalizedString(#"Close(关闭)",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];
}
}
-(bool)CanMakePay
{
return [SKPaymentQueue canMakePayments];
}
-(void)RequestProductData
{
NSSet *nsset = [NSSet setWithObject:buyType];
SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
request.delegate=self;
[request start];
}
//<SKProductsRequestDelegate>
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray *myProduct = response.products;
// populate UI
for(SKProduct *product in myProduct){
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
[request autorelease];
}
//alert error
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Alert",NULL) message:[error localizedDescription]
delegate:nil cancelButtonTitle:NSLocalizedString(#"Close",nil) otherButtonTitles:nil];
[alerView show];
[alerView release];
}
-(void) requestDidFinish:(SKRequest *)request
{
}
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{
NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
[transactions release];
}
//<SKPaymentTransactionObserver>
//----listen to the purchase result
//
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased://purchase ok
[self completeTransaction:transaction];
[[transaction payment] quantity];
NSLog(#"%#",[[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding ]);
NSLog(#"transaction id:%#",[transaction transactionIdentifier]);
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
NSLog(#"-----交易失败 --------");
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
case SKPaymentTransactionStatePurchasing:
break;
default:
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void)recordTransaction:(NSString *)product{
}
//
-(void)provideContent:(NSString *)product{
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction{
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
}
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{
}