【问题标题】:SKPayment Error, SKProduct keeps returning nillSKPayment 错误,SKProduct 一直返回 nil
【发布时间】:2015-06-02 20:02:14
【问题描述】:

我正在使用 Spritekit,我正在尝试在我的游戏中启用 SKPayments,以便在用户购买后为他们提供 5 条生命。但是每次我运行该应用程序并尝试进行购买时,我都会收到一个错误,即我的 SKProduct FiveLives 为零。我不断收到错误的代码和行如下。有人可以帮帮我吗?

self.product1ID = @"com.retrogames.5Lives";

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

[self getProduct1ID:self];

SKPayment *payment = [SKPayment paymentWithProduct:_FiveLives];
ERROR LINE  [[SKPaymentQueue defaultQueue] addPayment:payment];

}

我班的其他人都在这里

StoreViewController.h

#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>    
@interface StoreViewController : UIViewController <SKPaymentTransactionObserver, SKProductsRequestDelegate>{
SKProductsRequest *productsRequest;
}



@property (strong, nonatomic) SKProduct *FiveLives;
@property (strong, nonatomic) NSString *product1ID;

- (IBAction)_5Lives:(id)sender;

-(void)getProduct1ID:(UIViewController *)viewcontroller;

@end

StoreViewController.M

  #import "StoreViewController.h"

 @interface StoreViewController ()

 @end

 @implementation StoreViewController

 - (void)viewDidLoad {
 [super viewDidLoad];

   // Do any additional setup after loading the view.
  }

  - (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
   // Dispose of any resources that can be recreated.
 }

 -(void)getProduct1ID:(UIViewController *)viewcontroller{

  if ([SKPaymentQueue canMakePayments]) {
    SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithObject:self.product1ID]];
    request.delegate = self;
    [request start];
    }
   else{
       UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Sorry purchase was unsuccessful" message:@"Please enable in app purchases in your settings" delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
    [alert show];

   }


  }

  #pragma mark _
  #pragma mark SKProductsRequestDelegate

  -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {

   NSArray *Products = response.products;

   if (Products.count != 0) {

    _FiveLives = Products[0];

    }

   Products = response.invalidProductIdentifiers;

    for (SKProduct *FiveLives in Products) {
      NSLog(@"Product Not Found : %@", FiveLives);
    }

   }

 - (IBAction)_5Lives:(id)sender {
self.product1ID = @"com.retrogames.5Lives";

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

[self getProduct1ID:self];

SKPayment *payment = [SKPayment paymentWithProduct:_FiveLives];
[[SKPaymentQueue defaultQueue] addPayment:payment];

 }

 - (IBAction)RestorePurchases:(id)sender{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
 }

 -(void)add5Lives{
NSLog(@"BUY5LIVES");
GameLives = GameLives + 5;
    [[NSUserDefaults standardUserDefaults] setInteger:GameLives forKey:@"Key"];

   }

  -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for (SKPaymentTransaction *transaction in transactions) {

    switch (transaction.transactionState) {
        case SKPaymentTransactionStatePurchased:[self add5Lives];{

            NSLog(@"TRANSACTION SUCCESSFUL");
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            break;
        }
        case SKPaymentTransactionStateFailed:NSLog(@"TRANSACTION FAILED");{
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Payment was unsuccessful" message:@"Payment was unable to be processed." delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
            [alert show];
        }

            break;

            default:
            break;
    }
    }
}

使用教程后的新代码

购买

PurchaseLives.m

+ (PurchaseLives *)sharedInstance {
static dispatch_once_t once;
static PurchaseLives * sharedInstance;
dispatch_once(&once, ^{
    NSSet * productIdentifiers = [NSSet setWithObjects:
                                  @"com.retrogames.5Lives",
nil];
    sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
});
return sharedInstance;
}

 @end

IAPHelper

IAPHelper.m
NSString *const IAPHelperProductPurchasedNotification = @"IAPHelperProductPurchasedNotification";
@interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
@end

