当前位置: 动力学知识库 > 问答 > 编程问答 >

objective c - SKPayment Error, SKProduct keeps returning nill

问题描述:

I am working in Spritekit and I am trying to enable SKPayments in my game to give the user 5 lives once they make their purchase. But everytime I run the app and try to make a purchase I keep getting an error that my SKProduct FiveLives is nil. The code and the line I keep getting the error on is below. Can somebody please help me?

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

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

[self getProduct1ID:self];

SKPayment *payment = [SKPayment paymentWithProduct:_FiveLives];

ERROR LINE [[SKPaymentQueue defaultQueue] addPayment:payment];

}

The Rest of my class is here

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;

}

}

}

NEW CODE AFTER I USED THE TUTORIAL

PURCHASELIVES

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

BUYLIVES

#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

网友答案:

The problem is here:

[self getProduct1ID:self];

SKPayment *payment = [SKPayment paymentWithProduct:_FiveLives];

getProduct1ID issues a request to receive all products from the store. However this request is NON-blocking. meaning that by the time the request completes you are already calling paymentWithProduct. And in this time _FiveLives was yet to be set with the valid product and it will be nil.

What I'll recommend you to do is call getProduct1ID at the START of your application (I do it in the app delegate method application:didFinishLaunchingWithOptions:) and store the product as you do in a member variable you can use later.

Then when the user clicks the purchase button just use the product instance to make the purchase.

I'll also recommend you to look here and see how the flow of IAP is commited. Great tutorial which I followed myself and it works perfectly.

分享给朋友:
您可能感兴趣的文章:
随机阅读: