In App Purchase sandbox not prompting for my login/pass - iphone

We're developing an app which (ofcourse) uses in app purchases (IAP).
I've done everything in the guide to enable iap and everything works fine, untill I want to make purchase.
Some of the code:
MainViewController.m
-(void)viewDidLoad {
if ([SKPaymentQueue canMakePayments]) {
MyStoreObserver *observer = [[MyStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObjects: #"com.company.app.product1", #"com.company.app.product1", nil]];
request.delegate = self;
[request start];
}
};
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
for (SKProduct *prod in response.products) {
NSLog(#"%# (%#)", prod.localizedTitle, prod.price);
}
[request release];
};
-(IBAction)clickBuy:(UIButton *)__sender {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"com.company.app.product1"];
[[SKPaymentQueue defaultQueue] addPayment:payment];
};
MyStoreObserver.m
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
NSLog(#"SKPaymentTransactionStatePurchased");
break;
case SKPaymentTransactionStateFailed:
NSLog(#"SKPaymentTransactionStateFailed");
break;
case SKPaymentTransactionStateRestored:
NSLog(#"SKPaymentTransactionStateRestored");
break;
case SKPaymentTransactionStatePurchasing:
NSLog(#"SKPaymentTransactionStatePurchasing");
default:
break;
}
}
};
The productRequest: delegate method shows 2 products with their name / price. Like I entered in the iTunes connect site.
But once I click the 'buy' button, no dialog pops up or asks me for my credentials. Only "SKPaymentTransactionStatePurchasing" is logged.
And I:
- ... have logged out in the settings/store pane
- ... am using the right provisioning profiles
- ... am desperate
Anyone?

I encountered a similar problem, but mine was more of a boneheaded move on my part. I had 'refactored' the call to finishTransaction so that it was being called for every state in transactionState:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
// do stuff
break;
case SKPaymentTransactionStateFailed:
// do stuff
break;
case SKPaymentTransactionStateRestored:
// do stuff
break;
default:
break;
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
Turns out™, this will also call finishTransaction on SKPaymentTransactionStatePurchasing, which will cause a crash. Moving finishTransaction back into each case of the switch statement fixed that.

After Pulling my hair out in frustration with a similar problem (instead of not being asked for my credentials it was automatically filling in the email address without the option to change it even when logged out of the store in the settings app). I discovered that I had a failed transaction stuck in the queue from development builds on the same device, I had to clear all of the transactions in the queue on the device and then try to test again.
NSArray *transactions = [[SKPaymentQueue defaultQueue] transactions];
for(id transaction in transactions){
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
I hooked this code upto an IBOutlet and after being run once my in app purchases worked.

I also had been pulling my hair out a bit with this. Turns out a simple reboot of the test device got everything working fine.

An iPhone can be restricted from accessing the Apple App Store. For example, parents can restrict their children’s ability to purchase additional content.
Before placing transaction make sure, can you u buy or not?Check it like this -
-(IBAction)clickBuy:(UIButton *)__sender {
if ([SKPaymentQueue canMakePayments]) {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:#"Product_id"];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else {
//show appropriate message
}
}

Are you testing on iPhone/iPad Simulator 4.2 or something? That might be the problem. Testing on iPhone/iPad Simulator 5.0, or the device, will run storekit correctly.

Related

displaying dialogue after in-app purchase complete

I have implemented the SKPaymentTransactionObserver in my apps AppDelegate the way Apple recommended:
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
self.products = response.products;
}
- (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;
}
}
}
I'd like for my app to send a dialogue message to the user when the following happens: purchase successful, purchase failed, restore successful, restore failed. I'm having a little trouble wrapping my head around how I can do this with my design set up. I have a couple of questions:
1) The alert needs to be posted in the view controller where the transaction is initialized. How can I make the AppDelegate communicate with this view controller to let it know when an event has happened? Do I set up a delegate for the AppDelegate? This seems kind of funny to me...is there a better way?
2) Where do I send the message? Should it be in finishTransaction (do I need to override?) or somewhere else?
Passing a notification will be the best way for it. The view controller which invokes the payment procedure should register for notification.
On the completion of a transaction, app delegate will post the notification which controller will receive and it will show the appropriate message.

In consumable InAppPurchase, delegate of cancel transaction not found in ios5.1 but found in ios 6.1

On confirm your In-App Purchase, I am cancelling the purchase.
In iOS 6.1 after cancel, control transfered to - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions method.
In iOS 5.1, no any delegate method will get called. So I am not able to control app on cancelling TRANSACTION.
My code to buy product:
- (void)buyProduct:(SKProduct *)product
{
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
Delegate Method:
#pragma mark SKPaymentTransactionOBserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSLog(#"updated 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];
case SKPaymentTransactionStatePurchasing:
NSLog(#"SKPaymentTransactionStatePurchasing");
break;
default:
break;
}
};
}
Please let me know what is the issue.
I found the solution. It was working in both iOS in device. The Problem is of validate receipt. If you are testing inApp purchase using test user then you need to use sandbox verify receipt that is
#define TMS_SANDBOX_VERIFY_RECEIPT_URL #"https://sandbox.itunes.apple.com/verifyReceipt". but when you are uploading app to the appstore then you need to change it to
#define ITMS_PROD_VERIFY_RECEIPT_URL  #"https://buy.itunes.apple.com/verifyReceipt".
So when user download the app from the app store then for the valid receipt verification 'ITMS_PROD_VERIFY_RECEIPT_URL' is must. otherwise it will give failed receipt verification.
So please use #define ITMS_PROD_VERIFY_RECEIPT_URL #"https://buy.itunes.apple.com/verifyReceipt" when you are uploading app to the app store.

Get list of purchased products, inApp Purchase iPhone

I am implementing in app purchases for my iOS application. Apple has rejected my binary for not restoring purchased products. In my application once the user taps the icon of product if item is locked he/she directed to inApp purchase process else the products gets openend. There is no visual Buy button. Now apple is saying to provide the restore button? Can anybody tell me how to handle this? I have tried
- (void) checkPurchasedItems
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}// Call This Function
//Then this delegate Function Will be fired
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
alreadyPurchasedItems = [[NSMutableArray alloc] init];
NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *ID = transaction.payment.productIdentifier;
[alreadyPurchasedItems addObject:ID];
}
}
On application launch but paymentQueueRestoreCompletedTransactionsFinished method is never called so that I can get the list of already purchased items and then directly inform user if he/she purchased this already.
How do you set the delegate of [SKPaymentQueue defaultQueue]? I guess you already do smt like:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
After that [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; should result in below method to be fired. So the case SKPaymentTransactionStateRestored is where you implement it:
-(void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
...
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
...
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
...
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:
break;
}
};
}
You may have a look at this tutorial, restoration is explained in more detail towards the very end of it.
http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial

updatedTransactions failed to be called back after iOS 4.2

We've been having a tricky problem with store kit. We are trying to implement correctly the in-app purchase renewable subscriptions process into an app that already is on the appStore with standard in-app purchase (non-consumable products) set up.
So far, and after spending 2 months fighting with the sandbox weird behavior, we came up with a working solution on a test iPad running iOS4.2.
Nasty surprise we got when testing this code on a iPad with 4.3 or 5.0, it does not have the same behavior.
We narrowed it down to this simple fact:
- iOS4.2 : the updatedTransactions callback is working properly
- iOS4.3 and above: the updatedTransactions callback is never called by the sandbox.
Any ideas on why a store kit code that works on iOS4.2 wouldn't work on following iOS versions? I didn't see anything deprecated on this.
Here is the code of our updatedTransactions code:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"Add payment queue");
for(SKPaymentTransaction *transaction in transactions) {
NSLog(#"Transaction state: %d, %d, %d, %d", transaction.transactionState, SKPaymentTransactionStatePurchased, SKPaymentTransactionStateFailed, SKPaymentTransactionStateRestored);
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
if([transaction.payment.productIdentifier isEqualToString:FM_PRODUCT_IDENTIFIER_SUBSCRIPTION]){
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:transaction.transactionReceipt forKey:#"TransactionReceiptOfTransaction"];
}
[self completeTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"%#", transaction.error);
[self failedTransaction:transaction];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error", #"")
message:NSLocalizedString(#"Your subscription has expired.", #"")
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
SAFE_RELEASE(alert);
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
if([transaction.error code] != SKErrorPaymentCancelled) {
if([transaction.payment.productIdentifier isEqualToString:FM_PRODUCT_IDENTIFIER_SUBSCRIPTION]){
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:transaction.transactionReceipt forKey:#"TransactionReceiptOfTransaction"];
}
}
break;
case SKPaymentTransactionStateRestored:
if([transaction.payment.productIdentifier isEqualToString:FM_PRODUCT_IDENTIFIER_SUBSCRIPTION]){
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:transaction.transactionReceipt forKey:#"TransactionReceiptOfTransaction"];
}
[self restoreTransaction:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
default:
NSLog(#"Other");
break;
}
}
}
Thanks,
Stephane
I had exactly the same problem and to solve it I switched the source file which contains the
addTransactionObserver call to ARC = none.
// StoreKit
CustomStoreObserver *observer = [[CustomStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
Seems that ARC or StoreKit framework can't work well together.
To switch a source file with the ARC = none, I set -fno-objc-arc flag as Josh Caswell says it in the "Disable Automatic Reference Counting for Some Files" question.
The answer is to add -fno-objc-arc to the compiler flags for the files you don't want ARC. In Xcode 4, you can do this under your target -> Build Phases -> Compile Sources.
Update: My explanation about that behavior is that ARC wants to manage the memory itself but it does not do it well for Storekit. ARC releases too early the 'observer' object. That cause the crash when the InAppPurchase module (Apple side) wants to advise your "updatedTransactions"
method. And for me to stuck 'observer' object in memory during the life of my application. I decided to give it the property retain in my .h file.

Problems with iPhone SDK StoreKit

In my iPhone app Im using StoreKit to make it possible for users to buy subscriptions in the app. The problem I'm having is that suddenly everytime I start the app SKPaymentTransactionStatePurchased is sent to the observer so the app tries to buy the subscription again and again. And if I try to buy the subscription again from the list of subscriptions in the app I get a message saying "You've already purchased this In App Purchase but it hasn't been downloaded." then failedTransaction is called with SKErrorPaymentCancelled.
EDIT: I have now found a lot of threads about this in the Apple Developer Forum, for example: https://devforums.apple.com/thread/73818 and /thread/73572, it seems like a lot of developers have the same problem..
This is the code I'm using, can you see something wrong with it?
-(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) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Error");
} else {
NSLog(#"Cancel");
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void) restoreTransaction: (SKPaymentTransaction *)transaction
{
[self subscribe:transaction];
}
-(void) completeTransaction: (SKPaymentTransaction *)transaction
{
[self subscribe:transaction];
}
-(void)subscribe: (SKPaymentTransaction*)transaction {
NSInteger errorCode = //Connects to my server that verifies receipt with Apple server etc..
if (errorCode==0) {
[self provideContent];
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
I had the exact problem when using the StoreKit the first time and it happened because as I was implemented the code I was leaving transactions unfinished.
So, when you start the app, you'll need to loop through the queue and finish all transactions. You shouldn't need to do this is you cover all outcomes (which, based on the code above, you did).
After talking to Apple support I was able to solve this issue. It seems to be a problem from Apple that has now been fixed.