-
Notifications
You must be signed in to change notification settings - Fork 11
Transaction persistence
Handsen Teng edited this page Jun 28, 2020
·
4 revisions
DYFStoreKit
provides two optional reference implementations for storing transactions in the Keychain(DYFStoreKeychainPersistence
) or 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.
- (void)storeReceipt {
DYFStoreLog();
NSURL *receiptURL = DYFStore.receiptURL;
NSData *data = [NSData dataWithContentsOfURL:receiptURL];
if (!data || data.length == 0) {
[self refreshReceipt];
return;
}
DYFStoreNotificationInfo *info = self.purchaseInfo;
DYFStore *store = DYFStore.defaultStore;
DYFStoreKeychainPersistence *persister = store.keychainPersister;
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];
// Makes the backup data.
DYFStoreUserDefaultsPersistence *uPersister = [[DYFStoreUserDefaultsPersistence alloc] init];
if (![uPersister containsTransaction:info.transactionIdentifier]) {
[uPersister storeTransaction:transaction];
}
[self verifyReceipt:data];
}
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
DYFStoreNotificationInfo *info = self.purchaseInfo;
DYFStore *store = DYFStore.defaultStore;
DYFStoreKeychainPersistence *persister = store.keychainPersister;
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];
}
});