From 0d9fc66400991cc915ee0221937f5d14fdbaf34c Mon Sep 17 00:00:00 2001 From: Nick Porter Date: Thu, 28 Sep 2023 13:05:16 -0600 Subject: [PATCH 1/7] Start sending networks in request --- Stripe/StripeiOSTests/STPCardBrandTest.swift | 41 +++++++++++++++ .../STPPaymentMethodCardParamsTest.swift | 31 +++++++++++ .../CardSectionWithScannerElement.swift | 18 +++++-- .../Source/PaymentSheet/Intent.swift | 2 +- .../STPPaymentMethodCardNetworksParams.swift | 51 +++++++++++++++++++ .../Types/STPPaymentMethodCardParams.swift | 9 +++- .../API Bindings/Models/STPCardBrand.swift | 27 ++++++++++ 7 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift diff --git a/Stripe/StripeiOSTests/STPCardBrandTest.swift b/Stripe/StripeiOSTests/STPCardBrandTest.swift index 7abfc73dab2..4b3e512557b 100644 --- a/Stripe/StripeiOSTests/STPCardBrandTest.swift +++ b/Stripe/StripeiOSTests/STPCardBrandTest.swift @@ -49,4 +49,45 @@ class STPCardBrandTest: XCTestCase { } } } + + func testApiValueFromBrand() { + let brands = [ + STPCardBrand.visa, + STPCardBrand.amex, + STPCardBrand.mastercard, + STPCardBrand.discover, + STPCardBrand.JCB, + STPCardBrand.dinersClub, + STPCardBrand.unionPay, + STPCardBrand.cartesBancaires, + STPCardBrand.unknown, + ] + + for brand in brands { + let string = STPCardBrandUtilities.apiValue(from: brand) + + switch brand { + case .amex: + XCTAssertEqual(string, "american express") + case .dinersClub: + XCTAssertEqual(string, "diners club") + case .discover: + XCTAssertEqual(string, "discover") + case .JCB: + XCTAssertEqual(string, "jcb") + case .mastercard: + XCTAssertEqual(string, "mastercard") + case .unionPay: + XCTAssertEqual(string, "unionpay") + case .visa: + XCTAssertEqual(string, "visa") + case .cartesBancaires: + XCTAssertEqual(string, "cartes_bancaires") + case .unknown: + XCTAssertEqual(string, "unknown") + @unknown default: + break + } + } + } } diff --git a/Stripe/StripeiOSTests/STPPaymentMethodCardParamsTest.swift b/Stripe/StripeiOSTests/STPPaymentMethodCardParamsTest.swift index fcdadf81953..b684214e142 100644 --- a/Stripe/StripeiOSTests/STPPaymentMethodCardParamsTest.swift +++ b/Stripe/StripeiOSTests/STPPaymentMethodCardParamsTest.swift @@ -19,11 +19,13 @@ class STPPaymentMethodCardParamsTest: XCTestCase { params1.cvc = "123" params1.expYear = 22 params1.expMonth = 12 + params1.networks = .init(preferred: "visa") let params2 = STPPaymentMethodCardParams() params2.number = "4242424242424242" params2.cvc = "123" params2.expYear = 22 params2.expMonth = 12 + params2.networks = .init(preferred: "visa") XCTAssertEqual(params1, params2) params1.additionalAPIParameters["test"] = "bla" XCTAssertNotEqual(params1, params2) @@ -65,4 +67,33 @@ class STPPaymentMethodCardParamsTest: XCTestCase { XCTAssertEqual(cardParams.addressCountry, "US") XCTAssertEqual(cardParams.addressZip, "12345") } + + func testPropertyNamesToFormFieldsMapping() { + // Test for STPPaymentMethodCardParams + let cardParams = STPPaymentMethodCardParams() + + let cardParamsExpectedMapping = [ + "number": "number", + "expMonth": "exp_month", + "expYear": "exp_year", + "cvc": "cvc", + "token": "token", + "networks": "networks", + ] + + let cardParamsMapping = type(of: cardParams).propertyNamesToFormFieldNamesMapping() + + XCTAssertEqual(cardParamsMapping, cardParamsExpectedMapping) + + // Test for STPPaymentMethodCardNetworksParams + let networksParams = STPPaymentMethodCardNetworksParams() + + let networksParamsExpectedMapping = [ + "preferred": "preferred", + ] + + let networksParamsMapping = type(of: networksParams).propertyNamesToFormFieldNamesMapping() + + XCTAssertEqual(networksParamsMapping, networksParamsExpectedMapping) + } } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift index e53b4e1a3ff..380f1d4850a 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift @@ -72,12 +72,20 @@ final class CardSection: ContainerElement { return params } : nil - var cardBrandDropDown: DropdownFieldElement? + var cardBrandDropDown: PaymentMethodElementWrapper? if cardBrandChoiceEligible { - cardBrandDropDown = DropdownFieldElement.makeCardBrandDropdown(theme: theme) + cardBrandDropDown = PaymentMethodElementWrapper(DropdownFieldElement.makeCardBrandDropdown(theme: theme)) { field, params in + guard let cardBrandCaseIndex = Int(field.selectedItem.rawData), + let cardBrand: STPCardBrand = .init(rawValue: cardBrandCaseIndex) else { + return params + } + + cardParams(for: params).networks = STPPaymentMethodCardNetworksParams(preferred: STPCardBrandUtilities.apiValue(from: cardBrand)) + return params + } } let panElement = PaymentMethodElementWrapper(TextFieldElement.PANConfiguration(defaultValue: defaultValues.pan, - cardBrandDropDown: cardBrandDropDown), theme: theme) { field, params in + cardBrandDropDown: cardBrandDropDown?.element), theme: theme) { field, params in cardParams(for: params).number = field.text return params } @@ -108,7 +116,7 @@ final class CardSection: ContainerElement { let allSubElements: [Element?] = [ nameElement, - panElement, + panElement, cardBrandDropDown, SectionElement.MultiElementRow([expiryElement, cvcElement], theme: theme), ] let subElements = allSubElements.compactMap { $0 } @@ -120,7 +128,7 @@ final class CardSection: ContainerElement { self.nameElement = nameElement?.element self.panElement = panElement.element - self.cardBrandDropDown = cardBrandDropDown + self.cardBrandDropDown = cardBrandDropDown?.element self.cvcElement = cvcElement.element self.expiryElement = expiryElement.element cardSection.delegate = self diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift index 65aef475d0f..ca20dcc8845 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift @@ -124,7 +124,7 @@ enum Intent { switch self { case .paymentIntent(let paymentIntent): // TODO(porter) Remove enviorment check - return (paymentIntent.cardBrandChoice?.eligible ?? false) && ProcessInfo.processInfo.environment["ENABLE_CBC"] == "true" + return (paymentIntent.cardBrandChoice?.eligible ?? false)// && ProcessInfo.processInfo.environment["ENABLE_CBC"] == "true" case .setupIntent, .deferredIntent: // TODO(porter) We will support SI and DI's later. return false } diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift new file mode 100644 index 00000000000..e3482d20fa4 --- /dev/null +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift @@ -0,0 +1,51 @@ +// +// STPPaymentMethodCardNetworksParams.swift +// StripePayments +// +// Created by Nick Porter on 9/28/23. +// + +import Foundation + +public class STPPaymentMethodCardNetworksParams: NSObject, STPFormEncodable { + + @objc public var additionalAPIParameters: [AnyHashable: Any] = [:] + + /// The network that your user selected for this payment + /// method. This must reflect an explicit user choice. If your user didn't + /// make a selection, then pass `null`. + @objc public var preferred: String? + + @objc public convenience init(preferred: String?) { + self.init() + self.preferred = preferred + } + + + // MARK: - Description + /// :nodoc: + @objc public override var description: String { + let props = [ + // Object + String(format: "%@: %p", NSStringFromClass(STPPaymentMethodCardNetworksParams.self), self), + // Preferred + "preferred = \(preferred ?? "")" + ] + + return "<\(props.joined(separator: "; "))>" + } + + // MARK: - STPFormEncodable + + @objc + public class func rootObjectName() -> String? { + return "networks" + } + + @objc + public static func propertyNamesToFormFieldNamesMapping() -> [String : String] { + return [ + NSStringFromSelector(#selector(getter: preferred)): "preferred", + ] + } +} diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardParams.swift index 5ddcc41efc3..38165ed14cd 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardParams.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardParams.swift @@ -39,8 +39,10 @@ public class STPPaymentMethodCardParams: NSObject, STPFormEncodable { @objc public var token: String? /// Card security code. It is highly recommended to always include this value. @objc public var cvc: String? - /// The last 4 digits of the card. + /// Information about the networks to use with this payment method. + @objc public var networks: STPPaymentMethodCardNetworksParams? + /// The last 4 digits of the card. @objc public var last4: String? { if number != nil && (number?.count ?? 0) >= 4 { return (number as NSString?)?.substring(from: (number?.count ?? 0) - 4) ?? "" @@ -60,6 +62,7 @@ public class STPPaymentMethodCardParams: NSObject, STPFormEncodable { "expMonth = \(expMonth ?? 0)", "expYear = \(expYear ?? 0)", "cvc = \(((cvc) != nil ? "" : nil) ?? "")", + "networks = \(networks?.description ?? "")", // Token "token = \(token ?? "")", ] @@ -82,6 +85,7 @@ public class STPPaymentMethodCardParams: NSObject, STPFormEncodable { NSStringFromSelector(#selector(getter: expYear)): "exp_year", NSStringFromSelector(#selector(getter: cvc)): "cvc", NSStringFromSelector(#selector(getter: token)): "token", + NSStringFromSelector(#selector(getter: networks)): "networks", ] } @@ -93,6 +97,7 @@ public class STPPaymentMethodCardParams: NSObject, STPFormEncodable { copyCardParams.expMonth = expMonth copyCardParams.expYear = expYear copyCardParams.cvc = cvc + copyCardParams.networks = networks return copyCardParams } @@ -119,6 +124,6 @@ public class STPPaymentMethodCardParams: NSObject, STPFormEncodable { } return number == other?.number && expMonth == other?.expMonth && expYear == other?.expYear - && cvc == other?.cvc && token == other?.token + && cvc == other?.cvc && token == other?.token && networks?.preferred == other?.networks?.preferred } } diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift b/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift index e592a3ec2a5..c0bcea7be65 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift @@ -73,4 +73,31 @@ public class STPCardBrandUtilities: NSObject { } } + /// Returns brand API string value from given card brand. + /// + /// - Parameter brand: The `STPCardBrand` to transform into a string. + /// - Returns: A `String` representing the card brand. This could be "visa", + @objc(apiValueFromCardBrand:) public static func apiValue(from brand: STPCardBrand) -> String { + switch brand { + case .visa: + return "visa" + case .amex: + return "american express" + case .mastercard: + return "mastercard" + case .discover: + return "discover" + case .JCB: + return "jcb" + case .dinersClub: + return "diners club" + case .unionPay: + return "unionpay" + case .cartesBancaires: + return "cartes_bancaires" + default: + return "unknown" + } + } + } From 8b0932c7d7fc238d042a670c9e6b5424570cf935 Mon Sep 17 00:00:00 2001 From: Nick Porter Date: Mon, 2 Oct 2023 10:40:07 -0600 Subject: [PATCH 2/7] Add HiddenElement --- .../CardSectionWithScannerElement.swift | 2 +- .../Source/PaymentSheet/Intent.swift | 2 +- .../STPPaymentMethodCardNetworksParams.swift | 17 ++++++------ .../Elements/Section/SectionElement.swift | 27 ++++++++++++++++++- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift index 380f1d4850a..efabc25dad0 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Elements/CardSectionWithScanner/CardSectionWithScannerElement.swift @@ -116,7 +116,7 @@ final class CardSection: ContainerElement { let allSubElements: [Element?] = [ nameElement, - panElement, cardBrandDropDown, + panElement, SectionElement.HiddenElement(cardBrandDropDown), SectionElement.MultiElementRow([expiryElement, cvcElement], theme: theme), ] let subElements = allSubElements.compactMap { $0 } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift index ca20dcc8845..65aef475d0f 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Intent.swift @@ -124,7 +124,7 @@ enum Intent { switch self { case .paymentIntent(let paymentIntent): // TODO(porter) Remove enviorment check - return (paymentIntent.cardBrandChoice?.eligible ?? false)// && ProcessInfo.processInfo.environment["ENABLE_CBC"] == "true" + return (paymentIntent.cardBrandChoice?.eligible ?? false) && ProcessInfo.processInfo.environment["ENABLE_CBC"] == "true" case .setupIntent, .deferredIntent: // TODO(porter) We will support SI and DI's later. return false } diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift index e3482d20fa4..a9942435c2b 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodCardNetworksParams.swift @@ -8,20 +8,19 @@ import Foundation public class STPPaymentMethodCardNetworksParams: NSObject, STPFormEncodable { - + @objc public var additionalAPIParameters: [AnyHashable: Any] = [:] - + /// The network that your user selected for this payment /// method. This must reflect an explicit user choice. If your user didn't /// make a selection, then pass `null`. @objc public var preferred: String? - + @objc public convenience init(preferred: String?) { self.init() self.preferred = preferred } - - + // MARK: - Description /// :nodoc: @objc public override var description: String { @@ -29,21 +28,21 @@ public class STPPaymentMethodCardNetworksParams: NSObject, STPFormEncodable { // Object String(format: "%@: %p", NSStringFromClass(STPPaymentMethodCardNetworksParams.self), self), // Preferred - "preferred = \(preferred ?? "")" + "preferred = \(preferred ?? "")", ] return "<\(props.joined(separator: "; "))>" } - + // MARK: - STPFormEncodable @objc public class func rootObjectName() -> String? { return "networks" } - + @objc - public static func propertyNamesToFormFieldNamesMapping() -> [String : String] { + public static func propertyNamesToFormFieldNamesMapping() -> [String: String] { return [ NSStringFromSelector(#selector(getter: preferred)): "preferred", ] diff --git a/StripeUICore/StripeUICore/Source/Elements/Section/SectionElement.swift b/StripeUICore/StripeUICore/Source/Elements/Section/SectionElement.swift index faa067a985a..834088e8580 100644 --- a/StripeUICore/StripeUICore/Source/Elements/Section/SectionElement.swift +++ b/StripeUICore/StripeUICore/Source/Elements/Section/SectionElement.swift @@ -31,7 +31,7 @@ import UIKit } var viewModel: SectionViewModel { return ViewModel( - views: elements.map({ $0.view }), + views: elements.filter { !($0.view is HiddenElement.HiddenView) }.map({ $0.view }), // filter out hidden views to prevent showing the separator title: title, errorText: errorText, subLabel: subLabel, @@ -102,3 +102,28 @@ extension SectionElement: ElementDelegate { delegate?.didUpdate(element: self) } } + +// MARK: HiddenElement + +extension SectionElement { + /// A simple container element where the element's view is hidden + /// Useful when an element is a part of a section but it's view is embeded into another element + /// E.g. card brand drop down embedded into the PAN textfield + @_spi(STP) public final class HiddenElement: ContainerElement { + final class HiddenView: UIView {} + + weak public var delegate: ElementDelegate? + public lazy var view: UIView = { + return HiddenView(frame: .zero) // Hide the element's view + }() + public let elements: [Element] + + public init?(_ element: Element?) { + guard let element = element else { + return nil + } + self.elements = [element] + element.delegate = self + } + } +} From 8808e929641715ca61488c61f214bb73e69a3134 Mon Sep 17 00:00:00 2001 From: Nick Porter Date: Mon, 2 Oct 2023 13:02:17 -0600 Subject: [PATCH 3/7] Fix auto advance behavior for DropDownFieldElement https://github.com/stripe/stripe-ios/issues/2936 --- .../StripeUICore/Source/Elements/DropdownFieldElement.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/StripeUICore/StripeUICore/Source/Elements/DropdownFieldElement.swift b/StripeUICore/StripeUICore/Source/Elements/DropdownFieldElement.swift index 16c0a8eac56..2c3e5d06c64 100644 --- a/StripeUICore/StripeUICore/Source/Elements/DropdownFieldElement.swift +++ b/StripeUICore/StripeUICore/Source/Elements/DropdownFieldElement.swift @@ -254,7 +254,10 @@ extension DropdownFieldElement: PickerFieldViewDelegate { didUpdate?(selectedIndex) } previouslySelectedIndex = selectedIndex - delegate?.continueToNextField(element: self) + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + self.delegate?.continueToNextField(element: self) + } } func didCancel(_ pickerFieldView: PickerFieldView) { From 7d9a8ca02c24585aa6105d0bc6ada96cb2f50d42 Mon Sep 17 00:00:00 2001 From: Nick Porter Date: Mon, 2 Oct 2023 13:05:34 -0600 Subject: [PATCH 4/7] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 591b4318793..fdca851062e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## x.x.x x-x-x +### PaymentSheet +* [Fixed] Fixed an issue when advancing from the country dropdown that prevented user's' from typing in their postal code. (#2936) + ## 23.17.0 2023-10-02 ### PaymentSheet * [Fixed] Fixed an issue with selecting from lists on macOS Catalyst. Note that only macOS 11 or later is supported: We do not recommend releasing a Catalyst app targeting macOS 10.15. From 1f87818b552e16bb1333bb75ecde82243429428c Mon Sep 17 00:00:00 2001 From: Nick Porter Date: Mon, 2 Oct 2023 13:52:51 -0600 Subject: [PATCH 5/7] Fix API value for card brands --- Stripe/StripeiOSTests/STPCardBrandTest.swift | 4 ++-- .../Source/API Bindings/Models/STPCardBrand.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Stripe/StripeiOSTests/STPCardBrandTest.swift b/Stripe/StripeiOSTests/STPCardBrandTest.swift index 4b3e512557b..4584e9ee4ab 100644 --- a/Stripe/StripeiOSTests/STPCardBrandTest.swift +++ b/Stripe/StripeiOSTests/STPCardBrandTest.swift @@ -68,9 +68,9 @@ class STPCardBrandTest: XCTestCase { switch brand { case .amex: - XCTAssertEqual(string, "american express") + XCTAssertEqual(string, "american_express") case .dinersClub: - XCTAssertEqual(string, "diners club") + XCTAssertEqual(string, "diners_club") case .discover: XCTAssertEqual(string, "discover") case .JCB: diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift b/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift index c0bcea7be65..227e3b66935 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/STPCardBrand.swift @@ -82,7 +82,7 @@ public class STPCardBrandUtilities: NSObject { case .visa: return "visa" case .amex: - return "american express" + return "american_express" case .mastercard: return "mastercard" case .discover: @@ -90,7 +90,7 @@ public class STPCardBrandUtilities: NSObject { case .JCB: return "jcb" case .dinersClub: - return "diners club" + return "diners_club" case .unionPay: return "unionpay" case .cartesBancaires: From b33c668a5cbad46fcc5ded5d2a5a506be756415a Mon Sep 17 00:00:00 2001 From: Nick Porter Date: Mon, 2 Oct 2023 14:09:28 -0600 Subject: [PATCH 6/7] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdca851062e..e709fb6a26e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## x.x.x x-x-x ### PaymentSheet -* [Fixed] Fixed an issue when advancing from the country dropdown that prevented user's' from typing in their postal code. (#2936) +* [Fixed] Fixed an issue when advancing from the country dropdown that prevented user's' from typing in their postal code. ([#2936](https://github.com/stripe/stripe-ios/issues/2936)) ## 23.17.0 2023-10-02 ### PaymentSheet From e056334206b0bf10e266fcbd6ea3602834fd29f2 Mon Sep 17 00:00:00 2001 From: Nick Porter <88012362+porter-stripe@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:21:26 -0700 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a8532b0fb..3be7c240995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,6 @@ ### PaymentsUI * [Fixed] An issue with `STPPaymentCardTextField`, where the `paymentCardTextFieldDidChange` delegate method wasn't being called after deleting an empty sub field. - ## 23.17.0 2023-10-02 ### PaymentSheet * [Fixed] Fixed an issue with selecting from lists on macOS Catalyst. Note that only macOS 11 or later is supported: We do not recommend releasing a Catalyst app targeting macOS 10.15.