Skip to content

Transaction persistence

Tenfay edited this page Jun 23, 2024 · 4 revisions

DYFStoreKit 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

- (void)storeReceipt
{
    DYFStoreLog();
    NSURL *receiptURL = DYFStore.receiptURL;
    NSData *data = [NSData dataWithContentsOfURL:receiptURL];
    if (!data || data.length == 0) {
        [self refreshReceipt];
        return;
    }
    
    DYFStoreNotificationInfo *info = self.purchaseInfo;
    DYFStoreUserDefaultsPersistence *persister = [[DYFStoreUserDefaultsPersistence alloc] init];
    
    DYFStoreTransaction *transaction = [[DYFStoreTransaction alloc] init];
    if (info.state == DYFStorePurchaseStateSucceeded) {
        transaction.state = DYFStoreTransactionStatePurchased;
    } else if (info.state == DYFStorePurchaseStateRestored) {
        transaction.state = DYFStoreTransactionStateRestored;
    }
    
    transaction.productIdentifier = info.productIdentifier;
    transaction.userIdentifier = info.userIdentifier;
    transaction.transactionIdentifier = info.transactionIdentifier;
    transaction.transactionTimestamp = info.transactionDate.timestamp;
    transaction.originalTransactionTimestamp = info.originalTransactionDate.timestamp;
    transaction.originalTransactionIdentifier = info.originalTransactionIdentifier;
    
    transaction.transactionReceipt = data.base64EncodedString;
    [persister storeTransaction:transaction];
    
    [self verifyReceipt:data];
}

Remove transaction

DYFStoreNotificationInfo *info = self.purchaseInfo;
DYFStore *store = DYFStore.defaultStore;
DYFStoreUserDefaultsPersistence *persister = [[DYFStoreUserDefaultsPersistence alloc] init];

if (info.state == DYFStorePurchaseStateRestored) {
    SKPaymentTransaction *transaction = [store extractRestoredTransaction:info.transactionIdentifier];
    [store finishTransaction:transaction];
} else {
    SKPaymentTransaction *transaction = [store extractPurchasedTransaction:info.transactionIdentifier];
    // The transaction can be finished only after the client and server adopt secure communication and data encryption and the receipt verification is passed. 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 that there are still outstanding transactions.
    [store finishTransaction:transaction];
}

[persister removeTransaction:info.transactionIdentifier];

if (info.originalTransactionIdentifier) {
    [persister removeTransaction:info.originalTransactionIdentifier];
}

Demo

To learn more, please view the Demo.

Clone this wiki locally