Home Unable to purchase IAP as sandbox user
Reply: 1

Unable to purchase IAP as sandbox user

Jack Richards
1#
Jack Richards Published in 2017-12-07 21:49:39Z

I am having a problem trying to purchase an IAP as a sandbox user. It's not the sandbox account that's the problem. It's the fact that my var products = [SKProduct]() array in IAPService.swift is empty.

Inside my StoreViewController.swift (which also stores my Game Center leaderboards):

class StoreViewController: UIViewController, GKGameCenterControllerDelegate {

    @IBAction func purchase(_ sender: UIButton) {
        IAPService.shared.purchase(product: .nonConsumable)
    }

    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        IAPService.shared.getProducts()

        print(IAPService.shared.products) // This is an empty array?

        authenticateLocalPlayer() // Related to Game Center
    }
}

Inside my IAPProducts.swift

enum IAPProducts: String {
    case nonConsumable = "com.nameofgame.nameofproduct"
}

Inside my IAPService.swift

import Foundation
import StoreKit

class IAPService: NSObject {

    private override init() {}
    static let shared = IAPService()

    var products = [SKProduct]()
    let paymentQueue = SKPaymentQueue.default()

    func getProducts() {
        let products: Set = [IAPProducts.nonConsumable.rawValue,
                             ]

        let request = SKProductsRequest(productIdentifiers: products)
        request.delegate = self
        request.start()
        paymentQueue.add(self)
    }

    func purchase(product: IAPProducts) {
        guard let productToPurchase = products.filter({ $0.productIdentifier == product.rawValue }).first else { return }

        let payment = SKPayment(product: productToPurchase)
        paymentQueue.add(payment)
    }
}

extension IAPService: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        products = response.products
    }
}

extension IAPService: SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {

            switch transaction.transactionState {
            case .purchased:

                if transaction.payment.productIdentifier == IAPProducts.nonConsumable.rawValue {
                    print("IAP Purchased")
                }
                break

            case .failed:
                SKPaymentQueue.default().finishTransaction(transaction)
                print(transaction.error!)
                break

            default: break
            }
        }
    }
}

Things I've done several things to try and solve the problem:

  1. Changed the Set in IAPService to an NSSet. This doesn't work because the set of Strings in let request = SKProductsRequest(productIdentifiers: products) cannot be converted to an NSSet.

  2. Checked multiple times that my BundleIDs match on Xcode and iTunes Connect. I've also done this check with the Product IDs to make sure there's a match between the IAP ID in iTunes Connect and the IAPProducts enum.

  3. I added this IAP on iTunes Connect 3 days ago, and I've checked that all my contracts for paid apps are in effect.

Please help me with this issue. Thanks.

Paulw11
2#
Paulw11 Reply to 2017-12-07 22:45:20Z

From the SKProductsRequest documentation

Note

Be sure to keep a strong reference to the request object; otherwise, the system might deallocate the request before it can complete.

Since you are creating your SKProductsRequest instance as a local variable, it is being released as soon as the getProducts function returns, before the delegate method can be called.

You need to use a property to hold a strong reference to your products request:

class IAPService: NSObject {

    private override init() {}
    static let shared = IAPService()

    var products = [SKProduct]()
    var request: SKProductsRequest?
    let paymentQueue = SKPaymentQueue.default()

    func getProducts() {
        let products: Set = [IAPProducts.nonConsumable.rawValue,
                             ]

        self.request = SKProductsRequest(productIdentifiers: products)
        request?.delegate = self
        request?.start()
        paymentQueue.add(self)
    }

    func purchase(product: IAPProducts) {
        guard let productToPurchase = products.filter({ $0.productIdentifier == product.rawValue }).first else { return }

        let payment = SKPayment(product: productToPurchase)
        paymentQueue.add(payment)
    }
}

extension IAPService: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        if !response.invalidProductIdentifiers.isEmpty {
            print("Invalid products identifiers received")
        }
        products = response.products
        self.request = nil
    }

    func request(_ request: SKRequest, didFailWithError error:Error) {
        print("Product request failed: \(error.localizedDescription)")
    }
}
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.296856 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO