I tried several solutions on this board but never successful. What I want is when the user click Buy Button, an UIAlertView with UIActivityIndicatorView appears waiting for the app accessing the app store. But I do not know where to dismiss this UIAlertView once the purchase is done. I know that to dismiss a UIAlertView, we use: [alert dismissWithClickedButtonIndex:-1 animated:YES];
So would you please help me answer two questions:
1) Is my code below OK or any other better to achieve it?
2) Where should I dismiss UIAlertView for all cases:
User accepts to purchase
User cancels to purchase
Purchase is not successful
Following is my code:
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
UIAlertView *alert;
for(SKPaymentTransaction *transaction in transactions){
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
alert = [[UIAlertView alloc]initWithTitle: #"In App Purchase" message: #"Processing your purchase..." delegate: nil cancelButtonTitle: nil otherButtonTitles: nil];
UIActivityIndicatorView *ind = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
[ind startAnimating];
[alert addSubview: ind];
[alert show];
[ind release];
[alert release];
break;
case SKPaymentTransactionStatePurchased:
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Complete"
message:#"You have bought the full version!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code !=SKErrorPaymentCancelled) {
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Error"
message:#"Purchase not successful!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
[tmp release];
}
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
}
}
}
Try add
case SKPaymentTransactionStatePurchased:
[alert dismissWithClickedButtonIndex:-1 animated:YES];
and
case SKPaymentTransactionStateFailed:
[alert dismissWithClickedButtonIndex:-1 animated:YES];
Related
Hey guys i am frustrated to retrieve a product from iTunesConnect but it always return invalid ID. I fulfill all requirements but not getting any response from it. I followed these steps and my answer is Yes..
.Please give me some solution to solve this problem. Thanks in advance..
1.Have you enabled In-App Purchases for your App ID?
2.Have you checked Cleared for Sale for your product?
3.Have you submitted (and optionally rejected) your application binary?
4.Does your project’s .plist Bundle ID match your App ID?
5.Have you generated and installed a new provisioning profile for the new App ID?
6.Have you configured your project to code sign using this new provisioning profile?
7.Are you building for iPhone OS 3.0 or above?
8.Are you using the full product ID when when making an SKProductRequest? Have you waited several hours since adding your product to iTunes Connect?
9.Are your bank details active on iTunes Connect? (via Mark) Have you tried deleting the app from your device and reinstalling? (via Hector, S3B, Alex O, Joe, and Alberto)
Code:
-(void)fetchAvailableProducts{
NSSet *productSet = [NSSet setWithObjects:#"com.magazineapp.books",nil];
productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productSet];
productsRequest.delegate = self;
[productsRequest start];
}
- (BOOL)canMakePurchases
{
return [SKPaymentQueue canMakePayments];
}
- (void)purchaseMyProduct:(SKProduct*)product{
if ([self canMakePurchases]) {
SKPayment *payment = [SKPayment paymentWithProductIdentifier:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// [[SKPaymentQueue defaultQueue] addPayment:payment];
}
else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Purchases are disabled in your device" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
}
-(IBAction)purchase:(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:kMySubscriptionFeature]) {
NSLog(#"Purchased ");
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:
#"Purchase is completed succesfully" message:nil delegate:
self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(#"Restored ");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
NSLog(#"Purchase failed ");
break;
default:
break;
}
}
}
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
SKProduct *validProduct = nil;
NSUInteger count = [response.products count];
if (count>0) {
validProducts = response.products;
validProduct = [response.products objectAtIndex:0];
if ([validProduct.productIdentifier
isEqualToString:kMySubscriptionFeature]) {
DLog(#"vaild product");
}
} else {
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Not Available"
message:#"No products to purchase"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
[activityIndicatorView stopAnimating];
}
I'm having a trouble with in app purchase things when an error is occurred.
I might be misunderstood in app purchase transaction, so let me know if there is something wrong.
In the payment queue observer is same.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
switch(transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self purchasedTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
break;
default:
break;
}
}
and in the restoreTransaction is
-(void)restoreTransaction:(SKPaymentTransaction *)transaction{
NSLog(#"restore");
[app hideIndicatorView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
});
[self verifyReceipt];
}
I charge the in app currency, in the verifyReceipt method which looks like
- (void)verifyReceipt
{
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (!receipt) { /* No local receipt -- handle the error. */
if ([UIAlertController class]) {
UIAlertController *alert = [UIAlertController
alertControllerWithTitle:nil
message:#"verify fail"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:nil];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:#"verify fail" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}else{
//server confirmation with [receipt base64EncodedStringWithOptions:0]
}
It doesn't work when a user takes restore transaction.
(Normally, it occurred when the user has some network problem or etc...)
So, please tell me, my work flow is wrong? or I missed something?
Is there any way to test restore transaction?
Only the email-subject is getting set to "Test mail" and recipients remain empty. MailController opens for a while and gives the alert as "Message Cancelled".
Anyone please help me out!
- (IBAction)sendEmail:(id)sender { //This is a button to send E-mail
mailController=[[MFMailComposeViewController alloc]init];
NSString *emailBody = #"Test mail from Fortune";
[mailController setToRecipients:[NSArray arrayWithObjects:#"hi#fortune.com",#"hello#fortune.com", nil]];
[mailController setCcRecipients:#[#"gthg65#gmail.com"]];
[mailController setBccRecipients:#[#"resumes#fortune.com"]];
[mailController setMessageBody:emailBody isHTML:NO];
[mailController setSubject:#"Test mail "];
mailController.mailComposeDelegate=self;
[self presentViewController:mailController animated:YES completion:nil];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
NSString *messageResult;
if (error!=nil)
{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Mail Error" message:[error localizedDescription] delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
else{
switch (result) {
case MFMailComposeResultCancelled:
messageResult=#"Mail Cancelled";
break;
case MFMailComposeResultFailed:
messageResult=#"Mail Failed";
break;
case MFMailComposeResultSaved:
messageResult=#"Mail Saved";
break;
case MFMailComposeResultSent:
messageResult=#"Mail Sent";
break;
default:
break;
}
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Mail Result" message:messageResult delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Works on a test device. Also Apple recently changed their developer account memberships and you can register for a free apple developer account and run the app on a test device that way.
i made a game in sprite kit i thought to make full and lite one instead i made in-app purchase i thought its easy with one target, my idea is users can play upto 99 points and if they want to play rest of the game they have to buy , as i made 2 scenes 1) mainmenu scene 2) playscene i have buttons for IAP and restore in both scenes and even while users playing at 99 point i add this code.if this way is wrong which is the best way to approach please let me know as this is my first sprite kit app
if (_gameScore==99){
//added this code in buy button as well
BOOL isInappSuccess = [[NSUserDefaults standardUserDefaults] boolForKey:#"IsInAppComplete"];
if (!isInappSuccess) {
//[SVProgressHUD showWithStatus:#"Loading..." maskType:SVProgressHUDMaskTypeGradient];
[[InAppRageIAPHelper sharedHelper] buyProductIdentifier:INAPP_PRODUCT_ADV];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"InApp" message:#"Item Already Purchased" delegate:Nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
//for non-consumable product you have to add following code in your application
//function checks whether user purchase a non-consumable or not
/*
change all function acc to your game
*/
-(void)setDone:(Boolean)status
{
[_urnsuserDefault setBool:status forKey:#“done];
[_urnsuserDefault synchronize];
}
-(Boolean)getDone
{
Boolean status=[_urnsuserDefault boolForKey:#"done"];
return status;
}
//if user already purchase non-consumable or not (check for Restore if user purchase non-consumable product);
-(void)checkForRestore
{
if([self getDone])
{
//if user successfully purchase a in app item
UIAlertView *productAlertPur = [[UIAlertView alloc]
initWithTitle:#"Restored"
message:#“your” msg
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[productAlertPur show];
}
else
{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
}
-(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
UIAlertView * removedAlert = [[UIAlertView alloc]
initWithTitle:nil message:[NSString stringWithFormat:#"Restoration failed as %#.",error.localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[removedAlert show];
}
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
purchasedItemIDs = [[NSMutableArray alloc] init];
if(queue.transactions.count==0){
UIAlertView *noproductAlert = [[UIAlertView alloc]
initWithTitle:#"Restored"
message:#"No Purchases to Restore"
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[noproductAlert show];
}
else{
if(![self getDone])
{
NSString *productID = #“company.game.productid;
[purchasedItemIDs addObject:productID];
UIAlertView *restoredAlert = [[UIAlertView alloc]
initWithTitle:#"Restored"
message:[NSString stringWithFormat:#"Math Lab Restored successfully."]
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[restoredAlert show];
[self viewWillAppear:YES];
[self setDone:TRUE];
}
}
}
I've developed an app that allows the user to text contacts that they add in the app. The problem I'm having is it seems that if the user already exists in the person's native iOS address book, the text will send no problem. But if the contact exists only within the app, the text will not go through. Has anyone else experienced something like this before?
EDIT: Code below
if([MFMessageComposeViewController canSendText])
{
controller.body = #"";
controller.recipients = arrayContactMobileStrings;
controller.messageComposeDelegate = self;
[self presentViewController:controller animated:YES completion:nil];
}
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Message not sent", #"") message:NSLocalizedString(#"Error sending message", #"")
delegate:self cancelButtonTitle:NSLocalizedString(#"OK", #"") otherButtonTitles: nil];
switch (result)
{
case MessageComposeResultCancelled:
[alert show];
break;
case MessageComposeResultFailed:
[alert show];
break;
case MessageComposeResultSent:
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:nil];
}