diff --git a/Stripe/AddPaymentMethodViewController.swift b/Stripe/AddPaymentMethodViewController.swift index 85f7d0becee..6e163856e35 100644 --- a/Stripe/AddPaymentMethodViewController.swift +++ b/Stripe/AddPaymentMethodViewController.swift @@ -46,6 +46,15 @@ class AddPaymentMethodViewController: UIViewController { PaymentSheet.supportedLinkPaymentMethods : PaymentSheet.supportedPaymentMethods ) } + + let serverFilteredPaymentMethods = PaymentSheet.PaymentMethodType.recommendedPaymentMethodTypes(from: intent).filter({$0 != .USBankAccount}) + let paymentTypesFiltered = paymentTypes.filter({$0 != .USBankAccount}) + if serverFilteredPaymentMethods != paymentTypesFiltered { + let result = serverFilteredPaymentMethods.symmetricDifference(paymentTypes) + STPAnalyticsClient.sharedClient.logClientFilteredPaymentMethods(clientFilteredPaymentMethods: result.stringList()) + } else { + STPAnalyticsClient.sharedClient.logClientFilteredPaymentMethodsNone() + } return paymentTypes }() var selectedPaymentMethodType: PaymentSheet.PaymentMethodType { diff --git a/Stripe/PaymentMethodType.swift b/Stripe/PaymentMethodType.swift index 42eba83c552..11da32acf89 100644 --- a/Stripe/PaymentMethodType.swift +++ b/Stripe/PaymentMethodType.swift @@ -10,7 +10,7 @@ import UIKit @_spi(STP) import StripeCore @_spi(STP) import StripeUICore extension PaymentSheet { - public enum PaymentMethodType: Equatable { + public enum PaymentMethodType: Equatable, Hashable { func supportsAddingRequirements() -> [PaymentMethodTypeRequirement] { switch(self) { @@ -232,3 +232,22 @@ extension STPPaymentMethodParams { } } } + +extension Array where Element == PaymentSheet.PaymentMethodType { + func stringList() -> String { + var stringList: [String] = [] + for paymentType in self { + let type = PaymentSheet.PaymentMethodType.string(from: paymentType) ?? "unknown" + stringList.append(type) + } + guard let data = try? JSONSerialization.data(withJSONObject: stringList, options: []) else { + return "[]" + } + return String(data: data, encoding: .utf8) ?? "[]" + } + func symmetricDifference(_ other: Array) -> Array where Element == PaymentSheet.PaymentMethodType { + let set1 = Set(self) + let set2 = Set(other) + return Array(set1.symmetricDifference(set2)) + } +} diff --git a/Stripe/PaymentSheet+API.swift b/Stripe/PaymentSheet+API.swift index da48f1ba4a3..09eea7c5fc8 100644 --- a/Stripe/PaymentSheet+API.swift +++ b/Stripe/PaymentSheet+API.swift @@ -311,8 +311,10 @@ extension PaymentSheet { } intentPromise.resolve(with: .paymentIntent(paymentIntent)) } - - configuration.apiClient.retrievePaymentIntentWithPreferences(withClientSecret: clientSecret) { result in + let additionalParameters = ["merchant_support_async": configuration.allowsDelayedPaymentMethods] + configuration.apiClient.retrievePaymentIntentWithPreferences( + withClientSecret: clientSecret, + additionalParameters: additionalParameters) { result in switch result { case .success(let paymentIntent): paymentIntentHandlerCompletionBlock(paymentIntent) diff --git a/Stripe/STPAPIClient+Payments.swift b/Stripe/STPAPIClient+Payments.swift index 927be9f4f3f..1ac5de05fd1 100644 --- a/Stripe/STPAPIClient+Payments.swift +++ b/Stripe/STPAPIClient+Payments.swift @@ -963,6 +963,7 @@ extension STPAPIClient { func retrievePaymentIntentWithPreferences( withClientSecret secret: String, + additionalParameters: [String: Any] = [:], completion: @escaping STPPaymentIntentWithPreferencesCompletionBlock ) { var parameters: [String: Any] = [:] @@ -975,6 +976,9 @@ extension STPAPIClient { parameters["client_secret"] = secret parameters["type"] = "payment_intent" parameters["expand"] = ["payment_method_preference.payment_intent.payment_method"] + for (apKey, apValue) in additionalParameters { + parameters[apKey] = apValue + } if let languageCode = Locale.current.languageCode, let regionCode = Locale.current.regionCode { diff --git a/Stripe/STPAnalyticsClient+LUXE.swift b/Stripe/STPAnalyticsClient+LUXE.swift index 6b5ba9f4010..3d5e69434aa 100644 --- a/Stripe/STPAnalyticsClient+LUXE.swift +++ b/Stripe/STPAnalyticsClient+LUXE.swift @@ -11,4 +11,10 @@ extension STPAnalyticsClient { func logLUXESerializeFailure() { self.logPaymentSheetEvent(event: .luxeSerializeFailure) } + func logClientFilteredPaymentMethods(clientFilteredPaymentMethods: String) { + self.logPaymentSheetEvent(event: .luxeClientFilteredPaymentMethods, params: ["client_filtered_payment_methods": clientFilteredPaymentMethods]) + } + func logClientFilteredPaymentMethodsNone() { + self.logPaymentSheetEvent(event: .luxeClientFilteredPaymentMethodsNone) + } } diff --git a/Stripe/STPAnalyticsClient+PaymentSheet.swift b/Stripe/STPAnalyticsClient+PaymentSheet.swift index 9cecfd14525..807e8f214c2 100644 --- a/Stripe/STPAnalyticsClient+PaymentSheet.swift +++ b/Stripe/STPAnalyticsClient+PaymentSheet.swift @@ -214,7 +214,8 @@ extension STPAnalyticsClient { duration: TimeInterval? = nil, linkEnabled: Bool? = nil, activeLinkSession: Bool? = nil, - configuration: PaymentSheet.Configuration? = nil + configuration: PaymentSheet.Configuration? = nil, + params: [String: Any] = [:] ) { var additionalParams = [:] as [String: Any] if isSimulatorOrTest { @@ -226,7 +227,9 @@ extension STPAnalyticsClient { additionalParams["active_link_session"] = activeLinkSession additionalParams["session_id"] = AnalyticsHelper.shared.sessionID additionalParams["mpe_config"] = configuration?.analyticPayload - + for (param, param_value) in params { + additionalParams[param] = param_value + } let analytic = PaymentSheetAnalytic(event: event, paymentConfiguration: nil, productUsage: productUsage, diff --git a/StripeCore/StripeCore/Source/Analytics/STPAnalyticEvent.swift b/StripeCore/StripeCore/Source/Analytics/STPAnalyticEvent.swift index e1ba948b35e..d98249ab356 100644 --- a/StripeCore/StripeCore/Source/Analytics/STPAnalyticEvent.swift +++ b/StripeCore/StripeCore/Source/Analytics/STPAnalyticEvent.swift @@ -121,7 +121,9 @@ import Foundation // MARK: - LUXE case luxeSerializeFailure = "luxe_serialize_failure" - + case luxeClientFilteredPaymentMethods = "luxe_client_filtered_payment_methods" + case luxeClientFilteredPaymentMethodsNone = "luxe_client_filtered_payment_methods_none" + // MARK: Address Element case adddressShow = "mc_address_show" case addressCompleted = "mc_addres_completed" diff --git a/Tests/Tests/PaymentSheetPaymentMethodTypeTest.swift b/Tests/Tests/PaymentSheetPaymentMethodTypeTest.swift index 35a1ee0963e..5556c542c11 100644 --- a/Tests/Tests/PaymentSheetPaymentMethodTypeTest.swift +++ b/Tests/Tests/PaymentSheetPaymentMethodTypeTest.swift @@ -187,6 +187,53 @@ class PaymentSheetPaymentMethodTypeTest: XCTestCase { configuration: configuration, intent: intent)) } + func testArrayToString() { + let paymentMethodTypes: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay")] + + let strList = paymentMethodTypes.stringList() + + XCTAssertEqual(strList, "[\"card\",\"llammaPay\"]") + } + func testArrayToString_empty() { + let paymentMethodTypes: [PaymentSheet.PaymentMethodType] = [] + + let strList = paymentMethodTypes.stringList() + + XCTAssertEqual(strList, "[]") + } + func testSymmetricDifference_same() { + let paymentMethodTypes1: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay")] + let paymentMethodTypes2: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay")] + + let result = paymentMethodTypes1.symmetricDifference(paymentMethodTypes2) + + XCTAssertEqual(result, []) + } + func testSymmetricDifference_difference1() { + let paymentMethodTypes1: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay"), .dynamic("wechatpay")] + let paymentMethodTypes2: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay")] + + let result = paymentMethodTypes1.symmetricDifference(paymentMethodTypes2) + + XCTAssertEqual(result, [.dynamic("wechatpay")]) + } + func testSymmetricDifference_difference2() { + let paymentMethodTypes1: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay")] + let paymentMethodTypes2: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay"), .dynamic("wechatpay")] + + let result = paymentMethodTypes1.symmetricDifference(paymentMethodTypes2) + + XCTAssertEqual(result, [.dynamic("wechatpay")]) + } + func testSymmetricDifference_differenceInBoth() { + let paymentMethodTypes1: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay"), .dynamic("wechatpay")] + let paymentMethodTypes2: [PaymentSheet.PaymentMethodType] = [.card, .dynamic("llammaPay"), .dynamic("affirm")] + + let result = paymentMethodTypes1.symmetricDifference(paymentMethodTypes2) + + XCTAssertTrue(result == [.dynamic("wechatpay"), .dynamic("affirm")] || + result == [.dynamic("affirm"), .dynamic("wechatpay")]) + } private func constructPI(paymentMethodTypes: [String],