From fc064d516aea732beef58eaea8105589efbf7179 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 21 Apr 2023 12:03:12 -0700 Subject: [PATCH] `PurchaseOrchestrator`: fix incorrect `InitiationSource` for SK1 queue transactions --- .../Purchases/PurchasesOrchestrator.swift | 47 ++++++++++++++----- .../PurchasesOrchestratorTests.swift | 4 ++ .../Purchases/PurchasesPurchasingTests.swift | 21 +++++---- .../PurchasesTransactionHandlingTests.swift | 3 +- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift index a7e4214236..127ef45be2 100644 --- a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift +++ b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift @@ -570,9 +570,14 @@ extension PurchasesOrchestrator: StoreKit1WrapperDelegate { let storeTransaction = StoreTransaction(sk1Transaction: transaction) switch transaction.transactionState { - case .restored, // for observer mode - .purchased: - self.handlePurchasedTransaction(storeTransaction, storefront: storeKit1Wrapper.currentStorefront) + case .restored: // for observer mode + self.handlePurchasedTransaction(storeTransaction, + storefront: storeKit1Wrapper.currentStorefront, + restored: true) + case .purchased: + self.handlePurchasedTransaction(storeTransaction, + storefront: storeKit1Wrapper.currentStorefront, + restored: false) case .purchasing: break case .failed: @@ -708,15 +713,19 @@ extension PurchasesOrchestrator: @unchecked Sendable {} private extension PurchasesOrchestrator { func handlePurchasedTransaction(_ transaction: StoreTransaction, - storefront: StorefrontType?) { + storefront: StorefrontType?, + restored: Bool) { self.receiptFetcher.receiptData( refreshPolicy: self.refreshRequestPolicy(forProductIdentifier: transaction.productIdentifier) ) { receiptData in if let receiptData = receiptData, !receiptData.isEmpty { - self.fetchProductsAndPostReceipt(withTransaction: transaction, - receiptData: receiptData, - initiationSource: .purchase, - storefront: storefront) + self.fetchProductsAndPostReceipt( + withTransaction: transaction, + receiptData: receiptData, + initiationSource: self.initiationSource(for: transaction.productIdentifier, + restored: restored), + storefront: storefront + ) } else { self.handleReceiptPost(withTransaction: transaction, result: .failure(.missingReceiptFile()), @@ -799,6 +808,20 @@ private extension PurchasesOrchestrator { #endif } } + + private func initiationSource( + for productIdentifier: String, + restored: Bool + ) -> ProductRequestData.InitiationSource { + let hasPurchaseCallback = self.purchaseCompleteCallbacksByProductID.value.keys.contains(productIdentifier) + + switch (hasPurchaseCallback, restored) { + case (true, false): return .purchase + case (true, true): return .restore + case (false, _): return .queue + } + } + } @available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *) @@ -919,8 +942,8 @@ private extension PurchasesOrchestrator { let unsyncedAttributes = self.unsyncedAttributes self.backend.post(receiptData: receiptData, - appUserID: appUserID, - isRestore: allowSharingAppStoreAccount, + appUserID: self.appUserID, + isRestore: self.allowSharingAppStoreAccount, productData: productData, presentedOfferingIdentifier: presentedOfferingID, observerMode: self.observerMode, @@ -1177,7 +1200,9 @@ extension PurchasesOrchestrator { } ) - self.handlePurchasedTransaction(transaction, storefront: storefront) + self.handlePurchasedTransaction(transaction, + storefront: storefront, + restored: false) } } diff --git a/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift b/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift index e3d8f785f6..5e4a8ab4f5 100644 --- a/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift +++ b/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift @@ -198,6 +198,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { expect(self.backend.invokedPostReceiptDataCount) == 1 expect(self.backend.invokedPostReceiptDataParameters?.productData).toNot(beNil()) expect(self.backend.invokedPostReceiptDataParameters?.offeringIdentifier) == "offering" + expect(self.backend.invokedPostReceiptDataParameters?.initiationSource) == .purchase } func testSK1PurchaseDoesNotAlwaysRefreshReceiptInProduction() async throws { @@ -436,6 +437,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { expect(self.backend.invokedPostReceiptDataCount) == 1 expect(self.backend.invokedPostReceiptDataParameters?.productData).toNot(beNil()) expect(self.backend.invokedPostReceiptDataParameters?.offeringIdentifier).to(beNil()) + expect(self.backend.invokedPostReceiptDataParameters?.initiationSource) == .purchase } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) @@ -639,6 +641,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { expect(transaction.finishInvoked) == true expect(self.backend.invokedPostReceiptData) == true expect(self.backend.invokedPostReceiptDataParameters?.isRestore) == false + expect(self.backend.invokedPostReceiptDataParameters?.initiationSource) == .queue } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) @@ -722,6 +725,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { expect(transaction.finishInvoked) == false expect(self.backend.invokedPostReceiptData) == true expect(self.backend.invokedPostReceiptDataParameters?.isRestore) == true + expect(self.backend.invokedPostReceiptDataParameters?.initiationSource) == .queue } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) diff --git a/Tests/UnitTests/Purchasing/Purchases/PurchasesPurchasingTests.swift b/Tests/UnitTests/Purchasing/Purchases/PurchasesPurchasingTests.swift index 432a1a2966..d4cfd8db9f 100644 --- a/Tests/UnitTests/Purchasing/Purchases/PurchasesPurchasingTests.swift +++ b/Tests/UnitTests/Purchasing/Purchases/PurchasesPurchasingTests.swift @@ -32,14 +32,15 @@ class PurchasesPurchasingTests: BasePurchasesTests { let transaction = MockTransaction() transaction.mockPayment = try XCTUnwrap(self.storeKit1Wrapper.payment) - transaction.mockState = SKPaymentTransactionState.purchasing + transaction.mockState = .purchasing self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) - transaction.mockState = SKPaymentTransactionState.purchased + transaction.mockState = .purchased self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) expect(self.backend.postReceiptDataCalled) == true expect(self.backend.postedIsRestore) == false + expect(self.backend.postedInitiationSource) == .purchase expect(self.purchasesDelegate.customerInfoReceivedCount).toEventually(equal(1)) } @@ -68,13 +69,14 @@ class PurchasesPurchasingTests: BasePurchasesTests { let transaction = MockTransaction() transaction.mockPayment = try XCTUnwrap(self.storeKit1Wrapper.payment) - transaction.mockState = SKPaymentTransactionState.purchasing + transaction.mockState = .purchasing self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) - transaction.mockState = SKPaymentTransactionState.purchased + transaction.mockState = .purchased self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) expect(self.backend.postReceiptDataCalled) == true + expect(self.backend.postedInitiationSource) == .purchase expect(self.backend.postedIsRestore) == false } @@ -86,11 +88,11 @@ class PurchasesPurchasingTests: BasePurchasesTests { let transaction = MockTransaction() transaction.mockPayment = try XCTUnwrap(self.storeKit1Wrapper.payment) + transaction.mockState = .purchasing - transaction.mockState = SKPaymentTransactionState.purchasing self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) - transaction.mockState = SKPaymentTransactionState.purchased + transaction.mockState = .purchased self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) expect(self.backend.postReceiptDataCalled) == true @@ -944,10 +946,10 @@ class PurchasesPurchasingCustomSetupTests: BasePurchasesTests { let transaction = MockTransaction() transaction.mockPayment = try XCTUnwrap(self.storeKit1Wrapper.payment) - transaction.mockState = SKPaymentTransactionState.purchasing + transaction.mockState = .purchasing self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) - transaction.mockState = SKPaymentTransactionState.purchased + transaction.mockState = .purchased self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) expect(self.backend.postReceiptDataCalled) == true @@ -963,10 +965,11 @@ class PurchasesPurchasingCustomSetupTests: BasePurchasesTests { let transaction = MockTransaction() transaction.mockPayment = try XCTUnwrap(self.storeKit1Wrapper.payment) - transaction.mockState = SKPaymentTransactionState.restored + transaction.mockState = .restored self.storeKit1Wrapper.delegate?.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) expect(self.backend.postReceiptDataCalled) == true + expect(self.backend.postedInitiationSource) == .restore expect(self.storeKit1Wrapper.finishCalled).toEventually(beFalse()) } diff --git a/Tests/UnitTests/Purchasing/Purchases/PurchasesTransactionHandlingTests.swift b/Tests/UnitTests/Purchasing/Purchases/PurchasesTransactionHandlingTests.swift index 2a3351ed3b..50158b1fde 100644 --- a/Tests/UnitTests/Purchasing/Purchases/PurchasesTransactionHandlingTests.swift +++ b/Tests/UnitTests/Purchasing/Purchases/PurchasesTransactionHandlingTests.swift @@ -194,7 +194,8 @@ class PurchasesTransactionHandlingTests: BasePurchasesTests { transaction.mockState = .purchased try self.delegate.storeKit1Wrapper(self.storeKit1Wrapper, updatedTransaction: transaction) - expect(self.backend.postReceiptDataCalled).to(beTrue()) + expect(self.backend.postReceiptDataCalled) == true + expect(self.backend.postedInitiationSource) == .queue expect(self.purchasesDelegate.customerInfoReceivedCount).toEventually(equal(2)) }