diff --git a/Adyen.xcodeproj/project.pbxproj b/Adyen.xcodeproj/project.pbxproj index 2d0880c19b..f0da73cbe9 100644 --- a/Adyen.xcodeproj/project.pbxproj +++ b/Adyen.xcodeproj/project.pbxproj @@ -258,6 +258,7 @@ A04E60CD27DFA2BD0051C72C /* SessionAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04E60CC27DFA2BD0051C72C /* SessionAPIClient.swift */; }; A04E60D527EDDF4F0051C72C /* AdyenSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04E60D427EDDF4F0051C72C /* AdyenSessionDelegate.swift */; }; A04E60DB27F30D900051C72C /* SessionDelegateMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04E60DA27F30D900051C72C /* SessionDelegateMock.swift */; }; + A04EA85E2B69509500723F39 /* AnalyticsEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04EA85D2B69509500723F39 /* AnalyticsEnvironment.swift */; }; A04F8C2229E5950100F3F62B /* AdyenCashAppPay.docc in Sources */ = {isa = PBXBuildFile; fileRef = A04F8C2129E5950100F3F62B /* AdyenCashAppPay.docc */; }; A04F8C2329E5950100F3F62B /* AdyenCashAppPay.h in Headers */ = {isa = PBXBuildFile; fileRef = A04F8C2029E5950100F3F62B /* AdyenCashAppPay.h */; settings = {ATTRIBUTES = (Public, ); }; }; A04F8C2B29E5957B00F3F62B /* Adyen.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2C0E03322097917008616F6 /* Adyen.framework */; }; @@ -282,6 +283,11 @@ A0BC64E628F062E400CED2A1 /* AdyenSessionAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0BC64E528F062E400CED2A1 /* AdyenSessionAware.swift */; }; A0C9B59B288AE34600D6BDAB /* InstallmentOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0F41E5526CBCA6E0089AD6C /* InstallmentOptions.swift */; }; A0D48FB827109B0200C0B82C /* ArrayHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D48FB727109B0200C0B82C /* ArrayHelpers.swift */; }; + A0DB48662AFD020400348C83 /* AnalyticsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48652AFD020400348C83 /* AnalyticsRequest.swift */; }; + A0DB48682AFD068400348C83 /* AdyenAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48672AFD068400348C83 /* AdyenAnalytics.swift */; }; + A0DB486A2AFD0BDC00348C83 /* AnalyticsEventInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48692AFD0BDC00348C83 /* AnalyticsEventInfo.swift */; }; + A0DB486C2AFD0BEE00348C83 /* AnalyticsEventLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB486B2AFD0BEE00348C83 /* AnalyticsEventLog.swift */; }; + A0DB486E2AFD0BFC00348C83 /* AnalyticsEventError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB486D2AFD0BFC00348C83 /* AnalyticsEventError.swift */; }; A0DDA6A72A6162F500EBD6AF /* AdyenSessionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DDA6A62A6162F500EBD6AF /* AdyenSessionResult.swift */; }; A0DE8F6D26CEA04500F2F1E8 /* Installments.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DE8F6C26CEA04500F2F1E8 /* Installments.swift */; }; A0F41EAC26CD4AA50089AD6C /* FormCardInstallmentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0F41EAB26CD4AA50089AD6C /* FormCardInstallmentsItem.swift */; }; @@ -307,22 +313,21 @@ C9454C37276A340B0086C218 /* BACSDirectDebitPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9454C35276A33A00086C218 /* BACSDirectDebitPresentationDelegate.swift */; }; C9454C38276A34150086C218 /* BACSDirectDebitPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9454C35276A33A00086C218 /* BACSDirectDebitPresentationDelegate.swift */; }; C94632BE27BA6985003DD81F /* AnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94632BD27BA6985003DD81F /* AnalyticsProvider.swift */; }; - C94632C227BAA81F003DD81F /* TelemetryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94632C127BAA81F003DD81F /* TelemetryRequest.swift */; }; C95903DE275A48D000E7D3BC /* BACSDirectDebitComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C95903DD275A48D000E7D3BC /* BACSDirectDebitComponentTests.swift */; }; C96688BF26A6FC1C00DC7297 /* AffirmComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96688BE26A6FC1C00DC7297 /* AffirmComponentTests.swift */; }; C96E07A3283B92E300345732 /* BACSDirectDebitComponentTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96E07A1283B92D500345732 /* BACSDirectDebitComponentTrackerTests.swift */; }; C978F5EF2732CD6A00F59B3C /* FormCardSecurityCodeItemViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C978F5EE2732CD6A00F59B3C /* FormCardSecurityCodeItemViewTests.swift */; }; - C97C16AA28059A5A00534419 /* TelemetryTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97C16A928059A5A00534419 /* TelemetryTrackerTests.swift */; }; - C97C16AC280702B200534419 /* TelemetryFlavorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97C16AB280702B200534419 /* TelemetryFlavorTests.swift */; }; + C97C16AA28059A5A00534419 /* AnalyticsEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97C16A928059A5A00534419 /* AnalyticsEventTests.swift */; }; + C97C16AC280702B200534419 /* AnalyticsFlavorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97C16AB280702B200534419 /* AnalyticsFlavorTests.swift */; }; C97C16AD2807076400534419 /* AnalyticsProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C0005B280468E100CE2EEC /* AnalyticsProviderMock.swift */; }; C981254E2851E9AF006D1374 /* PaymentComponentSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C981254C2851E903006D1374 /* PaymentComponentSubject.swift */; }; C98125502851E9E4006D1374 /* PaymentComponentSubjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C981254F2851E9E4006D1374 /* PaymentComponentSubjectTests.swift */; }; C982FFD826946F0800AED849 /* AffirmComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982FFD726946F0800AED849 /* AffirmComponent.swift */; }; C982FFDC2694792F00AED849 /* AffirmPaymentMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = C982FFDB2694792F00AED849 /* AffirmPaymentMethod.swift */; }; - C9B6683527C7CB7A006950B9 /* TelemetryTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B6683427C7CB7A006950B9 /* TelemetryTracker.swift */; }; - C9B6683727C8D7FB006950B9 /* TelemetryData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B6683627C8D7FB006950B9 /* TelemetryData.swift */; }; + C9B6683527C7CB7A006950B9 /* AnalyticsFlavor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B6683427C7CB7A006950B9 /* AnalyticsFlavor.swift */; }; + C9B6683727C8D7FB006950B9 /* AnalyticsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B6683627C8D7FB006950B9 /* AnalyticsData.swift */; }; C9B6683927C903FE006950B9 /* AdyenContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B6683827C903FE006950B9 /* AdyenContext.swift */; }; - C9BAE20F27BEA68D002F5728 /* CheckoutAttemptIdRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BAE20E27BEA68D002F5728 /* CheckoutAttemptIdRequest.swift */; }; + C9BAE20F27BEA68D002F5728 /* InitialAnalyticsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BAE20E27BEA68D002F5728 /* InitialAnalyticsRequest.swift */; }; C9BB460427622D9B00E6730B /* BACSConfirmationViewProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BB460327622D9B00E6730B /* BACSConfirmationViewProtocolMock.swift */; }; C9BB460627622E9600E6730B /* BACSConfirmationPresenterProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BB460527622E9600E6730B /* BACSConfirmationPresenterProtocolMock.swift */; }; C9BB460927622F4100E6730B /* BACSConfirmationPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BB460827622F4100E6730B /* BACSConfirmationPresenterTests.swift */; }; @@ -433,7 +438,7 @@ E72375DD27AABF450020DCF9 /* AdyenWeChatPayInternal in Frameworks */ = {isa = PBXBuildFile; productRef = E72375DC27AABF450020DCF9 /* AdyenWeChatPayInternal */; }; E72521EE25517EB100533E35 /* BLIKComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72521ED25517EB100533E35 /* BLIKComponentTests.swift */; }; E7275BF9255012F600907CF9 /* FormLabelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7275BF8255012F600907CF9 /* FormLabelItem.swift */; }; - E72D9BAA26A6E66800FBDA48 /* Dimentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72D9BA926A6E66800FBDA48 /* Dimentions.swift */; }; + E72D9BAA26A6E66800FBDA48 /* Dimensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72D9BA926A6E66800FBDA48 /* Dimensions.swift */; }; E72DA33F23E19DE300707638 /* PreselectedPaymentMethodComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72DA33C23E19DE300707638 /* PreselectedPaymentMethodComponent.swift */; }; E72DA34123E1BE5300707638 /* DropInNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72DA34023E1BE5300707638 /* DropInNavigationController.swift */; }; E7388D8523DB1F9A008E62B8 /* DimmingPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7388D8423DB1F9A008E62B8 /* DimmingPresentationController.swift */; }; @@ -1499,6 +1504,7 @@ A04E60D227E0E6280051C72C /* XCTestCase+Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTestCase+Result.swift"; sourceTree = ""; }; A04E60D427EDDF4F0051C72C /* AdyenSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenSessionDelegate.swift; sourceTree = ""; }; A04E60DA27F30D900051C72C /* SessionDelegateMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDelegateMock.swift; sourceTree = ""; }; + A04EA85D2B69509500723F39 /* AnalyticsEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEnvironment.swift; sourceTree = ""; }; A04F8C1E29E5950100F3F62B /* AdyenCashAppPay.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AdyenCashAppPay.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A04F8C2029E5950100F3F62B /* AdyenCashAppPay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AdyenCashAppPay.h; sourceTree = ""; }; A04F8C2129E5950100F3F62B /* AdyenCashAppPay.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = AdyenCashAppPay.docc; sourceTree = ""; }; @@ -1519,6 +1525,11 @@ A0B180302A2DE445003C608E /* MealVoucherDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealVoucherDetails.swift; sourceTree = ""; }; A0BC64E528F062E400CED2A1 /* AdyenSessionAware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenSessionAware.swift; sourceTree = ""; }; A0D48FB727109B0200C0B82C /* ArrayHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayHelpers.swift; sourceTree = ""; }; + A0DB48652AFD020400348C83 /* AnalyticsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsRequest.swift; sourceTree = ""; }; + A0DB48672AFD068400348C83 /* AdyenAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalytics.swift; sourceTree = ""; }; + A0DB48692AFD0BDC00348C83 /* AnalyticsEventInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventInfo.swift; sourceTree = ""; }; + A0DB486B2AFD0BEE00348C83 /* AnalyticsEventLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventLog.swift; sourceTree = ""; }; + A0DB486D2AFD0BFC00348C83 /* AnalyticsEventError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventError.swift; sourceTree = ""; }; A0DDA6A62A6162F500EBD6AF /* AdyenSessionResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenSessionResult.swift; sourceTree = ""; }; A0DE8F6C26CEA04500F2F1E8 /* Installments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Installments.swift; sourceTree = ""; }; A0F41E5526CBCA6E0089AD6C /* InstallmentOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallmentOptions.swift; sourceTree = ""; }; @@ -1544,21 +1555,20 @@ C93B01B82760B06300D311A1 /* BACSConfirmationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSConfirmationPresenter.swift; sourceTree = ""; }; C9454C35276A33A00086C218 /* BACSDirectDebitPresentationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSDirectDebitPresentationDelegate.swift; sourceTree = ""; }; C94632BD27BA6985003DD81F /* AnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsProvider.swift; sourceTree = ""; }; - C94632C127BAA81F003DD81F /* TelemetryRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryRequest.swift; sourceTree = ""; }; C95903DD275A48D000E7D3BC /* BACSDirectDebitComponentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSDirectDebitComponentTests.swift; sourceTree = ""; }; C96688BE26A6FC1C00DC7297 /* AffirmComponentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AffirmComponentTests.swift; sourceTree = ""; }; C96E07A1283B92D500345732 /* BACSDirectDebitComponentTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSDirectDebitComponentTrackerTests.swift; sourceTree = ""; }; C978F5EE2732CD6A00F59B3C /* FormCardSecurityCodeItemViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormCardSecurityCodeItemViewTests.swift; sourceTree = ""; }; - C97C16A928059A5A00534419 /* TelemetryTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryTrackerTests.swift; sourceTree = ""; }; - C97C16AB280702B200534419 /* TelemetryFlavorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryFlavorTests.swift; sourceTree = ""; }; + C97C16A928059A5A00534419 /* AnalyticsEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventTests.swift; sourceTree = ""; }; + C97C16AB280702B200534419 /* AnalyticsFlavorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsFlavorTests.swift; sourceTree = ""; }; C981254C2851E903006D1374 /* PaymentComponentSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentComponentSubject.swift; sourceTree = ""; }; C981254F2851E9E4006D1374 /* PaymentComponentSubjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentComponentSubjectTests.swift; sourceTree = ""; }; C982FFD726946F0800AED849 /* AffirmComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AffirmComponent.swift; sourceTree = ""; }; C982FFDB2694792F00AED849 /* AffirmPaymentMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AffirmPaymentMethod.swift; sourceTree = ""; }; - C9B6683427C7CB7A006950B9 /* TelemetryTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryTracker.swift; sourceTree = ""; }; - C9B6683627C8D7FB006950B9 /* TelemetryData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryData.swift; sourceTree = ""; }; + C9B6683427C7CB7A006950B9 /* AnalyticsFlavor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsFlavor.swift; sourceTree = ""; }; + C9B6683627C8D7FB006950B9 /* AnalyticsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsData.swift; sourceTree = ""; }; C9B6683827C903FE006950B9 /* AdyenContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenContext.swift; sourceTree = ""; }; - C9BAE20E27BEA68D002F5728 /* CheckoutAttemptIdRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutAttemptIdRequest.swift; sourceTree = ""; }; + C9BAE20E27BEA68D002F5728 /* InitialAnalyticsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialAnalyticsRequest.swift; sourceTree = ""; }; C9BB460327622D9B00E6730B /* BACSConfirmationViewProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSConfirmationViewProtocolMock.swift; sourceTree = ""; }; C9BB460527622E9600E6730B /* BACSConfirmationPresenterProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSConfirmationPresenterProtocolMock.swift; sourceTree = ""; }; C9BB460827622F4100E6730B /* BACSConfirmationPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BACSConfirmationPresenterTests.swift; sourceTree = ""; }; @@ -1695,7 +1705,7 @@ E71E8F3D257921D10054B03D /* StoredBLIKPaymentMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredBLIKPaymentMethod.swift; sourceTree = ""; }; E72521ED25517EB100533E35 /* BLIKComponentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLIKComponentTests.swift; sourceTree = ""; }; E7275BF8255012F600907CF9 /* FormLabelItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabelItem.swift; sourceTree = ""; }; - E72D9BA926A6E66800FBDA48 /* Dimentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dimentions.swift; sourceTree = ""; }; + E72D9BA926A6E66800FBDA48 /* Dimensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dimensions.swift; sourceTree = ""; }; E72DA33C23E19DE300707638 /* PreselectedPaymentMethodComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreselectedPaymentMethodComponent.swift; sourceTree = ""; }; E72DA34023E1BE5300707638 /* DropInNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropInNavigationController.swift; sourceTree = ""; }; E736497E25277B6500AB76AE /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; @@ -3012,7 +3022,7 @@ isa = PBXGroup; children = ( C94632BD27BA6985003DD81F /* AnalyticsProvider.swift */, - C9B6683427C7CB7A006950B9 /* TelemetryTracker.swift */, + C9B6683427C7CB7A006950B9 /* AnalyticsFlavor.swift */, ); path = AnalyticsProvider; sourceTree = ""; @@ -3106,8 +3116,8 @@ children = ( C9C0005B280468E100CE2EEC /* AnalyticsProviderMock.swift */, C9C0005D280474DF00CE2EEC /* AnalyticsProviderTests.swift */, - C97C16A928059A5A00534419 /* TelemetryTrackerTests.swift */, - C97C16AB280702B200534419 /* TelemetryFlavorTests.swift */, + C97C16A928059A5A00534419 /* AnalyticsEventTests.swift */, + C97C16AB280702B200534419 /* AnalyticsFlavorTests.swift */, ); path = Analytics; sourceTree = ""; @@ -3115,8 +3125,8 @@ C9CA0B68282E7E4A00C4E9C2 /* Requests */ = { isa = PBXGroup; children = ( - C94632C127BAA81F003DD81F /* TelemetryRequest.swift */, - C9BAE20E27BEA68D002F5728 /* CheckoutAttemptIdRequest.swift */, + C9BAE20E27BEA68D002F5728 /* InitialAnalyticsRequest.swift */, + A0DB48652AFD020400348C83 /* AnalyticsRequest.swift */, ); path = Requests; sourceTree = ""; @@ -3124,7 +3134,11 @@ C9CA0B69282E7E5D00C4E9C2 /* Models */ = { isa = PBXGroup; children = ( - C9B6683627C8D7FB006950B9 /* TelemetryData.swift */, + C9B6683627C8D7FB006950B9 /* AnalyticsData.swift */, + A0DB48672AFD068400348C83 /* AdyenAnalytics.swift */, + A0DB48692AFD0BDC00348C83 /* AnalyticsEventInfo.swift */, + A0DB486B2AFD0BEE00348C83 /* AnalyticsEventLog.swift */, + A0DB486D2AFD0BFC00348C83 /* AnalyticsEventError.swift */, ); path = Models; sourceTree = ""; @@ -3532,7 +3546,7 @@ E28098B9220DB9E70087928F /* List */, E2C0E0BF220D7E53008616F6 /* Views */, F9175F7625948EDF00D653BE /* View Controllers */, - E72D9BA926A6E66800FBDA48 /* Dimentions.swift */, + E72D9BA926A6E66800FBDA48 /* Dimensions.swift */, ); path = UI; sourceTree = ""; @@ -4842,6 +4856,7 @@ E7B6280724ED65D2000CEC6E /* Requests */, E90933FE228AFC6600C9F04B /* Environment.swift */, 5A4702172664E9440023F264 /* APIContext.swift */, + A04EA85D2B69509500723F39 /* AnalyticsEnvironment.swift */, ); path = APIClient; sourceTree = ""; @@ -6472,7 +6487,7 @@ F96F44DD23BE487500871C1F /* LocalizationParameters.swift in Sources */, E7085A8D2627132B00D0153B /* PostalAddress.swift in Sources */, F9589D272601F19E00E4113F /* EmailFormItemInjector.swift in Sources */, - E72D9BAA26A6E66800FBDA48 /* Dimentions.swift in Sources */, + E72D9BAA26A6E66800FBDA48 /* Dimensions.swift in Sources */, F9620DB323C75C19005209FC /* PaymentComponentBuilder.swift in Sources */, F96757BF27CF690600A16FB6 /* PartialPaymentError.swift in Sources */, E7085B122628B29600D0153B /* BasePickerInputControl.swift in Sources */, @@ -6506,6 +6521,7 @@ F9A6C41D26550B7100D8CD3E /* AlreadyPaidPaymentComponent.swift in Sources */, F9D644E024D2E4210059CBE3 /* EmailValidator.swift in Sources */, F9639B3324DD96990073F38A /* PaymentStatusRequest.swift in Sources */, + A0DB48662AFD020400348C83 /* AnalyticsRequest.swift in Sources */, 5AD40E75262F04440090E01C /* UIProgressViewHelpers.swift in Sources */, F91664F023E41D7300C10738 /* AnyEncodable.swift in Sources */, E7D531192446F525000046B4 /* FormSeparatorItemView.swift in Sources */, @@ -6519,8 +6535,9 @@ E28098B3220DA7980087928F /* Component.swift in Sources */, F95B6D8E25231715002C9062 /* RegularExpressionValidator.swift in Sources */, 002B93082951F120000B93F4 /* FormSegmentedControlItem.swift in Sources */, - C9B6683527C7CB7A006950B9 /* TelemetryTracker.swift in Sources */, - C9BAE20F27BEA68D002F5728 /* CheckoutAttemptIdRequest.swift in Sources */, + A04EA85E2B69509500723F39 /* AnalyticsEnvironment.swift in Sources */, + C9B6683527C7CB7A006950B9 /* AnalyticsFlavor.swift in Sources */, + C9BAE20F27BEA68D002F5728 /* InitialAnalyticsRequest.swift in Sources */, F926D52023F4217A00D058D3 /* DeviceDependent.swift in Sources */, F9A6C4BB2657AFF600D8CD3E /* FormSpacerItem.swift in Sources */, 00346FC429895B6A00F7DA94 /* ModalToolbar.swift in Sources */, @@ -6561,6 +6578,7 @@ E224088D22B0FD220058923E /* StoredPayPalPaymentMethod.swift in Sources */, F99D2F0A266136A700BB5B2F /* AppleWalletPassResponse.swift in Sources */, F9639B3524DD97A30073F38A /* PaymentStatusResponse.swift in Sources */, + A0DB486E2AFD0BFC00348C83 /* AnalyticsEventError.swift in Sources */, F9BA21C2246E82EE00D36A63 /* SizeUtilities.swift in Sources */, F97C829225BB156600D7F85C /* AbstractPersonalInformationComponent+Extensions.swift in Sources */, 81BA08372A49C93100308160 /* FormSelectableValueItemView.swift in Sources */, @@ -6572,7 +6590,6 @@ F9354BED23A7C34D00A6760B /* ButtonStyle.swift in Sources */, F9B8C50C23FE962D00C4D0FB /* FormPhoneNumberItem.swift in Sources */, E2C0E0BC220B2976008616F6 /* FormItemView.swift in Sources */, - C94632C227BAA81F003DD81F /* TelemetryRequest.swift in Sources */, E7763A1125F02BD20046371E /* GiftCardPaymentMethod.swift in Sources */, E788A4FB2658034400089448 /* ShopperInformation.swift in Sources */, E7085B172628B29600D0153B /* PhoneExtensionInputControl.swift in Sources */, @@ -6604,6 +6621,7 @@ 00EACBBD2876F9DC0082B360 /* FormAttributedLabelItem.swift in Sources */, F90E95F227280BD5007E382B /* CoreListDataSource.swift in Sources */, F926D53323F5A5D000D058D3 /* NumericStringValidator.swift in Sources */, + A0DB48682AFD068400348C83 /* AdyenAnalytics.swift in Sources */, E702BDE42602149E00280682 /* Assertion.swift in Sources */, E288EB962267652D000E960C /* AdyenObserver.swift in Sources */, F9EDB7862391734200CFB3C9 /* UnknownError.swift in Sources */, @@ -6634,6 +6652,7 @@ F9D5752A237C6084009C18B5 /* StoredBCMCPaymentMethod.swift in Sources */, E2289B3C23D0BBB1002844BF /* ListSectionHeaderStyle.swift in Sources */, E715A7342A1B96890047B87A /* UIApplicationHelpers.swift in Sources */, + A0DB486C2AFD0BEE00348C83 /* AnalyticsEventLog.swift in Sources */, F926D53723F6A35700D058D3 /* NumericFormatter.swift in Sources */, A026C85127C4FE4700E6C34A /* BasicComponentConfiguration.swift in Sources */, E7085B162628B29600D0153B /* FormPhoneExtensionPickerItemView.swift in Sources */, @@ -6654,13 +6673,14 @@ E2C0E096220B04F0008616F6 /* FormViewController.swift in Sources */, F9FE25D62626CD49001874BB /* ReadyToSubmitPaymentComponentDelegate.swift in Sources */, F99D4556262717A500880D72 /* FormErrorItemView.swift in Sources */, + A0DB486A2AFD0BDC00348C83 /* AnalyticsEventInfo.swift in Sources */, F96286BD256BDEB000043FE3 /* CoreBundleExtension.swift in Sources */, E7085D7D262EEF6200D0153B /* AllRegions.swift in Sources */, F919DF9124E682480027976E /* ClientKeyResponse.swift in Sources */, F97C81AF25BAC8E600D7F85C /* AbstractPersonalInformationComponent.swift in Sources */, F97842D726C2758E00677458 /* InstantPaymentMethod.swift in Sources */, F97CCD3C24360A4000BC560A /* BundleHelpers.swift in Sources */, - C9B6683727C8D7FB006950B9 /* TelemetryData.swift in Sources */, + C9B6683727C8D7FB006950B9 /* AnalyticsData.swift in Sources */, E788A4EF2658030100089448 /* DisplayInformation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6690,7 +6710,7 @@ E9E3DAB2221D79C800697074 /* CardNumberFormatterTests.swift in Sources */, F9EDB796239663F800CFB3C9 /* PaymentComponentMock.swift in Sources */, F9B9F625295485A2008C2E49 /* ThreeDSResultExtension.swift in Sources */, - C97C16AA28059A5A00534419 /* TelemetryTrackerTests.swift in Sources */, + C97C16AA28059A5A00534419 /* AnalyticsEventTests.swift in Sources */, E7D189B025D31936006AD3B7 /* DropInActionTests.swift in Sources */, C95903DE275A48D000E7D3BC /* BACSDirectDebitComponentTests.swift in Sources */, 81825CBB2AC59C4000F91912 /* UIViewController+Search.swift in Sources */, @@ -6757,7 +6777,7 @@ E74D918325AF3FE600743B0C /* CardBrandProviderTests.swift in Sources */, 813BF1122B2365400096940E /* XCTestCase+FirstResponder.swift in Sources */, E773EA3C2523432B00119499 /* CardTypeProviderMock.swift in Sources */, - C97C16AC280702B200534419 /* TelemetryFlavorTests.swift in Sources */, + C97C16AC280702B200534419 /* AnalyticsFlavorTests.swift in Sources */, E9E3DB062226B3B200697074 /* AdyenCoderTests.swift in Sources */, F99D4603262D647400880D72 /* AddressViewModelTests.swift in Sources */, E9E3DABE221EA47800697074 /* StringExtensionsTests.swift in Sources */, diff --git a/Adyen/Analytics/AnalyticsProvider/AnalyticsFlavor.swift b/Adyen/Analytics/AnalyticsProvider/AnalyticsFlavor.swift new file mode 100644 index 0000000000..15a65ce8e4 --- /dev/null +++ b/Adyen/Analytics/AnalyticsProvider/AnalyticsFlavor.swift @@ -0,0 +1,22 @@ +// +// Copyright (c) 2023 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import Foundation + +@_spi(AdyenInternal) +public enum AnalyticsFlavor { + case components(type: PaymentMethodType) + case dropIn(type: String = "dropin", paymentMethods: [String]) + + public var value: String { + switch self { + case .components: + return "components" + case .dropIn: + return "dropin" + } + } +} diff --git a/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift b/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift index 4ad447a8b6..5610de9722 100644 --- a/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift +++ b/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift @@ -14,12 +14,9 @@ public struct AnalyticsConfiguration { /// A Boolean value that determines whether analytics is enabled. public var isEnabled = true - - @_spi(AdyenInternal) - public var isTelemetryEnabled = true @_spi(AdyenInternal) - public var context: TelemetryContext = .init() + public var context: AnalyticsContext = .init() // MARK: - Initializers @@ -28,20 +25,26 @@ public struct AnalyticsConfiguration { } @_spi(AdyenInternal) -/// Additional fields to be provided with a ``TelemetryRequest`` +/// Additional fields to be provided with an ``InitialAnalyticsRequest`` public struct AdditionalAnalyticsFields { /// The amount of the payment public let amount: Amount? + + public let sessionId: String? + + public init(amount: Amount?, sessionId: String?) { + self.amount = amount + self.sessionId = sessionId + } } @_spi(AdyenInternal) -public protocol AnalyticsProviderProtocol: TelemetryTrackerProtocol { - - var checkoutAttemptId: String? { get } +public protocol AnalyticsProviderProtocol { - func fetchAndCacheCheckoutAttemptIdIfNeeded() + /// Sends the initial data and retrieves the checkout attempt id as a response. + func sendInitialAnalytics(with flavor: AnalyticsFlavor, additionalFields: AdditionalAnalyticsFields?) - var additionalFields: (() -> AdditionalAnalyticsFields)? { get } + var checkoutAttemptId: String? { get } } internal final class AnalyticsProvider: AnalyticsProviderProtocol { @@ -51,8 +54,7 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { internal let apiClient: APIClientProtocol internal let configuration: AnalyticsConfiguration internal private(set) var checkoutAttemptId: String? - internal var additionalFields: (() -> AdditionalAnalyticsFields)? - private let uniqueAssetAPIClient: UniqueAssetAPIClient + private let uniqueAssetAPIClient: UniqueAssetAPIClient // MARK: - Initializers @@ -62,32 +64,34 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { ) { self.apiClient = apiClient self.configuration = configuration - self.uniqueAssetAPIClient = UniqueAssetAPIClient(apiClient: apiClient) + self.uniqueAssetAPIClient = UniqueAssetAPIClient(apiClient: apiClient) } // MARK: - Internal - - internal func fetchAndCacheCheckoutAttemptIdIfNeeded() { - fetchCheckoutAttemptId { _ in /* Do nothing, the point is to trigger the fetching and cache the value */ } - } - internal func fetchCheckoutAttemptId(completion: @escaping (String?) -> Void) { + internal func sendInitialAnalytics(with flavor: AnalyticsFlavor, additionalFields: AdditionalAnalyticsFields?) { guard configuration.isEnabled else { checkoutAttemptId = "do-not-track" - completion(checkoutAttemptId) return } + + let analyticsData = AnalyticsData(flavor: flavor, + additionalFields: additionalFields, + context: configuration.context) - let checkoutAttemptIdRequest = CheckoutAttemptIdRequest() + let initialAnalyticsRequest = InitialAnalyticsRequest(data: analyticsData) - uniqueAssetAPIClient.perform(checkoutAttemptIdRequest) { [weak self] result in - switch result { - case let .success(response): - self?.checkoutAttemptId = response.identifier - completion(response.identifier) - case .failure: - completion(nil) - } + uniqueAssetAPIClient.perform(initialAnalyticsRequest) { [weak self] result in + self?.saveCheckoutAttemptId(from: result) + } + } + + private func saveCheckoutAttemptId(from result: Result) { + switch result { + case let .success(response): + checkoutAttemptId = response.checkoutAttemptId + case .failure: + checkoutAttemptId = nil } } } diff --git a/Adyen/Analytics/AnalyticsProvider/TelemetryTracker.swift b/Adyen/Analytics/AnalyticsProvider/TelemetryTracker.swift deleted file mode 100644 index 6f1b870f51..0000000000 --- a/Adyen/Analytics/AnalyticsProvider/TelemetryTracker.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) 2023 Adyen N.V. -// -// This file is open source and available under the MIT license. See the LICENSE file for more info. -// - -import Foundation - -@_spi(AdyenInternal) -public enum TelemetryFlavor { - case components(type: PaymentMethodType) - case dropIn(type: String = "dropin", paymentMethods: [String]) - - // The `dropInComponent` type describes a component within the drop-in component. - // In telemetry, we need to distinguish when a component is used from the drop-in - // and when it's used as standalone. - case dropInComponent - - public var value: String { - switch self { - case .components: - return "components" - case .dropIn: - return "dropin" - case .dropInComponent: - return "dropInComponent" - } - } -} - -@_spi(AdyenInternal) -public protocol TelemetryTrackerProtocol { - func sendTelemetryEvent(flavor: TelemetryFlavor) -} - -// MARK: - TelemetryTrackerProtocol - -@_spi(AdyenInternal) -extension AnalyticsProvider: TelemetryTrackerProtocol { - - internal func sendTelemetryEvent(flavor: TelemetryFlavor) { - guard configuration.isEnabled else { return } - guard configuration.isTelemetryEnabled else { return } - if case .dropInComponent = flavor { return } - - let additionalFields = additionalFields?() - - let telemetryData = TelemetryData( - flavor: flavor, - amount: additionalFields?.amount, - context: configuration.context - ) - - fetchCheckoutAttemptId { [weak self] checkoutAttemptId in - let telemetryRequest = TelemetryRequest(data: telemetryData, - checkoutAttemptId: checkoutAttemptId) - self?.apiClient.perform(telemetryRequest) { _ in } - } - } -} diff --git a/Adyen/Analytics/Models/AdyenAnalytics.swift b/Adyen/Analytics/Models/AdyenAnalytics.swift new file mode 100644 index 0000000000..d031d9785f --- /dev/null +++ b/Adyen/Analytics/Models/AdyenAnalytics.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2023 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import Foundation + +@_spi(AdyenInternal) +/// Used as a singleton to update the sessionId +public final class AnalyticsForSession { + + /// Needed to be able to determine if using session + public static var sessionId: String? + + private init() { /* Private empty init */ } +} + +@_spi(AdyenInternal) +/// A protocol that defines the events that can occur under Checkout Analytics. +public protocol AnalyticsEvent: Encodable { + var timestamp: TimeInterval { get } + + var component: String { get } +} + +@_spi(AdyenInternal) +public extension AnalyticsEvent { + + var timestamp: TimeInterval { + Date().timeIntervalSince1970 + } +} diff --git a/Adyen/Analytics/Models/TelemetryData.swift b/Adyen/Analytics/Models/AnalyticsData.swift similarity index 84% rename from Adyen/Analytics/Models/TelemetryData.swift rename to Adyen/Analytics/Models/AnalyticsData.swift index 5693846d97..a5b04159e5 100644 --- a/Adyen/Analytics/Models/TelemetryData.swift +++ b/Adyen/Analytics/Models/AnalyticsData.swift @@ -11,7 +11,7 @@ import UIKit /// /// Used to e.g. override the version + platform from within the Flutter SDK @_spi(AdyenInternal) -public struct TelemetryContext { +public struct AnalyticsContext { internal let version: String internal let platform: Platform @@ -26,7 +26,7 @@ public struct TelemetryContext { } @_spi(AdyenInternal) -public extension TelemetryContext { +public extension AnalyticsContext { enum Platform: String { case iOS = "ios" @@ -35,7 +35,7 @@ public extension TelemetryContext { } } -internal struct TelemetryData: Encodable { +internal struct AnalyticsData: Encodable { // MARK: - Properties @@ -64,6 +64,8 @@ internal struct TelemetryData: Encodable { return identifier + String(UnicodeScalar(UInt8(value))) } }() + + internal let deviceModel = UIDevice.current.model internal let systemVersion = UIDevice.current.systemVersion @@ -79,19 +81,20 @@ internal struct TelemetryData: Encodable { internal var amount: Amount? + internal var sessionId: String? + internal var paymentMethods: [String] = [] internal let component: String // MARK: - Initializers - internal init( - flavor: TelemetryFlavor, - amount: Amount?, - context: TelemetryContext - ) { + internal init(flavor: AnalyticsFlavor, + additionalFields: AdditionalAnalyticsFields?, + context: AnalyticsContext) { self.flavor = flavor.value - self.amount = amount + self.amount = additionalFields?.amount + self.sessionId = additionalFields?.sessionId self.version = context.version self.platform = context.platform.rawValue @@ -102,8 +105,6 @@ internal struct TelemetryData: Encodable { self.component = type case let .components(type): self.component = type.rawValue - default: - self.component = "" } } } diff --git a/Adyen/Analytics/Models/AnalyticsEventError.swift b/Adyen/Analytics/Models/AnalyticsEventError.swift new file mode 100644 index 0000000000..353d20459c --- /dev/null +++ b/Adyen/Analytics/Models/AnalyticsEventError.swift @@ -0,0 +1,30 @@ +// +// Copyright (c) 2023 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import Foundation + +@_spi(AdyenInternal) +/// Represents an error in the analytics scheme that indicates the flow was interrupted due to an error in the SDK. +public struct AnalyticsEventError: AnalyticsEvent { + + public var component: String + + public var type: ErrorType + + public var code: String? + + public var message: String? + + public enum ErrorType: String, Encodable { + case network = "Network" + case implementation = "Implementation" + case `internal` = "Internal" + case api = "ApiError" + case sdk = "SdkError" + case thirdParty = "ThirdParty" + case generic = "Generic" + } +} diff --git a/Adyen/Analytics/Models/AnalyticsEventInfo.swift b/Adyen/Analytics/Models/AnalyticsEventInfo.swift new file mode 100644 index 0000000000..32f3d827a0 --- /dev/null +++ b/Adyen/Analytics/Models/AnalyticsEventInfo.swift @@ -0,0 +1,34 @@ +// +// Copyright (c) 2023 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import Foundation + +@_spi(AdyenInternal) +/// Represents an info event in the analytics scheme that can occur +/// multiple times during the checkout flow, such as input field focus/unfocus etc. +public struct AnalyticsEventInfo: AnalyticsEvent { + public var component: String + + public var type: InfoType + + public var target: String? + + public var isStoredPaymentMethod: Bool? + + public var brand: String? + + public var validationErrorCode: String? + + public var validationErrorMessage: String? + + public enum InfoType: String, Encodable { + case selected = "Selected" + case focus = "Focus" + case unfocus = "Unfocus" + case validationError = "ValidationError" + case rendered = "Rendered" + } +} diff --git a/Adyen/Analytics/Models/AnalyticsEventLog.swift b/Adyen/Analytics/Models/AnalyticsEventLog.swift new file mode 100644 index 0000000000..a0e3ee1a45 --- /dev/null +++ b/Adyen/Analytics/Models/AnalyticsEventLog.swift @@ -0,0 +1,39 @@ +// +// Copyright (c) 2023 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import Foundation + +@_spi(AdyenInternal) +/// A log in the analytics scheme represents important checkpoints such as the pay button press, 3ds challenge etc. +public struct AnalyticsEventLog: AnalyticsEvent { + + public var component: String + + public var type: LogType + + public var subType: LogSubType + + public var target: String + + public var message: String? + + public enum LogType: String, Encodable { + case action = "Action" + case submit = "Submit" + case redirect = "Redirect" + case threeDS2 = "ThreeDS2" + } + + public enum LogSubType: String, Encodable { + case threeDS2 = "ThreeDS2" + case redirect = "Redirect" + case voucher = "Voucher" + case await = "Await" + case qrCode = "QrCode" + case bankTransfer = "BankTransfer" + case sdk = "Sdk" + } +} diff --git a/Adyen/Analytics/Requests/AnalyticsRequest.swift b/Adyen/Analytics/Requests/AnalyticsRequest.swift new file mode 100644 index 0000000000..812f3fcaea --- /dev/null +++ b/Adyen/Analytics/Requests/AnalyticsRequest.swift @@ -0,0 +1,45 @@ +// +// Copyright (c) 2023 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import AdyenNetworking +import Foundation + +internal struct AnalyticsResponse: Response { /* Empty response */ } + +internal struct AnalyticsRequest: APIRequest { + + internal typealias ResponseType = AnalyticsResponse + + internal let path: String + + internal var counter: UInt = 0 + + internal let headers: [String: String] = [:] + + internal let queryParameters: [URLQueryItem] = [] + + internal let method: HTTPMethod = .post + + internal var channel: String = "iOS" + + internal var infos: [AnalyticsEventInfo] = [] + + internal var logs: [AnalyticsEventLog] = [] + + internal var errors: [AnalyticsEventError] = [] + + internal init(checkoutAttemptId: String) { + self.path = "checkoutanalytics/v3/analytics/\(checkoutAttemptId)" + } + + private enum CodingKeys: String, CodingKey { + case channel + case infos = "info" + case logs + case errors + + } +} diff --git a/Adyen/Analytics/Requests/CheckoutAttemptIdRequest.swift b/Adyen/Analytics/Requests/CheckoutAttemptIdRequest.swift deleted file mode 100644 index 8c1e3fc969..0000000000 --- a/Adyen/Analytics/Requests/CheckoutAttemptIdRequest.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2022 Adyen N.V. -// -// This file is open source and available under the MIT license. See the LICENSE file for more info. -// - -import AdyenNetworking -import Foundation - -internal struct CheckoutAttemptIdResponse: Response { - - // MARK: - Properties - - internal let identifier: String - - internal enum CodingKeys: String, CodingKey { - case identifier = "id" - } -} - -internal struct CheckoutAttemptIdRequest: APIRequest { - - internal typealias ResponseType = CheckoutAttemptIdResponse - - internal let path: String = "checkoutshopper/v2/analytics/id" - - internal var counter: UInt = 0 - - internal let headers: [String: String] = [:] - - internal let queryParameters: [URLQueryItem] = [] - - internal let method: HTTPMethod = .post - - internal let experiments: [String] = [] - - internal enum CodingKeys: CodingKey { - case experiments - } -} diff --git a/Adyen/Analytics/Requests/TelemetryRequest.swift b/Adyen/Analytics/Requests/InitialAnalyticsRequest.swift similarity index 74% rename from Adyen/Analytics/Requests/TelemetryRequest.swift rename to Adyen/Analytics/Requests/InitialAnalyticsRequest.swift index 0d9cfa4c9b..37ae06ecf9 100644 --- a/Adyen/Analytics/Requests/TelemetryRequest.swift +++ b/Adyen/Analytics/Requests/InitialAnalyticsRequest.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 Adyen N.V. +// Copyright (c) 2022 Adyen N.V. // // This file is open source and available under the MIT license. See the LICENSE file for more info. // @@ -7,13 +7,22 @@ import AdyenNetworking import Foundation -internal struct TelemetryResponse: Response { /* Empty response */ } +internal struct InitialAnalyticsResponse: Response { -internal struct TelemetryRequest: APIRequest { + // MARK: - Properties - internal typealias ResponseType = TelemetryResponse + internal let checkoutAttemptId: String - internal let path: String = "checkoutshopper/v2/analytics/log" + internal enum CodingKeys: String, CodingKey { + case checkoutAttemptId + } +} + +internal struct InitialAnalyticsRequest: APIRequest { + + internal typealias ResponseType = InitialAnalyticsResponse + + internal let path: String = "checkoutanalytics/v3/analytics" internal var counter: UInt = 0 @@ -31,6 +40,7 @@ internal struct TelemetryRequest: APIRequest { private let flavor: String private let userAgent: String? private var deviceBrand: String + private var deviceModel: String private let systemVersion: String private let referrer: String private let screenWidth: Int @@ -38,17 +48,19 @@ internal struct TelemetryRequest: APIRequest { private let paymentMethods: [String] private let component: String internal let amount: Amount? - internal let checkoutAttemptId: String? + internal let sessionId: String? // MARK: - Initializers - internal init(data: TelemetryData, checkoutAttemptId: String?) { + internal init(data: AnalyticsData) { self.version = data.version + self.platform = data.platform self.channel = data.channel self.locale = data.locale self.flavor = data.flavor self.userAgent = data.userAgent self.deviceBrand = data.deviceBrand + self.deviceModel = data.deviceModel self.systemVersion = data.systemVersion self.referrer = data.referrer self.screenWidth = data.screenWidth @@ -56,25 +68,25 @@ internal struct TelemetryRequest: APIRequest { self.paymentMethods = data.paymentMethods self.component = data.component self.amount = data.amount - self.platform = data.platform - self.checkoutAttemptId = checkoutAttemptId + self.sessionId = data.sessionId } internal enum CodingKeys: CodingKey { case version + case platform case channel case locale case flavor case userAgent case deviceBrand + case deviceModel case systemVersion case referrer case screenWidth case containerWidth case paymentMethods case component - case checkoutAttemptId case amount - case platform + case sessionId } } diff --git a/Adyen/Core/APIClient/AnalyticsEnvironment.swift b/Adyen/Core/APIClient/AnalyticsEnvironment.swift new file mode 100644 index 0000000000..60692d0c96 --- /dev/null +++ b/Adyen/Core/APIClient/AnalyticsEnvironment.swift @@ -0,0 +1,60 @@ +// +// Copyright (c) 2024 Adyen N.V. +// +// This file is open source and available under the MIT license. See the LICENSE file for more info. +// + +import AdyenNetworking +import Foundation + +/// Enum that defines the analytics environment URLs.. +@_spi(AdyenInternal) +public enum AnalyticsEnvironment: String, AnyAPIEnvironment { + + case test = "https://checkoutanalytics-test.adyen.com/" + + case liveEurope = "https://checkoutanalytics-live.adyen.com/" + + case liveAustralia = "https://checkoutanalytics-live-au.adyen.com/" + + case liveUnitedStates = "https://checkoutanalytics-live-us.adyen.com/" + + case liveApse = "https://checkoutanalytics-live-apse.adyen.com/" + + case liveIndia = "https://checkoutanalytics-live-in.adyen.com/" + + @_spi(AdyenInternal) + case beta = "https://beta.adyen.com/checkoutanalytics/v3/analytics/" + + @_spi(AdyenInternal) + case local = "http://localhost:8080/" + + public var baseURL: URL { URL(string: rawValue)! } + +} + +extension Environment { + + internal func toAnalyticsEnvironment() -> AnalyticsEnvironment { + switch self { + case .beta: + return .beta + case .test: + return .test + case .liveApse: + return .liveApse + case .liveIndia: + return .liveIndia + case .liveEurope: + return .liveEurope + case .liveAustralia: + return .liveAustralia + case .liveUnitedStates: + return .liveUnitedStates + case .local: + return .local + default: + return .test + } + } +} diff --git a/Adyen/Core/APIClient/Environment.swift b/Adyen/Core/APIClient/Environment.swift index 96ecd904e8..4e3f8d349e 100644 --- a/Adyen/Core/APIClient/Environment.swift +++ b/Adyen/Core/APIClient/Environment.swift @@ -59,3 +59,6 @@ public struct Environment: AnyAPIEnvironment { } } + +@_spi(AdyenInternal) +extension Environment: Equatable {} diff --git a/Adyen/Core/AdyenContext/AdyenContext.swift b/Adyen/Core/AdyenContext/AdyenContext.swift index 00ad48b696..eaccefbdcc 100644 --- a/Adyen/Core/AdyenContext/AdyenContext.swift +++ b/Adyen/Core/AdyenContext/AdyenContext.swift @@ -19,7 +19,7 @@ public final class AdyenContext: PaymentAware { public private(set) var payment: Payment? @_spi(AdyenInternal) - public let analyticsProvider: AnalyticsProviderProtocol + public let analyticsProvider: AnalyticsProviderProtocol? // MARK: - Initializers @@ -30,28 +30,28 @@ public final class AdyenContext: PaymentAware { /// - payment: The payment information. public convenience init(apiContext: APIContext, payment: Payment?, analyticsConfiguration: AnalyticsConfiguration = .init()) { - let analyticsProvider = AnalyticsProvider( - apiClient: APIClient(apiContext: apiContext), - configuration: analyticsConfiguration - ) + var analyticsProvider: AnalyticsProviderProtocol? + if let analyticsEnvironment = (apiContext.environment as? Environment)?.toAnalyticsEnvironment(), + let analyticsApiContext = try? APIContext(environment: analyticsEnvironment, + clientKey: apiContext.clientKey) { + + analyticsProvider = AnalyticsProvider( + apiClient: APIClient(apiContext: analyticsApiContext), + configuration: analyticsConfiguration + ) + } self.init( apiContext: apiContext, payment: payment, analyticsProvider: analyticsProvider ) - - analyticsProvider.additionalFields = { [weak self] in - .init( - amount: self?.payment?.amount - ) - } } /// Internal init for testing only internal init(apiContext: APIContext, payment: Payment?, - analyticsProvider: AnalyticsProviderProtocol) { + analyticsProvider: AnalyticsProviderProtocol?) { self.apiContext = apiContext self.analyticsProvider = analyticsProvider self.payment = payment diff --git a/Adyen/Core/Components/AbstractPersonalInformationComponent/AbstractPersonalInformationComponent.swift b/Adyen/Core/Components/AbstractPersonalInformationComponent/AbstractPersonalInformationComponent.swift index 749d022c22..a741bb5665 100644 --- a/Adyen/Core/Components/AbstractPersonalInformationComponent/AbstractPersonalInformationComponent.swift +++ b/Adyen/Core/Components/AbstractPersonalInformationComponent/AbstractPersonalInformationComponent.swift @@ -262,7 +262,10 @@ extension AbstractPersonalInformationComponent: ViewControllerDelegate { // MARK: - ViewControllerDelegate public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() populateFields() } + + public func viewDidLoad(viewController: UIViewController) { + sendInitialAnalytics() + } } diff --git a/Adyen/Core/Components/InstantPaymentComponent.swift b/Adyen/Core/Components/InstantPaymentComponent.swift index bb2dee719d..0865f3ebe2 100644 --- a/Adyen/Core/Components/InstantPaymentComponent.swift +++ b/Adyen/Core/Components/InstantPaymentComponent.swift @@ -56,14 +56,12 @@ public final class InstantPaymentComponent: PaymentComponent { /// Generate the payment details and invoke PaymentsComponentDelegate method. public func initiatePayment() { - sendTelemetryEvent() + // We are not attempting to fetch the checkoutAttemptId as it won't be ready for the payment + // and we don't want to block it for an analytics call. submit(data: paymentData) } } -@_spi(AdyenInternal) -extension InstantPaymentComponent: TrackableComponent {} - /// Describes a payment details that contains nothing but the payment method type name. public struct InstantPaymentDetails: PaymentMethodDetails { diff --git a/Adyen/Core/Components/StoredPaymentMethodComponent.swift b/Adyen/Core/Components/StoredPaymentMethodComponent.swift index 8f0c8d24ee..d7a470b8f2 100644 --- a/Adyen/Core/Components/StoredPaymentMethodComponent.swift +++ b/Adyen/Core/Components/StoredPaymentMethodComponent.swift @@ -42,12 +42,7 @@ public final class StoredPaymentMethodComponent: PaymentComponent, // MARK: - PresentableComponent public lazy var viewController: UIViewController = { - Analytics.sendEvent( - component: storedPaymentMethod.type.rawValue, - flavor: _isDropIn ? .dropin : .components, - context: context.apiContext - ) - sendTelemetryEvent() + sendInitialAnalytics() let localizationParameters = configuration.localizationParameters let displayInformation = storedPaymentMethod.displayInformation(using: localizationParameters) diff --git a/Adyen/Core/Core Protocols/PaymentComponent.swift b/Adyen/Core/Core Protocols/PaymentComponent.swift index fa3e570a81..03f179fb44 100644 --- a/Adyen/Core/Core Protocols/PaymentComponent.swift +++ b/Adyen/Core/Core Protocols/PaymentComponent.swift @@ -31,10 +31,8 @@ extension PaymentComponent { /// - component: The component from which the payment originates. public func submit(data: PaymentComponentData, component: PaymentComponent? = nil) { let component = component ?? self - /// try to fetch the fetchCheckoutAttemptId to get cached if its not already cached - component.context.analyticsProvider.fetchAndCacheCheckoutAttemptIdIfNeeded() - let updatedData = data.replacing(checkoutAttemptId: component.context.analyticsProvider.checkoutAttemptId) + let updatedData = data.replacing(checkoutAttemptId: component.context.analyticsProvider?.checkoutAttemptId) guard updatedData.browserInfo == nil else { delegate?.didSubmit(updatedData, from: component) diff --git a/Adyen/Core/Core Protocols/PresentableComponent.swift b/Adyen/Core/Core Protocols/PresentableComponent.swift index 510f0b23dd..15b89eb4b0 100644 --- a/Adyen/Core/Core Protocols/PresentableComponent.swift +++ b/Adyen/Core/Core Protocols/PresentableComponent.swift @@ -62,15 +62,29 @@ public extension PresentableComponent { @_spi(AdyenInternal) public protocol TrackableComponent: Component { + + /// Sends the initial data and retrieves the checkout attempt id + func sendInitialAnalytics() +} - func sendTelemetryEvent() +@_spi(AdyenInternal) +extension TrackableComponent where Self: ViewControllerDelegate { + + public func viewDidLoad(viewController: UIViewController) { + sendInitialAnalytics() + } } @_spi(AdyenInternal) extension TrackableComponent where Self: PaymentMethodAware { - - public func sendTelemetryEvent() { - let flavor: TelemetryFlavor = _isDropIn ? .dropInComponent : .components(type: paymentMethod.type) - context.analyticsProvider.sendTelemetryEvent(flavor: flavor) + + public func sendInitialAnalytics() { + // initial call is not needed again if inside dropIn + guard !_isDropIn else { return } + let flavor: AnalyticsFlavor = .components(type: paymentMethod.type) + let amount = context.payment?.amount + let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: AnalyticsForSession.sessionId) + context.analyticsProvider?.sendInitialAnalytics(with: flavor, + additionalFields: additionalFields) } } diff --git a/Adyen/UI/Dimentions.swift b/Adyen/UI/Dimensions.swift similarity index 100% rename from Adyen/UI/Dimentions.swift rename to Adyen/UI/Dimensions.swift diff --git a/Adyen/UI/View Controllers/SearchViewController/SearchViewController.swift b/Adyen/UI/View Controllers/SearchViewController/SearchViewController.swift index b033c15936..bb72c5b925 100644 --- a/Adyen/UI/View Controllers/SearchViewController/SearchViewController.swift +++ b/Adyen/UI/View Controllers/SearchViewController/SearchViewController.swift @@ -26,6 +26,9 @@ public class SearchViewController: UIViewController, AdyenObserver { internal let viewModel: ViewModel internal let emptyView: SearchResultsEmptyView + /// Delegate to handle different viewController events. + public weak var delegate: ViewControllerDelegate? + public lazy var resultsListViewController = ListViewController(style: viewModel.style) /// Initializes the search view controller. @@ -71,6 +74,8 @@ public class SearchViewController: UIViewController, AdyenObserver { view.addSubview(loadingView) + delegate?.viewDidLoad(viewController: self) + emptyView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissKeyboardTapped))) emptyView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(emptyView) @@ -102,6 +107,8 @@ public class SearchViewController: UIViewController, AdyenObserver { override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + delegate?.viewWillAppear(viewController: self) + if viewModel.shouldFocusSearchBarOnAppearance { DispatchQueue.main.async { // Fix animation glitch on iOS 17 self.searchBar.becomeFirstResponder() @@ -109,6 +116,11 @@ public class SearchViewController: UIViewController, AdyenObserver { } } + override open func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + delegate?.viewDidAppear(viewController: self) + } + private func setupConstraints() { NSLayoutConstraint.activate([ diff --git a/AdyenCard/Components/Card/CardComponentExtensions.swift b/AdyenCard/Components/Card/CardComponentExtensions.swift index 546cec01a6..96e7e2f536 100644 --- a/AdyenCard/Components/Card/CardComponentExtensions.swift +++ b/AdyenCard/Components/Card/CardComponentExtensions.swift @@ -66,16 +66,10 @@ extension CardComponent: TrackableComponent {} extension CardComponent: ViewControllerDelegate { public func viewDidLoad(viewController: UIViewController) { - Analytics.sendEvent(component: paymentMethod.type.rawValue, - flavor: _isDropIn ? .dropin : .components, - context: context.apiContext) + sendInitialAnalytics() // just cache the public key value fetchCardPublicKey(notifyingDelegateOnFailure: false) } - - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } } extension KCPDetails { diff --git a/AdyenCard/Components/GiftCardComponent/GiftCardComponent+Extensions.swift b/AdyenCard/Components/GiftCardComponent/GiftCardComponent+Extensions.swift index 3a4d475409..cd01c2d0f2 100644 --- a/AdyenCard/Components/GiftCardComponent/GiftCardComponent+Extensions.swift +++ b/AdyenCard/Components/GiftCardComponent/GiftCardComponent+Extensions.swift @@ -14,17 +14,10 @@ extension GiftCardComponent: TrackableComponent {} extension GiftCardComponent: ViewControllerDelegate { public func viewDidLoad(viewController: UIViewController) { - Analytics.sendEvent(component: paymentMethod.type.rawValue, - flavor: _isDropIn ? .dropin : .components, - context: context.apiContext) + sendInitialAnalytics() // just cache the public key value fetchCardPublicKey(notifyingDelegateOnFailure: false) } - - /// :nodoc: - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } } extension GiftCardComponent { diff --git a/AdyenCard/Components/Stored Card/StoredCardComponent.swift b/AdyenCard/Components/Stored Card/StoredCardComponent.swift index 2774f0ffe5..02b741dc74 100644 --- a/AdyenCard/Components/Stored Card/StoredCardComponent.swift +++ b/AdyenCard/Components/Stored Card/StoredCardComponent.swift @@ -37,12 +37,7 @@ internal final class StoredCardComponent: PaymentComponent, PaymentAware, Presen } internal lazy var storedCardAlertManager: StoredCardAlertManager = { - Analytics.sendEvent( - component: paymentMethod.type.rawValue, - flavor: _isDropIn ? .dropin : .components, - context: context.apiContext - ) - sendTelemetryEvent() + sendInitialAnalytics() let manager = StoredCardAlertManager(paymentMethod: storedCardPaymentMethod, context: context, diff --git a/AdyenCashAppPay/CashAppPayComponent.swift b/AdyenCashAppPay/CashAppPayComponent.swift index 9b17fa5d58..960a827a2c 100644 --- a/AdyenCashAppPay/CashAppPayComponent.swift +++ b/AdyenCashAppPay/CashAppPayComponent.swift @@ -253,9 +253,4 @@ extension CashAppPayComponent: TrackableComponent {} @available(iOS 13.0, *) @_spi(AdyenInternal) -extension CashAppPayComponent: ViewControllerDelegate { - - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } -} +extension CashAppPayComponent: ViewControllerDelegate {} diff --git a/AdyenComponents/ACH Direct Debit/ACHDirectDebitComponent.swift b/AdyenComponents/ACH Direct Debit/ACHDirectDebitComponent.swift index 3e42659743..a6f6e825e6 100644 --- a/AdyenComponents/ACH Direct Debit/ACHDirectDebitComponent.swift +++ b/AdyenComponents/ACH Direct Debit/ACHDirectDebitComponent.swift @@ -288,17 +288,10 @@ extension ACHDirectDebitComponent: TrackableComponent {} extension ACHDirectDebitComponent: ViewControllerDelegate { public func viewDidLoad(viewController: UIViewController) { - Analytics.sendEvent(component: paymentMethod.type.rawValue, - flavor: _isDropIn ? .dropin : .components, - context: context.apiContext) + sendInitialAnalytics() // just cache the public key value fetchCardPublicKey(notifyingDelegateOnFailure: false) } - - /// :nodoc: - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } } /// Describes any configuration for the ACH Direct Debit component. diff --git a/AdyenComponents/Apple Pay/ApplePayComponent.swift b/AdyenComponents/Apple Pay/ApplePayComponent.swift index d583f9ee37..875bb4e2d0 100644 --- a/AdyenComponents/Apple Pay/ApplePayComponent.swift +++ b/AdyenComponents/Apple Pay/ApplePayComponent.swift @@ -130,12 +130,4 @@ extension ApplePayComponent { extension ApplePayComponent: TrackableComponent {} @_spi(AdyenInternal) -extension ApplePayComponent: ViewControllerDelegate { - public func viewDidLoad(viewController: UIViewController) { /* Empty implementation */ } - - public func viewDidAppear(viewController: UIViewController) { /* Empty implementation */ } - - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } -} +extension ApplePayComponent: ViewControllerDelegate {} diff --git a/AdyenComponents/BACS Direct Debit/BACSDirectDebitComponent.swift b/AdyenComponents/BACS Direct Debit/BACSDirectDebitComponent.swift index 6ebaccbf5f..a92433719a 100644 --- a/AdyenComponents/BACS Direct Debit/BACSDirectDebitComponent.swift +++ b/AdyenComponents/BACS Direct Debit/BACSDirectDebitComponent.swift @@ -70,8 +70,7 @@ public final class BACSDirectDebitComponent: PaymentComponent, PaymentAware, Pre style: configuration.style) let tracker = BACSDirectDebitComponentTracker(paymentMethod: bacsPaymentMethod, - apiContext: context.apiContext, - telemetryTracker: context.analyticsProvider, + context: context, isDropIn: _isDropIn) let itemsFactory = BACSItemsFactory(styleProvider: configuration.style, localizationParameters: configuration.localizationParameters, diff --git a/AdyenComponents/BACS Direct Debit/Scenes/Input/BACSInputPresenter.swift b/AdyenComponents/BACS Direct Debit/Scenes/Input/BACSInputPresenter.swift index f15425faf1..01ce7afcd1 100644 --- a/AdyenComponents/BACS Direct Debit/Scenes/Input/BACSInputPresenter.swift +++ b/AdyenComponents/BACS Direct Debit/Scenes/Input/BACSInputPresenter.swift @@ -56,7 +56,7 @@ internal class BACSInputPresenter: BACSInputPresenterProtocol { // MARK: - BACSInputPresenterProtocol internal func viewDidLoad() { - tracker.sendTelemetryEvent() + tracker.sendInitialAnalytics() createItems() setupView() } diff --git a/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift b/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift index e9af945b1b..cf44cc27a1 100644 --- a/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift +++ b/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift @@ -8,7 +8,7 @@ import Foundation internal protocol BACSDirectDebitComponentTrackerProtocol: AnyObject { - func sendTelemetryEvent() + func sendInitialAnalytics() } internal class BACSDirectDebitComponentTracker: BACSDirectDebitComponentTrackerProtocol { @@ -16,27 +16,29 @@ internal class BACSDirectDebitComponentTracker: BACSDirectDebitComponentTrackerP // MARK: - Properties private let paymentMethod: BACSDirectDebitPaymentMethod - private let apiContext: APIContext - private let telemetryTracker: TelemetryTrackerProtocol + private let context: AdyenContext private let isDropIn: Bool // MARK: - Initializers internal init(paymentMethod: BACSDirectDebitPaymentMethod, - apiContext: APIContext, - telemetryTracker: TelemetryTrackerProtocol, + context: AdyenContext, isDropIn: Bool) { self.paymentMethod = paymentMethod - self.apiContext = apiContext - self.telemetryTracker = telemetryTracker + self.context = context self.isDropIn = isDropIn } // MARK: - BACSDirectDebitComponentTrackerProtocol - internal func sendTelemetryEvent() { - let flavor: TelemetryFlavor = isDropIn ? .dropInComponent : .components(type: paymentMethod.type) - telemetryTracker.sendTelemetryEvent(flavor: flavor) + internal func sendInitialAnalytics() { + // initial call is not needed again if inside dropIn + guard !isDropIn else { return } + let flavor: AnalyticsFlavor = .components(type: paymentMethod.type) + let amount = context.payment?.amount + let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: AnalyticsForSession.sessionId) + context.analyticsProvider?.sendInitialAnalytics(with: flavor, + additionalFields: additionalFields) } } diff --git a/AdyenComponents/BLIK/BLIKComponent.swift b/AdyenComponents/BLIK/BLIKComponent.swift index c943415638..b6f954db2f 100644 --- a/AdyenComponents/BLIK/BLIKComponent.swift +++ b/AdyenComponents/BLIK/BLIKComponent.swift @@ -120,10 +120,4 @@ public final class BLIKComponent: PaymentComponent, PresentableComponent, Paymen extension BLIKComponent: TrackableComponent {} @_spi(AdyenInternal) -extension BLIKComponent: ViewControllerDelegate { - // MARK: - ViewControllerDelegate - - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } -} +extension BLIKComponent: ViewControllerDelegate {} diff --git a/AdyenComponents/Boleto/BoletoComponent.swift b/AdyenComponents/Boleto/BoletoComponent.swift index e746e6feeb..741b490b23 100644 --- a/AdyenComponents/Boleto/BoletoComponent.swift +++ b/AdyenComponents/Boleto/BoletoComponent.swift @@ -90,6 +90,7 @@ public final class BoletoComponent: PaymentComponent, } (component.viewController as? SecuredViewController)?.delegate = self component.delegate = self + component._isDropIn = _isDropIn return component }() @@ -171,13 +172,8 @@ extension BoletoComponent: TrackableComponent {} @_spi(AdyenInternal) extension BoletoComponent: ViewControllerDelegate { - - public func viewDidLoad(viewController: UIViewController) {} - - public func viewDidAppear(viewController: UIViewController) {} - + public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() prefillFields(for: formComponent) } } diff --git a/AdyenComponents/Issuer List/IssuerListComponent.swift b/AdyenComponents/Issuer List/IssuerListComponent.swift index 1392c4eb0c..b7ac725e93 100644 --- a/AdyenComponents/Issuer List/IssuerListComponent.swift +++ b/AdyenComponents/Issuer List/IssuerListComponent.swift @@ -64,12 +64,15 @@ public final class IssuerListComponent: PaymentComponent, PaymentAware, Presenta handler(self.listItems(for: searchText)) } - return SearchViewController( + let searchViewController = SearchViewController( viewModel: viewModel, emptyView: IssuerListEmptyView( localizationParameters: configuration.localizationParameters ) ) + searchViewController.delegate = self + + return searchViewController }() public func stopLoading() { @@ -129,12 +132,7 @@ public final class IssuerListComponent: PaymentComponent, PaymentAware, Presenta } @_spi(AdyenInternal) -extension IssuerListComponent: ViewControllerDelegate { - - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } -} +extension IssuerListComponent: ViewControllerDelegate {} @_spi(AdyenInternal) extension IssuerListComponent: TrackableComponent {} diff --git a/AdyenComponents/SEPA Direct Debit/SEPADirectDebitComponent.swift b/AdyenComponents/SEPA Direct Debit/SEPADirectDebitComponent.swift index 7a6b3ffff6..23f77c95f5 100644 --- a/AdyenComponents/SEPA Direct Debit/SEPADirectDebitComponent.swift +++ b/AdyenComponents/SEPA Direct Debit/SEPADirectDebitComponent.swift @@ -140,9 +140,4 @@ public final class SEPADirectDebitComponent: PaymentComponent, PaymentAware, Pre extension SEPADirectDebitComponent: TrackableComponent {} @_spi(AdyenInternal) -extension SEPADirectDebitComponent: ViewControllerDelegate { - - public func viewWillAppear(viewController: UIViewController) { - sendTelemetryEvent() - } -} +extension SEPADirectDebitComponent: ViewControllerDelegate {} diff --git a/AdyenDropIn/Components/PaymentMethodListComponent.swift b/AdyenDropIn/Components/PaymentMethodListComponent.swift index 81a803b50b..1e5c5a7800 100644 --- a/AdyenDropIn/Components/PaymentMethodListComponent.swift +++ b/AdyenDropIn/Components/PaymentMethodListComponent.swift @@ -163,12 +163,6 @@ internal final class PaymentMethodListComponent: ComponentLoader, PresentableCom internal func stopLoading() { listViewController.stopLoading() } - - // MARK: - Private - - private func sendTelemetryEvent() { - context.analyticsProvider.sendTelemetryEvent(flavor: .dropIn(paymentMethods: [])) - } } extension PaymentMethodListComponent: ViewControllerDelegate { diff --git a/AdyenDropIn/Components/PreselectedPaymentMethodComponent.swift b/AdyenDropIn/Components/PreselectedPaymentMethodComponent.swift index 5f39ca147d..289633bce0 100644 --- a/AdyenDropIn/Components/PreselectedPaymentMethodComponent.swift +++ b/AdyenDropIn/Components/PreselectedPaymentMethodComponent.swift @@ -20,9 +20,10 @@ internal protocol PreselectedPaymentMethodComponentDelegate: AnyObject { /// A component that presents a single preselected payment method and option to open more payment methods. internal final class PreselectedPaymentMethodComponent: ComponentLoader, - PresentableComponent, - Localizable, - Cancellable { + PresentableComponent, + PaymentMethodAware, + Localizable, + Cancellable { private let title: String private let defaultComponent: PaymentComponent @@ -169,3 +170,6 @@ internal final class PreselectedPaymentMethodComponent: ComponentLoader, } extension PreselectedPaymentMethodComponent: ViewControllerDelegate {} + +@_spi(AdyenInternal) +extension PreselectedPaymentMethodComponent: TrackableComponent {} diff --git a/AdyenDropIn/DropInComponentExtensions.swift b/AdyenDropIn/DropInComponentExtensions.swift index cd4f64cbd7..2a7a3aff05 100644 --- a/AdyenDropIn/DropInComponentExtensions.swift +++ b/AdyenDropIn/DropInComponentExtensions.swift @@ -21,8 +21,7 @@ import UIKit extension DropInComponent: PaymentMethodListComponentDelegate { internal func didLoad(_ paymentMethodListComponent: PaymentMethodListComponent) { - let paymentMethodTypes = paymentMethods.regular.map(\.type.rawValue) - context.analyticsProvider.sendTelemetryEvent(flavor: .dropIn(paymentMethods: paymentMethodTypes)) + sendInitialAnalytics() } internal func didSelect(_ component: PaymentComponent, @@ -51,10 +50,8 @@ extension DropInComponent: PaymentComponentDelegate { public func didSubmit(_ data: PaymentComponentData, from component: PaymentComponent) { paymentInProgress = true - /// try to fetch the fetchCheckoutAttemptId to get cached if its not already cached - component.context.analyticsProvider.fetchAndCacheCheckoutAttemptIdIfNeeded() - let updatedData = data.replacing(checkoutAttemptId: component.context.analyticsProvider.checkoutAttemptId) + let updatedData = data.replacing(checkoutAttemptId: component.context.analyticsProvider?.checkoutAttemptId) guard updatedData.browserInfo == nil else { self.delegate?.didSubmit(updatedData, from: component, in: self) @@ -158,3 +155,15 @@ extension DropInComponent: ReadyToSubmitPaymentComponentDelegate { rootComponent = newRoot } } + +@_spi(AdyenInternal) +extension DropInComponent: TrackableComponent { + + public func sendInitialAnalytics() { + let paymentMethodTypes = paymentMethods.regular.map(\.type.rawValue) + let flavor = AnalyticsFlavor.dropIn(paymentMethods: paymentMethodTypes) + let amount = context.payment?.amount + let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: AnalyticsForSession.sessionId) + context.analyticsProvider?.sendInitialAnalytics(with: flavor, additionalFields: additionalFields) + } +} diff --git a/AdyenSession/AdyenSession.swift b/AdyenSession/AdyenSession.swift index 0c4c0b420a..107365740a 100644 --- a/AdyenSession/AdyenSession.swift +++ b/AdyenSession/AdyenSession.swift @@ -119,6 +119,7 @@ public final class AdyenSession { sessionContext: sessionContext) session.delegate = delegate session.presentationDelegate = presentationDelegate + AnalyticsForSession.sessionId = sessionContext.identifier completion(.success(session)) case let .failure(error): completion(.failure(error)) diff --git a/Tests/Adyen Tests/Analytics/AnalyticsEventTests.swift b/Tests/Adyen Tests/Analytics/AnalyticsEventTests.swift new file mode 100644 index 0000000000..5b79e7d8a6 --- /dev/null +++ b/Tests/Adyen Tests/Analytics/AnalyticsEventTests.swift @@ -0,0 +1,97 @@ +// +// AnalyticsEventTests.swift +// AdyenUIKitTests +// +// Created by Naufal Aros on 4/12/22. +// Copyright © 2022 Adyen. All rights reserved. +// + +import XCTest +@_spi(AdyenInternal) @testable import Adyen +@testable import AdyenNetworking + +class AnalyticsEventTests: XCTestCase { + + var apiClient: APIClientMock! + var sut: AnalyticsProviderProtocol! + + override func setUpWithError() throws { + try super.setUpWithError() + apiClient = APIClientMock() + let checkoutAttemptIdResponse = InitialAnalyticsResponse(checkoutAttemptId: "checkoutAttempId1") + let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + apiClient.mockedResults = [checkoutAttemptIdResult] + sut = AnalyticsProvider(apiClient: apiClient, configuration: .init()) + } + + override func tearDownWithError() throws { + apiClient = nil + sut = nil + try super.tearDownWithError() + } + + private func sendInitialAnalytics(flavor: AnalyticsFlavor = .components(type: .achDirectDebit)) { + sut.sendInitialAnalytics(with: flavor, additionalFields: nil) + } + + func testSendInitialEventGivenAnalyticsIsDisabledShouldNotSendAnyRequest() throws { + // Given + var analyticsConfiguration = AnalyticsConfiguration() + analyticsConfiguration.isEnabled = false + sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) + + let expectedRequestCalls = 0 + + // When + sendInitialAnalytics() + + // Then + XCTAssertEqual(expectedRequestCalls, apiClient.counter, "One or more analytics requests were sent.") + XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") + } + + func testSendInitialEventGivenEnabledAndFlavorIsComponentsShouldSendInitialRequest() throws { + // Given + let analyticsConfiguration = AnalyticsConfiguration() + sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) + + let flavor: AnalyticsFlavor = .components(type: .affirm) + let expectedRequestCalls = 1 + + let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + apiClient.mockedResults = [checkoutAttemptIdResult] + + // When + sendInitialAnalytics(flavor: flavor) + + // Then + wait(for: .milliseconds(1)) + XCTAssertEqual(expectedRequestCalls, apiClient.counter, "Invalid request number made.") + XCTAssertEqual(sut.checkoutAttemptId, "cb3eef98-978e-4f6f-b299-937a4450be1f1648546838056be73d8f38ee8bcc3a65ec14e41b037a59f255dcd9e83afe8c06bd3e7abcad993") + } + + func testSendInitialEventGivenEnabledAndFlavorIsDropInShouldSendInitialRequest() throws { + // Given + let analyticsConfiguration = AnalyticsConfiguration() + sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) + + let flavor: AnalyticsFlavor = .dropIn(paymentMethods: ["scheme", "paypal", "affirm"]) + let expectedRequestCalls = 1 + + let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + apiClient.mockedResults = [checkoutAttemptIdResult] + + // When + sendInitialAnalytics(flavor: flavor) + + // Then + wait(for: .milliseconds(1)) + XCTAssertEqual(expectedRequestCalls, apiClient.counter, "Invalid request number made.") + } + + // MARK: - Private + + private var checkoutAttemptIdResponse: InitialAnalyticsResponse { + .init(checkoutAttemptId: "cb3eef98-978e-4f6f-b299-937a4450be1f1648546838056be73d8f38ee8bcc3a65ec14e41b037a59f255dcd9e83afe8c06bd3e7abcad993") + } +} diff --git a/Tests/Adyen Tests/Analytics/TelemetryFlavorTests.swift b/Tests/Adyen Tests/Analytics/AnalyticsFlavorTests.swift similarity index 59% rename from Tests/Adyen Tests/Analytics/TelemetryFlavorTests.swift rename to Tests/Adyen Tests/Analytics/AnalyticsFlavorTests.swift index 3e079fbc38..96d0086a9c 100644 --- a/Tests/Adyen Tests/Analytics/TelemetryFlavorTests.swift +++ b/Tests/Adyen Tests/Analytics/AnalyticsFlavorTests.swift @@ -1,5 +1,5 @@ // -// TelemetryFlavorTests.swift +// AnalyticsFlavorTests.swift // AdyenUIKitTests // // Created by Naufal Aros on 4/13/22. @@ -9,16 +9,16 @@ import XCTest @_spi(AdyenInternal) @testable import Adyen -class TelemetryFlavorTests: XCTestCase { +class AnalyticsFlavorTests: XCTestCase { - var sut: TelemetryFlavor! + var sut: AnalyticsFlavor! override func tearDownWithError() throws { sut = nil try super.tearDownWithError() } - func testTelemetryFlavorValueWhenFlavorIsComponentsMatchesFlavorType() throws { + func testAnalyticsFlavorValueWhenFlavorIsComponentsMatchesFlavorType() throws { // Given let expectedFlavorValue = "components" @@ -29,7 +29,7 @@ class TelemetryFlavorTests: XCTestCase { XCTAssertEqual(expectedFlavorValue, sut.value) } - func testTelemetryFlavorValueWhenFlavorIsDropInMatchesFlavorType() throws { + func testAnalyticsFlavorValueWhenFlavorIsDropInMatchesFlavorType() throws { // Given let expectedFlavorValue = "dropin" @@ -39,15 +39,4 @@ class TelemetryFlavorTests: XCTestCase { // Then XCTAssertEqual(expectedFlavorValue, sut.value) } - - func testTelemetryFlavorValueWhenFlavorIsDropInComponentMatchesFlavorType() throws { - // Given - let expectedFlavorValue = "dropInComponent" - - // When - sut = .dropInComponent - - // Then - XCTAssertEqual(expectedFlavorValue, sut.value) - } } diff --git a/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift b/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift index 3e8a4664d3..11009fa78d 100644 --- a/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift +++ b/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift @@ -11,29 +11,19 @@ import Foundation class AnalyticsProviderMock: AnalyticsProviderProtocol { - var additionalFields: (() -> AdditionalAnalyticsFields)? - var checkoutAttemptId: String? { underlyingCheckoutAttemptId } + var checkoutAttemptId: String? // MARK: - checkoutAttemptId - - func fetchCheckoutAttemptId(completion: @escaping (String?) -> Void) { - completion(underlyingCheckoutAttemptId) - } - func fetchAndCacheCheckoutAttemptIdIfNeeded() { - fetchCheckoutAttemptId(completion: { _ in }) + func sendInitialAnalytics(with flavor: AnalyticsFlavor, additionalFields: AdditionalAnalyticsFields?) { + initialEventCallsCount += 1 + checkoutAttemptId = _checkoutAttemptId } - var underlyingCheckoutAttemptId: String? - - // MARK: - sendTelemetryEvent - - var sendTelemetryEventCallsCount = 0 - var sendTelemetryEventCalled: Bool { - sendTelemetryEventCallsCount > 0 - } + var _checkoutAttemptId: String? - func sendTelemetryEvent(flavor: TelemetryFlavor) { - sendTelemetryEventCallsCount += 1 + var initialEventCallsCount = 0 + var initialEventCalled: Bool { + initialEventCallsCount > 0 } } diff --git a/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift b/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift index a9d29c47e2..21ba4a2d5e 100644 --- a/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift +++ b/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift @@ -19,34 +19,26 @@ class AnalyticsProviderTests: XCTestCase { // Then XCTAssertTrue(sut.configuration.isEnabled) - XCTAssertTrue(sut.configuration.isTelemetryEnabled) } func testFetchCheckoutAttemptIdWhenAnalyticsIsEnabledShouldTriggerRequest() throws { // Given var analyticsConfiguration = AnalyticsConfiguration() analyticsConfiguration.isEnabled = true + let apiClient = APIClientMock() let sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) let expectedCheckoutAttemptId = checkoutAttemptIdMockValue - let checkoutAttemptIdResponse = CheckoutAttemptIdResponse(identifier: expectedCheckoutAttemptId) - let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + let initialAnalyticsResponse = InitialAnalyticsResponse(checkoutAttemptId: expectedCheckoutAttemptId) + let checkoutAttemptIdResult: Result = .success(initialAnalyticsResponse) apiClient.mockedResults = [checkoutAttemptIdResult] - let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") - // When - sut.fetchCheckoutAttemptId { receivedCheckoutAttemptId in - - // Then - XCTAssertNotNil(receivedCheckoutAttemptId, "The checkoutAttemptId is nil.") - XCTAssertEqual(expectedCheckoutAttemptId, receivedCheckoutAttemptId, "The received checkoutAttemptId is not the expected one.") - fetchCheckoutAttemptIdExpection.fulfill() - } - - waitForExpectations(timeout: 10) + sut.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: nil) + + wait(until: sut, at: \.checkoutAttemptId, is: expectedCheckoutAttemptId) } func testFetchCheckoutAttemptIdWhenAnalyticsIsDisabledShouldNotTriggerCheckoutAttemptIdRequest() throws { @@ -56,48 +48,37 @@ class AnalyticsProviderTests: XCTestCase { let apiClient = APIClientMock() let sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") - // When - sut.fetchCheckoutAttemptId { receivedCheckoutAttemptId in - XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") - fetchCheckoutAttemptIdExpection.fulfill() - } - - waitForExpectations(timeout: 10) + sut.sendInitialAnalytics(with: .components(type: .affirm), additionalFields: nil) + XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") } func testFetchCheckoutAttemptIdWhenRequestSucceedShouldCallCompletionWithNonNilValue() throws { // Given var analyticsConfiguration = AnalyticsConfiguration() analyticsConfiguration.isEnabled = true + let apiClient = APIClientMock() let sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) let expectedCheckoutAttemptId = checkoutAttemptIdMockValue - let checkoutAttemptIdResponse = CheckoutAttemptIdResponse(identifier: expectedCheckoutAttemptId) - let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + let initialAnalyticsResponse = InitialAnalyticsResponse(checkoutAttemptId: expectedCheckoutAttemptId) + let checkoutAttemptIdResult: Result = .success(initialAnalyticsResponse) apiClient.mockedResults = [checkoutAttemptIdResult] - let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") - // When - sut.fetchCheckoutAttemptId { receivedCheckoutAttemptId in - - // Then - XCTAssertNotNil(receivedCheckoutAttemptId, "The checkoutAttemptId is nil.") - XCTAssertEqual(expectedCheckoutAttemptId, receivedCheckoutAttemptId, "The received checkoutAttemptId is not the expected one.") - fetchCheckoutAttemptIdExpection.fulfill() - } - - waitForExpectations(timeout: 10) + sut.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: nil) + + // Then + wait(until: sut, at: \.checkoutAttemptId, is: expectedCheckoutAttemptId) } func testFetchCheckoutAttemptIdWhenAnalyticsIsEnabledGivenFailureShouldCallCompletionWithNilValue() throws { // Given var analyticsConfiguration = AnalyticsConfiguration() analyticsConfiguration.isEnabled = true + let apiClient = APIClientMock() let sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) @@ -105,17 +86,10 @@ class AnalyticsProviderTests: XCTestCase { let checkoutAttemptIdResult: Result = .failure(error) apiClient.mockedResults = [checkoutAttemptIdResult] - let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") - // When - sut.fetchCheckoutAttemptId { receivedCheckoutAttemptId in - - // Then - XCTAssertNil(receivedCheckoutAttemptId, "The checkoutAttemptId is not nil.") - fetchCheckoutAttemptIdExpection.fulfill() - } - - waitForExpectations(timeout: 10) + sut.sendInitialAnalytics(with: .components(type: .atome), additionalFields: nil) + // Then + XCTAssertNil(sut.checkoutAttemptId, "The checkoutAttemptId is not nil.") } func testFetchCheckoutAttemptIdWhenAnalyticsIsEnabledShouldSetCheckoutAttemptIdProperty() throws { @@ -128,16 +102,15 @@ class AnalyticsProviderTests: XCTestCase { let expectedCheckoutAttemptId = checkoutAttemptIdMockValue - let checkoutAttemptIdResponse = CheckoutAttemptIdResponse(identifier: expectedCheckoutAttemptId) - let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + let initialAnalyticsResponse = InitialAnalyticsResponse(checkoutAttemptId: expectedCheckoutAttemptId) + let checkoutAttemptIdResult: Result = .success(initialAnalyticsResponse) apiClient.mockedResults = [checkoutAttemptIdResult] // When - sut.fetchCheckoutAttemptId { _ in - - // Then - XCTAssertEqual(expectedCheckoutAttemptId, sut.checkoutAttemptId) - } + sut.sendInitialAnalytics(with: .components(type: .atome), additionalFields: nil) + + // Then + wait(until: sut, at: \.checkoutAttemptId, is: expectedCheckoutAttemptId) } func testFetchCheckoutAttemptIdWhenAnalyticsIsDisabledShouldNotSetCheckoutAttemptIdProperty() throws { @@ -148,37 +121,33 @@ class AnalyticsProviderTests: XCTestCase { let apiClient = APIClientMock() let sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - let checkoutAttemptIdResponse = CheckoutAttemptIdResponse(identifier: checkoutAttemptIdMockValue) - let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) + let initialAnalyticsResponse = InitialAnalyticsResponse(checkoutAttemptId: checkoutAttemptIdMockValue) + let checkoutAttemptIdResult: Result = .success(initialAnalyticsResponse) apiClient.mockedResults = [checkoutAttemptIdResult] // When - sut.fetchCheckoutAttemptId { _ in - - // Then - XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") - } + sut.sendInitialAnalytics(with: .components(type: .affirm), additionalFields: nil) + // Then + XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") } - func testTelemetryRequest() throws { + func testInitialRequest() throws { // Given - let checkoutAttemptId = self.checkoutAttemptIdMockValue + let checkoutAttemptId = checkoutAttemptIdMockValue - let telemetryExpectation = expectation(description: "Telemetry request is triggered") + let analyticsExpectation = expectation(description: "Initial request is triggered") let apiClient = APIClientMock() apiClient.mockedResults = [ - .success(CheckoutAttemptIdResponse(identifier: checkoutAttemptId)), - .success(TelemetryResponse()) + .success(InitialAnalyticsResponse(checkoutAttemptId: checkoutAttemptId)), ] apiClient.onExecute = { request in - if let telemetryRequest = request as? TelemetryRequest { - XCTAssertNil(telemetryRequest.amount) - XCTAssertEqual(telemetryRequest.checkoutAttemptId, checkoutAttemptId) - XCTAssertEqual(telemetryRequest.version, adyenSdkVersion) - XCTAssertEqual(telemetryRequest.platform, "ios") - telemetryExpectation.fulfill() + if let initialAnalyticsdRequest = request as? InitialAnalyticsRequest { + XCTAssertNil(initialAnalyticsdRequest.amount) + XCTAssertEqual(initialAnalyticsdRequest.version, adyenSdkVersion) + XCTAssertEqual(initialAnalyticsdRequest.platform, "ios") + analyticsExpectation.fulfill() } } @@ -189,9 +158,9 @@ class AnalyticsProviderTests: XCTestCase { // When - analyticsProvider.sendTelemetryEvent(flavor: .components(type: .achDirectDebit)) + analyticsProvider.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: nil) - wait(for: [telemetryExpectation], timeout: 10) + wait(for: [analyticsExpectation], timeout: 10) } func testAdditionalFields() throws { @@ -199,22 +168,20 @@ class AnalyticsProviderTests: XCTestCase { // Given let amount = Amount(value: 1, currencyCode: "EUR") - let checkoutAttemptId = self.checkoutAttemptIdMockValue + let checkoutAttemptId = checkoutAttemptIdMockValue - let telemetryExpectation = expectation(description: "Telemetry request is triggered") + let analyticsExpectation = expectation(description: "Initial request is triggered") let apiClient = APIClientMock() apiClient.mockedResults = [ - .success(CheckoutAttemptIdResponse(identifier: checkoutAttemptId)), - .success(TelemetryResponse()) + .success(InitialAnalyticsResponse(checkoutAttemptId: checkoutAttemptId)) ] apiClient.onExecute = { request in - if let telemetryRequest = request as? TelemetryRequest { - XCTAssertEqual(telemetryRequest.amount, amount) - XCTAssertEqual(telemetryRequest.checkoutAttemptId, checkoutAttemptId) - XCTAssertEqual(telemetryRequest.version, "version") - XCTAssertEqual(telemetryRequest.platform, "react-native") - telemetryExpectation.fulfill() + if let initialAnalyticsdRequest = request as? InitialAnalyticsRequest { + XCTAssertEqual(initialAnalyticsdRequest.amount, amount) + XCTAssertEqual(initialAnalyticsdRequest.version, "version") + XCTAssertEqual(initialAnalyticsdRequest.platform, "react-native") + analyticsExpectation.fulfill() } } @@ -226,52 +193,41 @@ class AnalyticsProviderTests: XCTestCase { configuration: analyticsConfiguration ) - analyticsProvider.additionalFields = { - .init(amount: amount) - } - // When + let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: nil) + analyticsProvider.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: additionalFields) - analyticsProvider.sendTelemetryEvent(flavor: .components(type: .achDirectDebit)) - - wait(for: [telemetryExpectation], timeout: 10) + wait(for: [analyticsExpectation], timeout: 10) } - func testTelemetryRequestEncoding() throws { + func testInitialRequestEncoding() throws { - let telemetryData = TelemetryData( - flavor: .dropInComponent, - amount: .init(value: 1, currencyCode: "EUR"), - context: .init( - version: "version", - platform: .flutter - ) - ) + let analyticsData = AnalyticsData(flavor: .components(type: .achDirectDebit), + additionalFields: AdditionalAnalyticsFields(amount: .init(value: 1, currencyCode: "EUR"), sessionId: "test_session_id"), + context: AnalyticsContext(version: "version", platform: .flutter)) - let request = TelemetryRequest( - data: telemetryData, - checkoutAttemptId: checkoutAttemptIdMockValue - ) + let request = InitialAnalyticsRequest(data: analyticsData) let encodedRequest = try JSONEncoder().encode(request) let decodedRequest = try XCTUnwrap(JSONSerialization.jsonObject(with: encodedRequest) as? [String: Any]) let expectedDecodedRequest = [ "locale": "en_US", - "paymentMethods": telemetryData.paymentMethods, + "paymentMethods": analyticsData.paymentMethods, "platform": "flutter", - "component": "", - "flavor": "dropInComponent", + "component": "ach", + "flavor": "components", "channel": "iOS", - "systemVersion": telemetryData.systemVersion, - "screenWidth": telemetryData.screenWidth, - "referrer": telemetryData.referrer, - "deviceBrand": telemetryData.deviceBrand, + "systemVersion": analyticsData.systemVersion, + "screenWidth": analyticsData.screenWidth, + "referrer": analyticsData.referrer, + "deviceBrand": analyticsData.deviceBrand, + "deviceModel": analyticsData.deviceModel, "amount": [ "currency": "EUR", "value": 1 ] as [String: Any], - "checkoutAttemptId": checkoutAttemptIdMockValue, + "sessionId": "test_session_id", "version": "version" ] as [String: Any] diff --git a/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift b/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift deleted file mode 100644 index 12581c44a2..0000000000 --- a/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// TelemetryTrackerTests.swift -// AdyenUIKitTests -// -// Created by Naufal Aros on 4/12/22. -// Copyright © 2022 Adyen. All rights reserved. -// - -import XCTest -@_spi(AdyenInternal) @testable import Adyen -@testable import AdyenNetworking - -class TelemetryTrackerTests: XCTestCase { - - var apiClient: APIClientMock! - var sut: TelemetryTrackerProtocol! - - override func setUpWithError() throws { - try super.setUpWithError() - apiClient = APIClientMock() - sut = AnalyticsProvider(apiClient: apiClient, configuration: .init()) - } - - override func tearDownWithError() throws { - apiClient = nil - sut = nil - try super.tearDownWithError() - } - - func testSendTelemetryEventGivenAnalyticsIsDisabledAndTelemetryIsEnabledShouldNotSendAnyRequest() throws { - // Given - var analyticsConfiguration = AnalyticsConfiguration() - analyticsConfiguration.isEnabled = false - analyticsConfiguration.isTelemetryEnabled = true - sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - - let expectedRequestCalls = 0 - - // When - sut.sendTelemetryEvent(flavor: .components(type: .affirm)) - - // Then - XCTAssertEqual(expectedRequestCalls, apiClient.counter, "One or more telemetry requests were sent.") - } - - func testSendTelemetryEventGivenTelemetryIsDisabledShouldNotSendAnyRequest() throws { - // Given - var analyticsConfiguration = AnalyticsConfiguration() - analyticsConfiguration.isTelemetryEnabled = false - sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - - let expectedRequestCalls = 0 - - // When - sut.sendTelemetryEvent(flavor: .components(type: .affirm)) - - // Then - XCTAssertEqual(expectedRequestCalls, apiClient.counter, "One or more telemetry requests were sent.") - } - - func testSendTelemetryEventGivenTelemetryIsEnabledAndFlavorIsDropInComponentShouldNotSendAnyRequest() throws { - // Given - var analyticsConfiguration = AnalyticsConfiguration() - analyticsConfiguration.isTelemetryEnabled = true - sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - - let flavor: TelemetryFlavor = .dropInComponent - let expectedRequestCalls = 0 - - // When - sut.sendTelemetryEvent(flavor: flavor) - - // Then - XCTAssertEqual(expectedRequestCalls, apiClient.counter, "One or more telemetry requests were sent.") - } - - func testSendTelemetryEventGivenTelemetryIsEnabledAndFlavorIsComponentsShouldSendTelemetryRequest() throws { - // Given - var analyticsConfiguration = AnalyticsConfiguration() - analyticsConfiguration.isTelemetryEnabled = true - sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - - let flavor: TelemetryFlavor = .components(type: .affirm) - let expectedRequestCalls = 2 - - let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) - let telemetryResult: Result = .success(telemetryResponse) - apiClient.mockedResults = [checkoutAttemptIdResult, telemetryResult] - - // When - sut.sendTelemetryEvent(flavor: flavor) - - // Then - wait(for: .milliseconds(1)) - XCTAssertEqual(expectedRequestCalls, apiClient.counter, "Invalid request number made.") - } - - func testSendTelemetryEventGivenTelemetryIsEnabledAndFlavorIsDropInShouldSendTelemetryRequest() throws { - // Given - var analyticsConfiguration = AnalyticsConfiguration() - analyticsConfiguration.isTelemetryEnabled = true - sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) - - let flavor: TelemetryFlavor = .dropIn(paymentMethods: ["scheme", "paypal", "affirm"]) - let expectedRequestCalls = 2 - - let checkoutAttemptIdResult: Result = .success(checkoutAttemptIdResponse) - let telemetryResult: Result = .success(telemetryResponse) - apiClient.mockedResults = [checkoutAttemptIdResult, telemetryResult] - - // When - sut.sendTelemetryEvent(flavor: flavor) - - // Then - wait(for: .milliseconds(1)) - XCTAssertEqual(expectedRequestCalls, apiClient.counter, "Invalid request number made.") - } - - // MARK: - Private - - private var checkoutAttemptIdResponse: CheckoutAttemptIdResponse { - .init(identifier: "cb3eef98-978e-4f6f-b299-937a4450be1f1648546838056be73d8f38ee8bcc3a65ec14e41b037a59f255dcd9e83afe8c06bd3e7abcad993") - } - - private let telemetryResponse = TelemetryResponse() -} diff --git a/Tests/Adyen Tests/Core/AdyenContextTests.swift b/Tests/Adyen Tests/Core/AdyenContextTests.swift index 905bc1852b..7cdfad7dbb 100644 --- a/Tests/Adyen Tests/Core/AdyenContextTests.swift +++ b/Tests/Adyen Tests/Core/AdyenContextTests.swift @@ -6,6 +6,7 @@ @testable @_spi(AdyenInternal) import Adyen @testable import AdyenEncryption +@testable import AdyenNetworking import XCTest class AdyenContextTests: XCTestCase { @@ -21,10 +22,42 @@ class AdyenContextTests: XCTestCase { payment: .init(amount: oneEUR, countryCode: "NL") ) - XCTAssertEqual(context.analyticsProvider.additionalFields?().amount, oneEUR) - + XCTAssertEqual(context.payment?.amount, oneEUR) context.update(payment: Payment(amount: twoEUR, countryCode: "NL")) + XCTAssertEqual(context.payment?.amount, twoEUR) + } + + func testPublicInit() { + let context = AdyenContext(apiContext: Dummy.apiContext, payment: Dummy.payment) + + XCTAssertEqual(context.payment?.amount, Dummy.payment.amount) + XCTAssertEqual(context.apiContext.clientKey, Dummy.apiContext.clientKey) + } + + func testInternalInit() { + let context = AdyenContext(apiContext: Dummy.apiContext, payment: Dummy.payment, analyticsProvider: AnalyticsProviderMock()) - XCTAssertEqual(context.analyticsProvider.additionalFields?().amount, twoEUR) + XCTAssertEqual(context.payment?.amount, Dummy.payment.amount) + XCTAssertEqual(context.apiContext.clientKey, Dummy.apiContext.clientKey) + XCTAssertNotNil(context.analyticsProvider) } + + func testInitWithRegularEnvironmentShouldHaveAnalyticsProvider() { + let context = AdyenContext(apiContext: Dummy.apiContext, payment: Dummy.payment) + + XCTAssertNotNil(context.analyticsProvider) + } + + func testInitWithDifferentEnvironmentShouldNotHaveAnalyticsProvider() { + let apiContext = try! APIContext(environment: TestEnvironment.test, clientKey: "local_DUMMYKEYFORTESTING") + + let context = AdyenContext(apiContext: apiContext, payment: Dummy.payment) + XCTAssertNil(context.analyticsProvider) + } +} + +enum TestEnvironment: AnyAPIEnvironment { + case test + + var baseURL: URL { URL(string: "test")! } } diff --git a/Tests/Card Tests/BCMCComponentTests.swift b/Tests/Card Tests/BCMCComponentTests.swift index 8c0bd46f0d..39d3572dd5 100644 --- a/Tests/Card Tests/BCMCComponentTests.swift +++ b/Tests/Card Tests/BCMCComponentTests.swift @@ -513,7 +513,7 @@ class BCMCComponentTests: XCTestCase { XCTAssertEqual(sut.viewController.title, cardPaymentMethod.name) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -526,10 +526,10 @@ class BCMCComponentTests: XCTestCase { context: context) // When - sut.cardViewController.viewWillAppear(true) + sut.viewDidLoad(viewController: sut.cardViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } func fillCard(on view: UIView, with card: Card, simulateKeyStrokes: Bool = false) { diff --git a/Tests/Card Tests/CardComponentTests.swift b/Tests/Card Tests/CardComponentTests.swift index c3e74cfd5b..776900d7ae 100644 --- a/Tests/Card Tests/CardComponentTests.swift +++ b/Tests/Card Tests/CardComponentTests.swift @@ -1960,7 +1960,7 @@ class CardComponentTests: XCTestCase { waitForExpectations(timeout: 10, handler: nil) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -1969,10 +1969,10 @@ class CardComponentTests: XCTestCase { configuration: CardComponent.Configuration()) // When - sut.cardViewController.viewWillAppear(false) + sut.viewDidLoad(viewController: sut.cardViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } diff --git a/Tests/Card Tests/StoredCardComponentTests.swift b/Tests/Card Tests/StoredCardComponentTests.swift index 58385d3a32..84fd5fc3a3 100644 --- a/Tests/Card Tests/StoredCardComponentTests.swift +++ b/Tests/Card Tests/StoredCardComponentTests.swift @@ -225,7 +225,7 @@ class StoredCardComponentTests: XCTestCase { alertController.dismiss(animated: false, completion: nil) } - func testViewDidLoadShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialEvent() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -237,7 +237,7 @@ class StoredCardComponentTests: XCTestCase { sut.viewController.viewDidLoad() // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } // MARK: - Private diff --git a/Tests/Card Tests/StoredPaymentMethodComponentTests.swift b/Tests/Card Tests/StoredPaymentMethodComponentTests.swift index b43cde5229..b8c11b100a 100644 --- a/Tests/Card Tests/StoredPaymentMethodComponentTests.swift +++ b/Tests/Card Tests/StoredPaymentMethodComponentTests.swift @@ -135,7 +135,7 @@ class StoredPaymentMethodComponentTests: XCTestCase { XCTAssertEqual(viewController?.title, localizedString(.dropInStoredTitle, nil, paymentMethod.name)) } - func testViewDidLoadShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialEvent() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -147,6 +147,6 @@ class StoredPaymentMethodComponentTests: XCTestCase { sut.viewController.viewDidLoad() // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/ACH Direct Debit/ACHDirectDebitComponentTests.swift b/Tests/Components Tests/ACH Direct Debit/ACHDirectDebitComponentTests.swift index 250da9446f..486f949584 100644 --- a/Tests/Components Tests/ACH Direct Debit/ACHDirectDebitComponentTests.swift +++ b/Tests/Components Tests/ACH Direct Debit/ACHDirectDebitComponentTests.swift @@ -284,7 +284,7 @@ class ACHDirectDebitComponentTests: XCTestCase { wait(for: [expectation], timeout: 100) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() @@ -298,9 +298,9 @@ class ACHDirectDebitComponentTests: XCTestCase { publicKeyProvider: PublicKeyProviderMock()) // When - sut.viewWillAppear(viewController: sut.viewController) + sut.viewDidLoad(viewController: sut.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/Affirm/AffirmComponentTests.swift b/Tests/Components Tests/Affirm/AffirmComponentTests.swift index 2a81b5b1a7..781b22bb12 100644 --- a/Tests/Components Tests/Affirm/AffirmComponentTests.swift +++ b/Tests/Components Tests/Affirm/AffirmComponentTests.swift @@ -308,7 +308,7 @@ class AffirmComponentTests: XCTestCase { XCTAssertNil(deliveryAddressView.item.value) } - func testViewWillAppear_shouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -316,10 +316,10 @@ class AffirmComponentTests: XCTestCase { let mockViewController = UIViewController() // When - sut.viewWillAppear(viewController: mockViewController) + sut.viewDidLoad(viewController: mockViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } // MARK: - Private diff --git a/Tests/Components Tests/Apple Pay/ApplePayComponentTests.swift b/Tests/Components Tests/Apple Pay/ApplePayComponentTests.swift index ce0bd6809e..337f3e57a5 100644 --- a/Tests/Components Tests/Apple Pay/ApplePayComponentTests.swift +++ b/Tests/Components Tests/Apple Pay/ApplePayComponentTests.swift @@ -381,7 +381,7 @@ class ApplePayComponentTest: XCTestCase { XCTAssertTrue(compareCollections(supportedNetworks, [.masterCard, .elo])) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -394,10 +394,10 @@ class ApplePayComponentTest: XCTestCase { configuration: configuration) // When - sut.viewWillAppear(viewController: mockViewController) + sut.viewDidLoad(viewController: mockViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } private func getRandomContactFieldSet() -> Set { diff --git a/Tests/Components Tests/Apple Pay/PreApplePayComponentTests.swift b/Tests/Components Tests/Apple Pay/PreApplePayComponentTests.swift index ba09882f16..1bea81e1fa 100644 --- a/Tests/Components Tests/Apple Pay/PreApplePayComponentTests.swift +++ b/Tests/Components Tests/Apple Pay/PreApplePayComponentTests.swift @@ -119,7 +119,7 @@ class PreApplePayComponentTests: XCTestCase { func testSubmitWithAnalyticsEnabledShouldSetCheckoutAttemptIdInPaymentComponentData() throws { // Given let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" - analyticsProviderMock.underlyingCheckoutAttemptId = expectedCheckoutAttemptId + analyticsProviderMock._checkoutAttemptId = expectedCheckoutAttemptId let paymentMethodDetails = ApplePayDetails(paymentMethod: paymentMethod, token: "test_token", network: "test_network", @@ -142,7 +142,7 @@ class PreApplePayComponentTests: XCTestCase { func testSubmitWithAnalyticsDisabledShouldNotSetCheckoutAttemptIdInPaymentComponentData() throws { // Given - analyticsProviderMock.underlyingCheckoutAttemptId = nil + analyticsProviderMock._checkoutAttemptId = nil let paymentMethodDetails = ApplePayDetails(paymentMethod: paymentMethod, token: "test_token", network: "test_network", diff --git a/Tests/Components Tests/Atome/AtomeComponentTests.swift b/Tests/Components Tests/Atome/AtomeComponentTests.swift index cacbdf1df6..71b42433a4 100644 --- a/Tests/Components Tests/Atome/AtomeComponentTests.swift +++ b/Tests/Components Tests/Atome/AtomeComponentTests.swift @@ -79,7 +79,7 @@ class AtomeComponentTests: XCTestCase { XCTAssertFalse(phoneExtensions.isEmpty) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -88,10 +88,10 @@ class AtomeComponentTests: XCTestCase { let mockViewController = UIViewController() // When - sut.viewWillAppear(viewController: mockViewController) + sut.viewDidLoad(viewController: mockViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/BACS Direct Debit/Mocks/BACSDirectDebitComponentTrackerProtocolMock.swift b/Tests/Components Tests/BACS Direct Debit/Mocks/BACSDirectDebitComponentTrackerProtocolMock.swift index c52d7845b0..7d233b4e2a 100644 --- a/Tests/Components Tests/BACS Direct Debit/Mocks/BACSDirectDebitComponentTrackerProtocolMock.swift +++ b/Tests/Components Tests/BACS Direct Debit/Mocks/BACSDirectDebitComponentTrackerProtocolMock.swift @@ -14,12 +14,9 @@ class BACSDirectDebitComponentTrackerProtocolMock: BACSDirectDebitComponentTrack // MARK: - sendEvent - var sendTelemetryEventCallsCount = 0 - var sendTelemetryEventCalled: Bool { - sendTelemetryEventCallsCount > 0 - } - - func sendTelemetryEvent() { - sendTelemetryEventCallsCount += 1 + var initialEventCallsCount = 0 + + func sendInitialAnalytics() { + initialEventCallsCount += 1 } } diff --git a/Tests/Components Tests/BACS Direct Debit/Scenes/Input/BACSInputPresenterTests.swift b/Tests/Components Tests/BACS Direct Debit/Scenes/Input/BACSInputPresenterTests.swift index d118ca1dd4..65975cc92c 100644 --- a/Tests/Components Tests/BACS Direct Debit/Scenes/Input/BACSInputPresenterTests.swift +++ b/Tests/Components Tests/BACS Direct Debit/Scenes/Input/BACSInputPresenterTests.swift @@ -64,7 +64,7 @@ class BACSInputPresenterTests: XCTestCase { sut.viewDidLoad() // Then - XCTAssertEqual(tracker.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(tracker.initialEventCallsCount, 1) } func testContinuePaymentWhenButtonTappedShouldDisplayValidationOnView() throws { diff --git a/Tests/Components Tests/BACS Direct Debit/Trackers/BACSDirectDebitComponentTrackerTests.swift b/Tests/Components Tests/BACS Direct Debit/Trackers/BACSDirectDebitComponentTrackerTests.swift index ba07d73c35..67e86c95eb 100644 --- a/Tests/Components Tests/BACS Direct Debit/Trackers/BACSDirectDebitComponentTrackerTests.swift +++ b/Tests/Components Tests/BACS Direct Debit/Trackers/BACSDirectDebitComponentTrackerTests.swift @@ -18,9 +18,9 @@ class BACSDirectDebitComponentTrackerTests: XCTestCase { apiContext = Dummy.apiContext analyticsProvider = AnalyticsProviderMock() + let adyenContext = AdyenContext(apiContext: apiContext, payment: Dummy.payment, analyticsProvider: analyticsProvider) sut = BACSDirectDebitComponentTracker(paymentMethod: paymentMethod, - apiContext: apiContext, - telemetryTracker: analyticsProvider, + context: adyenContext, isDropIn: false) } @@ -31,11 +31,11 @@ class BACSDirectDebitComponentTrackerTests: XCTestCase { try super.tearDownWithError() } - func testSendTelemetryEventShouldCallAnalyticsProviderSendTelemetryEvent() throws { + func testSendInitialEventShouldCallAnalyticsProviderSendInitialEvent() throws { // When - sut.sendTelemetryEvent() + sut.sendInitialAnalytics() // Then - XCTAssertEqual(analyticsProvider.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProvider.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/BLIK Component/BLIKComponentTests.swift b/Tests/Components Tests/BLIK Component/BLIKComponentTests.swift index 06288d9655..bf35e6d64b 100644 --- a/Tests/Components Tests/BLIK Component/BLIKComponentTests.swift +++ b/Tests/Components Tests/BLIK Component/BLIKComponentTests.swift @@ -77,14 +77,14 @@ class BLIKComponentTests: XCTestCase { XCTAssertEqual(sut.requiresModalPresentation, true) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // When let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) sut = BLIKComponent(paymentMethod: method, context: context) - sut.viewWillAppear(viewController: sut.viewController) + sut.viewDidLoad(viewController: sut.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/Boleto/BoletoComponentTests.swift b/Tests/Components Tests/Boleto/BoletoComponentTests.swift index 2df054b199..70c859622c 100644 --- a/Tests/Components Tests/Boleto/BoletoComponentTests.swift +++ b/Tests/Components Tests/Boleto/BoletoComponentTests.swift @@ -28,14 +28,14 @@ class BoletoComponentTests: XCTestCase { let viewController = component.viewController - setupRootViewController(viewController) - let firstNameField: UITextField = try XCTUnwrap(viewController.view.findView(by: "firstNameItem.textField")) let lastNameField: UITextField = try XCTUnwrap(viewController.view.findView(by: "lastNameItem.textField")) let socialSecurityNumberField: UITextField = try XCTUnwrap(viewController.view.findView(by: "socialSecurityNumberItem.textField")) let emailField: UITextField = try XCTUnwrap(viewController.view.findView(by: "emailItem.textField")) let addressField: FormAddressPickerItemView = try XCTUnwrap(viewController.view.findView(by: "addressItem")) + setupRootViewController(viewController) + XCTAssertEqual(firstNameField.text, prefilledInformation.shopperName?.firstName) XCTAssertEqual(lastNameField.text, prefilledInformation.shopperName?.lastName) let formattedSocialSecurityNumber = brazilSocialSecurityNumberFormatter.formattedValue(for: prefilledInformation.socialSecurityNumber!) @@ -123,7 +123,7 @@ class BoletoComponentTests: XCTestCase { XCTAssertFalse(emailItem.isHidden) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -135,9 +135,9 @@ class BoletoComponentTests: XCTestCase { ) // When - component.viewWillAppear(viewController: component.viewController) + component.viewDidLoad(viewController: component.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/Cash App Pay/CashAppPayComponentTests.swift b/Tests/Components Tests/Cash App Pay/CashAppPayComponentTests.swift index f57414afec..cf640930ff 100644 --- a/Tests/Components Tests/Cash App Pay/CashAppPayComponentTests.swift +++ b/Tests/Components Tests/Cash App Pay/CashAppPayComponentTests.swift @@ -134,7 +134,7 @@ import XCTest XCTAssertFalse(sut.cashAppPayButton.showsActivityIndicator) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() @@ -145,10 +145,10 @@ import XCTest let sut = CashAppPayComponent(paymentMethod: paymentMethod, context: context, configuration: config) // When - sut.viewWillAppear(viewController: sut.viewController) + sut.viewDidLoad(viewController: sut.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } func testComponent_ShouldPaymentMethodTypeBeCashAppPay() throws { diff --git a/Tests/Components Tests/Doku/DokuComponentTests.swift b/Tests/Components Tests/Doku/DokuComponentTests.swift index 7006397f9c..5f2b79af9e 100644 --- a/Tests/Components Tests/Doku/DokuComponentTests.swift +++ b/Tests/Components Tests/Doku/DokuComponentTests.swift @@ -151,7 +151,7 @@ class DokuComponentTests: XCTestCase { XCTAssertTrue(email.isEmpty) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -160,10 +160,10 @@ class DokuComponentTests: XCTestCase { configuration: DokuComponent.Configuration()) // When - sut.viewWillAppear(viewController: sut.viewController) + sut.viewDidLoad(viewController: sut.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } // MARK: - Private diff --git a/Tests/Components Tests/Gift Card/GiftCardComponentTests.swift b/Tests/Components Tests/Gift Card/GiftCardComponentTests.swift index c858705337..d0fda93772 100644 --- a/Tests/Components Tests/Gift Card/GiftCardComponentTests.swift +++ b/Tests/Components Tests/Gift Card/GiftCardComponentTests.swift @@ -559,7 +559,7 @@ class GiftCardComponentTests: XCTestCase { waitForExpectations(timeout: 10, handler: nil) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -572,10 +572,10 @@ class GiftCardComponentTests: XCTestCase { let mockViewController = UIViewController() // When - sut.viewWillAppear(viewController: mockViewController) + sut.viewDidLoad(viewController: mockViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } func testGiftCardHidingSecurityCodeItemView() throws { diff --git a/Tests/Components Tests/Instant Payment/InstantPaymentComponentTests.swift b/Tests/Components Tests/Instant Payment/InstantPaymentComponentTests.swift index 545979b638..153adfa666 100644 --- a/Tests/Components Tests/Instant Payment/InstantPaymentComponentTests.swift +++ b/Tests/Components Tests/Instant Payment/InstantPaymentComponentTests.swift @@ -57,21 +57,6 @@ class InstantPaymentComponentTests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testInitiatePaymentShouldSendTelemetryEvent() throws { - // Given - let analyticsProviderMock = AnalyticsProviderMock() - let context = Dummy.context(with: analyticsProviderMock) - sut = InstantPaymentComponent(paymentMethod: paymentMethod, - context: context, - paymentData: paymentComponentData) - - // When - sut.initiatePayment() - - // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) - } - // MARK: - Private private var paymentComponentData: PaymentComponentData { diff --git a/Tests/Components Tests/IssuerList/IssuerListComponentTests.swift b/Tests/Components Tests/IssuerList/IssuerListComponentTests.swift index 5a7bda199c..aedf5fb191 100644 --- a/Tests/Components Tests/IssuerList/IssuerListComponentTests.swift +++ b/Tests/Components Tests/IssuerList/IssuerListComponentTests.swift @@ -55,7 +55,7 @@ class IssuerListComponentTests: XCTestCase { waitForExpectations(timeout: 10) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -64,9 +64,9 @@ class IssuerListComponentTests: XCTestCase { let mockViewController = UIViewController() // When - sut.viewWillAppear(viewController: mockViewController) + sut.viewDidLoad(viewController: mockViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/MB Way/MBWayComponentTests.swift b/Tests/Components Tests/MB Way/MBWayComponentTests.swift index 789e5cac42..99db85e08b 100644 --- a/Tests/Components Tests/MB Way/MBWayComponentTests.swift +++ b/Tests/Components Tests/MB Way/MBWayComponentTests.swift @@ -114,7 +114,7 @@ class MBWayComponentTests: XCTestCase { XCTAssertTrue(phoneNumber.isEmpty) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -123,10 +123,10 @@ class MBWayComponentTests: XCTestCase { configuration: MBWayComponent.Configuration()) // When - sut.viewWillAppear(viewController: sut.viewController) + sut.viewDidLoad(viewController: sut.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } // MARK: - Private diff --git a/Tests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 5126d8b290..899cb52482 100644 --- a/Tests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -43,7 +43,7 @@ class PaymentComponentSubjectTests: XCTestCase { func testSubmitWithAnalyticsEnabledShouldSetCheckoutAttemptIdInPaymentComponentData() throws { // Given let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" - analyticsProviderMock.underlyingCheckoutAttemptId = expectedCheckoutAttemptId + analyticsProviderMock._checkoutAttemptId = expectedCheckoutAttemptId let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) @@ -59,7 +59,7 @@ class PaymentComponentSubjectTests: XCTestCase { func testSubmitWithAnalyticsDisabledShouldNotSetCheckoutAttemptIdInPaymentComponentData() throws { // Given - analyticsProviderMock.underlyingCheckoutAttemptId = nil + analyticsProviderMock._checkoutAttemptId = nil let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) diff --git a/Tests/Components Tests/Qiwi Wallet/QiwiWalletComponentTests.swift b/Tests/Components Tests/Qiwi Wallet/QiwiWalletComponentTests.swift index a43234a805..faef470bf1 100644 --- a/Tests/Components Tests/Qiwi Wallet/QiwiWalletComponentTests.swift +++ b/Tests/Components Tests/Qiwi Wallet/QiwiWalletComponentTests.swift @@ -182,7 +182,7 @@ class QiwiWalletComponentTests: XCTestCase { } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -193,9 +193,9 @@ class QiwiWalletComponentTests: XCTestCase { configuration: QiwiWalletComponent.Configuration()) // When - sut.viewWillAppear(viewController: sut.viewController) + sut.viewDidLoad(viewController: sut.viewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Components Tests/SEPA Tests/SEPADirectDebitComponentTests.swift b/Tests/Components Tests/SEPA Tests/SEPADirectDebitComponentTests.swift index 0070e31cc6..281238b7b2 100644 --- a/Tests/Components Tests/SEPA Tests/SEPADirectDebitComponentTests.swift +++ b/Tests/Components Tests/SEPA Tests/SEPADirectDebitComponentTests.swift @@ -240,7 +240,7 @@ class SEPADirectDebitComponentTests: XCTestCase { wait(for: [expectation], timeout: 5) } - func testViewWillAppearShouldSendTelemetryEvent() throws { + func testViewDidLoadShouldSendInitialCall() throws { // Given let analyticsProviderMock = AnalyticsProviderMock() let context = Dummy.context(with: analyticsProviderMock) @@ -250,9 +250,9 @@ class SEPADirectDebitComponentTests: XCTestCase { context: context) // When - sut.viewWillAppear(viewController: mockViewController) + sut.viewDidLoad(viewController: mockViewController) // Then - XCTAssertEqual(analyticsProviderMock.sendTelemetryEventCallsCount, 1) + XCTAssertEqual(analyticsProviderMock.initialEventCallsCount, 1) } } diff --git a/Tests/Session Tests/SessionTests.swift b/Tests/Session Tests/SessionTests.swift index 498fc7d2f3..ed2b9e3884 100644 --- a/Tests/Session Tests/SessionTests.swift +++ b/Tests/Session Tests/SessionTests.swift @@ -21,7 +21,7 @@ class SessionTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() analyticsProviderMock = AnalyticsProviderMock() - analyticsProviderMock.underlyingCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" + analyticsProviderMock._checkoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" context = Dummy.context(with: analyticsProviderMock) } @@ -70,6 +70,7 @@ class SessionTests: XCTestCase { XCTAssertEqual(session.sessionContext.paymentMethods, expectedPaymentMethods) XCTAssertEqual(session.sessionContext.amount, .init(value: 220, currencyCode: "USD")) XCTAssertFalse(session.sessionContext.configuration.enableStoreDetails) + XCTAssertEqual(AnalyticsForSession.sessionId, "session_id") } expectation.fulfill() } @@ -107,7 +108,7 @@ class SessionTests: XCTestCase { func testDidSubmitWithCheckoutAttemptIdNonNilShouldIncludeCheckoutAttemptIdInPaymentComponentData() throws { // Given - let expectedCheckoutAttemptId = try XCTUnwrap(analyticsProviderMock.underlyingCheckoutAttemptId) + let expectedCheckoutAttemptId = try XCTUnwrap(analyticsProviderMock._checkoutAttemptId) let sessionAdvancedHandlerMock = SessionAdvancedHandlerMock() let sessionDelegateMock = SessionDelegateMock() @@ -135,7 +136,7 @@ class SessionTests: XCTestCase { func testDidSubmitWithCheckoutAttemptIdNilShouldNotIncludeCheckoutAttemptIdInPaymentComponentData() throws { // Given - analyticsProviderMock.underlyingCheckoutAttemptId = nil + analyticsProviderMock._checkoutAttemptId = nil let sessionAdvancedHandlerMock = SessionAdvancedHandlerMock() let sessionDelegateMock = SessionDelegateMock() diff --git a/spell-check-word-allow-list.yaml b/spell-check-word-allow-list.yaml index acd549e0cd..e673ba9288 100644 --- a/spell-check-word-allow-list.yaml +++ b/spell-check-word-allow-list.yaml @@ -191,3 +191,4 @@ whiteList: - mapkit - bizum - alipay + - checkoutanalytics