【问题标题】:iOS In app purchase doesn't call restoreTransaction on already purchased itemiOS 应用内购买不会对已购买的项目调用 restoreTransaction
【发布时间】:2013-08-11 08:37:12
【问题描述】:

我正在使用 ios 进行应用内购买,我几乎没有任何疑问,这些疑问会对像我这样的新手有所帮助,因此请了解应用内购买。

1) 如果用户“如果他之前删除了我的应用程序,则将我的应用程序安装到新设备或同一设备中”我的应用程序出现问题,当时用户尝试购买已购买的商品我的代码不会调用restoreTransaction updatedTransactions的开关盒

我收到 you ve already purchased this tap okay to downlaod it FREE Enviornment sandbox 的消息,它调用了 SKPaymentTransactionStatePurchased 案例,但它没有调用 SKPaymentTransactionStateRestored 在我的案例中会出现什么问题..

所以我已经实现了单独的恢复按钮来恢复用户已经带来的所有视频项目,所以只需要知道它会在苹果商店拒绝我的应用吗?

2) 购买商品时只询问一次密码,之后不再询问购买密码。它直接显示带有确认按钮的对话框,但我的项目经理说它应该为每次购买商品询问密码。

每次我尝试恢复购买时它都会要求输入密码..奇怪。

3) 目前我正在沙盒中测试,当我尝试使用真实的苹果 ID 购买时,它显示购买失败(我必须使用测试帐户来测试购买,如苹果文档所述)但我的项目经理说它应该要求新的如果您在沙箱中进行测试,请测试用户名(如文档所述,您必须手动退出设置,但我的项目经理希望它应该自动退出),

所以只需要问是否可以通过编码退出并显示标志框(我知道这是不可能的,但我问的是信息)

4) 目前我的应用程序正在沙盒环境中运行,但我是否需要为我的应用程序进行实际购买更改?..或者当苹果验证我的应用程序并对其进行签名并在应用程序上可用时,苹果会自动将沙盒更改为实际购买商店?

5)我正在我自己的服务器上验证交易,所以如果我在沙箱环境中,我将发送沙箱 1,否则我必须发送 0(目前我将沙箱值硬编码为 1)所以有什么方法可以检测环境是沙箱还是真实的?

这是我的购买代码和恢复按钮代码 任何帮助表示赞赏

购买代码

- (IBAction)PaymentButton:(id)sender {

loadingHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
loadingHUD.labelText = NSLocalizedString(@"Loading", nil);
[loadingHUD show:YES];
[self startPurchase];// call the restore Purchase method

  //[loadingHUD showWhileExecuting:@selector(startPurchase) onTarget:self withObject:nil animated:YES];// call the restore Purchase method
  }

- (void)startPurchase {
if([SKPaymentQueue canMakePayments]) {
    NSLog(@"IN-APP:can make payments");
    [self requestProductData];
}
else {
    NSLog(@"IN-APP:can't make payments");
    loadingHUD.hidden=YES;
 }
}

 - (void)requestProductData {
NSLog(@"IN-APP:requestProductData");
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObject:myIdentifier]];
request.delegate = self;
[request start];
NSLog(@"IN-APP:requestProductData END");
NSLog(@"Productdata is %@",myIdentifier);

   }

 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
  [[SKPaymentQueue defaultQueue] addTransactionObserver:self];  
   @try {
    SKProduct *product = [response.products objectAtIndex:0];
    SKPayment *newPayment = [SKPayment paymentWithProduct:product];
    [[SKPaymentQueue defaultQueue] addPayment:newPayment];
    NSLog(@"IN-APP:productsRequest END");

  }
 @catch (NSException *exception) {

     // Failed to purchase Hide the progress bar and Display Error Dialog
     loadingHUD.hidden=YES;
     UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Error in Product id can not purchase" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
     [alertView show];

   }

   }


    - (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(@"Transaction Completed");
// Finally, remove the transaction from the payment queue.
[self verifyReceipt:transaction]; // Call the verifyReceipt method to send transaction.bytes

 NSLog(@"Purchase Transaction finish");
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
     }

  - (void) restoreTransaction: (SKPaymentTransaction *)transaction

  NSLog(@"Transaction Restored %@",transaction.originalTransaction.payment.productIdentifier);
