diff --git a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift index 4dae2deab1..d858a3b56e 100644 --- a/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift +++ b/Sources/Purchasing/Purchases/PurchasesOrchestrator.swift @@ -829,14 +829,17 @@ extension PurchasesOrchestrator: StoreKit2TransactionListenerDelegate { updatedTransaction transaction: StoreTransactionType ) async throws { let storefront = await self.storefront(from: transaction) + let subscriberAttributes = self.unsyncedAttributes + let adServicesToken = self.attribution.unsyncedAdServicesToken - _ = try await Async.call { completed in + let result: Result = await Async.call { completed in self.transactionPoster.handlePurchasedTransaction( StoreTransaction.from(transaction: transaction), data: .init( appUserID: self.appUserID, presentedOfferingID: nil, - unsyncedAttributes: self.unsyncedAttributes, + unsyncedAttributes: subscriberAttributes, + aadAttributionToken: adServicesToken, storefront: storefront, source: .init( isRestore: self.allowSharingAppStoreAccount, @@ -844,9 +847,17 @@ extension PurchasesOrchestrator: StoreKit2TransactionListenerDelegate { ) ) ) { result in - completed(result.mapError(\.asPurchasesError)) + completed(result) } } + + self.handlePostReceiptResult(result, + subscriberAttributes: subscriberAttributes, + adServicesToken: adServicesToken) + + if let error = result.error { + throw error + } } private func storefront(from transaction: StoreTransactionType) async -> StorefrontType? { diff --git a/Tests/BackendIntegrationTests/StoreKitObserverModeIntegrationTests.swift b/Tests/BackendIntegrationTests/StoreKitObserverModeIntegrationTests.swift index 114859f0de..59418f7f33 100644 --- a/Tests/BackendIntegrationTests/StoreKitObserverModeIntegrationTests.swift +++ b/Tests/BackendIntegrationTests/StoreKitObserverModeIntegrationTests.swift @@ -91,6 +91,22 @@ class StoreKit1ObserverModeIntegrationTests: BaseStoreKitObserverModeIntegration try await self.verifyEntitlementWentThrough(info) } + func testPurchaseOutsideTheAppUpdatesCustomerInfoDelegate() async throws { + try self.testSession.buyProduct(productIdentifier: Self.monthlyNoIntroProductID) + + try await asyncWait( + until: { + await self.purchasesDelegate.customerInfo?.entitlements.active.isEmpty == false + }, + timeout: .seconds(4), + pollInterval: .milliseconds(100), + description: "Delegate should be notified" + ) + + let customerInfo = try XCTUnwrap(self.purchasesDelegate.customerInfo) + try await self.verifyEntitlementWentThrough(customerInfo) + } + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) func testSK2RenewalsPostReceiptOnlyOnceWhenSK1IsEnabled() async throws { try XCTSkipIf(Self.storeKit2Setting.isEnabledAndAvailable, "Test only for SK1") diff --git a/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift b/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift index 6f6fd6547d..64930d624a 100644 --- a/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift +++ b/Tests/StoreKitUnitTests/PurchasesOrchestratorTests.swift @@ -836,6 +836,9 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { expect(self.backend.invokedPostReceiptData) == true expect(self.backend.invokedPostReceiptDataParameters?.transactionData.source.isRestore) == false expect(self.backend.invokedPostReceiptDataParameters?.transactionData.source.initiationSource) == .queue + expect(self.customerInfoManager.invokedCacheCustomerInfo) == true + expect(self.customerInfoManager.invokedCacheCustomerInfoParameters?.appUserID) == Self.mockUserID + expect(self.customerInfoManager.invokedCacheCustomerInfoParameters?.info) === self.mockCustomerInfo } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) @@ -858,7 +861,7 @@ class PurchasesOrchestratorTests: StoreKitConfigTestCase { ) fail("Expected error") } catch { - expect(error).to(matchError(expectedError.asPurchasesError)) + expect(error).to(matchError(expectedError)) } expect(transaction.finishInvoked) == false