Testing In-app Purchases "Ask to buy" using StoreKit config file in Xcode StoreKit 2

264 Views Asked by At

So I am testing my in-app purchase implementation. I am currently testing Ask to buy or interrupted purchases using StoreKit config file in Xcode. Testing purchase errors is working good. When I am testing Ask to buy or Interrupted Purchase
According to apples documentation
Use updates to receive new transactions while the app is running. This sequence receives transactions that occur outside of the app, such as Ask to Buy transactions, subscription offer code redemptions, and purchases that customers make in the App Store.
When I accept or decline the transaction in Xcode. Transaction updates is not notified of the new transaction or failed transaction if I decline the transaction

This is my implementation of InappPurchase

enum PurchaseResult {
    case success(Date)
    case unverifiedResult
    case pending
    case userCancelled
    case unknownError
}

final class IAPManager: ObservableObject {
    private let userCache = UserCache.shared
    private let productIDs = ["my_product_identifier"]
    var updates: Task<Void, Never>? = nil
    static let shared = IAPManager()
    @Published var latestPurchaseTransaction: Transaction? = nil
       
    private init() {
        updates = newTransactionListenerTask()
        
        Task {
            await updateCurrentEntitlements()
        }
    }
    
    deinit {
        updates?.cancel()
    }
    
    private func newTransactionListenerTask() -> Task<Void, Never> {
        Task(priority: .background) {
            for await verificationResult in Transaction.updates {
                print("Transaction updates: \(verificationResult)")
                self.handle(updatedTransaction: verificationResult)
            }
        }
    }
    
    // This function fetches the lastest transactions the user has made
    private func updateCurrentEntitlements() async {
        guard let productID = productIDs.first, let verificationResult = await Transaction.latest(for: productID) else {
            // The user hasn't purchased this product.
            return
        }
        
        switch verificationResult {
        case .verified(let transaction):
            // Check the transaction and give the user access to purchased
            // content as appropriate.
            print("Latest transaction date: \(transaction.purchaseDate)")
            print("Expired date: \(transaction.expirationDate)")
            
            /*
             1. Fetch the date
             2. compare with saved date
             */
        case .unverified(let transaction, let verificationError):
            // Handle unverified transactions based
            // on your business model.
            Crashlytics.crashlytics().record(error: verificationError)
        }
    }
    
    func fetchProducts() async throws -> [Product] {
        return try await Product.products(for: productIDs)
    }
    
    func purchase(_ product: Product) async throws -> PurchaseResult {
        let result = try await product.purchase()
        
        switch result {
        case .success(let verificationResult):
            switch verificationResult {
            case .verified(let transaction):
                // Give the user access to purchased content.
                // Complete the transaction after providing
                // the user access to the content.
                await transaction.finish()
                return .success(transaction.purchaseDate)
            case .unverified(let transaction, let verificationError):
                // Handle unverified transactions based
                // on your business model.
                
                Crashlytics.crashlytics().record(error: verificationError)
                return .unverifiedResult
            }
        case .pending:
            // The purchase requires action from the customer.
            // If the transaction completes,
            // it's available through Transaction.updates.
            return .pending
        case .userCancelled:
            // The user canceled the purchase.
            return .userCancelled
        @unknown default:
            return .unknownError
        }
    }
        
    private func handle(updatedTransaction verificationResult: VerificationResult<Transaction>) {
        guard case .verified(let transaction) = verificationResult else {
            // Ignore unverified transactions.
            return
        }
        
        if let _ = transaction.revocationDate {
            // Remove access to the product identified by transaction.productID.
            // Transaction.revocationReason provides details about
            // the revoked transaction.
            
            UserDatabaseService.shared.removeGodMode()
        } else if let expirationDate = transaction.expirationDate,
                  expirationDate < Date() {
            // Do nothing, this subscription is expired.
            
            // we need to check the date in case we need to remove godmode
            
            userCache.fetchCurrentUser { user in
                if let user = user {
                    if user.godmode == true {
                        UserDatabaseService.shared.removeGodMode()
                    }
                }
            }
        } else if transaction.isUpgraded {
            // Do nothing, there is an active transaction
            // for a higher level of service.
            return
        } else {
            // Provide access to the product identified by
            // transaction.productID.
            
            UserDatabaseService.shared.startGodMode()
            
            Task.init {
                await transaction.finish()
            }
            
            latestPurchaseTransaction = transaction
            
            UIViewController.showGodModeActivatedViewController()
        }
    }
}

extension IAPManager {
    // Function for checking if user is currently subscribed
    func isUserSubscribedToGodMode() async -> Bool {
        guard let productID = productIDs.first, let verificationResult = await Transaction.latest(for: productID) else {
            // The user hasn't purchased this product.
            return false
        }
        
        if case let .verified(transaction) = verificationResult {
            if Date.daysSinceDate(transaction.purchaseDate) <= 7 {
                return true
            } else {
                return false
            }
        } else {
            return false
        }
    }
}

extension IAPManager {
    // Function for restoring previous subscription
    
    func canRestoreSubscription() async -> Bool  {
        guard let productID = productIDs.first, let verificationResult = await Transaction.latest(for: productID) else {
            // The user hasn't purchased this product.
            return false
        }
        
        if case .verified(_) = verificationResult {
            return true
        } else {
            return false
        }
    }
}


Here are the image showing selecting Ask to buy in Xcode. It shows disable because it is already enabled
enter image description here

Here is my image accepting the transaction in Xcode
enter image description here

0

There are 0 best solutions below