Skip to content

Transaction persistence

Tenfay edited this page Nov 11, 2024 · 6 revisions

DYFStore provides an optional reference implementation for storing transactions in NSUserDefaults(DYFStoreUserDefaultsPersistence).

When the client crashes during the payment process, it is particularly important to store transaction information. When StoreKit notifies the uncompleted payment again, it takes the data directly from keychain and performs the receipt verification until the transaction is completed.

Store transaction

func storeReceipt() {
    guard let url = DYFStore.receiptURL() else {
        self.refreshReceipt()
        return
    }
    do {
        let data = try Data(contentsOf: url)
        let info = self.purchaseInfo!
        let persister =  DYFStoreUserDefaultsPersistence()
        
        let tx = DYFStoreTransaction()
        if info.state! == .succeeded {
            tx.state = DYFStoreTransactionState.purchased.rawValue
        } else if info.state! == .restored {
            tx.state = DYFStoreTransactionState.restored.rawValue
        }
        
        tx.productIdentifier = info.productIdentifier
        tx.userIdentifier = info.userIdentifier
        tx.transactionTimestamp = info.transactionDate?.timestamp()
        tx.transactionIdentifier = info.transactionIdentifier
        tx.originalTransactionTimestamp = info.originalTransactionDate?.timestamp()
        tx.originalTransactionIdentifier = info.originalTransactionIdentifier
        
        tx.transactionReceipt = data.base64EncodedString()
        persister.storeTransaction(tx)
        
        self.verifyReceipt(data)
    } catch let error {
        DYFStoreLog("error: \(error.localizedDescription)")
        self.refreshReceipt()
        return
    }
}

Remove transaction

let info = self.purchaseInfo!
let store = DYFStore.default
let persister = DYFStoreUserDefaultsPersistence()
let identifier = info.transactionIdentifier!

if info.state! == .restored {
    let tx = store.extractRestoredTransaction(identifier)
    store.finishTransaction(tx)
} else {
    let tx = store.extractPurchasedTransaction(identifier)
    // The transaction can be finished only after the receipt verification passed under the client and the server can adopt the communication of security and data encryption. In this way, we can avoid refreshing orders and cracking in-app purchase. If we were unable to complete the verification we want StoreKit to keep reminding us of the transaction.
    store.finishTransaction(tx)
}

persister.removeTransaction(identifier)

if let id = info.originalTransactionIdentifier {
    persister.removeTransaction(id)
}

Demo

To learn more, please view the demo.

Clone this wiki locally