// You can create a method to record the transaction.
// [self recordTransaction: transaction];
loadingHUD.hidden=YES;

// You should make the update to your app based on what was purchased and inform user.
// [self provideContent: transaction.payment.productIdentifier];
// Finally, remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
  }

   - (void) failedTransaction: (SKPaymentTransaction *)transaction
   {
loadingHUD.hidden=YES;// hide loadingHUD

  if (transaction.error.code != SKErrorPaymentCancelled)
  {
    // Display an error here.
    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];
  }

为了恢复它简单

-(void)startRestore 
  {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; 
}
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
 {
if ([queue.transactions count] == 0)
{
    HUD.hidden=YES;
    UIAlertView *restorealert = [[UIAlertView alloc]
                                 initWithTitle:@"Restore"
                                 message:@"There is no products purchased by you"
                                 delegate:self
                                 cancelButtonTitle:@"Ok"
                                 otherButtonTitles:nil];

    [restorealert show];
}

else
{

    NSLog(@"received restored transactions: %i", queue.transactions.count);

    for (SKPaymentTransaction *transaction in queue.transactions)
    {
        NSString *temp = transaction.payment.productIdentifier;

        NSString *testID = [temp stringByReplacingOccurrencesOfString:projectIdString withString:@""];
        NSString *productID = [testID stringByReplacingOccurrencesOfString:@"." withString:@""]; // remove Dot
        NSLog(@"cutted string is %@",productID);

        [purchasedItemIDs addObject:productID];

        NSLog(@"** Purchased item is %@",purchasedItemIDs);
    }
    HUD.hidden=YES;
    HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    HUD.labelText = NSLocalizedString(@"Restoring", nil);
    [HUD showWhileExecuting:@selector(restorePurchasedItem) onTarget:self withObject:nil animated:YES];// call the restore Purchase method

  }
   }

【问题讨论】:

标签: iphone ios ipad in-app-purchase


【解决方案1】:

恕我直言,如果有不同的问题,您最好将这个问题剪裁一些。

无论如何,我会尽力回答:

1) 这就是它的意思。 如果您手动调用restoreCompletedTransactions,您将获得与SKPaymentTransactionStateRestored 状态的交易。 AFAIK,这是为此创建一个按钮(例如,“恢复购买”)的正常做法。

2) 对此无话可说。通常,每次,当您的应用要进行购买(并拿走一些用户的钱)时,它应该询问用户密码。

3) AFAIK,不。您通过 iTunes/AppStore 应用程序使用 Apple 服务器。记住用户的 iTunes 帐户是他们的工作。而且我认为他们没有给您任何使当前用户注销的方法。你的项目经理应该理解它:-)

4)、5) 生产/沙盒环境没有区别直到您尝试验证收据。如果您谈论使用服务器,我希望您使用服务器来验证收据。

在设备方面,您所做的就是使用StoreKit 框架。您无需定义任何指向 Apple 服务器的 URL,您只需使用框架的类并调用它的方法。 AFAIK,您无需同时为沙盒和生产支持对代码进行任何更改。

但在您的服务器端,情况有所不同:当您的应用程序已经投入生产时,您应该向 https://buy.itunes.apple.com/verifyReceipt 发送 HTTP POST 请求。另一方面,当您的应用仍在沙盒中时,请使用https://sandbox.itunes.apple.com/verifyReceipt

如何处理?观看关于自动更新订阅的 WWDC 2012 视频 308。他们提出了两种方法:

1) 智能服务器。 当您的应用程序向您的服务器发送收据时,它还会传递一些参数,让服务器知道使用哪个 Apple 的服务器。如我所见,您已经使用了这种方法。

2) 响应式服务器。您的服务器总是将收据发送到 Apple 的生产服务器。您检查响应,如果状态为21007(按照文档,“此收据是沙盒收据,但已发送到生产服务进行验证。”),您将相同的请求发送到 Apple 的沙盒服务器。