@implementation IAPHelper {
// 3
SKProductsRequest * _productsRequest;
// 4
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);
        }


    }
    [[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...");
   _productsRequest = nil;

  NSArray * skProducts = response.products;
  for (SKProduct * skProduct in skProducts) {
      NSLog(@"Found product: %@ %@ %0.2f",
          skProduct.productIdentifier,
          skProduct.localizedTitle,
          skProduct.price.floatValue);

       FiveLives = skProducts[0];
       NSLog(@"Products = %@",skProducts);

     }

    _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 %@...", FiveLives.productIdentifier);

 SKPayment * payment = [SKPayment paymentWithProduct:FiveLives];
  [[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;
    }
}
}



// 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];

 }

- (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];
 }


 @end

买生活

#import "BuyLivesViewController.h"
#import "PurchaseLives.h"
#import "IAP Helper.h"
@interface BuyLivesViewController (){
NSArray *_products;
}

@end

@implementation BuyLivesViewController

 - (void)viewDidLoad {
 [super viewDidLoad];
// Do any additional setup after loading the view.
 }

 - (void)didReceiveMemoryWarning {
 [super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated {
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];
 }

- (void)viewWillDisappear:(BOOL)animated {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 - (IBAction)Buy5Lives:(id)sender {



  [[PurchaseLives sharedInstance] buyProduct:FiveLives];
 }





 - (void)productPurchased:(NSNotification *)notification {

    NSString * productIdentifier = notification.object;
    [_products enumerateObjectsUsingBlock:^(SKProduct * product, NSUInteger idx, BOOL *stop) {
        if ([product.productIdentifier isEqualToString:productIdentifier]) {
        GameLives = GameLives + 5;

      }
   }];

  }
   @end

【问题讨论】:

  • 为什么您的产品标识符在开头和结尾都包含点?这是在itc中定义标识符的方式吗?
  • 对不起,我不是故意把点放在最后。请忽略。
  • 您的 5 个生命的产品标识符不同(上方和下方)。这是故意的吗?目前我不明白为什么它不应该工作,除非你因为无效的产品标识符而得到一个空产品。
  • 很抱歉。我也对其进行了编辑和更改。除此之外,您不明白为什么我的 _FiveLives 返回零?
  • 在这一行:_FiveLives = Products[0]; Products[0] 是否包含有效的 SKProduct 对象?

标签: objective-c debugging in-app-purchase skpaymenttransaction


【解决方案1】:

问题出在这里:

[self getProduct1ID:self];

SKPayment *payment = [SKPayment paymentWithProduct:_FiveLives];

getProduct1ID 发出从商店接收所有产品的请求。但是,此请求是非阻塞的。这意味着当请求完成时,您已经在调用paymentWithProduct。而此时_FiveLives 尚未设置为有效产品,它将是nil

我建议您在应用程序开始时调用getProduct1ID(我在应用程序委托方法application:didFinishLaunchingWithOptions: 中执行此操作)并将产品存储在您可以稍后使用的成员变量中.

然后当用户点击购买按钮时,只需使用产品实例进行购买。

我还建议你看看here,看看 IAP 的流程是如何提交的。我自己学习的很棒的教程,效果很好。

【讨论】:

  • 谢谢@giorashc。在application:didFinishLaunchingWithOptions:) 中写像StoreViewController *Store; [Store getProduct1ID:Store]; 这样简单的东西就够了吗?
  • 是的,但您的 StoreViewController 实例必须是全局共享的。如果您创建一个实例然后释放它,它将一无所获。看看我发布的链接。看看它如何使用单例模式来全局访问它
  • 我使用了您推荐给我的 Ray Wenderlich 教程,但该教程的目标是取代常规应用程序的表格视图应用程序。我试图针对 UIViewController 而不是 Tableview 的代码,但我仍然遇到同样的问题,因为我的 SKProduct 为 nil
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-02
  • 2016-10-05
  • 1970-01-01
  • 2016-08-13
  • 2011-04-07
  • 2021-09-18
  • 1970-01-01
相关资源
最近更新 更多