From 905e8992bba86b48c92405d1e680b018baea2190 Mon Sep 17 00:00:00 2001 From: stechiu Date: Wed, 11 Dec 2024 21:39:39 -0800 Subject: [PATCH 01/24] Added presentment details and events --- Braintree.xcodeproj/project.pbxproj | 16 +++++++ .../ShopperInsightsViewController.swift | 17 ++++++- .../Analytics/FPTIBatchData.swift | 16 +++++++ Sources/BraintreeCore/BTAPIClient.swift | 8 ++++ .../BTButtonOrder.swift | 14 ++++++ .../BTButtonType.swift | 8 ++++ .../BraintreeShopperInsights/BTPageType.swift | 44 +++++++++++++++++++ .../BTPresentmentDetails.swift | 22 ++++++++++ .../BTShopperInsightsClient.swift | 30 ++++--------- .../BraintreeTestShared/MockAPIClient.swift | 4 ++ 10 files changed, 156 insertions(+), 23 deletions(-) create mode 100644 Sources/BraintreeShopperInsights/BTButtonOrder.swift create mode 100644 Sources/BraintreeShopperInsights/BTButtonType.swift create mode 100644 Sources/BraintreeShopperInsights/BTPageType.swift create mode 100644 Sources/BraintreeShopperInsights/BTPresentmentDetails.swift diff --git a/Braintree.xcodeproj/project.pbxproj b/Braintree.xcodeproj/project.pbxproj index 38a990360..b7ec1f2d3 100644 --- a/Braintree.xcodeproj/project.pbxproj +++ b/Braintree.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 04AA31182D07974D0043ACAB /* BTButtonType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA31172D0797460043ACAB /* BTButtonType.swift */; }; + 04AA311A2D0797570043ACAB /* BTPresentmentDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */; }; + 04AA311E2D0798FC0043ACAB /* BTPageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA311D2D0798F70043ACAB /* BTPageType.swift */; }; + 04AA31202D07990E0043ACAB /* BTButtonOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA311F2D07990A0043ACAB /* BTButtonOrder.swift */; }; 0917F6E42A27BDC700ACED2E /* BTVenmoLineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096C6B2529CCDCEB00912863 /* BTVenmoLineItem.swift */; }; 09357DCB2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09357DCA2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift */; }; 1FEB89E614CB6BF0B9858EE4 /* Pods_Tests_IntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85BD589D380436A0C9D1DEC1 /* Pods_Tests_IntegrationTests.framework */; }; @@ -729,6 +733,10 @@ 035A59D91EA5DE97002960C8 /* BTLocalPaymentClient_UnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTLocalPaymentClient_UnitTests.swift; sourceTree = ""; }; 039A8BD91F9E993500D607E7 /* BTAmericanExpressRewardsBalance_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTAmericanExpressRewardsBalance_Tests.swift; sourceTree = ""; }; 03F921C1200EBB200076CD80 /* BTThreeDSecurePostalAddress_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTThreeDSecurePostalAddress_Tests.swift; sourceTree = ""; }; + 04AA31172D0797460043ACAB /* BTButtonType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTButtonType.swift; sourceTree = ""; }; + 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPresentmentDetails.swift; sourceTree = ""; }; + 04AA311D2D0798F70043ACAB /* BTPageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPageType.swift; sourceTree = ""; }; + 04AA311F2D07990A0043ACAB /* BTButtonOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTButtonOrder.swift; sourceTree = ""; }; 09357DCA2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTVenmoLineItem_Tests.swift; sourceTree = ""; }; 096C6B2529CCDCEB00912863 /* BTVenmoLineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTVenmoLineItem.swift; sourceTree = ""; }; 162174E1192D9220008DC35D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -1494,6 +1502,10 @@ 804698292B27C4D70090878E /* BraintreeShopperInsights */ = { isa = PBXGroup; children = ( + 04AA311F2D07990A0043ACAB /* BTButtonOrder.swift */, + 04AA311D2D0798F70043ACAB /* BTPageType.swift */, + 04AA31172D0797460043ACAB /* BTButtonType.swift */, + 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */, 62EA90482B63071800DD79BC /* BTEligiblePaymentMethods.swift */, 800ED7822B4F5B66007D8A30 /* BTEligiblePaymentsRequest.swift */, 8037BFAF2B2CCC130017072C /* BTShopperInsightsAnalytics.swift */, @@ -3364,11 +3376,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 04AA311E2D0798FC0043ACAB /* BTPageType.swift in Sources */, 804698382B27C53B0090878E /* BTShopperInsightsRequest.swift in Sources */, + 04AA31182D07974D0043ACAB /* BTButtonType.swift in Sources */, 624B27F72B6AE0C2000AC08A /* BTShopperInsightsError.swift in Sources */, 804698372B27C5390090878E /* BTShopperInsightsClient.swift in Sources */, 800ED7832B4F5B66007D8A30 /* BTEligiblePaymentsRequest.swift in Sources */, 8037BFB02B2CCC130017072C /* BTShopperInsightsAnalytics.swift in Sources */, + 04AA311A2D0797570043ACAB /* BTPresentmentDetails.swift in Sources */, + 04AA31202D07990E0043ACAB /* BTButtonOrder.swift in Sources */, 62EA90492B63071800DD79BC /* BTEligiblePaymentMethods.swift in Sources */, 804698392B27C53E0090878E /* BTShopperInsightsResult.swift in Sources */, ); diff --git a/Demo/Application/Features/ShopperInsightsViewController.swift b/Demo/Application/Features/ShopperInsightsViewController.swift index d46a14d53..6f7b500e2 100644 --- a/Demo/Application/Features/ShopperInsightsViewController.swift +++ b/Demo/Application/Features/ShopperInsightsViewController.swift @@ -111,7 +111,15 @@ class ShopperInsightsViewController: PaymentButtonBaseViewController { ] """ let paymentMethods = ["Apple Pay", "Card", "PayPal"] - shopperInsightsClient.sendPayPalPresentedEvent(paymentMethodsDisplayed: paymentMethods, experiment: sampleExperiment) + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .first, + experimentType: .control, + pageType: .about + ) + +// shopperInsightsClient.sendPayPalPresentedEvent(paymentMethodsDisplayed: paymentMethods, experiment: sampleExperiment) + + shopperInsightsClient.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) progressBlock("Tapped PayPal Vault") shopperInsightsClient.sendPayPalSelectedEvent() @@ -128,7 +136,12 @@ class ShopperInsightsViewController: PaymentButtonBaseViewController { } @objc func venmoButtonTapped(_ button: UIButton) { - shopperInsightsClient.sendVenmoPresentedEvent() + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .second, + experimentType: .control, + pageType: .about + ) + shopperInsightsClient.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) progressBlock("Tapped Venmo") shopperInsightsClient.sendVenmoSelectedEvent() diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index f986717b6..390a69b37 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -33,12 +33,15 @@ struct FPTIBatchData: Codable { /// UTC millisecond timestamp when a networking task started establishing a TCP connection. See [Apple's docs](https://developer.apple.com/documentation/foundation/urlsessiontasktransactionmetrics#3162615). /// `nil` if a persistent connection is used. let connectionStartTime: Int? + let buttonType: String? + let buttonOrder: Int? let correlationID: String? let endpoint: String? /// UTC millisecond timestamp when a networking task completed. let endTime: Int? let errorDescription: String? let eventName: String + let experimentType: String? /// True if the `BTConfiguration` was retrieved from local cache after `tokenize()` call. /// False if the `BTConfiguration` was fetched remotely after `tokenize()` call. let isConfigFromCache: Bool? @@ -48,6 +51,7 @@ struct FPTIBatchData: Codable { let linkType: String? /// The experiment details associated with a shopper insights flow let merchantExperiment: String? + let pageType: String? /// The list of payment methods displayed, in the same order in which they are rendered on the page, associated with the `BTShopperInsights` flow. let paymentMethodsDisplayed: String? /// Used for linking events from the client to server side request @@ -63,32 +67,40 @@ struct FPTIBatchData: Codable { init( appSwitchURL: URL? = nil, + buttonOrder: Int? = nil, + buttonType: String? = nil, connectionStartTime: Int? = nil, correlationID: String? = nil, endpoint: String? = nil, endTime: Int? = nil, errorDescription: String? = nil, eventName: String, + experimentType: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: String? = nil, merchantExperiment: String? = nil, + pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, requestStartTime: Int? = nil, startTime: Int? = nil ) { self.appSwitchURL = appSwitchURL?.absoluteString + self.buttonOrder = buttonOrder + self.buttonType = buttonType self.connectionStartTime = connectionStartTime self.correlationID = correlationID self.endpoint = endpoint self.endTime = endTime self.errorDescription = errorDescription self.eventName = eventName + self.experimentType = experimentType self.isConfigFromCache = isConfigFromCache self.isVaultRequest = isVaultRequest self.linkType = linkType self.merchantExperiment = merchantExperiment + self.pageType = pageType self.paymentMethodsDisplayed = paymentMethodsDisplayed self.payPalContextID = payPalContextID self.requestStartTime = requestStartTime @@ -97,14 +109,18 @@ struct FPTIBatchData: Codable { enum CodingKeys: String, CodingKey { case appSwitchURL = "url" + case buttonOrder = "button_rank" + case buttonType = "button_type" case connectionStartTime = "connect_start_time" case correlationID = "correlation_id" case errorDescription = "error_desc" case eventName = "event_name" + case experimentType = "experiment_type" case isConfigFromCache = "config_cached" case isVaultRequest = "is_vault" case linkType = "link_type" case merchantExperiment = "experiment" + case pageType = "page_type" case paymentMethodsDisplayed = "payment_methods_displayed" case payPalContextID = "paypal_context_id" case requestStartTime = "request_start_time" diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 1d3b04f4d..17e591438 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -303,12 +303,16 @@ import Foundation @_documentation(visibility: private) public func sendAnalyticsEvent( _ eventName: String, + buttonOrder: Int? = nil, + buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, + experimentType: String? = nil, merchantExperiment: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: LinkType? = nil, + pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, appSwitchURL: URL? = nil @@ -316,13 +320,17 @@ import Foundation analyticsService.sendAnalyticsEvent( FPTIBatchData.Event( appSwitchURL: appSwitchURL, + buttonOrder: buttonOrder, + buttonType: buttonType, correlationID: correlationID, errorDescription: errorDescription, eventName: eventName, + experimentType: experimentType, isConfigFromCache: isConfigFromCache, isVaultRequest: isVaultRequest, linkType: linkType?.rawValue, merchantExperiment: merchantExperiment, + pageType: pageType, paymentMethodsDisplayed: paymentMethodsDisplayed, payPalContextID: payPalContextID ) diff --git a/Sources/BraintreeShopperInsights/BTButtonOrder.swift b/Sources/BraintreeShopperInsights/BTButtonOrder.swift new file mode 100644 index 000000000..5955803c9 --- /dev/null +++ b/Sources/BraintreeShopperInsights/BTButtonOrder.swift @@ -0,0 +1,14 @@ +import Foundation + +/// The order or ranking in which payment buttons appear. +public enum BTButtonOrder: Int { + case first + case second + case third + case fourth + case fifth + case sixth + case seventh + case eighth + case other +} diff --git a/Sources/BraintreeShopperInsights/BTButtonType.swift b/Sources/BraintreeShopperInsights/BTButtonType.swift new file mode 100644 index 000000000..2835bf73f --- /dev/null +++ b/Sources/BraintreeShopperInsights/BTButtonType.swift @@ -0,0 +1,8 @@ +import Foundation + +/// The button type to be displayed or presented +public enum BTButtonType: String { + case payPal = "paypal" + case venmo = "venmo" + case other = "other" +} diff --git a/Sources/BraintreeShopperInsights/BTPageType.swift b/Sources/BraintreeShopperInsights/BTPageType.swift new file mode 100644 index 000000000..27abedae8 --- /dev/null +++ b/Sources/BraintreeShopperInsights/BTPageType.swift @@ -0,0 +1,44 @@ +import Foundation + +/// The type of page where the event occurred. +public enum BTPageType: String { + + /// A home page is the primary landing page that a visitor will view when they navigate to a website. + case homepage = "homepage" + + /// An About page is a section on a website that provides information about a company, organization, or individual. + case about = "about" + + /// A contact page is a page on a website for visitors to contact the organization or individual providing the website. + case contact = "contact" + + /// An intermediary step that users pass through on their way to a product-listing page that doesn't provide a complete + /// list of products but may showcase a few products and provide links to product subcategories. + case productCategory = "product_category" + + /// A product detail page (PDP) is a web page that outlines everything customers and buyers need to know about a + /// particular product. + case productDetails = "product_details" + + /// The page a user sees after entering a search query. + case search = "search" + + /// A cart is a digital shopping cart that allows buyers to inspect and organize items they plan to buy. + case cart = "cart" + + /// A checkout page is the page related to payment and shipping/billing details on an eCommerce store. + case checkout = "checkout" + + /// An order review page gives the buyer an overview of the goods or services that they have selected and summarizes + /// the order that they are about to place. + case orderReview = "order_review" + + /// The order confirmation page summarizes an order after checkout completes. + case orderConfirmation = "order_confirmation" + + /// Any other page available on a merchant’s site. + case other = "other" + + /// Popup cart displayed after “add to cart” click. + case miniCart = "mini_cart" +} diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift new file mode 100644 index 000000000..710a832c2 --- /dev/null +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -0,0 +1,22 @@ +import Foundation + +public enum BTExperimentType: String { + case test = "test" + case control = "control" +} + +public class BTPresentmentDetails { + var buttonOrder: BTButtonOrder + var experimentType: BTExperimentType + var pageType: BTPageType + + public init( + buttonOrder: BTButtonOrder, + experimentType: BTExperimentType, + pageType: BTPageType + ) { + self.buttonOrder = buttonOrder + self.experimentType = experimentType + self.pageType = pageType + } +} diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index f2b81849e..a0032343e 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -92,17 +92,19 @@ public class BTShopperInsightsClient { } } - /// Call this method when the PayPal button has been successfully displayed to the buyer. + /// Call this method when the PayPal or Venmo button has been successfully displayed to the buyer. /// This method sends analytics to help improve the Shopper Insights feature experience. /// - Parameters: - /// - paymentMethodsDisplayed: Optional: The list of available payment methods, rendered in the same order in which they are displayed i.e. ['Apple Pay', 'PayPal'] - /// - experiment: Optional: A `JSONObject` passed in as a string containing details of the merchant experiment. - public func sendPayPalPresentedEvent(paymentMethodsDisplayed: [String?] = [], experiment: String? = nil) { - let paymentMethodsDisplayedString = paymentMethodsDisplayed.compactMap { $0 }.joined(separator: ", ") + /// - `buttonType`: Type of button presented - PayPal, Venmo, or other + /// - `presentmentDetails`: Detailed information, including button order, experiment type, and + /// page type about the payment button that issent to analytics to help improve the Shopper Insights + /// feature experience. + public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { apiClient.sendAnalyticsEvent( BTShopperInsightsAnalytics.payPalPresented, - merchantExperiment: experiment, - paymentMethodsDisplayed: paymentMethodsDisplayedString + buttonOrder: presentmentDetails.buttonOrder.rawValue, + experimentType: presentmentDetails.experimentType.rawValue, + pageType: presentmentDetails.pageType.rawValue ) } @@ -112,20 +114,6 @@ public class BTShopperInsightsClient { apiClient.sendAnalyticsEvent(BTShopperInsightsAnalytics.payPalSelected) } - /// Call this method when the Venmo button has been successfully displayed to the buyer. - /// This method sends analytics to help improve the Shopper Insights feature experience. - /// - Parameters: - /// - paymentMethodsDisplayed: Optional: The list of available payment methods, rendered in the same order in which they are displayed. - /// - experiment: Optional: A `JSONObject` passed in as a string containing details of the merchant experiment. - public func sendVenmoPresentedEvent(paymentMethodsDisplayed: [String?] = [], experiment: String? = nil) { - let paymentMethodsDisplayedString = paymentMethodsDisplayed.compactMap { $0 }.joined(separator: ", ") - apiClient.sendAnalyticsEvent( - BTShopperInsightsAnalytics.venmoPresented, - merchantExperiment: experiment, - paymentMethodsDisplayed: paymentMethodsDisplayedString - ) - } - /// Call this method when the Venmo button has been selected/tapped by the buyer. /// This method sends analytics to help improve the Shopper Insights feature experience public func sendVenmoSelectedEvent() { diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index 77ff070ec..2497c7921 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -93,12 +93,16 @@ public class MockAPIClient: BTAPIClient { public override func sendAnalyticsEvent( _ name: String, + buttonOrder: Int? = nil, + buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, + experimentType: String? = nil, merchantExperiment experiment: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: LinkType? = nil, + pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, appSwitchURL: URL? = nil From e5d5ddd03c09c5042d0516c66f183a0c43f5b4a7 Mon Sep 17 00:00:00 2001 From: stechiu Date: Fri, 13 Dec 2024 15:44:31 -0800 Subject: [PATCH 02/24] Addressed PR comments --- Braintree.xcodeproj/project.pbxproj | 8 ++++++-- .../ShopperInsightsViewController.swift | 11 ----------- Sources/BraintreeCore/BTAPIClient.swift | 4 ++-- .../BTButtonOrder.swift | 1 + .../BTButtonType.swift | 7 ++++--- .../BTExperimentType.swift | 18 ++++++++++++++++++ .../BraintreeShopperInsights/BTPageType.swift | 7 ++++--- .../BTPresentmentDetails.swift | 6 +----- .../BTShopperInsightsClient.swift | 5 +---- 9 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 Sources/BraintreeShopperInsights/BTExperimentType.swift diff --git a/Braintree.xcodeproj/project.pbxproj b/Braintree.xcodeproj/project.pbxproj index b7ec1f2d3..6784fbd04 100644 --- a/Braintree.xcodeproj/project.pbxproj +++ b/Braintree.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 04AA311A2D0797570043ACAB /* BTPresentmentDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */; }; 04AA311E2D0798FC0043ACAB /* BTPageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA311D2D0798F70043ACAB /* BTPageType.swift */; }; 04AA31202D07990E0043ACAB /* BTButtonOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AA311F2D07990A0043ACAB /* BTButtonOrder.swift */; }; + 04B001102D0CF46E00C0060D /* BTExperimentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B0010F2D0CF46900C0060D /* BTExperimentType.swift */; }; 0917F6E42A27BDC700ACED2E /* BTVenmoLineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 096C6B2529CCDCEB00912863 /* BTVenmoLineItem.swift */; }; 09357DCB2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09357DCA2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift */; }; 1FEB89E614CB6BF0B9858EE4 /* Pods_Tests_IntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85BD589D380436A0C9D1DEC1 /* Pods_Tests_IntegrationTests.framework */; }; @@ -737,6 +738,7 @@ 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPresentmentDetails.swift; sourceTree = ""; }; 04AA311D2D0798F70043ACAB /* BTPageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPageType.swift; sourceTree = ""; }; 04AA311F2D07990A0043ACAB /* BTButtonOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTButtonOrder.swift; sourceTree = ""; }; + 04B0010F2D0CF46900C0060D /* BTExperimentType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTExperimentType.swift; sourceTree = ""; }; 09357DCA2A2FBEC10096D449 /* BTVenmoLineItem_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTVenmoLineItem_Tests.swift; sourceTree = ""; }; 096C6B2529CCDCEB00912863 /* BTVenmoLineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTVenmoLineItem.swift; sourceTree = ""; }; 162174E1192D9220008DC35D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -1503,11 +1505,12 @@ isa = PBXGroup; children = ( 04AA311F2D07990A0043ACAB /* BTButtonOrder.swift */, - 04AA311D2D0798F70043ACAB /* BTPageType.swift */, 04AA31172D0797460043ACAB /* BTButtonType.swift */, - 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */, 62EA90482B63071800DD79BC /* BTEligiblePaymentMethods.swift */, 800ED7822B4F5B66007D8A30 /* BTEligiblePaymentsRequest.swift */, + 04B0010F2D0CF46900C0060D /* BTExperimentType.swift */, + 04AA311D2D0798F70043ACAB /* BTPageType.swift */, + 04AA31192D0797510043ACAB /* BTPresentmentDetails.swift */, 8037BFAF2B2CCC130017072C /* BTShopperInsightsAnalytics.swift */, 8064F38E2B1E492F0059C4CB /* BTShopperInsightsClient.swift */, 624B27F62B6AE0C2000AC08A /* BTShopperInsightsError.swift */, @@ -3378,6 +3381,7 @@ files = ( 04AA311E2D0798FC0043ACAB /* BTPageType.swift in Sources */, 804698382B27C53B0090878E /* BTShopperInsightsRequest.swift in Sources */, + 04B001102D0CF46E00C0060D /* BTExperimentType.swift in Sources */, 04AA31182D07974D0043ACAB /* BTButtonType.swift in Sources */, 624B27F72B6AE0C2000AC08A /* BTShopperInsightsError.swift in Sources */, 804698372B27C5390090878E /* BTShopperInsightsClient.swift in Sources */, diff --git a/Demo/Application/Features/ShopperInsightsViewController.swift b/Demo/Application/Features/ShopperInsightsViewController.swift index 6f7b500e2..28cafce67 100644 --- a/Demo/Application/Features/ShopperInsightsViewController.swift +++ b/Demo/Application/Features/ShopperInsightsViewController.swift @@ -102,23 +102,12 @@ class ShopperInsightsViewController: PaymentButtonBaseViewController { } @objc func payPalVaultButtonTapped(_ button: UIButton) { - let sampleExperiment = - """ - [ - { "experimentName" : "payment ready conversion experiment" }, - { "experimentID" : "a1b2c3" }, - { "treatmentName" : "treatment group 1" } - ] - """ - let paymentMethods = ["Apple Pay", "Card", "PayPal"] let presentmentDetails = BTPresentmentDetails( buttonOrder: .first, experimentType: .control, pageType: .about ) -// shopperInsightsClient.sendPayPalPresentedEvent(paymentMethodsDisplayed: paymentMethods, experiment: sampleExperiment) - shopperInsightsClient.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) progressBlock("Tapped PayPal Vault") shopperInsightsClient.sendPayPalSelectedEvent() diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 17e591438..17fe510ba 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -303,6 +303,7 @@ import Foundation @_documentation(visibility: private) public func sendAnalyticsEvent( _ eventName: String, + appSwitchURL: URL? = nil, buttonOrder: Int? = nil, buttonType: String? = nil, correlationID: String? = nil, @@ -314,8 +315,7 @@ import Foundation linkType: LinkType? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, - payPalContextID: String? = nil, - appSwitchURL: URL? = nil + payPalContextID: String? = nil ) { analyticsService.sendAnalyticsEvent( FPTIBatchData.Event( diff --git a/Sources/BraintreeShopperInsights/BTButtonOrder.swift b/Sources/BraintreeShopperInsights/BTButtonOrder.swift index 5955803c9..1c9e36778 100644 --- a/Sources/BraintreeShopperInsights/BTButtonOrder.swift +++ b/Sources/BraintreeShopperInsights/BTButtonOrder.swift @@ -1,6 +1,7 @@ import Foundation /// The order or ranking in which payment buttons appear. +/// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonOrder: Int { case first case second diff --git a/Sources/BraintreeShopperInsights/BTButtonType.swift b/Sources/BraintreeShopperInsights/BTButtonType.swift index 2835bf73f..2833a3dfc 100644 --- a/Sources/BraintreeShopperInsights/BTButtonType.swift +++ b/Sources/BraintreeShopperInsights/BTButtonType.swift @@ -1,8 +1,9 @@ import Foundation /// The button type to be displayed or presented +/// Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonType: String { - case payPal = "paypal" - case venmo = "venmo" - case other = "other" + case other = "Other" + case payPal = "Paypal" + case venmo = "Venmo" } diff --git a/Sources/BraintreeShopperInsights/BTExperimentType.swift b/Sources/BraintreeShopperInsights/BTExperimentType.swift new file mode 100644 index 000000000..acf1ba31e --- /dev/null +++ b/Sources/BraintreeShopperInsights/BTExperimentType.swift @@ -0,0 +1,18 @@ +import Foundation + +/// +/// - Warning: This module is in beta. It's public API may change or be removed in future releases. +public enum BTExperimentType: String { + + case test = "test" + case control = "control" + + public var formattedExperiment: String { + """ + [ + { "exp_name" : "PaymentReady" } + { "treatment_name" : \(self.rawValue) } + ] + """ + } +} diff --git a/Sources/BraintreeShopperInsights/BTPageType.swift b/Sources/BraintreeShopperInsights/BTPageType.swift index 27abedae8..03fa1e87a 100644 --- a/Sources/BraintreeShopperInsights/BTPageType.swift +++ b/Sources/BraintreeShopperInsights/BTPageType.swift @@ -1,6 +1,7 @@ import Foundation /// The type of page where the event occurred. +/// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTPageType: String { /// A home page is the primary landing page that a visitor will view when they navigate to a website. @@ -36,9 +37,9 @@ public enum BTPageType: String { /// The order confirmation page summarizes an order after checkout completes. case orderConfirmation = "order_confirmation" - /// Any other page available on a merchant’s site. - case other = "other" - /// Popup cart displayed after “add to cart” click. case miniCart = "mini_cart" + + /// Any other page available on a merchant’s site. + case other = "other" } diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift index 710a832c2..bc5e865e0 100644 --- a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -1,10 +1,6 @@ import Foundation -public enum BTExperimentType: String { - case test = "test" - case control = "control" -} - +/// - Warning: This class is in beta. It's public API may change or be removed in future releases. public class BTPresentmentDetails { var buttonOrder: BTButtonOrder var experimentType: BTExperimentType diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index a0032343e..b4bf07f00 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -101,10 +101,7 @@ public class BTShopperInsightsClient { /// feature experience. public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { apiClient.sendAnalyticsEvent( - BTShopperInsightsAnalytics.payPalPresented, - buttonOrder: presentmentDetails.buttonOrder.rawValue, - experimentType: presentmentDetails.experimentType.rawValue, - pageType: presentmentDetails.pageType.rawValue + BTShopperInsightsAnalytics.payPalPresented ) } From 3dbb660446e6e9095e461db2683fe26778f96892 Mon Sep 17 00:00:00 2001 From: stechiu Date: Fri, 13 Dec 2024 16:29:53 -0800 Subject: [PATCH 03/24] Added doc strings --- .../BTButtonOrder.swift | 18 ++++++++++++++++++ .../BTButtonType.swift | 8 +++++++- .../BTExperimentType.swift | 5 ++++- .../BTPresentmentDetails.swift | 8 ++++++++ .../BTShopperInsightsClient.swift | 7 +++++-- 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Sources/BraintreeShopperInsights/BTButtonOrder.swift b/Sources/BraintreeShopperInsights/BTButtonOrder.swift index 1c9e36778..8bc2d1ef5 100644 --- a/Sources/BraintreeShopperInsights/BTButtonOrder.swift +++ b/Sources/BraintreeShopperInsights/BTButtonOrder.swift @@ -3,13 +3,31 @@ import Foundation /// The order or ranking in which payment buttons appear. /// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonOrder: Int { + + /// 0 case first + + /// 1 case second + + /// 2 case third + + /// 3 case fourth + + /// 4 case fifth + + /// 5 case sixth + + /// 6 case seventh + + /// 7 case eighth + + /// 8 case other } diff --git a/Sources/BraintreeShopperInsights/BTButtonType.swift b/Sources/BraintreeShopperInsights/BTButtonType.swift index 2833a3dfc..68d297bca 100644 --- a/Sources/BraintreeShopperInsights/BTButtonType.swift +++ b/Sources/BraintreeShopperInsights/BTButtonType.swift @@ -3,7 +3,13 @@ import Foundation /// The button type to be displayed or presented /// Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonType: String { - case other = "Other" + + /// PayPal button case payPal = "Paypal" + + /// Venmo button case venmo = "Venmo" + + /// All button types other than PayPal or Venmo + case other = "Other" } diff --git a/Sources/BraintreeShopperInsights/BTExperimentType.swift b/Sources/BraintreeShopperInsights/BTExperimentType.swift index acf1ba31e..a92bf1a3d 100644 --- a/Sources/BraintreeShopperInsights/BTExperimentType.swift +++ b/Sources/BraintreeShopperInsights/BTExperimentType.swift @@ -1,10 +1,13 @@ import Foundation -/// +/// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. /// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTExperimentType: String { + /// The test experiment case test = "test" + + /// The control experiment case control = "control" public var formattedExperiment: String { diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift index bc5e865e0..c500b3592 100644 --- a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -1,9 +1,17 @@ import Foundation +/// Detailed information, including button order, experiment type, and page type about the payment button that +/// is sent to analytics to help improve the Shopper Insights feature experience. /// - Warning: This class is in beta. It's public API may change or be removed in future releases. public class BTPresentmentDetails { + + /// The order or ranking in which payment buttons appear. var buttonOrder: BTButtonOrder + + /// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. var experimentType: BTExperimentType + + /// The type of page where the event occurred. var pageType: BTPageType public init( diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index b4bf07f00..4a9bd6aec 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -97,11 +97,14 @@ public class BTShopperInsightsClient { /// - Parameters: /// - `buttonType`: Type of button presented - PayPal, Venmo, or other /// - `presentmentDetails`: Detailed information, including button order, experiment type, and - /// page type about the payment button that issent to analytics to help improve the Shopper Insights + /// page type about the payment button that is sent to analytics to help improve the Shopper Insights /// feature experience. public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { apiClient.sendAnalyticsEvent( - BTShopperInsightsAnalytics.payPalPresented + buttonType.rawValue, + buttonOrder: presentmentDetails.buttonOrder.rawValue, + experimentType: presentmentDetails.experimentType.rawValue, + pageType: presentmentDetails.pageType.rawValue ) } From 1b9185ce76ee1a960e7e2f3d1bdeca6bd36ba0aa Mon Sep 17 00:00:00 2001 From: stechiu Date: Fri, 13 Dec 2024 16:37:14 -0800 Subject: [PATCH 04/24] Updated CHANGELOG and unit tests --- CHANGELOG.md | 5 ++++ .../BTShopperInsightsClient_Tests.swift | 27 ++++++++++++++----- .../BraintreeTestShared/MockAPIClient.swift | 4 +-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff8a0563d..0504b49ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ * BraintreeShopperInsights (BETA) * Add `shopperSessionID` to `BTShopperInsightsClient` initializer * Add `isPayPalAppInstalled()` and/or `isVenmoAppInstalled()` + * Replace `sendPayPalPresentedEvent()` and `sendPayPalPresentedEvent()` with `sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetail)` + * Add values to the following parameters to `presentmentDetails`: + * `experimentType` + * `pageType` + * `buttonOrder` ## 6.24.0 (2024-10-15) * BraintreePayPal diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift index 47806902f..9fb8c3ea7 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift @@ -202,15 +202,25 @@ class BTShopperInsightsClient_Tests: XCTestCase { // MARK: - Analytics func testSendPayPalPresentedEvent_sendsAnalytic() { - sut.sendPayPalPresentedEvent() - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:paypal-presented") + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .first, + experimentType: .control, + pageType: .about + ) + sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "PayPal") } func testSendPayPalPresentedEvent_whenPaymentMethodsDisplayedNotNil_sendsAnalytic() { let paymentMethods = ["Apple Pay", "Card", "PayPal"] - sut.sendPayPalPresentedEvent(paymentMethodsDisplayed: paymentMethods) + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .first, + experimentType: .control, + pageType: .about + ) + sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) XCTAssertEqual(mockAPIClient.postedPaymentMethodsDisplayed, paymentMethods.joined(separator: ", ")) - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:paypal-presented") + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "PayPal") } func testSendPayPalSelectedEvent_sendsAnalytic() { @@ -219,8 +229,13 @@ class BTShopperInsightsClient_Tests: XCTestCase { } func testSendVenmoPresentedEvent_sendsAnalytic() { - sut.sendVenmoPresentedEvent() - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:venmo-presented") + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .first, + experimentType: .control, + pageType: .about + ) + sut.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "Venmo") } func testSendVenmoSelectedEvent_sendsAnalytic() { diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index 2497c7921..bf894a156 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -93,6 +93,7 @@ public class MockAPIClient: BTAPIClient { public override func sendAnalyticsEvent( _ name: String, + appSwitchURL: URL? = nil, buttonOrder: Int? = nil, buttonType: String? = nil, correlationID: String? = nil, @@ -104,8 +105,7 @@ public class MockAPIClient: BTAPIClient { linkType: LinkType? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, - payPalContextID: String? = nil, - appSwitchURL: URL? = nil + payPalContextID: String? = nil ) { postedPayPalContextID = payPalContextID postedLinkType = linkType From b6305417171e675d2ba7898d588ae8f6356d7bc8 Mon Sep 17 00:00:00 2001 From: stechiu Date: Mon, 16 Dec 2024 15:43:15 -0800 Subject: [PATCH 05/24] Addressed PR comments --- Sources/BraintreeCore/Analytics/FPTIBatchData.swift | 2 +- .../BTShopperInsightsAnalytics.swift | 6 +++--- .../BTShopperInsightsClient.swift | 5 +++-- Sources/BraintreeVenmo/BTVenmoClient.swift | 8 ++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index 390a69b37..28cd9fcb8 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -109,7 +109,7 @@ struct FPTIBatchData: Codable { enum CodingKeys: String, CodingKey { case appSwitchURL = "url" - case buttonOrder = "button_rank" + case buttonOrder = "button_order" case buttonType = "button_type" case connectionStartTime = "connect_start_time" case correlationID = "correlation_id" diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsAnalytics.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsAnalytics.swift index fefbcd5fa..e00f0fbfd 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsAnalytics.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsAnalytics.swift @@ -3,11 +3,11 @@ import Foundation enum BTShopperInsightsAnalytics { // MARK: - Merchant Triggered Events - - static let payPalPresented = "shopper-insights:paypal-presented" + static let payPalSelected = "shopper-insights:paypal-selected" - static let venmoPresented = "shopper-insights:venmo-presented" static let venmoSelected = "shopper-insights:venmo-selected" + + static let buttonPresented = "shopper-insights:button-presented" // MARK: - SDK Triggered Events diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index 4a9bd6aec..7c7acec9e 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -101,9 +101,10 @@ public class BTShopperInsightsClient { /// feature experience. public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { apiClient.sendAnalyticsEvent( - buttonType.rawValue, + BTShopperInsightsAnalytics.buttonPresented, buttonOrder: presentmentDetails.buttonOrder.rawValue, - experimentType: presentmentDetails.experimentType.rawValue, + buttonType: buttonType.rawValue, + experimentType: presentmentDetails.experimentType.formattedExperiment, pageType: presentmentDetails.pageType.rawValue ) } diff --git a/Sources/BraintreeVenmo/BTVenmoClient.swift b/Sources/BraintreeVenmo/BTVenmoClient.swift index f5fa29150..dfc0dc442 100644 --- a/Sources/BraintreeVenmo/BTVenmoClient.swift +++ b/Sources/BraintreeVenmo/BTVenmoClient.swift @@ -393,20 +393,20 @@ import BraintreeCore if success { apiClient.sendAnalyticsEvent( BTVenmoAnalytics.appSwitchSucceeded, + appSwitchURL: appSwitchURL, isVaultRequest: shouldVault, linkType: linkType, - payPalContextID: payPalContextID, - appSwitchURL: appSwitchURL + payPalContextID: payPalContextID ) BTVenmoClient.venmoClient = self self.appSwitchCompletion = completion } else { apiClient.sendAnalyticsEvent( BTVenmoAnalytics.appSwitchFailed, + appSwitchURL: appSwitchURL, isVaultRequest: shouldVault, linkType: linkType, - payPalContextID: payPalContextID, - appSwitchURL: appSwitchURL + payPalContextID: payPalContextID ) notifyFailure(with: BTVenmoError.appSwitchFailed, completion: completion) } From 31c334ab23bb457d1c455cb234bc9ea8ae8409b7 Mon Sep 17 00:00:00 2001 From: stechiu Date: Tue, 17 Dec 2024 09:43:52 -0800 Subject: [PATCH 06/24] Addressed PR comments --- CHANGELOG.md | 2 +- Sources/BraintreeCore/Analytics/FPTIBatchData.swift | 4 ++-- Sources/BraintreeShopperInsights/BTButtonType.swift | 2 +- Sources/BraintreeShopperInsights/BTPresentmentDetails.swift | 6 +++--- .../BraintreeShopperInsights/BTShopperInsightsClient.swift | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ffe24c18..c297da7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ * BraintreeShopperInsights (BETA) * Add `shopperSessionID` to `BTShopperInsightsClient` initializer * Add `isPayPalAppInstalled()` and/or `isVenmoAppInstalled()` - * Replace `sendPayPalPresentedEvent()` and `sendPayPalPresentedEvent()` with `sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetail)` + * Replace `sendPayPalPresentedEvent()` and `sendPayPalPresentedEvent()` with `sendPresentedEvent(for:presentmentDetails:)` * Add values to the following parameters to `presentmentDetails`: * `experimentType` * `pageType` diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index 0f9d6d642..8a1387480 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -32,9 +32,9 @@ struct FPTIBatchData: Codable { let appSwitchURL: String? /// UTC millisecond timestamp when a networking task started establishing a TCP connection. See [Apple's docs](https://developer.apple.com/documentation/foundation/urlsessiontasktransactionmetrics#3162615). /// `nil` if a persistent connection is used. - let connectionStartTime: Int? - let buttonType: String? let buttonOrder: Int? + let buttonType: String? + let connectionStartTime: Int? let correlationID: String? let endpoint: String? /// UTC millisecond timestamp when a networking task completed. diff --git a/Sources/BraintreeShopperInsights/BTButtonType.swift b/Sources/BraintreeShopperInsights/BTButtonType.swift index 68d297bca..be013347c 100644 --- a/Sources/BraintreeShopperInsights/BTButtonType.swift +++ b/Sources/BraintreeShopperInsights/BTButtonType.swift @@ -5,7 +5,7 @@ import Foundation public enum BTButtonType: String { /// PayPal button - case payPal = "Paypal" + case payPal = "PayPal" /// Venmo button case venmo = "Venmo" diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift index c500b3592..9f0137688 100644 --- a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -1,8 +1,5 @@ import Foundation -/// Detailed information, including button order, experiment type, and page type about the payment button that -/// is sent to analytics to help improve the Shopper Insights feature experience. -/// - Warning: This class is in beta. It's public API may change or be removed in future releases. public class BTPresentmentDetails { /// The order or ranking in which payment buttons appear. @@ -14,6 +11,9 @@ public class BTPresentmentDetails { /// The type of page where the event occurred. var pageType: BTPageType + /// Detailed information, including button order, experiment type, and page type about the payment button that + /// is sent to analytics to help improve the Shopper Insights feature experience. + /// - Warning: This class is in beta. It's public API may change or be removed in future releases. public init( buttonOrder: BTButtonOrder, experimentType: BTExperimentType, diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index b99e4514c..b2debe98d 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -92,8 +92,8 @@ public class BTShopperInsightsClient { /// Call this method when the PayPal or Venmo button has been successfully displayed to the buyer. /// This method sends analytics to help improve the Shopper Insights feature experience. /// - Parameters: - /// - `buttonType`: Type of button presented - PayPal, Venmo, or other - /// - `presentmentDetails`: Detailed information, including button order, experiment type, and + /// - buttonType: Type of button presented - PayPal, Venmo, or Other + /// - presentmentDetails: Detailed information, including button order, experiment type, and /// page type about the payment button that is sent to analytics to help improve the Shopper Insights /// feature experience. public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { From 073cf76f68a4d2c040ffdee6688d162c72bca006 Mon Sep 17 00:00:00 2001 From: stechiu Date: Tue, 17 Dec 2024 10:39:16 -0800 Subject: [PATCH 07/24] Fixed failing tests --- Sources/BraintreeCore/BTAPIClient.swift | 1 - Sources/BraintreeShopperInsights/BTExperimentType.swift | 4 ++-- .../BTShopperInsightsAnalytics_Tests.swift | 4 ++-- UnitTests/BraintreeTestShared/MockAPIClient.swift | 3 ++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 6ecb3a5b6..c994a62f7 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -316,7 +316,6 @@ import Foundation pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, - appSwitchURL: URL? = nil, shopperSessionID: String? = nil ) { analyticsService.sendAnalyticsEvent( diff --git a/Sources/BraintreeShopperInsights/BTExperimentType.swift b/Sources/BraintreeShopperInsights/BTExperimentType.swift index a92bf1a3d..53ae86828 100644 --- a/Sources/BraintreeShopperInsights/BTExperimentType.swift +++ b/Sources/BraintreeShopperInsights/BTExperimentType.swift @@ -5,10 +5,10 @@ import Foundation public enum BTExperimentType: String { /// The test experiment - case test = "test" + case test /// The control experiment - case control = "control" + case control public var formattedExperiment: String { """ diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsAnalytics_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsAnalytics_Tests.swift index 991a240b4..54578a829 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsAnalytics_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsAnalytics_Tests.swift @@ -4,9 +4,9 @@ import XCTest final class BTShopperInsightsAnalytics_Tests: XCTestCase { func test_recommendedPaymentAnalyticEvents_sendExpectedEventNames() { - XCTAssertEqual(BTShopperInsightsAnalytics.payPalPresented, "shopper-insights:paypal-presented") + XCTAssertEqual(BTShopperInsightsAnalytics.buttonPresented, "shopper-insights:button-presented") XCTAssertEqual(BTShopperInsightsAnalytics.payPalSelected, "shopper-insights:paypal-selected") - XCTAssertEqual(BTShopperInsightsAnalytics.venmoPresented, "shopper-insights:venmo-presented") + XCTAssertEqual(BTShopperInsightsAnalytics.buttonPresented, "shopper-insights:button-presented") XCTAssertEqual(BTShopperInsightsAnalytics.venmoSelected, "shopper-insights:venmo-selected") XCTAssertEqual(BTShopperInsightsAnalytics.recommendedPaymentsStarted, "shopper-insights:get-recommended-payments:started") XCTAssertEqual(BTShopperInsightsAnalytics.recommendedPaymentsSucceeded, "shopper-insights:get-recommended-payments:succeeded") diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index ae4c5e455..f1c8229de 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -106,7 +106,8 @@ public class MockAPIClient: BTAPIClient { linkType: LinkType? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, - payPalContextID: String? = nil + payPalContextID: String? = nil, + shopperSessionID: String? = nil ) { postedPayPalContextID = payPalContextID postedLinkType = linkType From b89d854bfbb1c9f3f4573850c617171b6504d852 Mon Sep 17 00:00:00 2001 From: stechiu Date: Tue, 17 Dec 2024 10:46:32 -0800 Subject: [PATCH 08/24] Removed test from bad merge --- .../BTPayPalClient_Tests.swift | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift index 7d112d2d3..14527dbb6 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift @@ -982,24 +982,6 @@ class BTPayPalClient_Tests: XCTestCase { XCTAssertNil(lastPostParameters["merchant_app_return_url"] as? String) } - func testInvokedOpenURLSuccessfully_whenSuccess_sendsAppSwitchSucceededWithAppSwitchURL() { - let eventName = BTPayPalAnalytics.appSwitchSucceeded - let fakeURL = URL(string: "some-url")! - payPalClient.invokedOpenURLSuccessfully(true, url: fakeURL) { _, _ in } - - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.last!, eventName) - XCTAssertEqual(mockAPIClient.postedAppSwitchURL[eventName], fakeURL.absoluteString) - } - - func testInvokedOpenURLSuccessfully_whenFailure_sendsAppSwitchFailedWithAppSwitchURL() { - let eventName = BTPayPalAnalytics.appSwitchFailed - let fakeURL = URL(string: "some-url")! - payPalClient.invokedOpenURLSuccessfully(false, url: fakeURL) { _, _ in } - - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first!, eventName) - XCTAssertEqual(mockAPIClient.postedAppSwitchURL[eventName], fakeURL.absoluteString) - } - // MARK: - Analytics func testAPIClientMetadata_hasIntegrationSetToCustom() { From 58895de2286217bfab7dce52c8b2db2a96af178e Mon Sep 17 00:00:00 2001 From: stechiu Date: Tue, 17 Dec 2024 11:10:36 -0800 Subject: [PATCH 09/24] Fixed failing unit test --- .../BTShopperInsightsClient_Tests.swift | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift index 2b384e852..9e6a10d78 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift @@ -213,21 +213,9 @@ class BTShopperInsightsClient_Tests: XCTestCase { pageType: .about ) sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "PayPal") + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") } - - func testSendPayPalPresentedEvent_whenPaymentMethodsDisplayedNotNil_sendsAnalytic() { - let paymentMethods = ["Apple Pay", "Card", "PayPal"] - let presentmentDetails = BTPresentmentDetails( - buttonOrder: .first, - experimentType: .control, - pageType: .about - ) - sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) - XCTAssertEqual(mockAPIClient.postedPaymentMethodsDisplayed, paymentMethods.joined(separator: ", ")) - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "PayPal") - } - + func testSendPayPalSelectedEvent_sendsAnalytic() { sut.sendPayPalSelectedEvent() XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:paypal-selected") @@ -241,7 +229,7 @@ class BTShopperInsightsClient_Tests: XCTestCase { pageType: .about ) sut.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) - XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "Venmo") + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") } func testSendVenmoSelectedEvent_sendsAnalytic() { From 4c61f94fc7d12000de412432569b2bb18ebe2836 Mon Sep 17 00:00:00 2001 From: stechiu Date: Tue, 17 Dec 2024 12:23:25 -0800 Subject: [PATCH 10/24] Added unit tests --- .../BTShopperInsightsClient_Tests.swift | 8 ++++++++ UnitTests/BraintreeTestShared/MockAPIClient.swift | 10 +++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift index 9e6a10d78..61e7fd94c 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift @@ -214,6 +214,10 @@ class BTShopperInsightsClient_Tests: XCTestCase { ) sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") + XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) + XCTAssertEqual(mockAPIClient.postedButtonType, "PayPal") + XCTAssertEqual(mockAPIClient.postedExperimentType, "control") + XCTAssertEqual(mockAPIClient.postedPageType, "about") } func testSendPayPalSelectedEvent_sendsAnalytic() { @@ -230,6 +234,10 @@ class BTShopperInsightsClient_Tests: XCTestCase { ) sut.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") + XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) + XCTAssertEqual(mockAPIClient.postedButtonType, "Venmo") + XCTAssertEqual(mockAPIClient.postedExperimentType, "control") + XCTAssertEqual(mockAPIClient.postedPageType, "about") } func testSendVenmoSelectedEvent_sendsAnalytic() { diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index f1c8229de..97a43aa7b 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -12,12 +12,16 @@ public class MockAPIClient: BTAPIClient { public var lastGETAPIClientHTTPType: BTAPIClientHTTPService? public var postedAnalyticsEvents : [String] = [] - public var postedPayPalContextID: String? = nil - public var postedLinkType: LinkType? = nil + public var postedAppSwitchURL: [String: String?] = [:] + public var postedButtonOrder: Int? = nil + public var postedButtonType: String? = nil + public var postedExperimentType: String? = nil public var postedIsVaultRequest = false + public var postedLinkType: LinkType? = nil public var postedMerchantExperiment: String? = nil + public var postedPageType: String? = nil public var postedPaymentMethodsDisplayed: String? = nil - public var postedAppSwitchURL: [String: String?] = [:] + public var postedPayPalContextID: String? = nil public var postedShopperSessionID: String? = nil @objc public var cannedConfigurationResponseBody : BTJSON? = nil From 590ad215829b13810c9df7ed7a0d0e5f6ba3a237 Mon Sep 17 00:00:00 2001 From: stechiu Date: Tue, 17 Dec 2024 12:36:46 -0800 Subject: [PATCH 11/24] Added missing unit tests --- .../BTExperimentType.swift | 2 +- .../BTShopperInsightsClient.swift | 3 ++- .../BTShopperInsightsClient_Tests.swift | 16 ++++++++++++++-- .../BraintreeTestShared/MockAPIClient.swift | 4 ++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Sources/BraintreeShopperInsights/BTExperimentType.swift b/Sources/BraintreeShopperInsights/BTExperimentType.swift index 53ae86828..60cf4e2e9 100644 --- a/Sources/BraintreeShopperInsights/BTExperimentType.swift +++ b/Sources/BraintreeShopperInsights/BTExperimentType.swift @@ -14,7 +14,7 @@ public enum BTExperimentType: String { """ [ { "exp_name" : "PaymentReady" } - { "treatment_name" : \(self.rawValue) } + { "treatment_name" : "\(self.rawValue)" } ] """ } diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index b2debe98d..3e883ccd5 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -102,7 +102,8 @@ public class BTShopperInsightsClient { buttonOrder: presentmentDetails.buttonOrder.rawValue, buttonType: buttonType.rawValue, experimentType: presentmentDetails.experimentType.formattedExperiment, - pageType: presentmentDetails.pageType.rawValue + pageType: presentmentDetails.pageType.rawValue, + shopperSessionID: shopperSessionID ) } diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift index 61e7fd94c..58d5effe3 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift @@ -216,7 +216,13 @@ class BTShopperInsightsClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) XCTAssertEqual(mockAPIClient.postedButtonType, "PayPal") - XCTAssertEqual(mockAPIClient.postedExperimentType, "control") + XCTAssertEqual(mockAPIClient.postedExperimentType, + """ + [ + { "exp_name" : "PaymentReady" } + { "treatment_name" : "control" } + ] + """) XCTAssertEqual(mockAPIClient.postedPageType, "about") } @@ -236,7 +242,13 @@ class BTShopperInsightsClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) XCTAssertEqual(mockAPIClient.postedButtonType, "Venmo") - XCTAssertEqual(mockAPIClient.postedExperimentType, "control") + XCTAssertEqual(mockAPIClient.postedExperimentType, + """ + [ + { "exp_name" : "PaymentReady" } + { "treatment_name" : "control" } + ] + """) XCTAssertEqual(mockAPIClient.postedPageType, "about") } diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index 97a43aa7b..aa1ee24be 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -113,6 +113,10 @@ public class MockAPIClient: BTAPIClient { payPalContextID: String? = nil, shopperSessionID: String? = nil ) { + postedButtonType = buttonType + postedButtonOrder = buttonOrder + postedExperimentType = experimentType + postedPageType = pageType postedPayPalContextID = payPalContextID postedLinkType = linkType postedIsVaultRequest = isVaultRequest ?? false From 2cc089a1368502b6c337118f2b8f0f250f72d7f5 Mon Sep 17 00:00:00 2001 From: stechiu Date: Wed, 18 Dec 2024 13:31:29 -0800 Subject: [PATCH 12/24] Updated `buttonOrder` and doc strings --- CHANGELOG.md | 14 +++++++------ .../Analytics/FPTIBatchData.swift | 12 +++++++---- Sources/BraintreeCore/BTAPIClient.swift | 2 +- .../BTButtonOrder.swift | 20 +++++++++---------- .../BTButtonType.swift | 2 +- .../BraintreeShopperInsights/BTPageType.swift | 2 +- .../BTPresentmentDetails.swift | 4 ++++ .../BTShopperInsightsClient.swift | 4 ++-- 8 files changed, 35 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd3ad1563..4454088c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,6 @@ # Braintree iOS SDK Release Notes -## 6.25.0 (2024-12-11) -* BraintreePayPal - * Add `BTPayPalRequest.userPhoneNumber` optional property - * Add `shopperSessionID` to `BTPayPalCheckoutRequest` and `BTPayPalVaultRequest` -* BraintreeVenmo - * Send `url` in `event_params` for App Switch events to PayPal's analytics service (FPTI) +## unreleased * BraintreeShopperInsights (BETA) * Add `shopperSessionID` to `BTShopperInsightsClient` initializer * Add `isPayPalAppInstalled()` and/or `isVenmoAppInstalled()` @@ -15,6 +10,13 @@ * `pageType` * `buttonOrder` * Send `url` in `event_params` for App Switch events to PayPal's analytics service (FPTI) + +## 6.25.0 (2024-12-11) +* BraintreePayPal + * Add `BTPayPalRequest.userPhoneNumber` optional property + * Add `shopperSessionID` to `BTPayPalCheckoutRequest` and `BTPayPalVaultRequest` +* BraintreeVenmo + * Send `url` in `event_params` for App Switch events to PayPal's analytics service (FPTI) * BraintreeVenmo * Send `url` in `event_params` for App Switch events to PayPal's analytics service (FPTI) * Add `BTVenmoClient(apiClient:universalLink:)` to use Universal Links when redirecting back from the Venmo flow diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index 8a1387480..f83186ca0 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -30,10 +30,12 @@ struct FPTIBatchData: Codable { struct Event: Codable { let appSwitchURL: String? + /// The order or ranking in which payment buttons appear. + let buttonOrder: String? + /// The type of button displayed or presented + let buttonType: String? /// UTC millisecond timestamp when a networking task started establishing a TCP connection. See [Apple's docs](https://developer.apple.com/documentation/foundation/urlsessiontasktransactionmetrics#3162615). /// `nil` if a persistent connection is used. - let buttonOrder: Int? - let buttonType: String? let connectionStartTime: Int? let correlationID: String? let endpoint: String? @@ -41,6 +43,7 @@ struct FPTIBatchData: Codable { let endTime: Int? let errorDescription: String? let eventName: String + /// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. let experimentType: String? /// True if the `BTConfiguration` was retrieved from local cache after `tokenize()` call. /// False if the `BTConfiguration` was fetched remotely after `tokenize()` call. @@ -51,6 +54,7 @@ struct FPTIBatchData: Codable { let linkType: String? /// The experiment details associated with a shopper insights flow let merchantExperiment: String? + /// The type of page where the payment button is displayed or where an event occured. let pageType: String? /// The list of payment methods displayed, in the same order in which they are rendered on the page, associated with the `BTShopperInsights` flow. let paymentMethodsDisplayed: String? @@ -69,7 +73,7 @@ struct FPTIBatchData: Codable { init( appSwitchURL: URL? = nil, - buttonOrder: Int? = nil, + buttonOrder: String? = nil, buttonType: String? = nil, connectionStartTime: Int? = nil, correlationID: String? = nil, @@ -113,7 +117,7 @@ struct FPTIBatchData: Codable { enum CodingKeys: String, CodingKey { case appSwitchURL = "url" - case buttonOrder = "button_order" + case buttonOrder = "button_position" case buttonType = "button_type" case connectionStartTime = "connect_start_time" case correlationID = "correlation_id" diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index c994a62f7..4d6abbc55 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -304,7 +304,7 @@ import Foundation public func sendAnalyticsEvent( _ eventName: String, appSwitchURL: URL? = nil, - buttonOrder: Int? = nil, + buttonOrder: String? = nil, buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, diff --git a/Sources/BraintreeShopperInsights/BTButtonOrder.swift b/Sources/BraintreeShopperInsights/BTButtonOrder.swift index 8bc2d1ef5..d51d6047e 100644 --- a/Sources/BraintreeShopperInsights/BTButtonOrder.swift +++ b/Sources/BraintreeShopperInsights/BTButtonOrder.swift @@ -2,32 +2,32 @@ import Foundation /// The order or ranking in which payment buttons appear. /// - Warning: This module is in beta. It's public API may change or be removed in future releases. -public enum BTButtonOrder: Int { +public enum BTButtonOrder: String { /// 0 - case first + case first = "1" /// 1 - case second + case second = "2" /// 2 - case third + case third = "3" /// 3 - case fourth + case fourth = "4" /// 4 - case fifth + case fifth = "5" /// 5 - case sixth + case sixth = "6" /// 6 - case seventh + case seventh = "7" /// 7 - case eighth + case eighth = "8" /// 8 - case other + case other = "other" } diff --git a/Sources/BraintreeShopperInsights/BTButtonType.swift b/Sources/BraintreeShopperInsights/BTButtonType.swift index be013347c..8341aa7a1 100644 --- a/Sources/BraintreeShopperInsights/BTButtonType.swift +++ b/Sources/BraintreeShopperInsights/BTButtonType.swift @@ -1,6 +1,6 @@ import Foundation -/// The button type to be displayed or presented +/// The type of button displayed or presented /// Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonType: String { diff --git a/Sources/BraintreeShopperInsights/BTPageType.swift b/Sources/BraintreeShopperInsights/BTPageType.swift index 03fa1e87a..a301d93c9 100644 --- a/Sources/BraintreeShopperInsights/BTPageType.swift +++ b/Sources/BraintreeShopperInsights/BTPageType.swift @@ -1,6 +1,6 @@ import Foundation -/// The type of page where the event occurred. +/// The type of page where the payment button is displayed or where an event occured. /// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTPageType: String { diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift index 9f0137688..6cf5f3dfd 100644 --- a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -14,6 +14,10 @@ public class BTPresentmentDetails { /// Detailed information, including button order, experiment type, and page type about the payment button that /// is sent to analytics to help improve the Shopper Insights feature experience. /// - Warning: This class is in beta. It's public API may change or be removed in future releases. + /// - Parameters: + /// - buttonOrder: The order or ranking in which payment buttons appear. + /// - experimentType: The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. + /// - pageType: The type of page where the payment button is displayed or where an event occured. public init( buttonOrder: BTButtonOrder, experimentType: BTExperimentType, diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index 3e883ccd5..fe7327dca 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -92,8 +92,8 @@ public class BTShopperInsightsClient { /// Call this method when the PayPal or Venmo button has been successfully displayed to the buyer. /// This method sends analytics to help improve the Shopper Insights feature experience. /// - Parameters: - /// - buttonType: Type of button presented - PayPal, Venmo, or Other - /// - presentmentDetails: Detailed information, including button order, experiment type, and + /// - buttonType: Type of button presented - PayPal, Venmo, or Other + /// - presentmentDetails: Detailed information, including button order, experiment type, and /// page type about the payment button that is sent to analytics to help improve the Shopper Insights /// feature experience. public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { From c1461f68a91cea79152b5bdf4ed1e8198023a2d2 Mon Sep 17 00:00:00 2001 From: stechiu Date: Wed, 18 Dec 2024 14:42:27 -0800 Subject: [PATCH 13/24] Removed `experimentType` --- Sources/BraintreeCore/Analytics/FPTIBatchData.swift | 5 ----- Sources/BraintreeCore/BTAPIClient.swift | 4 +--- .../BraintreeShopperInsights/BTShopperInsightsClient.swift | 2 +- .../BTShopperInsightsClient_Tests.swift | 4 ++-- UnitTests/BraintreeTestShared/MockAPIClient.swift | 5 +---- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index f83186ca0..de6186dba 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -43,8 +43,6 @@ struct FPTIBatchData: Codable { let endTime: Int? let errorDescription: String? let eventName: String - /// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. - let experimentType: String? /// True if the `BTConfiguration` was retrieved from local cache after `tokenize()` call. /// False if the `BTConfiguration` was fetched remotely after `tokenize()` call. let isConfigFromCache: Bool? @@ -81,7 +79,6 @@ struct FPTIBatchData: Codable { endTime: Int? = nil, errorDescription: String? = nil, eventName: String, - experimentType: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: String? = nil, @@ -102,7 +99,6 @@ struct FPTIBatchData: Codable { self.endTime = endTime self.errorDescription = errorDescription self.eventName = eventName - self.experimentType = experimentType self.isConfigFromCache = isConfigFromCache self.isVaultRequest = isVaultRequest self.linkType = linkType @@ -123,7 +119,6 @@ struct FPTIBatchData: Codable { case correlationID = "correlation_id" case errorDescription = "error_desc" case eventName = "event_name" - case experimentType = "experiment_type" case isConfigFromCache = "config_cached" case isVaultRequest = "is_vault" case linkType = "link_type" diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 4d6abbc55..12f5ff794 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -308,11 +308,10 @@ import Foundation buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, - experimentType: String? = nil, - merchantExperiment: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: LinkType? = nil, + merchantExperiment: String? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, @@ -326,7 +325,6 @@ import Foundation correlationID: correlationID, errorDescription: errorDescription, eventName: eventName, - experimentType: experimentType, isConfigFromCache: isConfigFromCache, isVaultRequest: isVaultRequest, linkType: linkType?.rawValue, diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index fe7327dca..81baab11c 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -101,7 +101,7 @@ public class BTShopperInsightsClient { BTShopperInsightsAnalytics.buttonPresented, buttonOrder: presentmentDetails.buttonOrder.rawValue, buttonType: buttonType.rawValue, - experimentType: presentmentDetails.experimentType.formattedExperiment, + merchantExperiment: presentmentDetails.experimentType.formattedExperiment, pageType: presentmentDetails.pageType.rawValue, shopperSessionID: shopperSessionID ) diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift index 58d5effe3..d5583b21e 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift @@ -216,7 +216,7 @@ class BTShopperInsightsClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) XCTAssertEqual(mockAPIClient.postedButtonType, "PayPal") - XCTAssertEqual(mockAPIClient.postedExperimentType, + XCTAssertEqual(mockAPIClient.postedMerchantExperiment, """ [ { "exp_name" : "PaymentReady" } @@ -242,7 +242,7 @@ class BTShopperInsightsClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) XCTAssertEqual(mockAPIClient.postedButtonType, "Venmo") - XCTAssertEqual(mockAPIClient.postedExperimentType, + XCTAssertEqual(mockAPIClient.postedMerchantExperiment, """ [ { "exp_name" : "PaymentReady" } diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index aa1ee24be..79c75c57b 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -15,7 +15,6 @@ public class MockAPIClient: BTAPIClient { public var postedAppSwitchURL: [String: String?] = [:] public var postedButtonOrder: Int? = nil public var postedButtonType: String? = nil - public var postedExperimentType: String? = nil public var postedIsVaultRequest = false public var postedLinkType: LinkType? = nil public var postedMerchantExperiment: String? = nil @@ -103,11 +102,10 @@ public class MockAPIClient: BTAPIClient { buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, - experimentType: String? = nil, - merchantExperiment experiment: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: LinkType? = nil, + merchantExperiment experiment: String? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, @@ -115,7 +113,6 @@ public class MockAPIClient: BTAPIClient { ) { postedButtonType = buttonType postedButtonOrder = buttonOrder - postedExperimentType = experimentType postedPageType = pageType postedPayPalContextID = payPalContextID postedLinkType = linkType From 5ad96687ae2126e77bf1a79abac573765136b8da Mon Sep 17 00:00:00 2001 From: stechiu Date: Wed, 18 Dec 2024 16:19:13 -0800 Subject: [PATCH 14/24] Updated doc strings --- Sources/BraintreeShopperInsights/BTButtonType.swift | 2 +- Sources/BraintreeShopperInsights/BTPageType.swift | 2 +- Sources/BraintreeShopperInsights/BTPresentmentDetails.swift | 4 ++++ .../BraintreeShopperInsights/BTShopperInsightsClient.swift | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/BraintreeShopperInsights/BTButtonType.swift b/Sources/BraintreeShopperInsights/BTButtonType.swift index be013347c..8341aa7a1 100644 --- a/Sources/BraintreeShopperInsights/BTButtonType.swift +++ b/Sources/BraintreeShopperInsights/BTButtonType.swift @@ -1,6 +1,6 @@ import Foundation -/// The button type to be displayed or presented +/// The type of button displayed or presented /// Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonType: String { diff --git a/Sources/BraintreeShopperInsights/BTPageType.swift b/Sources/BraintreeShopperInsights/BTPageType.swift index 03fa1e87a..a301d93c9 100644 --- a/Sources/BraintreeShopperInsights/BTPageType.swift +++ b/Sources/BraintreeShopperInsights/BTPageType.swift @@ -1,6 +1,6 @@ import Foundation -/// The type of page where the event occurred. +/// The type of page where the payment button is displayed or where an event occured. /// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTPageType: String { diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift index 9f0137688..6cf5f3dfd 100644 --- a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -14,6 +14,10 @@ public class BTPresentmentDetails { /// Detailed information, including button order, experiment type, and page type about the payment button that /// is sent to analytics to help improve the Shopper Insights feature experience. /// - Warning: This class is in beta. It's public API may change or be removed in future releases. + /// - Parameters: + /// - buttonOrder: The order or ranking in which payment buttons appear. + /// - experimentType: The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. + /// - pageType: The type of page where the payment button is displayed or where an event occured. public init( buttonOrder: BTButtonOrder, experimentType: BTExperimentType, diff --git a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift index 3e883ccd5..768c3da36 100644 --- a/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift +++ b/Sources/BraintreeShopperInsights/BTShopperInsightsClient.swift @@ -92,8 +92,8 @@ public class BTShopperInsightsClient { /// Call this method when the PayPal or Venmo button has been successfully displayed to the buyer. /// This method sends analytics to help improve the Shopper Insights feature experience. /// - Parameters: - /// - buttonType: Type of button presented - PayPal, Venmo, or Other - /// - presentmentDetails: Detailed information, including button order, experiment type, and + /// - buttonType: Type of button presented - PayPal, Venmo, or Other + /// - presentmentDetails: Detailed information, including button order, experiment type, and /// page type about the payment button that is sent to analytics to help improve the Shopper Insights /// feature experience. public func sendPresentedEvent(for buttonType: BTButtonType, presentmentDetails: BTPresentmentDetails) { From 950b2e038021623e0998942d71f5166efc0d3607 Mon Sep 17 00:00:00 2001 From: stechiu Date: Wed, 18 Dec 2024 17:04:23 -0800 Subject: [PATCH 15/24] Fixed failing tests --- .../Analytics/FPTIBatchData.swift | 5 +++ Sources/BraintreeCore/BTAPIClient.swift | 2 +- .../BTShopperInsightsClient_Tests.swift | 38 +++++++++++++++++-- .../BraintreeTestShared/MockAPIClient.swift | 12 +++--- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index de6186dba..f83186ca0 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -43,6 +43,8 @@ struct FPTIBatchData: Codable { let endTime: Int? let errorDescription: String? let eventName: String + /// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. + let experimentType: String? /// True if the `BTConfiguration` was retrieved from local cache after `tokenize()` call. /// False if the `BTConfiguration` was fetched remotely after `tokenize()` call. let isConfigFromCache: Bool? @@ -79,6 +81,7 @@ struct FPTIBatchData: Codable { endTime: Int? = nil, errorDescription: String? = nil, eventName: String, + experimentType: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: String? = nil, @@ -99,6 +102,7 @@ struct FPTIBatchData: Codable { self.endTime = endTime self.errorDescription = errorDescription self.eventName = eventName + self.experimentType = experimentType self.isConfigFromCache = isConfigFromCache self.isVaultRequest = isVaultRequest self.linkType = linkType @@ -119,6 +123,7 @@ struct FPTIBatchData: Codable { case correlationID = "correlation_id" case errorDescription = "error_desc" case eventName = "event_name" + case experimentType = "experiment_type" case isConfigFromCache = "config_cached" case isVaultRequest = "is_vault" case linkType = "link_type" diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 12f5ff794..7bb81b48f 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -308,10 +308,10 @@ import Foundation buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, + merchantExperiment: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: LinkType? = nil, - merchantExperiment: String? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, diff --git a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift index d5583b21e..8440ac76f 100644 --- a/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift +++ b/UnitTests/BraintreeShopperInsightsTests/BTShopperInsightsClient_Tests.swift @@ -206,7 +206,7 @@ class BTShopperInsightsClient_Tests: XCTestCase { // MARK: - Analytics - func testSendPayPalPresentedEvent_sendsAnalytic() { + func testSendPayPalPresentedEvent_whenExperimentTypeIsControl_sendsAnalytic() { let presentmentDetails = BTPresentmentDetails( buttonOrder: .first, experimentType: .control, @@ -214,7 +214,7 @@ class BTShopperInsightsClient_Tests: XCTestCase { ) sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") - XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) + XCTAssertEqual(mockAPIClient.postedButtonOrder, "1") XCTAssertEqual(mockAPIClient.postedButtonType, "PayPal") XCTAssertEqual(mockAPIClient.postedMerchantExperiment, """ @@ -226,6 +226,22 @@ class BTShopperInsightsClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedPageType, "about") } + func testSendPayPalPresentedEvent_whenExperimentTypeIsTest_sendsAnalytic() { + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .first, + experimentType: .test, + pageType: .about + ) + sut.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) + XCTAssertEqual(mockAPIClient.postedMerchantExperiment, + """ + [ + { "exp_name" : "PaymentReady" } + { "treatment_name" : "test" } + ] + """) + } + func testSendPayPalSelectedEvent_sendsAnalytic() { sut.sendPayPalSelectedEvent() XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:paypal-selected") @@ -240,7 +256,7 @@ class BTShopperInsightsClient_Tests: XCTestCase { ) sut.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first, "shopper-insights:button-presented") - XCTAssertEqual(mockAPIClient.postedButtonOrder, 0) + XCTAssertEqual(mockAPIClient.postedButtonOrder, "1") XCTAssertEqual(mockAPIClient.postedButtonType, "Venmo") XCTAssertEqual(mockAPIClient.postedMerchantExperiment, """ @@ -251,6 +267,22 @@ class BTShopperInsightsClient_Tests: XCTestCase { """) XCTAssertEqual(mockAPIClient.postedPageType, "about") } + + func testSendVenmoPresentedEvent_whenExperimentTypeIsTest_sendsAnalytic() { + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .first, + experimentType: .test, + pageType: .about + ) + sut.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) + XCTAssertEqual(mockAPIClient.postedMerchantExperiment, + """ + [ + { "exp_name" : "PaymentReady" } + { "treatment_name" : "test" } + ] + """) + } func testSendVenmoSelectedEvent_sendsAnalytic() { sut.sendVenmoSelectedEvent() diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index 79c75c57b..ba9b05d81 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -13,7 +13,7 @@ public class MockAPIClient: BTAPIClient { public var postedAnalyticsEvents : [String] = [] public var postedAppSwitchURL: [String: String?] = [:] - public var postedButtonOrder: Int? = nil + public var postedButtonOrder: String? = nil public var postedButtonType: String? = nil public var postedIsVaultRequest = false public var postedLinkType: LinkType? = nil @@ -96,16 +96,16 @@ public class MockAPIClient: BTAPIClient { } public override func sendAnalyticsEvent( - _ name: String, + _ eventName: String, appSwitchURL: URL? = nil, - buttonOrder: Int? = nil, + buttonOrder: String? = nil, buttonType: String? = nil, correlationID: String? = nil, errorDescription: String? = nil, + merchantExperiment experiment: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: LinkType? = nil, - merchantExperiment experiment: String? = nil, pageType: String? = nil, paymentMethodsDisplayed: String? = nil, payPalContextID: String? = nil, @@ -119,8 +119,8 @@ public class MockAPIClient: BTAPIClient { postedIsVaultRequest = isVaultRequest ?? false postedMerchantExperiment = experiment postedPaymentMethodsDisplayed = paymentMethodsDisplayed - postedAppSwitchURL[name] = appSwitchURL?.absoluteString - postedAnalyticsEvents.append(name) + postedAppSwitchURL[eventName] = appSwitchURL?.absoluteString + postedAnalyticsEvents.append(eventName) postedShopperSessionID = shopperSessionID } From 69d5a88e621c71444940010e7e29d5ad2c1441d1 Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Thu, 19 Dec 2024 12:43:29 -0800 Subject: [PATCH 16/24] Remove experimentType --- Sources/BraintreeCore/Analytics/FPTIBatchData.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index f83186ca0..88a4490f1 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -25,7 +25,7 @@ struct FPTIBatchData: Codable { case fptiEvents = "event_params" } } - + /// Encapsulates a single event by it's name and timestamp. struct Event: Codable { @@ -43,8 +43,6 @@ struct FPTIBatchData: Codable { let endTime: Int? let errorDescription: String? let eventName: String - /// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. - let experimentType: String? /// True if the `BTConfiguration` was retrieved from local cache after `tokenize()` call. /// False if the `BTConfiguration` was fetched remotely after `tokenize()` call. let isConfigFromCache: Bool? @@ -81,7 +79,6 @@ struct FPTIBatchData: Codable { endTime: Int? = nil, errorDescription: String? = nil, eventName: String, - experimentType: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, linkType: String? = nil, @@ -102,7 +99,6 @@ struct FPTIBatchData: Codable { self.endTime = endTime self.errorDescription = errorDescription self.eventName = eventName - self.experimentType = experimentType self.isConfigFromCache = isConfigFromCache self.isVaultRequest = isVaultRequest self.linkType = linkType @@ -122,8 +118,7 @@ struct FPTIBatchData: Codable { case connectionStartTime = "connect_start_time" case correlationID = "correlation_id" case errorDescription = "error_desc" - case eventName = "event_name" - case experimentType = "experiment_type" + case eventName = "event_name" case isConfigFromCache = "config_cached" case isVaultRequest = "is_vault" case linkType = "link_type" From ac4f1effd01a83523e086d8ac8e2e74f90da6ebe Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Thu, 19 Dec 2024 12:54:20 -0800 Subject: [PATCH 17/24] Create functions to toggle the paypal or venmo buttons and send presentment analytics at the correct time. --- .../ShopperInsightsViewController.swift | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/Demo/Application/Features/ShopperInsightsViewController.swift b/Demo/Application/Features/ShopperInsightsViewController.swift index f4a89eea6..47c8ace2b 100644 --- a/Demo/Application/Features/ShopperInsightsViewController.swift +++ b/Demo/Application/Features/ShopperInsightsViewController.swift @@ -95,22 +95,50 @@ class ShopperInsightsViewController: PaymentButtonBaseViewController { let result = try await shopperInsightsClient.getRecommendedPaymentMethods(request: request, experiment: sampleExperiment) // swiftlint:disable:next line_length progressBlock("PayPal Recommended: \(result.isPayPalRecommended)\nVenmo Recommended: \(result.isVenmoRecommended)\nEligible in PayPal Network: \(result.isEligibleInPayPalNetwork)") - payPalVaultButton.isEnabled = result.isPayPalRecommended - venmoButton.isEnabled = result.isVenmoRecommended + + togglePayPalVaultButton(enabled: result.isPayPalRecommended) + toggleVenmoButton(enabled: result.isVenmoRecommended) } catch { progressBlock("Error: \(error.localizedDescription)") } } } - @objc func payPalVaultButtonTapped(_ button: UIButton) { + private func togglePayPalVaultButton(enabled: Bool) { + payPalVaultButton.isEnabled = enabled + + guard enabled else { return } + let presentmentDetails = BTPresentmentDetails( buttonOrder: .first, experimentType: .control, pageType: .about ) - - shopperInsightsClient.sendPresentedEvent(for: .payPal, presentmentDetails: presentmentDetails) + + shopperInsightsClient.sendPresentedEvent( + for: .payPal, + presentmentDetails: presentmentDetails + ) + } + + private func toggleVenmoButton(enabled: Bool) { + venmoButton.isEnabled = enabled + + guard enabled else { return } + + let presentmentDetails = BTPresentmentDetails( + buttonOrder: .second, + experimentType: .control, + pageType: .about + ) + + shopperInsightsClient.sendPresentedEvent( + for: .venmo, + presentmentDetails: presentmentDetails + ) + } + + @objc func payPalVaultButtonTapped(_ button: UIButton) { progressBlock("Tapped PayPal Vault") shopperInsightsClient.sendPayPalSelectedEvent() @@ -128,12 +156,6 @@ class ShopperInsightsViewController: PaymentButtonBaseViewController { } @objc func venmoButtonTapped(_ button: UIButton) { - let presentmentDetails = BTPresentmentDetails( - buttonOrder: .second, - experimentType: .control, - pageType: .about - ) - shopperInsightsClient.sendPresentedEvent(for: .venmo, presentmentDetails: presentmentDetails) progressBlock("Tapped Venmo") shopperInsightsClient.sendVenmoSelectedEvent() From 75674cad07e394d770d560385be1d6c9abe8faa9 Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Thu, 19 Dec 2024 12:59:38 -0800 Subject: [PATCH 18/24] Update docs --- .../BTButtonOrder.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/BraintreeShopperInsights/BTButtonOrder.swift b/Sources/BraintreeShopperInsights/BTButtonOrder.swift index d51d6047e..0018ecc29 100644 --- a/Sources/BraintreeShopperInsights/BTButtonOrder.swift +++ b/Sources/BraintreeShopperInsights/BTButtonOrder.swift @@ -4,30 +4,30 @@ import Foundation /// - Warning: This module is in beta. It's public API may change or be removed in future releases. public enum BTButtonOrder: String { - /// 0 + /// First place case first = "1" - /// 1 + /// Second place case second = "2" - /// 2 + /// Third place case third = "3" - /// 3 + /// Fourth place case fourth = "4" - /// 4 + /// Fifth place case fifth = "5" - /// 5 + /// Sixth place case sixth = "6" - /// 6 + /// Seventh place case seventh = "7" - /// 7 + /// Eighth place case eighth = "8" - /// 8 + /// Greater than Eighth place case other = "other" } From 2e6c85156f0b1dfc5e1a5e17e03618652ec2d090 Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Thu, 19 Dec 2024 13:14:20 -0800 Subject: [PATCH 19/24] Re add functions that were deleted. Chnage accessor for func used in unit test --- Sources/BraintreePayPal/BTPayPalClient.swift | 2 +- .../BTPayPalClient_Tests.swift | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index 30e876e3d..2a5252855 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -416,7 +416,7 @@ import BraintreeDataCollector } } - private func invokedOpenURLSuccessfully(_ success: Bool, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void) { + func invokedOpenURLSuccessfully(_ success: Bool, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void) { if success { apiClient.sendAnalyticsEvent( BTPayPalAnalytics.appSwitchSucceeded, diff --git a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift index 14527dbb6..8b6403295 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift @@ -982,6 +982,24 @@ class BTPayPalClient_Tests: XCTestCase { XCTAssertNil(lastPostParameters["merchant_app_return_url"] as? String) } + func testInvokedOpenURLSuccessfully_whenSuccess_sendsAppSwitchSucceededWithAppSwitchURL() { + let eventName = BTPayPalAnalytics.appSwitchSucceeded + let fakeURL = URL(string: "some-url")! + payPalClient.invokedOpenURLSuccessfully(true, url: fakeURL) { _, _ in } + + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.last!, eventName) + XCTAssertEqual(mockAPIClient.postedAppSwitchURL[eventName], fakeURL.absoluteString) + } + + func testInvokedOpenURLSuccessfully_whenFailure_sendsAppSwitchFailedWithAppSwitchURL() { + let eventName = BTPayPalAnalytics.appSwitchFailed + let fakeURL = URL(string: "some-url")! + payPalClient.invokedOpenURLSuccessfully(false, url: fakeURL) { _, _ in } + + XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.first!, eventName) + XCTAssertEqual(mockAPIClient.postedAppSwitchURL[eventName], fakeURL.absoluteString) + } + // MARK: - Analytics func testAPIClientMetadata_hasIntegrationSetToCustom() { From a9a6f6074db895cd5a8c09aff2a41b72751e27f0 Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Thu, 19 Dec 2024 13:34:15 -0800 Subject: [PATCH 20/24] Reverse changes to PayPalClient --- Sources/BraintreePayPal/BTPayPalClient.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index 2a5252855..8eab7029b 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -412,28 +412,28 @@ import BraintreeDataCollector } application.open(redirectURL) { success in - self.invokedOpenURLSuccessfully(success, completion: completion) + self.invokedOpenURLSuccessfully(success, url: redirectURL, completion: completion) } } - func invokedOpenURLSuccessfully(_ success: Bool, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void) { + func invokedOpenURLSuccessfully(_ success: Bool, url: URL, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void) { if success { apiClient.sendAnalyticsEvent( BTPayPalAnalytics.appSwitchSucceeded, + appSwitchURL: url, isVaultRequest: isVaultRequest, linkType: linkType, - payPalContextID: payPalContextID, - shopperSessionID: payPalRequest?.shopperSessionID + payPalContextID: payPalContextID ) BTPayPalClient.payPalClient = self appSwitchCompletion = completion } else { apiClient.sendAnalyticsEvent( BTPayPalAnalytics.appSwitchFailed, + appSwitchURL: url, isVaultRequest: isVaultRequest, linkType: linkType, - payPalContextID: payPalContextID, - shopperSessionID: payPalRequest?.shopperSessionID + payPalContextID: payPalContextID ) notifyFailure(with: BTPayPalError.appSwitchFailed, completion: completion) } From 3dbdf9ae5f331eac5e6c1339ef37e68b0d5d55ae Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Thu, 19 Dec 2024 14:57:50 -0800 Subject: [PATCH 21/24] Remove trailing white space --- Sources/BraintreeCore/Analytics/FPTIBatchData.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index 88a4490f1..083c29fae 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -118,7 +118,7 @@ struct FPTIBatchData: Codable { case connectionStartTime = "connect_start_time" case correlationID = "correlation_id" case errorDescription = "error_desc" - case eventName = "event_name" + case eventName = "event_name" case isConfigFromCache = "config_cached" case isVaultRequest = "is_vault" case linkType = "link_type" From 0791239d60a499e0c0d9246b4c582f45bd0eb098 Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Fri, 20 Dec 2024 09:09:45 -0600 Subject: [PATCH 22/24] Update Sources/BraintreeShopperInsights/BTPresentmentDetails.swift --- Sources/BraintreeShopperInsights/BTPresentmentDetails.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift index 6cf5f3dfd..1b33abc04 100644 --- a/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift +++ b/Sources/BraintreeShopperInsights/BTPresentmentDetails.swift @@ -2,13 +2,8 @@ import Foundation public class BTPresentmentDetails { - /// The order or ranking in which payment buttons appear. var buttonOrder: BTButtonOrder - - /// The experiment type that is sent to analytics to help improve the Shopper Insights feature experience. var experimentType: BTExperimentType - - /// The type of page where the event occurred. var pageType: BTPageType /// Detailed information, including button order, experiment type, and page type about the payment button that From 7b5590d38b7d2df79aa11d044fcf9bbf5fa55c91 Mon Sep 17 00:00:00 2001 From: Justin Warmkessel Date: Fri, 20 Dec 2024 09:06:59 -0800 Subject: [PATCH 23/24] Update --- Sources/BraintreePayPal/BTPayPalClient.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index 65824d32e..3487e1166 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -290,20 +290,20 @@ import BraintreeDataCollector if success { apiClient.sendAnalyticsEvent( BTPayPalAnalytics.appSwitchSucceeded, + appSwitchURL: url, isVaultRequest: isVaultRequest, linkType: linkType, - payPalContextID: payPalContextID, - appSwitchURL: url + payPalContextID: payPalContextID ) BTPayPalClient.payPalClient = self appSwitchCompletion = completion } else { apiClient.sendAnalyticsEvent( BTPayPalAnalytics.appSwitchFailed, + appSwitchURL: url, isVaultRequest: isVaultRequest, linkType: linkType, - payPalContextID: payPalContextID, - appSwitchURL: url + payPalContextID: payPalContextID ) notifyFailure(with: BTPayPalError.appSwitchFailed, completion: completion) } From 4095bf59b6da5f86106714d40ab294db4e14a407 Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Fri, 20 Dec 2024 11:23:39 -0600 Subject: [PATCH 24/24] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e7d091df..92c1cda7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ * `experimentType` * `pageType` * `buttonOrder` - * Send `url` in `event_params` for App Switch events to PayPal's analytics service (FPTI) ## 6.25.0 (2024-12-11) * BraintreePayPal