【讨论】:

    【解决方案2】:

    使用此代码,它可能会对您有所帮助....

        //To Show Alert of InAppPurchase
        - (void)showAlert
        {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"Click buy to 
        purchase full tracks and other functionalities of the application." delegate:self
                                          cancelButtonTitle:nil otherButtonTitles:nil, nil];
    
        [alert addButtonWithTitle:@"Buy"];
        [alert addButtonWithTitle:@"Restore Transaction"];
        [alert addButtonWithTitle:@"Cancel"];
        [alert show];
        }
    
    
        -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:
        (NSInteger)buttonIndex{
    
        if(buttonIndex==0)
        {
        // For buy an item
        [self Declarations];
        }
    
        else if(buttonIndex == 1)
        {
        //For Restore Previous Transaction
        [self restorePreviousTransaction:nil];
        }
    
        else
        {
           //Do something here..
        }
     }
    
       #pragma mark In App Purchase
    
       -(void)Declarations
       {
         if ([SKPaymentQueue canMakePayments]) {
    
         NSLog(@"parental functions are disabled");
    
         SKProductsRequest *productRequest = [[SKProductsRequest 
         alloc]initWithProductIdentifiers:[NSSet 
         setWithObjects:@"com.tapmobi.careerandsuccess.inapp",nil]];
    
         productRequest.delegate=self;
    
         [productRequest start];
    
         [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    
         }
    
         else 
         {
        NSLog(@"parental functions are enabled");
         }
      }
    
      - (IBAction)restorePreviousTransaction:(id)sender {
    
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
        [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
      }
    
      -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:
      (SKProductsResponse *)response
      {
    
      SKProduct *validProduct=nil;
    
      int count = [response.products count];
    
      NSLog(@"number of prouducts present:%d",count);
    
      if(count==0){
    
        [MBProgressHUD hideAllHUDsForView:self.view animated:YES];
        return;
    
      }
    
      validProduct = [response.products objectAtIndex:0];
    
      NSLog(@"the product is :%@",validProduct.localizedTitle);
    
      SKPayment *skpayment = [SKPayment paymentWithProduct:validProduct];
    
      [[SKPaymentQueue defaultQueue] addPayment:skpayment];
    
      [[SKPaymentQueue defaultQueue]addTransactionObserver:self];
    
     }
    
    
     -(void)requestDidFinish:(SKRequest *)request
     {
         [MBProgressHUD hideAllHUDsForView:self.view animated:YES];
     }
    
     -(void)request:(SKRequest *)request didFailWithError:(NSError *)error
     {
         [MBProgressHUD hideAllHUDsForView:self.view animated:YES];
    
          NSLog(@"Failed to connect with error: %@", [error localizedDescription]);
     }
    
     -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
     {
         [MBProgressHUD hideAllHUDsForView:self.view animated:YES];
    
         NSString *message = [[NSString alloc]init];
    
         BOOL bSuccess = NO;
    
         for (SKPaymentTransaction *transaction in transactions) {
    
         switch (transaction.transactionState) 
         {
            case SKPaymentTransactionStatePurchasing:
    
                NSLog(@"stuff is getting purchased");
                break;
    
            case SKPaymentTransactionStatePurchased:
    
                NSLog(@"purchased properly");
                message = @"Thank you.";
    
                [[NSUserDefaults standardUserDefaults] setValue:@"Full Version"        
                forKey:PURCHASED_KEY];
    
                [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                bSuccess = YES;
    
                break;
    
            case SKPaymentTransactionStateRestored:
    
                [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                break;
    
            case SKPaymentTransactionStateFailed:
    
                if (transaction.error.code != SKErrorPaymentCancelled) {
                    NSLog(@"error happened");
    
                    message = @"Purchase is not successfull. Try again later";
                }
    
                [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                break;
    
            default:
                break;
          }     
       }
    
        if (bSuccess){
    
            UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"" message:message   
            delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
    
            [alert show];
            }
       }
    

    快乐编码..

    【讨论】:

    • 但是在您的代码中您已经编写了 [[SKPaymentQueue defaultQueue]finishTransaction:transaction];休息;但在我的情况下,在购买已购买的产品时不会调用“SKPaymentTransactionStateRestored”..我会看到您已经免费下载该项目的对话框,但它会在 switch 中调用 SKPaymentTransactionStatePurchased 案例..
    • 通过这个链接..raywenderlich.com/21081/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-17
    • 2015-03-13
    相关资源
    最近更新 更多