'In app purchases not selectable in appstoreconnect

My new app has four in app purchases (consumables) and I submitted the first version with these IAPs. These IAP's were marked as "Ready for Review". However, the app got rejected due to another reason, and when I uploaded a new build, I couldn't select these IAP's anymore in the app details page, even though they're still "Ready for Review": screenshot of the app details page

So after resubmitting a new version of the app for review, I got this rejection information:

We found that your in-app purchase products exhibited one or more bugs when reviewed on iPad running iOS 15.4 on Wi-Fi.

Specifically, we were not able to buy the in app purchases. The buttons did not react to taps

Next Steps

When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code "Sandbox receipt used in production," you should validate against the test environment instead.

I tested everything on Testflight before and all the IAP's were working fine. I know that prior to submitting an app with IAP's, these purchases have to be selected on the app details page, so I'm curious why I can't select them and if that's causing the issue.

Right when the app launches, in the AppDelegate, I fetch the products: IAPManager.shared.fetchProducts()

and the code for the IAPManager is as follows:

final class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {

static let shared = IAPManager()
var products = [SKProduct]()
enum Product: String, CaseIterable {
    case firstIdentifier = "com.fahrprueferCreate.tokens_1_1000"
    case secondIdentifier = "com.FahrprueferCreate.tokens_5_4000"
    case thirdIdentifier = "com.FahrprueferCreate.tokens_10_8000"
    case fourthIdentifier = "com.FahrprueferCreate.tokens_20_15000"
    
    var count: Int {
        switch self {
        case .firstIdentifier:
            return 1
        case .secondIdentifier:
            return 5
        case .thirdIdentifier:
            return 10
        case .fourthIdentifier:
            return 20
        }
    }
}
private var completion: ((Int) -> Void)?

// Fetch Product Objects from Apple
func fetchProducts() {
    let request = SKProductsRequest(productIdentifiers: Set(Product.allCases.compactMap({ $0.rawValue})))
    request.delegate = self
    request.start()
}

// Prompt a product payment transaction
public func purchase(product: Product, completion: @escaping ((Int) -> Void)) {
    guard SKPaymentQueue.canMakePayments() else {
        // Show some error here
        return
    }
    
    guard let storeKitProduct = products.first(where: { $0.productIdentifier == product.rawValue }) else {
        return
    }
    self.completion = completion
    
    let payment = SKPayment(product: storeKitProduct)
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(payment)
}

// Observe the transaction state
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    transactions.forEach({ transaction in
        switch transaction.transactionState {
        case.purchasing:
            break
        case .purchased:
            if let product = Product(rawValue: transaction.payment.productIdentifier) {
                completion?(product.count)
            }
            SKPaymentQueue.default().finishTransaction(transaction)
            SKPaymentQueue.default().remove(self)
            break
        case .restored:
            break
        case .failed:
            break
        case .deferred:
            break
        @unknown default:
            break
        }
    })
}

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    self.products = response.products
    print("products: ", response.products)
}

func request(_ request: SKRequest, didFailWithError error: Error) {
    guard request is SKProductsRequest else {
        return
    }
    
    print("Product fetch request failed")
}

}



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source