Hi all. I'm attempting to add consumable IAPs to my IOS application. My code seems to work fine when I'm testing locally using the storekit.configuration; however, when I switched over to Sandbox testing, I get the Purchase pop-up in the app, and my PaymentQueue observer hits the "purchasing" status, but that's it. After I hit Purchase on the pop-up, my PaymentQueue observer never hits again. The purchase pop-up says the purchase was successful in the app, and it goes away, but it pops back up a few seconds later. In testing, my PaymentQueue observer hits and goes to the "purchased" case, so things work as-expected.
I've set my test account up in Sandbox as per Apple's instructions.
Has anyone else run into this? Is there a way for me to see the Queue real-time so I can see what's going on in there?
My app is still in development - I'm testing in TestFlight, but haven't submitted for full AppStore review yet, in case that matters....
Here's the code for my In-App Purchase manager, in case that helps.
//
// IAPManager.swift
//
//
import Foundation
import StoreKit
final class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
static let shared = IAPManager()
var products = [SKProduct]()
private var completion: ((Int) -> Void)?
enum Product: String, CaseIterable {
case tokens_10
var count : Int {
switch self {
case .tokens_10:
return 10
}
}
}
public func GetProducts() {
//Load all the product in our Product enum in
let request = SKProductsRequest(productIdentifiers: Set(Product.allCases.compactMap({ $0.rawValue })))
request.delegate = self
request.start()
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("Products found : \(response.products.count).")
self.products = response.products
}
public func purchase(product : Product, completion: @escaping ((Int) -> Void)) {
//Make sure the user is allowed to purchase stuff
guard SKPaymentQueue.canMakePayments() else {
return }
//Make sure the product IDs match
guard let storeKitProduct = products.first(where: { $0.productIdentifier == product.rawValue}) else {
return
}
self.completion = completion
let paymentRequest = SKPayment(product: storeKitProduct)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(paymentRequest)
}
func paymentQueue(_ queue: SKPaymentQueue,
shouldAddStorePayment payment: SKPayment,
for product: SKProduct) -> Bool {
print("Should add payment hit.")
return true
}
//MARK Error - 2024-02-02: Never hitting "case .purchased" when using Sandbox
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach( {
switch $0.transactionState {
case .purchasing:
print("Transaction purchasing.")
break
case .purchased:
print("Transaction PURCHASED.")
if let product = Product(rawValue: $0.payment.productIdentifier) {
completion?(product.count)
}
SKPaymentQueue.default().finishTransaction($0)
SKPaymentQueue.default().remove(self)
break
case .failed:
print("Transaction failed.")
break
case .restored:
print("Transaction restored.")
break
case .deferred:
print("Transaction deferred.")
break
@unknown default:
break
}
})
}
}
Any help would be greatly appreciated!
Mark