From caae093dbcb941e11a72285cdf10e3827368c3f5 Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Mon, 11 Mar 2024 15:40:38 -0500 Subject: [PATCH] Send `paypal_context_id` at Event Level (#1211) * Move paypal_context_id from batch_params to event_params * Cleaned up implementation * Alphabetized parameters order in sendAnalyticsEvent * Update tests --- CHANGELOG.md | 2 +- .../Analytics/BTAnalyticsService.swift | 20 ++++++++----------- .../Analytics/FPTIBatchData.swift | 9 ++++----- Sources/BraintreeCore/BTAPIClient.swift | 12 +++++------ Sources/BraintreePayPal/BTPayPalClient.swift | 2 +- .../BTPayPalNativeCheckoutClient.swift | 2 +- Sources/BraintreeVenmo/BTVenmoClient.swift | 12 +++++------ .../Analytics/FPTIBatchData_Tests.swift | 6 ++++-- .../Analytics/FakeAnalyticsService.swift | 6 +++--- .../BraintreeTestShared/MockAPIClient.swift | 6 +++--- 10 files changed, 37 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc6e730e6..d22b3fb82e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * If set to `true` customers will fallback to a web based Venmo flow if the Venmo app is not installed * This method uses Universal Links instead of URL Schemes * BraintreeCore - * Send `paypal_context_id` in `batch_params` to PayPal's analytics service (FPTI) when available + * Send `paypal_context_id` in `event_params` to PayPal's analytics service (FPTI) when available * Send `link_type` in `event_params` to PayPal's analytics service (FPTI) * Fix bug where FPTI analytic events were being sent multiple times diff --git a/Sources/BraintreeCore/Analytics/BTAnalyticsService.swift b/Sources/BraintreeCore/Analytics/BTAnalyticsService.swift index 6006c495ed..4dfc4f4dcb 100644 --- a/Sources/BraintreeCore/Analytics/BTAnalyticsService.swift +++ b/Sources/BraintreeCore/Analytics/BTAnalyticsService.swift @@ -12,8 +12,6 @@ class BTAnalyticsService: Equatable { private let apiClient: BTAPIClient - private var payPalContextID: String? - // MARK: - Initializer init(apiClient: BTAPIClient) { @@ -25,22 +23,22 @@ class BTAnalyticsService: Equatable { /// Sends analytics event to https://api.paypal.com/v1/tracking/batch/events/ via a background task. /// - Parameters: /// - eventName: Name of analytic event. - /// - errorDescription: Optional. Full error description returned to merchant. /// - correlationID: Optional. CorrelationID associated with the checkout session. - /// - payPalContextID: Optional. PayPal Context ID associated with the checkout session. + /// - errorDescription: Optional. Full error description returned to merchant. /// - linkType: Optional. The type of link the SDK will be handling, currently deeplink or universal. + /// - payPalContextID: Optional. PayPal Context ID associated with the checkout session. func sendAnalyticsEvent( _ eventName: String, - errorDescription: String? = nil, correlationID: String? = nil, - payPalContextID: String? = nil, - linkType: String? = nil + errorDescription: String? = nil, + linkType: String? = nil, + payPalContextID: String? = nil ) { Task(priority: .background) { await performEventRequest( eventName, - errorDescription: errorDescription, correlationID: correlationID, + errorDescription: errorDescription, linkType: linkType, payPalContextID: payPalContextID ) @@ -50,19 +48,18 @@ class BTAnalyticsService: Equatable { /// Exposed to be able to execute this function synchronously in unit tests func performEventRequest( _ eventName: String, - errorDescription: String? = nil, correlationID: String? = nil, + errorDescription: String? = nil, linkType: String? = nil, payPalContextID: String? = nil ) async { - self.payPalContextID = payPalContextID - let timestampInMilliseconds = UInt64(Date().timeIntervalSince1970 * 1000) let event = FPTIBatchData.Event( correlationID: correlationID, errorDescription: errorDescription, eventName: eventName, linkType: linkType, + payPalContextID: payPalContextID, timestamp: String(timestampInMilliseconds) ) @@ -101,7 +98,6 @@ class BTAnalyticsService: Equatable { environment: config.fptiEnvironment, integrationType: apiClient.metadata.integration.stringValue, merchantID: config.merchantID, - payPalContextID: payPalContextID, sessionID: sessionID, tokenizationKey: apiClient.tokenizationKey ) diff --git a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift index a5b770e0c9..4e3e2ca38f 100644 --- a/Sources/BraintreeCore/Analytics/FPTIBatchData.swift +++ b/Sources/BraintreeCore/Analytics/FPTIBatchData.swift @@ -32,6 +32,9 @@ struct FPTIBatchData: Codable { let eventName: String /// The type of link the SDK will be handling, currently deeplink or universal let linkType: String? + /// Used for linking events from the client to server side request + /// This value will be PayPal Order ID, Payment Token, EC token, Billing Agreement, or Venmo Context ID depending on the flow + let payPalContextID: String? let timestamp: String let tenantName: String = "Braintree" @@ -40,6 +43,7 @@ struct FPTIBatchData: Codable { case errorDescription = "error_desc" case eventName = "event_name" case linkType = "link_type" + case payPalContextID = "paypal_context_id" case timestamp = "t" case tenantName = "tenant_name" } @@ -101,10 +105,6 @@ struct FPTIBatchData: Codable { let merchantID: String? - /// Used for linking events from the client to server side request - /// This value will be PayPal Order ID, Payment Token, EC token or Billing Agreement depending on the flow - let payPalContextID: String? - let platform = "iOS" let sessionID: String @@ -127,7 +127,6 @@ struct FPTIBatchData: Codable { case isSimulator = "is_simulator" case merchantAppVersion = "mapv" case merchantID = "merchant_id" - case payPalContextID = "paypal_context_id" case platform = "platform" case sessionID = "session_id" case tokenizationKey = "tokenization_key" diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 3951cde025..f1e20c50f5 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -341,17 +341,17 @@ import Foundation @_documentation(visibility: private) public func sendAnalyticsEvent( _ eventName: String, - errorDescription: String? = nil, correlationID: String? = nil, - payPalContextID: String? = nil, - linkType: String? = nil + errorDescription: String? = nil, + linkType: String? = nil, + payPalContextID: String? = nil ) { analyticsService?.sendAnalyticsEvent( eventName, - errorDescription: errorDescription, correlationID: correlationID, - payPalContextID: payPalContextID, - linkType: linkType + errorDescription: errorDescription, + linkType: linkType, + payPalContextID: payPalContextID ) } diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index e111eedb5e..24bafae622 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -415,8 +415,8 @@ import BraintreeDataCollector private func notifyFailure(with error: Error, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void) { apiClient.sendAnalyticsEvent( BTPayPalAnalytics.tokenizeFailed, - errorDescription: error.localizedDescription, correlationID: clientMetadataID, + errorDescription: error.localizedDescription, payPalContextID: payPalContextID ) completion(nil, error) diff --git a/Sources/BraintreePayPalNativeCheckout/BTPayPalNativeCheckoutClient.swift b/Sources/BraintreePayPalNativeCheckout/BTPayPalNativeCheckoutClient.swift index 8448e0cf54..120860b856 100644 --- a/Sources/BraintreePayPalNativeCheckout/BTPayPalNativeCheckoutClient.swift +++ b/Sources/BraintreePayPalNativeCheckout/BTPayPalNativeCheckoutClient.swift @@ -190,8 +190,8 @@ import PayPalCheckout private func notifyFailure(with error: Error, completion: @escaping (BTPayPalNativeCheckoutAccountNonce?, Error?) -> Void) { apiClient.sendAnalyticsEvent( BTPayPalNativeCheckoutAnalytics.tokenizeFailed, - errorDescription: error.localizedDescription, correlationID: clientMetadataID, + errorDescription: error.localizedDescription, payPalContextID: payPalContextID ) completion(nil, error) diff --git a/Sources/BraintreeVenmo/BTVenmoClient.swift b/Sources/BraintreeVenmo/BTVenmoClient.swift index b0f111ce35..3b0cde906d 100644 --- a/Sources/BraintreeVenmo/BTVenmoClient.swift +++ b/Sources/BraintreeVenmo/BTVenmoClient.swift @@ -352,11 +352,11 @@ import BraintreeCore shouldVault = success && vault if success { - apiClient.sendAnalyticsEvent(BTVenmoAnalytics.appSwitchSucceeded, linkType: linkType) + apiClient.sendAnalyticsEvent(BTVenmoAnalytics.appSwitchSucceeded, linkType: linkType, payPalContextID: payPalContextID) BTVenmoClient.venmoClient = self self.appSwitchCompletion = completion } else { - apiClient.sendAnalyticsEvent(BTVenmoAnalytics.appSwitchFailed, linkType: linkType) + apiClient.sendAnalyticsEvent(BTVenmoAnalytics.appSwitchFailed, linkType: linkType, payPalContextID: payPalContextID) notifyFailure(with: BTVenmoError.appSwitchFailed, completion: completion) } } @@ -416,7 +416,7 @@ import BraintreeCore with result: BTVenmoAccountNonce, completion: @escaping (BTVenmoAccountNonce?, Error?) -> Void ) { - apiClient.sendAnalyticsEvent(BTVenmoAnalytics.tokenizeSucceeded, payPalContextID: payPalContextID, linkType: linkType) + apiClient.sendAnalyticsEvent(BTVenmoAnalytics.tokenizeSucceeded, linkType: linkType, payPalContextID: payPalContextID) completion(result, nil) } @@ -424,14 +424,14 @@ import BraintreeCore apiClient.sendAnalyticsEvent( BTVenmoAnalytics.tokenizeFailed, errorDescription: error.localizedDescription, - payPalContextID: payPalContextID, - linkType: linkType + linkType: linkType, + payPalContextID: payPalContextID ) completion(nil, error) } private func notifyCancel(completion: @escaping (BTVenmoAccountNonce?, Error?) -> Void) { - apiClient.sendAnalyticsEvent(BTVenmoAnalytics.appSwitchCanceled, payPalContextID: payPalContextID, linkType: linkType) + apiClient.sendAnalyticsEvent(BTVenmoAnalytics.appSwitchCanceled, linkType: linkType, payPalContextID: payPalContextID) completion(nil, BTVenmoError.canceled) } } diff --git a/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift b/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift index 6a39596421..37e5938782 100644 --- a/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift +++ b/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift @@ -11,7 +11,6 @@ final class FPTIBatchData_Tests: XCTestCase { environment: "fake-env", integrationType: "fake-integration-type", merchantID: "fake-merchant-id", - payPalContextID: "fake-order-id", sessionID: "fake-session", tokenizationKey: "fake-auth" ) @@ -22,6 +21,7 @@ final class FPTIBatchData_Tests: XCTestCase { errorDescription: "fake-error-description-1", eventName: "fake-event-1", linkType: "universal", + payPalContextID: "fake-order-id", timestamp: "fake-time-1" ), FPTIBatchData.Event( @@ -29,6 +29,7 @@ final class FPTIBatchData_Tests: XCTestCase { errorDescription: nil, eventName: "fake-event-2", linkType: nil, + payPalContextID: "fake-order-id-2", timestamp: "fake-time-2" ) ] @@ -73,7 +74,6 @@ final class FPTIBatchData_Tests: XCTestCase { XCTAssertNotNil(batchParams["mapv"] as? String) // Unable to specify bundle version number within test targets XCTAssertTrue((batchParams["mobile_device_model"] as! String).matches("iPhone\\d,\\d|x86_64|arm64")) XCTAssertEqual(batchParams["merchant_id"] as! String, "fake-merchant-id") - XCTAssertEqual(batchParams["paypal_context_id"] as! String, "fake-order-id") XCTAssertEqual(batchParams["platform"] as? String, "iOS") XCTAssertEqual(batchParams["session_id"] as? String, "fake-session") XCTAssertEqual(batchParams["tokenization_key"] as! String, "fake-auth") @@ -87,6 +87,8 @@ final class FPTIBatchData_Tests: XCTestCase { XCTAssertEqual(eventParams[1]["tenant_name"] as? String, "Braintree") XCTAssertEqual(eventParams[0]["link_type"] as? String, "universal") XCTAssertNil(eventParams[1]["link_type"]) + XCTAssertEqual(eventParams[0]["paypal_context_id"] as! String, "fake-order-id") + XCTAssertEqual(eventParams[1]["paypal_context_id"] as! String, "fake-order-id-2") XCTAssertEqual(eventParams[0]["error_desc"] as? String, "fake-error-description-1") XCTAssertNil(eventParams[1]["error_desc"]) XCTAssertEqual(eventParams[0]["correlation_id"] as? String, "fake-correlation-id-1") diff --git a/UnitTests/BraintreeCoreTests/Analytics/FakeAnalyticsService.swift b/UnitTests/BraintreeCoreTests/Analytics/FakeAnalyticsService.swift index 12168ea7d4..eb87e6ab5c 100644 --- a/UnitTests/BraintreeCoreTests/Analytics/FakeAnalyticsService.swift +++ b/UnitTests/BraintreeCoreTests/Analytics/FakeAnalyticsService.swift @@ -6,10 +6,10 @@ class FakeAnalyticsService: BTAnalyticsService { override func sendAnalyticsEvent( _ eventName: String, - errorDescription: String? = nil, correlationID: String? = nil, - payPalContextID: String? = nil, - linkType: String? = nil + errorDescription: String? = nil, + linkType: String? = nil, + payPalContextID: String? = nil ) { self.lastEvent = eventName } diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index 323a9ea0b6..1af0a4a6c5 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -84,10 +84,10 @@ public class MockAPIClient: BTAPIClient { public override func sendAnalyticsEvent( _ name: String, - errorDescription: String? = nil, correlationID: String? = nil, - payPalContextID: String? = nil, - linkType: String? = nil + errorDescription: String? = nil, + linkType: String? = nil, + payPalContextID: String? = nil ) { postedPayPalContextID = payPalContextID postedLinkType = linkType