Search code examples
iosswiftstorekit

Cannot get receipt data


I am implementing the discount code feature and we use the following code to show the redeem sheet.

SKPaymentQueue.default().presentCodeRedemptionSheet()

After redemption, I have to show the information about the discount code redemption and I know I can get that information by checking the receipt data. To get the receipt data after applying the discount, I use the following code.

if let url = Bundle.main.appStoreReceiptURL, 
   let data = try? Data(contentsOf: url) {
      let receiptBase64 = data.base64EncodedString()
      print(receiptBase64)
}

Here, data is always nil. What's wrong here? I'm testing this feature on real devices.

The URL is: file:///private/var/mobile/Containers/Data/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/StoreKit/sandboxReceipt


Solution

  • The issue was I used the wrong code to finalize the transaction.

    Wrong code:

    func purchase(_ product: Product) async throws -> StoreKit.Transaction? {
        let result = try await product.purchase()
        switch result {
            case .success(let verification):
                let transaction = try checkVerified(verification)
                await transaction.finish() // I thought when this code runs the receipt data is generated but it doesn't.
                return transaction
            case .userCancelled, .pending:
                return nil
            default:
                return nil
            }
        }
    }
    
    func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
            case .unverified:
                throw StoreError.failedVerification
            case .verified(let safe):
                return safe
            }
        }
    }
    

    Fix: SKPaymentQueue.default().finishTransaction(SKPaymentTransaction) should be called.

    extension IAPManager: SKPaymentTransactionObserver {
        public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            for transaction in transactions {
                switch (transaction.transactionState) {
                case .purchased:
                    SKPaymentQueue.default().finishTransaction(transaction) // This is correct.
                    break
                case .failed:
                    SKPaymentQueue.default().finishTransaction(transaction)
                    break
                case .restored:
                    SKPaymentQueue.default().finishTransaction(transaction)
                    break
                case .deferred, .purchasing:
                    break
                default:
                    break
                }
            }
        }
    }
    

    Now, I see the receipt data!