iOS应用内购买不适用于TestFlight中的非沙盒帐户

时间:2020-11-01 12:43:00

标签: ios swift in-app-purchase app-store-connect

我使用https://www.raywenderlich.com/5456-in-app-purchase-tutorial-getting-started中的教程在应用中实现了应用内购买。我没有使用自己的服务器来验证购买或类似的事情。

我创建了一个沙箱用户来测试代码。一切正常,但是,如果我尝试使用个人的Apple ID登录,则购买将失败(在TestFlight上测试)。

这是预期的行为还是我做错了什么? https://stackoverflow.com/a/37042040/11912101声明每个帐户都应该能够购买商品。

此外,我在App Store Connect中为沙盒用户启用了“中断购买”选项。即使购买已完成并且该商品在我的应用程序中已解锁并被添加到purchaseProductIdentifiers中,下面的代码仍将运行函数failed(...)。有没有办法处理那些被中断的购买?

感谢您的回答!

import StoreKit

public struct InAppPurchases {
    
    static let proVersionID = "myapp.proversion"
    private static let productIdentifiers: Set<ProductIdentifier> = [proVersionID]
    
    public static let helper = InAppPurchaseHelper(productIds: InAppPurchases.productIdentifiers)
}

func resourceNameForProductIdentifier(_ productIdentifier: String) -> String? {
    return productIdentifier.components(separatedBy: ".").last
}

public typealias ProductIdentifier = String
public typealias ProductsRequestCompletionHandler = (_ success: Bool, _ products: [SKProduct]?) -> Void

extension Notification.Name {
    static let IAPHelperPurchaseNotification = Notification.Name("IAPHelperPurchaseNotification")
}

open class InAppPurchaseHelper: NSObject, ObservableObject  {
    
    private let productIdentifiers: Set<ProductIdentifier>
    private var purchasedProductIdentifiers: Set<ProductIdentifier> = [] {
        willSet {
            self.objectWillChange.send()
        }
    }
    private var productsRequest: SKProductsRequest?
    private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?

    @Published var didFail = false
    
    var availableProducts = [SKProduct]() {
        willSet {
            DispatchQueue.main.async {
                self.objectWillChange.send()
            }
        }}
    
    public init(productIds: Set<ProductIdentifier>) {
        productIdentifiers = productIds
        for productIdentifier in productIds {
            let purchased = UserDefaults.standard.bool(forKey: productIdentifier)
            if purchased {
                purchasedProductIdentifiers.insert(productIdentifier)
                print("Previously purchased: \(productIdentifier)")
            } else {
                print("Not purchased: \(productIdentifier)")
            }
        }
        super.init()
        
        SKPaymentQueue.default().add(self)
        
        reloadInAppPurchases()   
    }
    
    func reloadInAppPurchases() {
        DispatchQueue.main.async {
            
            InAppPurchases.helper.requestProducts{ [weak self] success, products in
                guard let self = self else { return }
                if success {
                    self.availableProducts = products!
                }
            }
        }
    }
}

extension InAppPurchaseHelper {
    
    public func requestProducts(_ completionHandler: @escaping ProductsRequestCompletionHandler) {
        productsRequest?.cancel()
        productsRequestCompletionHandler = completionHandler
        
        productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
        productsRequest!.delegate = self
        productsRequest!.start()
    }
    
    public func buyProduct(_ product: SKProduct) {
        print("Buying \(product.productIdentifier)...")
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
    }
    
    public func isProductPurchased(_ productIdentifier: ProductIdentifier) -> Bool {
        return purchasedProductIdentifiers.contains(productIdentifier)
    }
    
    public func isProVersion () -> Bool {
        if(isProductPurchased(InAppPurchases.proVersionID)) {
            return true
        }
        return false
    }
    
    public class func canMakePayments() -> Bool {
        return SKPaymentQueue.canMakePayments()
    }
    
    public func restorePurchases() {
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
}

extension InAppPurchaseHelper: SKProductsRequestDelegate {
    
    public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        print("Loaded list of products...")
        let products = response.products
        productsRequestCompletionHandler?(true, products)
        clearRequestAndHandler()
        
        for p in products {
            print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")
        }
    }
    
    public func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Failed to load list of products.")
        print("Error: \(error.localizedDescription)")
        productsRequestCompletionHandler?(false, nil)
        clearRequestAndHandler()
    }
    
    private func clearRequestAndHandler() {
        productsRequest = nil
        productsRequestCompletionHandler = nil
    }
}


extension InAppPurchaseHelper: SKPaymentTransactionObserver {
    
    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch (transaction.transactionState) {
            case .purchased:
                complete(transaction: transaction)
                break
            case .failed:
                fail(transaction: transaction)
                break
            case .restored:
                restore(transaction: transaction)
                break
            case .deferred:
                break
            case .purchasing:
                break
            @unknown default:
                break
            }
        }
    }
    
    private func complete(transaction: SKPaymentTransaction) {
        print("complete...")
        deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)
        SKPaymentQueue.default().finishTransaction(transaction)
    }
    
    private func restore(transaction: SKPaymentTransaction) {
        guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
        
        print("restore... \(productIdentifier)")
        deliverPurchaseNotificationFor(identifier: productIdentifier)
        SKPaymentQueue.default().finishTransaction(transaction)
    }
    
    private func fail(transaction: SKPaymentTransaction) {
        print("fail...")
        
        if let transactionError = transaction.error as NSError?,
           let localizedDescription = transaction.error?.localizedDescription,
           transactionError.code != SKError.paymentCancelled.rawValue {
            print("Transaction Error: \(localizedDescription)")
        }
        
        SKPaymentQueue.default().finishTransaction(transaction)
        
        if(!isProVersion()){
            didFail.toggle()
        }
    }
    
    private func deliverPurchaseNotificationFor(identifier: String?) {
        guard let identifier = identifier else { return }
        
        purchasedProductIdentifiers.insert(identifier)
        UserDefaults.standard.set(true, forKey: identifier)
        NotificationCenter.default.post(name: .IAPHelperPurchaseNotification, object: identifier)
    }
}

1 个答案:

答案 0 :(得分:0)

请参见thisthis

我最近没有其他帐户测试IAP的问题。这很奇怪,因为默认情况下testflight是沙盒环境,所以我认为不需要像发布版本那样等待,但也许要等几个小时,以防万一。