diff --git a/Example/Basic Integration/CheckoutViewController.swift b/Example/Basic Integration/CheckoutViewController.swift
index 39d6f5cd732..c5caa08057e 100644
--- a/Example/Basic Integration/CheckoutViewController.swift
+++ b/Example/Basic Integration/CheckoutViewController.swift
@@ -84,6 +84,7 @@ class CheckoutViewController: UIViewController {
config.requiredShippingAddressFields = settings.requiredShippingAddressFields
config.shippingType = settings.shippingType
config.additionalPaymentOptions = settings.additionalPaymentOptions
+ config.cardScanningEnabled = true
self.country = settings.country
self.paymentCurrency = settings.currency
diff --git a/Example/UI Examples/BrowseViewController.swift b/Example/UI Examples/BrowseViewController.swift
index 9921ef3eee8..d8e6d8e8e33 100644
--- a/Example/UI Examples/BrowseViewController.swift
+++ b/Example/UI Examples/BrowseViewController.swift
@@ -93,6 +93,7 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
present(navigationController, animated: true, completion: nil)
case .STPAddCardViewController:
let config = STPPaymentConfiguration()
+ config.cardScanningEnabled = true
let viewController = STPAddCardViewController(configuration: config, theme: theme)
viewController.apiClient = MockAPIClient()
viewController.delegate = self
@@ -101,6 +102,7 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
present(navigationController, animated: true, completion: nil)
case .STPAddCardViewControllerWithAddress:
let config = STPPaymentConfiguration()
+ config.cardScanningEnabled = true
config.requiredBillingAddressFields = .full
let viewController = STPAddCardViewController(configuration: config, theme: theme)
viewController.apiClient = MockAPIClient()
@@ -113,6 +115,7 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
config.additionalPaymentOptions = [.default, .FPX]
config.requiredBillingAddressFields = .none
config.appleMerchantIdentifier = "dummy-merchant-id"
+ config.cardScanningEnabled = true
let viewController = STPPaymentOptionsViewController(configuration: config,
theme: theme,
customerContext: self.customerContext,
@@ -126,6 +129,7 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
config.additionalPaymentOptions = .default
config.requiredBillingAddressFields = .none
config.appleMerchantIdentifier = "dummy-merchant-id"
+ config.cardScanningEnabled = true
let viewController = STPPaymentOptionsViewController(configuration: config,
theme: theme,
customerContext: self.customerContext,
diff --git a/Example/UI Examples/Info.plist b/Example/UI Examples/Info.plist
index 4222ac2dd31..3783cf6d5d8 100644
--- a/Example/UI Examples/Info.plist
+++ b/Example/UI Examples/Info.plist
@@ -22,6 +22,8 @@
UILaunchStoryboardName
LaunchScreen
+ NSCameraUsageDescription
+ Granting access to your camera will allow you to scan payment cards.
UIRequiredDeviceCapabilities
armv7
diff --git a/LocalizationTester/ViewController.m b/LocalizationTester/ViewController.m
index 8ea30e35f28..0a942584b97 100644
--- a/LocalizationTester/ViewController.m
+++ b/LocalizationTester/ViewController.m
@@ -156,7 +156,6 @@ - (void)tableView:(__unused UITableView *)tableView didSelectRowAtIndexPath:(__u
STPPaymentConfiguration *configuration = [[STPPaymentConfiguration alloc] init];
configuration.requiredBillingAddressFields = STPBillingAddressFieldsFull;
STPAddCardViewController *addCardVC = [[STPAddCardViewController alloc] initWithConfiguration:configuration theme:[STPTheme defaultTheme]];
- addCardVC.alwaysShowScanCardButton = YES;
addCardVC.alwaysEnableDoneButton = YES;
addCardVC.delegate = self;
vc = addCardVC;
diff --git a/README.md b/README.md
index 3f89941cd96..35c9bf49e78 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Table of contents
* [Getting Started](#getting-started)
* [Integration](#integration)
* [Examples](#examples)
- * [Card IO](#card-io)
+ * [Card scanning](#card-scanning-beta)
* [Contributing](#contributing)
* [Migrating](#migrating-from-older-versions)
@@ -51,7 +51,7 @@ You can use these individually, or take all of the prebuilt UI in one flow by fo
From left to right: [STPAddCardViewController](https://stripe.dev/stripe-ios/docs/Classes/STPAddCardViewController.html), [STPPaymentOptionsViewController](https://stripe.dev/stripe-ios/docs/Classes/STPPaymentOptionsViewController.html), [STPShippingAddressViewController](https://stripe.dev/stripe-ios/docs/Classes/STPShippingAddressViewController.html)
-**Card Scanning**: We support card scanning capabilities using card.io. See our [Card IO](#card-io) section.
+**Card scanning**: We support card scanning on iOS 13 and higher. See our [Card scanning](#card-scanning-beta) section.
## Releases
@@ -89,11 +89,13 @@ Check out [stripe-samples](https://github.com/stripe-samples/) for more, includi
- [Accepting a card payment](https://github.com/stripe-samples/card-payment-charges-api) (Charges API)
-## Card IO
+## Card scanning (Beta)
-To add card scanning capabilities to our prebuilt UI components, [install card.io](https://github.com/card-io/card.io-iOS-SDK#setup) alongside our SDK. You'll also need to set `NSCameraUsageDescription` in your application's plist, and provide a reason for accessing the camera (e.g. "To scan cards").
+To add card scanning capabilities to our prebuilt UI components, set the `cardScanningEnabled` option on your `STPPaymentConfiguration`. You'll also need to set `NSCameraUsageDescription` in your application's plist, and provide a reason for accessing the camera (e.g. "To scan cards"). Card scanning is supported on devices with iOS 13 or higher.
-Demo this in our [Basic Integration example app](https://github.com/stripe/stripe-ios/tree/v19.4.0/Example/Basic&20Integration) by running `./install_cardio.rb`, which will download and install card.io in the project. Now, when you run the example app on a device, you'll see a "Scan Card" button when adding a new card.
+Demo this in our [Basic Integration example app](https://github.com/stripe/stripe-ios/tree/v19.4.0/Example/Basic%20Integration). When you run the example app on a device, you'll see a "Scan Card" button when adding a new card.
+
+This feature is currently in beta. Please file bugs on our [GitHub issues page](https://github.com/stripe/stripe-ios/issues).
## Contributing
diff --git a/Stripe.xcodeproj/project.pbxproj b/Stripe.xcodeproj/project.pbxproj
index fcf6383699b..f85ba9955df 100644
--- a/Stripe.xcodeproj/project.pbxproj
+++ b/Stripe.xcodeproj/project.pbxproj
@@ -366,6 +366,18 @@
0731329D2277ABF40019CE3F /* STPPinManagementService.h in Headers */ = {isa = PBXBuildFile; fileRef = 0731328D2277A3F60019CE3F /* STPPinManagementService.h */; settings = {ATTRIBUTES = (Public, ); }; };
0731329E2277ABF60019CE3F /* STPPinManagementService.m in Sources */ = {isa = PBXBuildFile; fileRef = 0731328E2277A3F60019CE3F /* STPPinManagementService.m */; };
0731329F227CFE410019CE3F /* STPIssuingCardPin.h in Headers */ = {isa = PBXBuildFile; fileRef = 073132932277A72D0019CE3F /* STPIssuingCardPin.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 311A475624EB1D2300576D92 /* STPCardScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 311A475424EB1D2300576D92 /* STPCardScanner.h */; };
+ 311A475724EB1D2300576D92 /* STPCardScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 311A475424EB1D2300576D92 /* STPCardScanner.h */; };
+ 311A475824EB1D2300576D92 /* STPCardScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 311A475524EB1D2300576D92 /* STPCardScanner.m */; };
+ 311A475924EB1D2300576D92 /* STPCardScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 311A475524EB1D2300576D92 /* STPCardScanner.m */; };
+ 311A475C24EB21AE00576D92 /* STPCameraView.h in Headers */ = {isa = PBXBuildFile; fileRef = 311A475A24EB21AE00576D92 /* STPCameraView.h */; };
+ 311A475D24EB21AE00576D92 /* STPCameraView.h in Headers */ = {isa = PBXBuildFile; fileRef = 311A475A24EB21AE00576D92 /* STPCameraView.h */; };
+ 311A475E24EB21AE00576D92 /* STPCameraView.m in Sources */ = {isa = PBXBuildFile; fileRef = 311A475B24EB21AE00576D92 /* STPCameraView.m */; };
+ 311A475F24EB21AE00576D92 /* STPCameraView.m in Sources */ = {isa = PBXBuildFile; fileRef = 311A475B24EB21AE00576D92 /* STPCameraView.m */; };
+ 311A476224EB27D000576D92 /* STPCardScannerTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 311A476024EB27D000576D92 /* STPCardScannerTableViewCell.h */; };
+ 311A476324EB27D000576D92 /* STPCardScannerTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 311A476024EB27D000576D92 /* STPCardScannerTableViewCell.h */; };
+ 311A476424EB27D000576D92 /* STPCardScannerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 311A476124EB27D000576D92 /* STPCardScannerTableViewCell.m */; };
+ 311A476524EB27D000576D92 /* STPCardScannerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 311A476124EB27D000576D92 /* STPCardScannerTableViewCell.m */; };
3142E72722FCC19800DDA097 /* STPFPXBankBrand.h in Headers */ = {isa = PBXBuildFile; fileRef = 31F5B33322FCAC4000A71C64 /* STPFPXBankBrand.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A532384A713001FE708 /* STPKlarnaLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 314B6A512384A713001FE708 /* STPKlarnaLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
314B6A542384A713001FE708 /* STPKlarnaLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 314B6A512384A713001FE708 /* STPKlarnaLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1190,10 +1202,6 @@
C1717DB11CC00ED60009CF4A /* STPAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = C1080F471CBECF7B007B2D89 /* STPAddress.h */; settings = {ATTRIBUTES = (Public, ); }; };
C175B7941FE834A3009F5A0E /* STPCustomer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C175B7931FE834A3009F5A0E /* STPCustomer+Private.h */; };
C175B7951FE834A3009F5A0E /* STPCustomer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C175B7931FE834A3009F5A0E /* STPCustomer+Private.h */; };
- C1785F5C1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */; };
- C1785F5D1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */; };
- C1785F5E1EC60B5E00E9CFAC /* STPCardIOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C1785F5B1EC60B5E00E9CFAC /* STPCardIOProxy.m */; };
- C1785F5F1EC60B5E00E9CFAC /* STPCardIOProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C1785F5B1EC60B5E00E9CFAC /* STPCardIOProxy.m */; };
C17A030D1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */; };
C17A030E1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C17A030C1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m */; };
C17D24EE1E37DBAC005CB188 /* STPSourceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C17D24ED1E37DBAC005CB188 /* STPSourceTest.m */; };
@@ -1775,6 +1783,12 @@
073132942277A72D0019CE3F /* STPIssuingCardPin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPIssuingCardPin.m; sourceTree = ""; };
0731329A2277AA200019CE3F /* STPPinManagementServiceFunctionalTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPinManagementServiceFunctionalTest.m; sourceTree = ""; };
11C74B9B164043050071C2CA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 311A475424EB1D2300576D92 /* STPCardScanner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STPCardScanner.h; sourceTree = ""; };
+ 311A475524EB1D2300576D92 /* STPCardScanner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPCardScanner.m; sourceTree = ""; };
+ 311A475A24EB21AE00576D92 /* STPCameraView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STPCameraView.h; sourceTree = ""; };
+ 311A475B24EB21AE00576D92 /* STPCameraView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPCameraView.m; sourceTree = ""; };
+ 311A476024EB27D000576D92 /* STPCardScannerTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STPCardScannerTableViewCell.h; sourceTree = ""; };
+ 311A476124EB27D000576D92 /* STPCardScannerTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPCardScannerTableViewCell.m; sourceTree = ""; };
3142E72922FCC26500DDA097 /* stp_bank_fpx_hong_leong_bank@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_bank_fpx_hong_leong_bank@2x.png"; sourceTree = ""; };
3142E72A22FCC26600DDA097 /* stp_bank_fpx_public_bank@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_bank_fpx_public_bank@2x.png"; sourceTree = ""; };
3142E72C22FCC26600DDA097 /* stp_bank_fpx_maybank2e@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_bank_fpx_maybank2e@2x.png"; sourceTree = ""; };
@@ -2315,8 +2329,6 @@
C15B02721EA176090026E606 /* StripeErrorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StripeErrorTest.m; sourceTree = ""; };
C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPFormTextFieldTest.m; sourceTree = ""; };
C175B7931FE834A3009F5A0E /* STPCustomer+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPCustomer+Private.h"; sourceTree = ""; };
- C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPCardIOProxy.h; sourceTree = ""; };
- C1785F5B1EC60B5E00E9CFAC /* STPCardIOProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPCardIOProxy.m; sourceTree = ""; };
C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPAddressFieldTableViewCell.h; sourceTree = ""; };
C17A030C1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddressFieldTableViewCell.m; sourceTree = ""; };
C17D24ED1E37DBAC005CB188 /* STPSourceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourceTest.m; sourceTree = ""; };
@@ -3611,6 +3623,10 @@
F1728CEC1EAAA2C3002E0C29 /* UI Components */ = {
isa = PBXGroup;
children = (
+ 311A475A24EB21AE00576D92 /* STPCameraView.h */,
+ 311A475B24EB21AE00576D92 /* STPCameraView.m */,
+ 311A475424EB1D2300576D92 /* STPCardScanner.h */,
+ 311A475524EB1D2300576D92 /* STPCardScanner.m */,
04BC29A21CD8697900318357 /* STPTheme.h */,
04BC29A31CD8697900318357 /* STPTheme.m */,
04B31DF71D11AC6400EF1631 /* STPUserInformation.h */,
@@ -3669,8 +3685,6 @@
366658C32411934900D00354 /* STPBSBNumberValidator.m */,
F12829D81D7747E4008B10D6 /* STPBundleLocator.h */,
F12829D91D7747E4008B10D6 /* STPBundleLocator.m */,
- C1785F5A1EC60B5E00E9CFAC /* STPCardIOProxy.h */,
- C1785F5B1EC60B5E00E9CFAC /* STPCardIOProxy.m */,
3691EB6F2119111A008C49E1 /* STPCardValidator+Private.h */,
3691EB702119111A008C49E1 /* STPCardValidator+Private.m */,
04FCFA171BD59A8C00297732 /* STPCategoryLoader.h */,
@@ -3949,6 +3963,8 @@
C17A030C1CBEE7A2006C819F /* STPAddressFieldTableViewCell.m */,
31B9609522FE20DF00DC6FD9 /* STPBankSelectionTableViewCell.h */,
31B9609422FE20DE00DC6FD9 /* STPBankSelectionTableViewCell.m */,
+ 311A476024EB27D000576D92 /* STPCardScannerTableViewCell.h */,
+ 311A476124EB27D000576D92 /* STPCardScannerTableViewCell.m */,
C1363BB51D7633D800EB82B4 /* STPPaymentOptionTableViewCell.h */,
C1363BB61D7633D800EB82B4 /* STPPaymentOptionTableViewCell.m */,
04B31DFD1D131D9000EF1631 /* STPPaymentCardTextFieldCell.h */,
@@ -3991,6 +4007,7 @@
04F94DA11D229F12004FC826 /* STPAddressFieldTableViewCell.h in Headers */,
B613DD3822C5452600C7603F /* STPSetupIntentEnums.h in Headers */,
B6027BC32230ABAE0025DB29 /* STPPaymentMethodCardParams.h in Headers */,
+ 311A475724EB1D2300576D92 /* STPCardScanner.h in Headers */,
B69CFB462236F8E3001E9885 /* STPPaymentMethodCardPresent.h in Headers */,
B61D4B922457671F001AEBEF /* STPPaymentIntentShippingDetails.h in Headers */,
319A609C22E9186B00AACF66 /* STDSThreeDSProtocolVersion.h in Headers */,
@@ -4022,7 +4039,6 @@
F1D3A2661EBA5BAE0095BFA9 /* STPPaymentCardTextField+Private.h in Headers */,
F1DEB8911E2052150066B8E8 /* STPCoreScrollViewController.h in Headers */,
36B6CB9D235A3CF300331C38 /* STPMandateCustomerAcceptanceParams.h in Headers */,
- C1785F5D1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */,
B6E2F309222F442E0001FED4 /* STPPaymentMethodCardChecks.h in Headers */,
319A608D22E9186B00AACF66 /* STDSFooterCustomization.h in Headers */,
C1363BB81D7633D800EB82B4 /* STPPaymentOptionTableViewCell.h in Headers */,
@@ -4050,12 +4066,14 @@
36854421242977DA000B0B4B /* STPMultiFormTextField.h in Headers */,
04F94DCB1D22A229004FC826 /* UIView+Stripe_FirstResponder.h in Headers */,
319A609122E9186B00AACF66 /* STDSJSONEncoder.h in Headers */,
+ 311A476324EB27D000576D92 /* STPCardScannerTableViewCell.h in Headers */,
319A608222E9186B00AACF66 /* STDSAlreadyInitializedException.h in Headers */,
04F94DD11D22A239004FC826 /* STPPromise.h in Headers */,
0413CB2F233FECD5006429EA /* STPPushProvisioningDetails.h in Headers */,
C1BD9B351E3940C400CEE925 /* STPSourceVerification.h in Headers */,
36E582FD22B456760044F82C /* STPAuthenticationContext.h in Headers */,
F1A0197D1EA5733200354301 /* STPSourceParams+Private.h in Headers */,
+ 311A475D24EB21AE00576D92 /* STPCameraView.h in Headers */,
04633B161CD45222009D4FB5 /* STPAPIClient+ApplePay.h in Headers */,
0413CB2B233FECD5006429EA /* STPPushProvisioningContext.h in Headers */,
0438EF2E1B7416BB00D506CC /* STPFormTextField.h in Headers */,
@@ -4294,6 +4312,7 @@
36B6CB6A23551A0200331C38 /* STPPaymentMethodSEPADebitParams.h in Headers */,
B690DDEC222F01BF000B902D /* STPPaymentMethodBillingDetails.h in Headers */,
04695AD91C77F9EF00E08063 /* STPDelegateProxy.h in Headers */,
+ 311A475C24EB21AE00576D92 /* STPCameraView.h in Headers */,
31F5B33522FCAC4000A71C64 /* STPFPXBankBrand.h in Headers */,
B613DD3222C536C900C7603F /* STPSetupIntent.h in Headers */,
0433EB491BD06313003912B4 /* NSDictionary+Stripe.h in Headers */,
@@ -4351,7 +4370,6 @@
04CDB5121A5F30A700B854EE /* STPToken.h in Headers */,
8BD87B921EFB1C1E00269C2B /* STPSourceVerification+Private.h in Headers */,
B69CABB4246DC2AC0081B1EF /* STPPaymentMethodAlipayParams.h in Headers */,
- C1785F5C1EC60B5E00E9CFAC /* STPCardIOProxy.h in Headers */,
049952CF1BCF13510088C703 /* STPAPIRequest.h in Headers */,
C15993381D8808680047950D /* STPShippingMethodTableViewCell.h in Headers */,
049A3FB21CC9FEFC00F57DE7 /* UIToolbar+Stripe_InputAccessory.h in Headers */,
@@ -4418,6 +4436,7 @@
315CB8D722E7D96100E612A3 /* STDSJSONEncoder.h in Headers */,
448895A82452452600F7D0C2 /* STPPaymentMethodPrzelewy24Params.h in Headers */,
31F5A50B22F0EFB10033663B /* STPPaymentMethodFPX.h in Headers */,
+ 311A476224EB27D000576D92 /* STPCardScannerTableViewCell.h in Headers */,
3626617F23C908BA00B13AE0 /* STPConfirmPaymentMethodOptions.h in Headers */,
315CB8D822E7D96100E612A3 /* STDSChallengeParameters.h in Headers */,
315CB8D922E7D96100E612A3 /* STDSJSONDecodable.h in Headers */,
@@ -4471,6 +4490,7 @@
3604006F22C18C78004CF80B /* STPThreeDSFooterCustomization.h in Headers */,
0438EF381B7416BB00D506CC /* STPPaymentCardTextFieldViewModel.h in Headers */,
04CDE5BC1BC1F21500548833 /* STPCardParams.h in Headers */,
+ 311A475624EB1D2300576D92 /* STPCardScanner.h in Headers */,
B699BC9324577FED009107F9 /* STPPaymentIntentShippingDetailsAddressParams.h in Headers */,
C19D098F1EAEAE4000A4AB3E /* STPTelemetryClient.h in Headers */,
B3BDCAD620EEF5EC0034F7F5 /* STPPaymentIntentParams.h in Headers */,
@@ -5332,7 +5352,6 @@
3619624C244FA66D0025D60B /* STPPaymentMethodGiropayParams.m in Sources */,
B604CF2422C56E9B00A23CC4 /* STPIntentActionRedirectToURL.m in Sources */,
F19491E51E60DD72001E1FC2 /* STPSourceSEPADebitDetails.m in Sources */,
- C1785F5F1EC60B5E00E9CFAC /* STPCardIOProxy.m in Sources */,
0438EF311B7416BB00D506CC /* STPFormTextField.m in Sources */,
04B31DF51D09F0A800EF1631 /* UIViewController+Stripe_NavigationItemProxy.m in Sources */,
B6472192246CC1D20098DE24 /* STPConfirmAlipayOptions.m in Sources */,
@@ -5360,6 +5379,7 @@
B640DB1522C58E82003C8810 /* STPSetupIntentConfirmParams.m in Sources */,
04A4C38C1C4F25F900B3B290 /* NSArray+Stripe.m in Sources */,
04F94DCE1D22A232004FC826 /* UIViewController+Stripe_KeyboardAvoiding.m in Sources */,
+ 311A475924EB1D2300576D92 /* STPCardScanner.m in Sources */,
36E582FB22B4566A0044F82C /* STPPaymentHandler.m in Sources */,
B69CABB7246DC2AC0081B1EF /* STPPaymentMethodAlipayParams.m in Sources */,
04B31E021D131D9000EF1631 /* STPPaymentCardTextFieldCell.m in Sources */,
@@ -5445,6 +5465,7 @@
C18410791EC2529400178149 /* STPEphemeralKeyManager.m in Sources */,
0731329E2277ABF60019CE3F /* STPPinManagementService.m in Sources */,
04F94DB41D229F71004FC826 /* STPPaymentActivityIndicatorView.m in Sources */,
+ 311A476524EB27D000576D92 /* STPCardScannerTableViewCell.m in Sources */,
448895AC2452456800F7D0C2 /* STPPaymentMethodPrzelewy24Params.m in Sources */,
C1BD9B251E393FFE00CEE925 /* STPSourceReceiver.m in Sources */,
B6B5FC44222F4C0200440249 /* STPPaymentMethodThreeDSecureUsage.m in Sources */,
@@ -5483,6 +5504,7 @@
04BC29A11CD8412000318357 /* STPPaymentContext.m in Sources */,
314B6A562384A713001FE708 /* STPKlarnaLineItem.m in Sources */,
04CDE5C71BC20AF800548833 /* STPBankAccountParams.m in Sources */,
+ 311A475F24EB21AE00576D92 /* STPCameraView.m in Sources */,
314B6A5C2384ABF9001FE708 /* STPSourceKlarnaDetails.m in Sources */,
C1363BBA1D7633D800EB82B4 /* STPPaymentOptionTableViewCell.m in Sources */,
);
@@ -5547,6 +5569,7 @@
F12829DC1D7747E4008B10D6 /* STPBundleLocator.m in Sources */,
04BC29BE1CDD535700318357 /* STPSwitchTableViewCell.m in Sources */,
314B6A5B2384ABF9001FE708 /* STPSourceKlarnaDetails.m in Sources */,
+ 311A475E24EB21AE00576D92 /* STPCameraView.m in Sources */,
F1DEB88C1E2047CA0066B8E8 /* STPCoreTableViewController.m in Sources */,
44BDCFD3245A278F007EE6D5 /* STPPaymentMethodBancontact.m in Sources */,
04E39F6B1CED48D500AF3B96 /* UIBarButtonItem+Stripe.m in Sources */,
@@ -5609,6 +5632,7 @@
049880FE1CED5A2300EA4FFD /* STPPaymentConfiguration.m in Sources */,
046FE9A21CE55D1D00DA6A7B /* STPPaymentActivityIndicatorView.m in Sources */,
31B9609622FE20DF00DC6FD9 /* STPBankSelectionTableViewCell.m in Sources */,
+ 311A476424EB27D000576D92 /* STPCardScannerTableViewCell.m in Sources */,
04E7D4BF233FF29100FC8C02 /* STPFakeAddPaymentPassViewController.m in Sources */,
B621F061223465EE002141B7 /* STPPaymentMethodCardWalletVisaCheckout.m in Sources */,
045D71221CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m in Sources */,
@@ -5630,7 +5654,6 @@
314F9CC2235E66920059E2F6 /* STPFPXBankStatusResponse.m in Sources */,
073132972277A72D0019CE3F /* STPIssuingCardPin.m in Sources */,
04695ADC1C77F9EF00E08063 /* STPPhoneNumberValidator.m in Sources */,
- C1785F5E1EC60B5E00E9CFAC /* STPCardIOProxy.m in Sources */,
04CDB5141A5F30A700B854EE /* STPToken.m in Sources */,
44BDCFD9245A2BFA007EE6D5 /* STPPaymentMethodBancontactParams.m in Sources */,
B6A46F7822FCDB81001991B2 /* STPPaymentIntentLastPaymentError.m in Sources */,
@@ -5672,6 +5695,7 @@
04695AD41C77F9DB00E08063 /* NSString+Stripe.m in Sources */,
F15232261EA9303800D65C67 /* STPURLCallbackHandler.m in Sources */,
B68D52E422A739AA00D4E8BA /* STPSourceWeChatPayDetails.m in Sources */,
+ 311A475824EB1D2300576D92 /* STPCardScanner.m in Sources */,
F1BEB2FF1F3508BB0043F48C /* NSError+Stripe.m in Sources */,
B699BC9524577FED009107F9 /* STPPaymentIntentShippingDetailsAddressParams.m in Sources */,
049952D01BCF13510088C703 /* STPAPIRequest.m in Sources */,
diff --git a/Stripe/PublicHeaders/STPPaymentConfiguration.h b/Stripe/PublicHeaders/STPPaymentConfiguration.h
index 22718a0b5b7..7d18023ed9e 100644
--- a/Stripe/PublicHeaders/STPPaymentConfiguration.h
+++ b/Stripe/PublicHeaders/STPPaymentConfiguration.h
@@ -114,6 +114,21 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign, readwrite) BOOL canDeletePaymentOptions;
+/**
+ Determines whether STPAddCardViewController allows the user to
+ scan cards using the camera on devices running iOS 13 or later.
+
+ To use this feature, you must also set the `NSCameraUsageDescription`
+ value in your app's Info.plist.
+
+ @note This feature is currently in beta. Please file bugs at
+ https://github.com/stripe/stripe-ios/issues
+
+ The default value is NO. The default may be changed to YES in a future
+ version of the Stripe SDK.
+ */
+@property (nonatomic, assign, readwrite) BOOL cardScanningEnabled;
+
#pragma mark - Deprecated
/**
diff --git a/Stripe/Resources/Localizations/en.lproj/Localizable.strings b/Stripe/Resources/Localizations/en.lproj/Localizable.strings
index 4982e97c727..f163b59bf25 100644
--- a/Stripe/Resources/Localizations/en.lproj/Localizable.strings
+++ b/Stripe/Resources/Localizations/en.lproj/Localizable.strings
@@ -199,6 +199,9 @@
/* Error when 3DS2 authentication timed out. */
"Timed out authenticating your payment method -- try again" = "Timed out authenticating your payment method -- try again";
+/* Error when the user hasn't allowed the current app to access the camera when scanning a payment card. 'Settings' is the localized name of the iOS Settings app. */
+"To scan your card, you'll need to allow access to your camera in Settings." = "To scan your card, you'll need to allow access to your camera in Settings.";
+
/* Default missing source type label */
"Unknown" = "Unknown";
diff --git a/Stripe/STPAddCardViewController+Private.h b/Stripe/STPAddCardViewController+Private.h
index 85a5758e454..4c379600922 100644
--- a/Stripe/STPAddCardViewController+Private.h
+++ b/Stripe/STPAddCardViewController+Private.h
@@ -14,7 +14,6 @@
@interface STPAddCardViewController (Private)
@property (nonatomic) STPAddress *shippingAddress;
-@property (nonatomic) BOOL alwaysShowScanCardButton;
@property (nonatomic) BOOL alwaysEnableDoneButton;
- (void)commonInitWithConfiguration:(STPPaymentConfiguration *)configuration;
diff --git a/Stripe/STPAddCardViewController.m b/Stripe/STPAddCardViewController.m
index 427974be287..acfe1e62988 100644
--- a/Stripe/STPAddCardViewController.m
+++ b/Stripe/STPAddCardViewController.m
@@ -12,7 +12,9 @@
#import "STPAddressFieldTableViewCell.h"
#import "STPAddressViewModel.h"
#import "STPAnalyticsClient.h"
-#import "STPCardIOProxy.h"
+#import "STPCameraView.h"
+#import "STPCardScanner.h"
+#import "STPCardScannerTableViewCell.h"
#import "STPCardValidator.h"
#import "STPColorUtils.h"
#import "STPCoreTableViewController+Private.h"
@@ -41,12 +43,11 @@
@interface STPAddCardViewController ()<
STPAddressViewModelDelegate,
- STPCardIOProxyDelegate,
+ STPCardScannerDelegate,
STPPaymentCardTextFieldDelegate,
UITableViewDelegate,
UITableViewDataSource>
-@property (nonatomic) BOOL alwaysShowScanCardButton;
@property (nonatomic) BOOL alwaysEnableDoneButton;
@property (nonatomic) STPPaymentConfiguration *configuration;
@property (nonatomic) STPAddress *shippingAddress;
@@ -54,7 +55,9 @@ @interface STPAddCardViewController ()<
@property (nonatomic, weak) UIImageView *cardImageView;
@property (nonatomic) UIBarButtonItem *doneItem;
@property (nonatomic) STPSectionHeaderView *cardHeaderView;
-@property (nonatomic) STPCardIOProxy *cardIOProxy;
+@property (nonatomic) STPCardScanner *cardScanner API_AVAILABLE(ios(13.0));
+@property (nonatomic) STPCardScannerTableViewCell *scannerCell;
+@property (nonatomic) BOOL isScanning;
@property (nonatomic) STPSectionHeaderView *addressHeaderView;
@property (nonatomic) STPPaymentCardTextFieldCell *paymentCell;
@property (nonatomic) BOOL loading;
@@ -63,13 +66,17 @@ @interface STPAddCardViewController ()<
@property (nonatomic) STPAddressViewModel *addressViewModel;
@property (nonatomic) UIToolbar *inputAccessoryToolbar;
@property (nonatomic) BOOL lookupSucceeded;
+
+@property (nonatomic) NSTimer *scannerCompleteAnimationTimer;
+
@end
static NSString *const STPPaymentCardCellReuseIdentifier = @"STPPaymentCardCellReuseIdentifier";
typedef NS_ENUM(NSUInteger, STPPaymentCardSection) {
STPPaymentCardNumberSection = 0,
- STPPaymentCardBillingAddressSection = 1,
+ STPPaymentCardScannerSection = 1,
+ STPPaymentCardBillingAddressSection = 2,
};
@implementation STPAddCardViewController
@@ -100,6 +107,11 @@ - (void)commonInitWithConfiguration:(STPPaymentConfiguration *)configuration {
self.title = STPLocalizedString(@"Add a Card", @"Title for Add a Card view");
}
+- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return 44.0;
+}
+
- (void)createAndSetupViews {
[super createAndSetupViews];
@@ -186,11 +198,20 @@ - (void)viewDidLayoutSubviews {
}
- (void)setUpCardScanningIfAvailable {
- if ([STPCardIOProxy isCardIOAvailable] || self.alwaysShowScanCardButton) {
- self.cardIOProxy = [[STPCardIOProxy alloc] initWithDelegate:self];
+ if (@available(iOS 13.0, *)) {
+ if (![STPCardScanner cardScanningAvailable] || !self.configuration.cardScanningEnabled) {
+ return;
+ }
+ STPCardScannerTableViewCell *scannerCell = [[STPCardScannerTableViewCell alloc] init];
+ _scannerCell = scannerCell;
+
+ STPCardScanner *cardScanner = [[STPCardScanner alloc] initWithDelegate:self];
+ cardScanner.cameraView = scannerCell.cameraView;
+ _cardScanner = cardScanner;
+
self.cardHeaderView.buttonHidden = NO;
[self.cardHeaderView.button setTitle:STPLocalizedString(@"Scan Card", @"Text for button to scan a credit card") forState:UIControlStateNormal];
- [self.cardHeaderView.button addTarget:self action:@selector(presentCardIO) forControlEvents:UIControlEventTouchUpInside];
+ [self.cardHeaderView.button addTarget:self action:@selector(scanCard) forControlEvents:UIControlEventTouchUpInside];
[self.cardHeaderView setNeedsLayout];
}
}
@@ -202,8 +223,33 @@ - (void)setAlwaysEnableDoneButton:(BOOL)alwaysEnableDoneButton {
}
}
-- (void)presentCardIO {
- [self.cardIOProxy presentCardIOFromViewController:self];
+- (void)setIsScanning:(BOOL)isScanning {
+ if (_isScanning == isScanning) {
+ return;
+ }
+ _isScanning = isScanning;
+
+ self.cardHeaderView.button.enabled = !isScanning;
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:STPPaymentCardScannerSection];
+ [self.tableView beginUpdates];
+ if (isScanning) {
+ [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
+ } else {
+ [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
+ }
+ [self.tableView endUpdates];
+ if (isScanning) {
+ [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+ }
+ [self updateInputAccessoryVisiblity];
+}
+
+- (void)scanCard {
+ if (@available(iOS 13.0, *)) {
+ [self.view endEditing:YES];
+ self.isScanning = YES;
+ [self.cardScanner start];
+ }
}
- (void)endEditing {
@@ -225,7 +271,6 @@ - (void)updateAppearance {
self.paymentCell.theme = self.theme;
self.cardHeaderView.theme = self.theme;
self.addressHeaderView.theme = self.theme;
-
for (STPAddressFieldTableViewCell *cell in self.addressViewModel.addressCells) {
cell.theme = self.theme;
}
@@ -270,6 +315,7 @@ - (UIResponder *)firstEmptyField {
if (self.paymentCell.isEmpty) {
return self.paymentCell;
}
+
for (STPAddressFieldTableViewCell *cell in self.addressViewModel.addressCells) {
if (cell.contents.length == 0) {
return cell;
@@ -409,6 +455,12 @@ - (void)paymentCardTextFieldDidEndEditingCVC:(STPPaymentCardTextField *)textFiel
} completion:nil];
}
+- (void)paymentCardTextFieldDidBeginEditing:(nonnull STPPaymentCardTextField *)textField {
+ if (@available(iOS 13.0, *)) {
+ [[self cardScanner] stop];
+ }
+}
+
#pragma mark - STPAddressViewModelDelegate
- (void)addressViewModel:(__unused STPAddressViewModel *)addressViewModel addedCellAtIndex:(NSUInteger)index {
@@ -438,12 +490,14 @@ - (void)addressViewModelDidUpdate:(__unused STPAddressViewModel *)addressViewMod
#pragma mark - UITableView
- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView {
- return 2;
+ return 3;
}
- (NSInteger)tableView:(__unused UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == STPPaymentCardNumberSection) {
return 1;
+ } else if (section == STPPaymentCardScannerSection) {
+ return _isScanning ? 1 : 0;
} else if (section == STPPaymentCardBillingAddressSection) {
return self.addressViewModel.addressCells.count;
}
@@ -457,6 +511,9 @@ - (UITableViewCell *)tableView:(__unused UITableView *)tableView
case STPPaymentCardNumberSection:
cell = self.paymentCell;
break;
+ case STPPaymentCardScannerSection:
+ cell = self.scannerCell;
+ break;
case STPPaymentCardBillingAddressSection:
cell = [self.addressViewModel.addressCells stp_boundSafeObjectAtIndex:indexPath.row];
break;
@@ -492,6 +549,8 @@ - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSIntege
return [self.cardHeaderView sizeThatFits:fittingSize].height;
} else if (section == STPPaymentCardBillingAddressSection && numberOfRows != 0) {
return [self.addressHeaderView sizeThatFits:fittingSize].height;
+ } else if (section == STPPaymentCardScannerSection) {
+ return 0.01f;
} else if (numberOfRows != 0) {
return tableView.sectionHeaderHeight;
}
@@ -526,11 +585,57 @@ - (void)useShippingAddress:(__unused UIButton *)sender {
[self.tableView endUpdates];
}
-#pragma mark - STPCardIOProxyDelegate
+#pragma mark - STPCardScanner
-- (void)cardIOProxy:(__unused STPCardIOProxy *)proxy didFinishWithCardParams:(STPPaymentMethodCardParams *)cardParams {
+- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator
+{
+ [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+ if (@available(iOS 13.0, *)) {
+ UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
+ if (UIDeviceOrientationIsPortrait(orientation) || UIDeviceOrientationIsLandscape(orientation)) {
+ _cardScanner.deviceOrientation = orientation;
+ }
+ if (_isScanning) {
+ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:STPPaymentCardScannerSection];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+ });
+ }
+ }
+}
+
+- (void)cardScanner:(__unused STPCardScanner *)scanner didFinishWithCardParams:(nullable STPPaymentMethodCardParams *)cardParams error:(nullable NSError *)error API_AVAILABLE(ios(13)){
+ if (error) {
+ [self handleError:error];
+ }
+
+ static NSTimeInterval const kSTPCardScanAnimationTime = 0.04;
if (cardParams) {
- self.paymentCell.paymentField.cardParams = cardParams;
+ self.view.userInteractionEnabled = NO;
+ self.paymentCell.paymentField.inputView = [[UIView alloc] init];
+ __block NSUInteger i = 0;
+ self.scannerCompleteAnimationTimer = [NSTimer scheduledTimerWithTimeInterval:kSTPCardScanAnimationTime repeats:YES block:^(NSTimer * _Nonnull timer) {
+ i++;
+ STPPaymentMethodCardParams *newParams = [[STPPaymentMethodCardParams alloc] init];
+ if (i < [cardParams.number length]) {
+ newParams.number = [cardParams.number substringToIndex:i];
+ } else {
+ newParams.number = cardParams.number;
+ }
+ self.paymentCell.paymentField.cardParams = newParams;
+ if (i > [cardParams.number length]) {
+ self.paymentCell.paymentField.cardParams = cardParams;
+ self.isScanning = NO;
+ self.paymentCell.paymentField.inputView = nil;
+ // Force the inputView to reload by asking the text field to resign/become first responder:
+ [self.paymentCell.paymentField resignFirstResponder];
+ [self.paymentCell.paymentField becomeFirstResponder];
+ [timer invalidate];
+ self.view.userInteractionEnabled = YES;
+ }
+ }];
+ } else {
+ self.isScanning = NO;
}
}
diff --git a/Stripe/STPAnalyticsClient.h b/Stripe/STPAnalyticsClient.h
index 6978e2366d9..eb05dbfbe45 100644
--- a/Stripe/STPAnalyticsClient.h
+++ b/Stripe/STPAnalyticsClient.h
@@ -72,4 +72,8 @@
intentID:(NSString *)intentID
errorDictionary:(NSDictionary *)errorDictionary;
+- (void)logCardScanSucceededWithDuration:(NSTimeInterval)duration;
+
+- (void)logCardScanCancelledWithDuration:(NSTimeInterval)duration;
+
@end
diff --git a/Stripe/STPAnalyticsClient.m b/Stripe/STPAnalyticsClient.m
index 50d80569e2e..d3ae04aa36e 100644
--- a/Stripe/STPAnalyticsClient.m
+++ b/Stripe/STPAnalyticsClient.m
@@ -16,7 +16,7 @@
#import "STPAddCardViewController+Private.h"
#import "STPAddCardViewController.h"
#import "STPCard.h"
-#import "STPCardIOProxy.h"
+#import "STPCardScanner.h"
#import "STPFormEncodable.h"
#import "STPPaymentCardTextField.h"
#import "STPPaymentCardTextField+Private.h"
@@ -321,6 +321,28 @@ - (void)log3DS2ChallengeFlowCompletedWithConfiguration:(STPPaymentConfiguration
[self logPayload:payload];
}
+- (void)logCardScanSucceededWithDuration:(NSTimeInterval)duration {
+ NSMutableDictionary *payload = [self.class commonPayload];
+ [payload addEntriesFromDictionary:@{
+ @"event": @"stripeios.cardscan_success",
+ @"duration": @(round(duration)),
+ @"additional_info": [self additionalInfo],
+ }];
+ [payload addEntriesFromDictionary:[self productUsageDictionary]];
+ [self logPayload:payload];
+}
+
+- (void)logCardScanCancelledWithDuration:(NSTimeInterval)duration {
+ NSMutableDictionary *payload = [self.class commonPayload];
+ [payload addEntriesFromDictionary:@{
+ @"event": @"stripeios.cardscan_cancel",
+ @"duration": @(round(duration)),
+ @"additional_info": [self additionalInfo],
+ }];
+ [payload addEntriesFromDictionary:[self productUsageDictionary]];
+ [self logPayload:payload];
+}
+
#pragma mark - Helpers
+ (NSMutableDictionary *)commonPayload {
@@ -340,7 +362,12 @@ + (NSMutableDictionary *)commonPayload {
payload[@"app_name"] = [NSBundle stp_applicationName];
payload[@"app_version"] = [NSBundle stp_applicationVersion];
payload[@"apple_pay_enabled"] = @([Stripe deviceSupportsApplePay]);
- payload[@"ocr_type"] = [STPCardIOProxy isCardIOAvailable] ? @"card_io" : @"none";
+ payload[@"ocr_type"] = @"none";
+ if (@available(iOS 13.0, *)) {
+ if([[STPAnalyticsClient sharedClient].productUsage containsObject:NSStringFromClass([STPCardScanner class])]) {
+ payload[@"ocr_type"] = @"stripe";
+ }
+ }
return payload;
}
diff --git a/Stripe/STPCameraView.h b/Stripe/STPCameraView.h
new file mode 100644
index 00000000000..27f61cd5d7d
--- /dev/null
+++ b/Stripe/STPCameraView.h
@@ -0,0 +1,22 @@
+//
+// STPCameraView.h
+// Stripe
+//
+// Created by David Estes on 8/17/20.
+// Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+#import
+NS_ASSUME_NONNULL_BEGIN
+
+@class AVCaptureSession, AVCaptureVideoPreviewLayer;
+
+@interface STPCameraView : UIView
+
+@property (nonatomic, strong, nullable) AVCaptureSession *captureSession;
+@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *videoPreviewLayer;
+- (void)playSnapshotAnimation;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Stripe/STPCameraView.m b/Stripe/STPCameraView.m
new file mode 100644
index 00000000000..522c71ad4dd
--- /dev/null
+++ b/Stripe/STPCameraView.m
@@ -0,0 +1,62 @@
+//
+// STPCameraView.m
+// Stripe
+//
+// Created by David Estes on 8/17/20.
+// Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+#import "STPCameraView.h"
+#import
+
+@implementation STPCameraView {
+ CALayer *_flashLayer;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame];
+ _flashLayer = [[CALayer alloc] init];
+ [self.layer addSublayer:_flashLayer];
+ _flashLayer.masksToBounds = YES;
+ _flashLayer.backgroundColor = [[UIColor blackColor] CGColor];
+ _flashLayer.opacity = 0.0;
+ self.layer.masksToBounds = YES;
+ self.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+ return self;
+}
+
+- (AVCaptureSession *)captureSession {
+ return [self.videoPreviewLayer session];
+}
+
+- (void)setCaptureSession:(AVCaptureSession *)captureSession {
+ return [self.videoPreviewLayer setSession:captureSession];
+}
+
+- (void)playSnapshotAnimation {
+ [CATransaction begin];
+ [CATransaction setValue:(id)kCFBooleanTrue
+ forKey:kCATransactionDisableActions];
+ _flashLayer.frame = CGRectMake(0, 0, self.layer.bounds.size.width, self.layer.bounds.size.height);
+ _flashLayer.opacity = 1.0;
+ [CATransaction commit];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
+ fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
+ fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
+ fadeAnim.duration = 1.0;
+ [self->_flashLayer addAnimation:fadeAnim forKey:@"opacity"];
+ self->_flashLayer.opacity = 0.0;
+ });
+}
+
++ (Class)layerClass {
+ return [AVCaptureVideoPreviewLayer class];
+}
+
+- (AVCaptureVideoPreviewLayer *)videoPreviewLayer {
+ return (AVCaptureVideoPreviewLayer *)self.layer;
+}
+
+@end
diff --git a/Stripe/STPCardIOProxy.h b/Stripe/STPCardIOProxy.h
deleted file mode 100644
index 86f566a3022..00000000000
--- a/Stripe/STPCardIOProxy.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// STPCardIOAdapter.h
-// Stripe
-//
-// Created by Ben Guo on 5/12/17.
-// Copyright © 2017 Stripe, Inc. All rights reserved.
-//
-
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@class STPCardIOProxy, STPPaymentMethodCardParams;
-
-@protocol STPCardIOProxyDelegate
-- (void)cardIOProxy:(STPCardIOProxy *)proxy didFinishWithCardParams:(STPPaymentMethodCardParams *)cardParams;
-@end
-
-@interface STPCardIOProxy : NSObject
-
-+ (BOOL)isCardIOAvailable;
-- (instancetype)init __attribute__((unavailable("Use initWithDelegate")));
-- (instancetype)initWithDelegate:(id)delegate;
-- (void)presentCardIOFromViewController:(UIViewController *)viewController;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Stripe/STPCardIOProxy.m b/Stripe/STPCardIOProxy.m
deleted file mode 100644
index 970e6c8a198..00000000000
--- a/Stripe/STPCardIOProxy.m
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// STPCardIOProxy.m
-// Stripe
-//
-// Created by Ben Guo on 5/12/17.
-// Copyright © 2017 Stripe, Inc. All rights reserved.
-//
-
-#import "STPCardIOProxy.h"
-
-#import "FauxPasAnnotations.h"
-#import "STPPaymentMethodCardParams.h"
-#import "STPAnalyticsClient.h"
-
-@protocol STPClassProxy
-+ (Class)proxiedClass;
-+ (BOOL)proxiedClassExists;
-@end
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wincomplete-implementation"
-@interface STPCardIOUtilitiesProxy : NSObject
-+ (BOOL)canReadCardWithCamera;
-@end
-
-@implementation STPCardIOUtilitiesProxy
-+ (Class)proxiedClass {
- return NSClassFromString(@"CardIOUtilities");
-}
-+ (BOOL)proxiedClassExists {
- Class proxiedClass = [self proxiedClass];
- return proxiedClass && [proxiedClass respondsToSelector:@selector(canReadCardWithCamera)];
-}
-@end
-
-@interface STPCardIOCreditCardInfoProxy : NSObject
-@property (nonatomic, strong) NSString *cardNumber;
-@property (nonatomic, assign, readwrite) NSUInteger expiryMonth;
-@property (nonatomic, assign, readwrite) NSUInteger expiryYear;
-@property (nonatomic, copy, readwrite) NSString *cvv;
-@end
-
-@implementation STPCardIOCreditCardInfoProxy
-+ (Class)proxiedClass {
- return NSClassFromString(@"CardIOCreditCardInfo");
-}
-+ (BOOL)proxiedClassExists {
- Class proxiedClass = [self proxiedClass];
- return proxiedClass
- && [proxiedClass instancesRespondToSelector:@selector(cardNumber)]
- && [proxiedClass instancesRespondToSelector:@selector(expiryMonth)]
- && [proxiedClass instancesRespondToSelector:@selector(expiryYear)]
- && [proxiedClass instancesRespondToSelector:@selector(cvv)];
-}
-@end
-
-@interface STPCardIOPaymentViewControllerProxy : UIViewController
-+ (id)initWithPaymentDelegate:id;
-@property (nonatomic, assign, readwrite) BOOL hideCardIOLogo;
-@property (nonatomic, assign, readwrite) BOOL disableManualEntryButtons;
-@property (nonatomic, assign, readwrite) CGFloat scannedImageDuration;
-@end
-
-@implementation STPCardIOPaymentViewControllerProxy
-+ (Class)proxiedClass {
- return NSClassFromString(@"CardIOPaymentViewController");
-}
-+ (BOOL)proxiedClassExists {
- Class proxiedClass = [self proxiedClass];
- return proxiedClass
- && [proxiedClass instancesRespondToSelector:@selector(initWithPaymentDelegate:)]
- && [proxiedClass instancesRespondToSelector:@selector(setHideCardIOLogo:)]
- && [proxiedClass instancesRespondToSelector:@selector(setDisableManualEntryButtons:)]
- && [proxiedClass instancesRespondToSelector:@selector(setScannedImageDuration:)];
-}
-@end
-#pragma clang diagnostic pop
-
-@interface STPCardIOProxy ()
-@property (nonatomic, weak) iddelegate;
-@end
-
-@implementation STPCardIOProxy
-
-+ (BOOL)isCardIOAvailable {
-#if TARGET_OS_SIMULATOR
- return NO;
-#else
- if ([STPCardIOPaymentViewControllerProxy proxiedClassExists]
- && [STPCardIOCreditCardInfoProxy proxiedClassExists]
- && [STPCardIOUtilitiesProxy proxiedClassExists]) {
- return [[STPCardIOUtilitiesProxy proxiedClass] canReadCardWithCamera];
- }
- return NO;
-#endif
-}
-
-- (instancetype)initWithDelegate:(id)delegate {
- self = [super init];
- if (self) {
- _delegate = delegate;
- }
- return self;
-}
-
-- (void)presentCardIOFromViewController:(UIViewController *)viewController {
- STPCardIOPaymentViewControllerProxy *cardIOViewController = [[[STPCardIOPaymentViewControllerProxy proxiedClass] alloc] initWithPaymentDelegate:self];
- cardIOViewController.hideCardIOLogo = YES;
- cardIOViewController.disableManualEntryButtons = YES;
- cardIOViewController.scannedImageDuration = 0;
- [viewController presentViewController:cardIOViewController animated:YES completion:nil];
-}
-
-- (void)userDidCancelPaymentViewController:(UIViewController *)scanViewController { FAUXPAS_IGNORED_ON_LINE(UnusedMethod)
- [scanViewController dismissViewControllerAnimated:YES completion:nil];
- [[STPAnalyticsClient sharedClient] addAdditionalInfo:@"cardio_canceled"];
-}
-
-- (void)userDidProvideCreditCardInfo:(STPCardIOCreditCardInfoProxy *)info inPaymentViewController:(UIViewController *)scanViewController { FAUXPAS_IGNORED_ON_LINE(UnusedMethod)
- [scanViewController dismissViewControllerAnimated:YES completion:^{
- STPPaymentMethodCardParams *cardParams = [STPPaymentMethodCardParams new];
- cardParams.number = info.cardNumber;
- cardParams.expMonth = @(info.expiryMonth);
- cardParams.expYear = @(info.expiryYear);
- cardParams.cvc = info.cvv;
- [self.delegate cardIOProxy:self didFinishWithCardParams:cardParams];
- [[STPAnalyticsClient sharedClient] addAdditionalInfo:@"cardio_used"];
- }];
-}
-
-@end
diff --git a/Stripe/STPCardScanner.h b/Stripe/STPCardScanner.h
new file mode 100644
index 00000000000..12ff3df70ac
--- /dev/null
+++ b/Stripe/STPCardScanner.h
@@ -0,0 +1,44 @@
+//
+// STPCardScanner.h
+// Stripe
+//
+// Created by David Estes on 8/17/20.
+// Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+#import
+#import "STPCameraView.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class STPCardScanner, STPPaymentMethodCardParams;
+
+extern NSString *const STPCardScannerErrorDomain;
+
+typedef NS_ENUM(NSInteger, STPCardScannerError) {
+ /**
+ Camera not available.
+ */
+ STPCardScannerErrorCameraNotAvailable,
+};
+
+API_AVAILABLE(ios(13.0))
+@protocol STPCardScannerDelegate
+- (void)cardScanner:(STPCardScanner *)scanner didFinishWithCardParams:(nullable STPPaymentMethodCardParams *)cardParams error:(nullable NSError *)error;
+@end
+
+API_AVAILABLE(ios(13.0))
+@interface STPCardScanner : NSObject
+
++ (BOOL)cardScanningAvailable;
+
+@property (nonatomic, weak) STPCameraView *cameraView;
+@property (atomic) UIDeviceOrientation deviceOrientation;
+
+- (instancetype)init __attribute__((unavailable("Use initWithDelegate")));
+- (instancetype)initWithDelegate:(id)delegate;
+- (void)start;
+- (void)stop;
+
+@end
+NS_ASSUME_NONNULL_END
diff --git a/Stripe/STPCardScanner.m b/Stripe/STPCardScanner.m
new file mode 100644
index 00000000000..fc865ebbc0d
--- /dev/null
+++ b/Stripe/STPCardScanner.m
@@ -0,0 +1,418 @@
+//
+// STPCardScanner.m
+// Stripe
+//
+// Created by David Estes on 8/17/20.
+// Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+#import "STPCardScanner.h"
+#import "STPAnalyticsClient.h"
+
+#import
+#import
+
+#import "STPCardValidator+Private.h"
+#import "STPPaymentMethodCardParams.h"
+#import "STPStringUtils.h"
+#import "STPLocalizationUtils.h"
+#import "StripeError.h"
+
+// The number of successful scans required for both card number and expiration date before returning a result.
+static const NSUInteger kSTPCardScanningMinimumValidScans = 2;
+// If no expiration date is found, we'll return a result after this many successful scans.
+static const NSUInteger kSTPCardScanningMaxValidScans = 3;
+// Once one successful scan is found, we'll stop scanning after this many seconds.
+static const NSTimeInterval kSTPCardScanningTimeout = 1.0;
+
+NSString * const STPCardScannerErrorDomain = @"STPCardScannerErrorDomain";
+
+@interface STPCardScanner ()
+@property (nonatomic, weak) iddelegate;
+@property (nonatomic, strong) AVCaptureDevice *captureDevice;
+
+@property (nonatomic, strong) AVCaptureSession *captureSession;
+@property (nonatomic, strong, readwrite) dispatch_queue_t captureSessionQueue;
+
+@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;
+@property (nonatomic, strong) dispatch_queue_t videoDataOutputQueue;
+
+@property (nonatomic, strong) VNRecognizeTextRequest *textRequest;
+
+@property (atomic) BOOL isScanning;
+@property (atomic) BOOL didTimeout;
+@property (atomic) BOOL timeoutStarted;
+
+@property (atomic) UIDeviceOrientation _stp_deviceOrientation;
+@property (atomic) AVCaptureVideoOrientation videoOrientation;
+@property (atomic) CGImagePropertyOrientation textOrientation;
+
+@property (nonatomic) CGRect regionOfInterest;
+
+@property (nonatomic) NSCountedSet *detectedNumbers;
+@property (nonatomic) NSCountedSet *detectedExpirations;
+
+@property (nonatomic) NSDate *startTime;
+
+@end
+
+@implementation STPCardScanner
+
+#pragma mark Public
+
++ (BOOL)cardScanningAvailable {
+ // Always allow in tests:
+ if (NSClassFromString(@"XCTest") != nil) {
+ return YES;
+ }
+
+ // iOS will kill the app if it tries to request the camera without an NSCameraUsageDescription
+ static BOOL cameraHasUsageDescription = NO;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ if ([[[NSBundle mainBundle] infoDictionary] objectForKey:@"NSCameraUsageDescription"] != nil) {
+ cameraHasUsageDescription = YES;
+ }
+ });
+ return cameraHasUsageDescription;
+}
+
++ (NSError *)stp_cardScanningError {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: STPLocalizedString(@"To scan your card, you'll need to allow access to your camera in Settings.", @"Error when the user hasn't allowed the current app to access the camera when scanning a payment card. 'Settings' is the localized name of the iOS Settings app."),
+ STPErrorMessageKey: @"The camera couldn't be used."
+ };
+ return [[NSError alloc] initWithDomain:STPCardScannerErrorDomain code:STPCardScannerErrorCameraNotAvailable userInfo:userInfo];
+}
+
+- (instancetype)initWithDelegate:(id)delegate {
+ self = [super init];
+ if (self) {
+ self.delegate = delegate;
+ self.captureSessionQueue = dispatch_queue_create("com.stripe.CardScanning.CaptureSessionQueue", nil);
+ self.deviceOrientation = [[UIDevice currentDevice] orientation];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ if (self.isScanning) {
+ [self.captureDevice unlockForConfiguration];
+ [self.captureSession stopRunning];
+ }
+}
+
+- (void)start {
+ if (self.isScanning) {
+ return;
+ }
+ [[STPAnalyticsClient sharedClient] addClassToProductUsageIfNecessary:[self class]];
+ self.startTime = [NSDate date];
+
+ self.isScanning = YES;
+ self.didTimeout = NO;
+ self.timeoutStarted = NO;
+
+ dispatch_async(_captureSessionQueue, ^{
+ self.detectedNumbers = [[NSCountedSet alloc] initWithCapacity:5];
+ self.detectedExpirations = [[NSCountedSet alloc] initWithCapacity:5];
+ [self setupCamera];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.cameraView.captureSession = self.captureSession;
+ self.cameraView.videoPreviewLayer.connection.videoOrientation = self.videoOrientation;
+ });
+ });
+}
+
+- (void)stop {
+ [self stopWithError:nil];
+}
+
+- (void)stopWithError:(nullable NSError *)error {
+ if (self.isScanning) {
+ [self finishWithParams:nil error:error];
+ }
+}
+
+#pragma mark Setup
+
+- (void)setupCamera {
+ __weak typeof(self) weakSelf = self;
+ self.textRequest = [[VNRecognizeTextRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
+ __strong typeof(self) strongSelf = weakSelf;
+ if (!strongSelf.isScanning) {
+ return;
+ }
+ if (error) {
+ [strongSelf stopWithError:[STPCardScanner stp_cardScanningError]];
+ return;
+ }
+ [strongSelf processVNRequest:request];
+ }];
+
+ AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
+ self.captureDevice = captureDevice;
+
+ self.captureSession = [[AVCaptureSession alloc] init];
+ self.captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
+
+ NSError *deviceInputError;
+ AVCaptureDeviceInput *deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&deviceInputError];
+ if (deviceInputError) {
+ [self stopWithError:[STPCardScanner stp_cardScanningError]];
+ return;
+ }
+
+ if ([self.captureSession canAddInput:deviceInput]) {
+ [self.captureSession addInput:deviceInput];
+ } else {
+ [self stopWithError:[STPCardScanner stp_cardScanningError]];
+ return;
+ }
+
+ self.videoDataOutputQueue = dispatch_queue_create("com.stripe.CardScanning.VideoDataOutputQueue", nil);
+ self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
+ self.videoDataOutput.alwaysDiscardsLateVideoFrames = YES;
+ [self.videoDataOutput setSampleBufferDelegate:self queue:self.videoDataOutputQueue];
+
+ // This is the recommended pixel buffer format for Vision:
+ [self.videoDataOutput setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)}];
+
+ if ([self.captureSession canAddOutput:self.videoDataOutput]) {
+ [self.captureSession addOutput:self.videoDataOutput];
+ } else {
+ [self stopWithError:[STPCardScanner stp_cardScanningError]];
+ return;
+ }
+
+ // This improves recognition quality, but means the VideoDataOutput buffers won't match what we're seeing on screen.
+ [[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setPreferredVideoStabilizationMode:AVCaptureVideoStabilizationModeAuto];
+
+ [self.captureSession startRunning];
+
+ NSError *lockError;
+ [self.captureDevice lockForConfiguration:&lockError];
+ if (lockError == nil) {
+ self.captureDevice.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
+ }
+}
+
+#pragma mark Processing
+
+- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
+ if (!self.isScanning) {
+ return;
+ }
+ CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+ if (pixelBuffer == nil) {
+ return;
+ }
+ self.textRequest.recognitionLevel = VNRequestTextRecognitionLevelAccurate;
+ self.textRequest.usesLanguageCorrection = NO;
+ self.textRequest.regionOfInterest = self.regionOfInterest;
+ VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithCVPixelBuffer:pixelBuffer orientation:self.textOrientation options:@{}];
+ __unused NSError *requestError;
+ [handler performRequests:@[self.textRequest] error:&requestError];
+}
+
+- (void)processVNRequest:(VNRequest * _Nonnull)request {
+ NSMutableArray *allNumbers = [[NSMutableArray alloc] init];
+ for (VNRecognizedTextObservation *observation in request.results) {
+ NSArray *candidates = [observation topCandidates:5];
+ NSString *topCandidate = [[candidates firstObject] string];
+ if ([[STPCardValidator sanitizedNumericStringForString:topCandidate] length] >= 4) {
+ [allNumbers addObject:topCandidate];
+ }
+ for (VNRecognizedText *recognizedText in candidates) {
+ NSString *possibleNumber = [STPCardValidator sanitizedNumericStringForString:recognizedText.string];
+ if ([possibleNumber length] < 4) {
+ continue; // This probably isn't something we're interested in, so don't bother processing it.
+ }
+
+ // First strategy: We check if Vision sent us a number in a group on its own. If that fails, we'll try
+ // to catch it later when we iterate over all the numbers.
+ if ([STPCardValidator validationStateForNumber:possibleNumber validatingCardBrand:YES] == STPCardValidationStateValid) {
+ [self addDetectedNumber:possibleNumber];
+ } else if ([possibleNumber length] >= 4 && [possibleNumber length] <= 6 && [STPStringUtils stringMayContainExpirationDate:recognizedText.string]) {
+ // Try to parse anything that looks like an expiration date.
+ NSString *expirationString = [STPStringUtils expirationDateStringFromString:recognizedText.string];
+ NSString *sanitizedExpiration = [STPCardValidator sanitizedNumericStringForString:expirationString];
+ NSString *month = [sanitizedExpiration substringToIndex:2];
+ NSString *year = [sanitizedExpiration substringFromIndex:2];
+
+ // Ignore expiration dates 10+ years in the future, as they're likely to be incorrect recognitions
+ NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+ NSDateComponents *currentDateComponents = [calendar components:NSCalendarUnitYear fromDate:[NSDate date]];
+ NSInteger maxYear = ([currentDateComponents year] % 100) + 10;
+
+ if ([STPCardValidator validationStateForExpirationYear:year inMonth:month] == STPCardValidationStateValid && [year integerValue] < maxYear) {
+ [self addDetectedExpiration:sanitizedExpiration];
+ }
+ }
+ }
+ }
+ // Second strategy: We look for consecutive groups of 4/4/4/4 or 4/6/5
+ // Vision is sending us groups like ["1234 565", "1234 1"], so we'll normalize these into groups with spaces:
+ NSArray *allGroups = [[allNumbers componentsJoinedByString:@" "] componentsSeparatedByString:@" "];
+ for (NSInteger i = 0; i < (NSInteger)[allGroups count] - 3; i++) {
+ NSString *string1 = allGroups[i];
+ NSString *string2 = allGroups[i + 1];
+ NSString *string3 = allGroups[i + 2];
+ NSString *string4 = @"";
+ if (i + 3 < (NSInteger)[allGroups count]) {
+ string4 = allGroups[i + 3];
+ }
+ // Then we'll go through each group and build a potential match:
+ NSString *potentialCardString = [NSString stringWithFormat:@"%@%@%@%@", string1, string2, string3, string4];
+ NSString *potentialAmexString = [NSString stringWithFormat:@"%@%@%@", string1, string2, string3];
+
+ // Then we'll add valid matches. It's okay if we add a number a second time after doing so above, as the success of that first pass means it's more likely to be a good match.
+ if ([STPCardValidator validationStateForNumber:potentialCardString validatingCardBrand:YES] == STPCardValidationStateValid) {
+ [self addDetectedNumber:potentialCardString];
+ } else if ([STPCardValidator validationStateForNumber:potentialAmexString validatingCardBrand:YES] == STPCardValidationStateValid) {
+ [self addDetectedNumber:potentialAmexString];
+ }
+ }
+}
+
+
+- (void)addDetectedNumber:(NSString *)number {
+ [self.detectedNumbers addObject:number];
+
+ // Set a timeout: If we don't get enough scans in the next 1 second, we'll use the best option we have.
+ if (!self.timeoutStarted) {
+ self.timeoutStarted = YES;
+ __weak typeof(self) weakSelf = self;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ __strong typeof(self) strongSelf = weakSelf;
+ [strongSelf.cameraView playSnapshotAnimation];
+ });
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kSTPCardScanningTimeout * NSEC_PER_SEC)), self.videoDataOutputQueue, ^{
+ __strong typeof(self) strongSelf = weakSelf;
+ if (strongSelf.isScanning) {
+ strongSelf.didTimeout = YES;
+ [strongSelf finishIfReady];
+ }
+ });
+ }
+
+ if ([_detectedNumbers countForObject:number] >= kSTPCardScanningMinimumValidScans) {
+ [self finishIfReady];
+ }
+}
+
+- (void)addDetectedExpiration:(NSString *)expiration {
+ [self.detectedExpirations addObject:expiration];
+ if ([self.detectedExpirations countForObject:expiration] >= kSTPCardScanningMinimumValidScans) {
+ [self finishIfReady];
+ }
+}
+
+#pragma mark Completion
+
+- (void)finishIfReady {
+ if (!self.isScanning) {
+ return;
+ }
+ NSCountedSet *detectedNumbers = self.detectedNumbers;
+ NSCountedSet *detectedExpirations = self.detectedExpirations;
+
+ NSString *topNumber = [[detectedNumbers.allObjects sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
+ NSUInteger c1 = [detectedNumbers countForObject:obj1];
+ NSUInteger c2 = [detectedNumbers countForObject:obj2];
+ if (c1 < c2) {
+ return NSOrderedAscending;
+ } else if (c1 > c2) {
+ return NSOrderedDescending;
+ } else {
+ return NSOrderedSame;
+ }
+ }] lastObject];
+ NSString *topExpiration = [[detectedExpirations.allObjects sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
+ NSUInteger c1 = [detectedExpirations countForObject:obj1];
+ NSUInteger c2 = [detectedExpirations countForObject:obj2];
+ if (c1 < c2) {
+ return NSOrderedAscending;
+ } else if (c1 > c2) {
+ return NSOrderedDescending;
+ } else {
+ return NSOrderedSame;
+ }
+ }] lastObject];
+
+ if (self.didTimeout ||
+ (([detectedNumbers countForObject:topNumber] >= kSTPCardScanningMinimumValidScans) && ([detectedExpirations countForObject:topExpiration] >= kSTPCardScanningMinimumValidScans))
+ || ([detectedNumbers countForObject:topNumber] >= kSTPCardScanningMaxValidScans)
+ ) {
+ STPPaymentMethodCardParams *params = [[STPPaymentMethodCardParams alloc] init];
+ params.number = topNumber;
+ if (topExpiration) {
+ params.expMonth = @([[topExpiration substringToIndex:2] integerValue]);
+ params.expYear = @([[topExpiration substringFromIndex:2] integerValue]);
+ }
+ [self finishWithParams:params error:nil];
+ }
+}
+
+- (void)finishWithParams:(STPPaymentMethodCardParams *)params error:(NSError *)error {
+ NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:self.startTime];
+ self.isScanning = NO;
+ [self.captureDevice unlockForConfiguration];
+ [self.captureSession stopRunning];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (params == nil) {
+ [[STPAnalyticsClient sharedClient] logCardScanCancelledWithDuration:duration];
+ } else {
+ [[STPAnalyticsClient sharedClient] logCardScanSucceededWithDuration:duration];
+ }
+
+ self.cameraView.captureSession = nil;
+ [self.delegate cardScanner:self didFinishWithCardParams:params error:error];
+ });
+}
+
+#pragma mark Orientation
+
+- (void)setDeviceOrientation:(UIDeviceOrientation)newDeviceOrientation {
+ self._stp_deviceOrientation = newDeviceOrientation;
+
+ // This is an optimization for portrait mode: The card will be centered in the screen,
+ // so we can ignore the top and bottom. We'll use the whole frame in landscape.
+ CGRect kSTPCardScanningScreenCenter = CGRectMake(0, (CGFloat)0.3, 1, (CGFloat)0.4);
+
+ // iOS camera image data is returned in LandcapeLeft orientation by default. We'll flip it as needed:
+ switch (newDeviceOrientation) {
+ case UIDeviceOrientationPortrait:
+ case UIDeviceOrientationFaceUp:
+ case UIDeviceOrientationFaceDown:
+ self.videoOrientation = AVCaptureVideoOrientationPortrait;
+ self.textOrientation = kCGImagePropertyOrientationRight;
+ self.regionOfInterest = kSTPCardScanningScreenCenter;
+ break;
+ case UIDeviceOrientationPortraitUpsideDown:
+ self.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
+ self.textOrientation = kCGImagePropertyOrientationLeft;
+ self.regionOfInterest = kSTPCardScanningScreenCenter;
+ break;
+ case UIDeviceOrientationLandscapeLeft:
+ self.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
+ self.textOrientation = kCGImagePropertyOrientationUp;
+ self.regionOfInterest = CGRectMake(0, 0, 1, 1);
+ break;
+ case UIDeviceOrientationLandscapeRight:
+ self.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
+ self.textOrientation = kCGImagePropertyOrientationDown;
+ self.regionOfInterest = CGRectMake(0, 0, 1, 1);
+ break;
+ default:
+ break;
+ }
+ self.cameraView.videoPreviewLayer.connection.videoOrientation = _videoOrientation;
+}
+
+- (UIDeviceOrientation)deviceOrientation {
+ return self._stp_deviceOrientation;
+}
+
+@end
diff --git a/Stripe/STPCardScannerTableViewCell.h b/Stripe/STPCardScannerTableViewCell.h
new file mode 100644
index 00000000000..3705b5f10a9
--- /dev/null
+++ b/Stripe/STPCardScannerTableViewCell.h
@@ -0,0 +1,23 @@
+//
+// STPCardScannerTableViewCell.h
+// Stripe
+//
+// Created by David Estes on 8/17/20.
+// Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+#import
+
+@class STPTheme;
+@class STPCameraView;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface STPCardScannerTableViewCell : UITableViewCell
+
+@property (nonatomic, weak, readonly) STPCameraView *cameraView;
+@property (nonatomic, copy) STPTheme *theme;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Stripe/STPCardScannerTableViewCell.m b/Stripe/STPCardScannerTableViewCell.m
new file mode 100644
index 00000000000..a14eda84661
--- /dev/null
+++ b/Stripe/STPCardScannerTableViewCell.m
@@ -0,0 +1,59 @@
+//
+// STPCardScannerTableViewCell.m
+// Stripe
+//
+// Created by David Estes on 8/17/20.
+// Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+#import "STPCardScannerTableViewCell.h"
+#import "STPCameraView.h"
+
+#import "STPTheme.h"
+#import "UIView+Stripe_SafeAreaBounds.h"
+
+@interface STPCardScannerTableViewCell()
+
+@property (nonatomic, weak) STPCameraView *cameraView;
+
+@end
+
+@implementation STPCardScannerTableViewCell
+
+static const CGFloat cardSizeRatio = 2.125f/3.370f; // ID-1 card size (in inches)
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ STPCameraView *cameraView = [[STPCameraView alloc] initWithFrame:self.bounds];
+ [self.contentView addSubview:cameraView];
+ _cameraView = cameraView;
+ _theme = [STPTheme defaultTheme];
+ [self.cameraView setTranslatesAutoresizingMaskIntoConstraints:NO];
+ [self.contentView addConstraints:@[
+ [cameraView.heightAnchor constraintEqualToAnchor:cameraView.widthAnchor multiplier:cardSizeRatio],
+ [cameraView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:0],
+ [cameraView.leftAnchor constraintEqualToAnchor:self.contentView.leftAnchor constant:0],
+ [cameraView.rightAnchor constraintEqualToAnchor:self.contentView.rightAnchor constant:0],
+ [cameraView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0]]];
+ [self updateAppearance];
+ }
+ return self;
+}
+
+- (void)layoutSubviews {
+
+ [super layoutSubviews];
+}
+
+- (void)setTheme:(STPTheme *)theme {
+ _theme = theme;
+ [self updateAppearance];
+}
+
+- (void)updateAppearance {
+ // The first few frames of the camera view will be black, so our background should be black too.
+ self.cameraView.backgroundColor = [UIColor blackColor];
+}
+
+@end
diff --git a/Stripe/STPPaymentConfiguration.m b/Stripe/STPPaymentConfiguration.m
index c6da1775985..e8bf3f63910 100644
--- a/Stripe/STPPaymentConfiguration.m
+++ b/Stripe/STPPaymentConfiguration.m
@@ -48,6 +48,7 @@ - (instancetype)init {
_shippingType = STPShippingTypeShipping;
_companyName = [NSBundle stp_applicationName];
_canDeletePaymentOptions = YES;
+ _cardScanningEnabled = NO;
}
return self;
}
@@ -141,6 +142,7 @@ - (NSString *)description {
[NSString stringWithFormat:@"companyName = %@", self.companyName],
[NSString stringWithFormat:@"appleMerchantIdentifier = %@", self.appleMerchantIdentifier],
[NSString stringWithFormat:@"canDeletePaymentOptions = %@", (self.canDeletePaymentOptions) ? @"YES" : @"NO"],
+ [NSString stringWithFormat:@"cardScanningEnabled = %@", (self.cardScanningEnabled) ? @"YES" : @"NO"],
];
return [NSString stringWithFormat:@"<%@>", [props componentsJoinedByString:@"; "]];
@@ -158,6 +160,7 @@ - (id)copyWithZone:(__unused NSZone *)zone {
copy.companyName = self.companyName;
copy.appleMerchantIdentifier = self.appleMerchantIdentifier;
copy.canDeletePaymentOptions = self.canDeletePaymentOptions;
+ copy.cardScanningEnabled = self.cardScanningEnabled;
copy.availableCountries = _availableCountries;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
diff --git a/Stripe/STPStringUtils.h b/Stripe/STPStringUtils.h
index aebff938088..ba2b9c89178 100644
--- a/Stripe/STPStringUtils.h
+++ b/Stripe/STPStringUtils.h
@@ -61,4 +61,10 @@ typedef void(^STPTaggedSubstringsCompletionBlock)(NSString *string, NSDictionary
+ (NSString *)expirationDateStringFromString:(NSString *)string;
+/**
+ * Returns YES if the string is likely to contain something formatted similar to an expiration date.
+ * It doesn't confirm that the expiration date is valid, or that it is even a date.
+ */
++ (BOOL)stringMayContainExpirationDate:(NSString *)string;
+
@end
diff --git a/Stripe/STPStringUtils.m b/Stripe/STPStringUtils.m
index 9ef193254bd..71faf813a39 100644
--- a/Stripe/STPStringUtils.m
+++ b/Stripe/STPStringUtils.m
@@ -133,4 +133,16 @@ + (NSString *)expirationDateStringFromString:(NSString *)string {
return string;
}
++ (BOOL)stringMayContainExpirationDate:(NSString *)string {
+ static dispatch_once_t onceToken;
+ static NSRegularExpression *regex = nil;
+ dispatch_once(&onceToken, ^{
+ regex = [[NSRegularExpression alloc] initWithPattern:@"(\\w{2}(\\/|\\.)(\\w{2}|\\w{4}))"
+ options:0
+ error:NULL];
+ });
+ NSTextCheckingResult *result = [[regex matchesInString:string options:0 range:NSMakeRange(0, string.length)] firstObject];
+ return (result && [result numberOfRanges] > 0);
+}
+
@end
diff --git a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testDefaultAppearance_STPAUBECSDebitFormView_defaultAppearance@2x.png b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testDefaultAppearance_STPAUBECSDebitFormView_defaultAppearance@2x.png
index 380a88a827e..8ff65e5008f 100644
Binary files a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testDefaultAppearance_STPAUBECSDebitFormView_defaultAppearance@2x.png and b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testDefaultAppearance_STPAUBECSDebitFormView_defaultAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailAppearance_STPAUBECSDebitFormView_invalidBSBAndEmailAppearance@2x.png b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailAppearance_STPAUBECSDebitFormView_invalidBSBAndEmailAppearance@2x.png
index 36cdecefc63..dc9da8b03d5 100644
Binary files a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailAppearance_STPAUBECSDebitFormView_invalidBSBAndEmailAppearance@2x.png and b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailAppearance_STPAUBECSDebitFormView_invalidBSBAndEmailAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailCustomization_STPAUBECSDebitFormView_invalidBSBAndEmailCustomization@2x.png b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailCustomization_STPAUBECSDebitFormView_invalidBSBAndEmailCustomization@2x.png
index 428c5c2f296..1a0f6f59c60 100644
Binary files a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailCustomization_STPAUBECSDebitFormView_invalidBSBAndEmailCustomization@2x.png and b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testInvalidBSBAndEmailCustomization_STPAUBECSDebitFormView_invalidBSBAndEmailCustomization@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testNoDataCustomization_STPAUBECSDebitFormView_noDataCustomization@2x.png b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testNoDataCustomization_STPAUBECSDebitFormView_noDataCustomization@2x.png
index 17b589a53ea..b1f59d12af1 100644
Binary files a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testNoDataCustomization_STPAUBECSDebitFormView_noDataCustomization@2x.png and b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testNoDataCustomization_STPAUBECSDebitFormView_noDataCustomization@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataAppearance_STPAUBECSDebitFormView_withDataAppearance@2x.png b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataAppearance_STPAUBECSDebitFormView_withDataAppearance@2x.png
index facf9e67e28..d4aa9dec674 100644
Binary files a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataAppearance_STPAUBECSDebitFormView_withDataAppearance@2x.png and b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataAppearance_STPAUBECSDebitFormView_withDataAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataCustomization_STPAUBECSDebitFormView_withDataAppearance@2x.png b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataCustomization_STPAUBECSDebitFormView_withDataAppearance@2x.png
index 2d4fc5c2b6a..e7b48b7e9c1 100644
Binary files a/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataCustomization_STPAUBECSDebitFormView_withDataAppearance@2x.png and b/Tests/ReferenceImages_64/STPAUBECSDebitFormViewSnapshotTests/testWithDataCustomization_STPAUBECSDebitFormView_withDataAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_CA@2x.png
index 8acaef53273..09e3d0e2527 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_GB@2x.png
index fc75c60186d..df1fcbe4ed1 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_MX@2x.png
index 5c62281bd4f..af10aa5a5b7 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_US@2x.png
index 3298dbea8bd..266c0654bab 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_delivery@2x.png
index 3fe8192d2c2..ea8035690a4 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_no_country@2x.png
index f898b32c500..419279510ab 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testChinese_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_CA@2x.png
index 0f1abb93d58..1d8987b21f3 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_GB@2x.png
index 84b688fd645..6b47ecb0d95 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_MX@2x.png
index 82fc8e04915..fbda5cfea84 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_US@2x.png
index 03b54f89985..c9e4c4e5793 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_delivery@2x.png
index d36057620f6..6402deb2e06 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_no_country@2x.png
index 2c39fa8f107..f34c1304fa9 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testDutch_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_CA@2x.png
index bac5b02172c..b4e0715b82b 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_GB@2x.png
index 65da22922a7..6d456bfb1e0 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_MX@2x.png
index f4a2b0d23c2..fd242ad143a 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_US@2x.png
index f9e584bf8c8..5e945ea04f5 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_delivery@2x.png
index bfc3056d285..ad4f14ee71f 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_no_country@2x.png
index da141d23caa..f98de9d38ff 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testEnglish_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_CA@2x.png
index efb55ebcab0..63d7df4c807 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_GB@2x.png
index a389d4288e7..674a9b7b9f7 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_MX@2x.png
index 09b05e65a6d..5a96e1d32be 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_US@2x.png
index 052ec03d111..7e965e6d9be 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_delivery@2x.png
index e915e164f21..90cbe44414b 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_no_country@2x.png
index e915e164f21..90cbe44414b 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testFrench_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_CA@2x.png
index 336a7e85d69..741df1cbc9f 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_GB@2x.png
index 465a827d180..cd5da20354d 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_MX@2x.png
index 46d8ce01f70..a34f355512f 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_US@2x.png
index 45b3fb98833..b903438c218 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_delivery@2x.png
index 2543f530f49..4e3e54a4efd 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_no_country@2x.png
index 1fb6a68e797..7efd71a74c5 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testGerman_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_CA@2x.png
index 3c4d1243d02..d8c7c5e2ffa 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_GB@2x.png
index 403d5599eb7..477e727ff6e 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_MX@2x.png
index e01defc329e..89cbcd2a257 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_US@2x.png
index 140bda51751..5aee8ad3f7b 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_delivery@2x.png
index e75b1350845..d20bdbad7c6 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_no_country@2x.png
index 87598a5164a..5ee465b6ffe 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testItalian_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_CA@2x.png
index bf40045f1ef..08c40a43751 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_GB@2x.png
index 75a99b9c3f5..9378bae660b 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_MX@2x.png
index 664c6bd1bd5..357d4eb1ce2 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_US@2x.png
index d3864990943..7c339c3d6a4 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_delivery@2x.png
index bf3ee905c14..d39103089c0 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_no_country@2x.png
index 7190ddf49ef..4c8e69186eb 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testJapanese_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_CA@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_CA@2x.png
index f3ca4390a7c..8d48d1bddcc 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_CA@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_CA@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_GB@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_GB@2x.png
index 4f106e417df..a0a4b8e82cf 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_GB@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_GB@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_MX@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_MX@2x.png
index eb30709afc5..49586d7e255 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_MX@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_MX@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_US@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_US@2x.png
index 376bc7f7cfa..984bb2f6d1d 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_US@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_US@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_delivery@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_delivery@2x.png
index 295d4b40fb4..e9716d407c8 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_delivery@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_no_country@2x.png b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_no_country@2x.png
index 80b2c724726..f9d628850cc 100644
Binary files a/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_no_country@2x.png and b/Tests/ReferenceImages_64/STPAddCardViewControllerLocalizationTests/testSpanish_no_country@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPLabeledFormTextFieldViewSnapshotTests/testAppearance_STPLabeledFormTextFieldView_defaultAppearance@2x.png b/Tests/ReferenceImages_64/STPLabeledFormTextFieldViewSnapshotTests/testAppearance_STPLabeledFormTextFieldView_defaultAppearance@2x.png
index f5887598ebc..d612fab625f 100644
Binary files a/Tests/ReferenceImages_64/STPLabeledFormTextFieldViewSnapshotTests/testAppearance_STPLabeledFormTextFieldView_defaultAppearance@2x.png and b/Tests/ReferenceImages_64/STPLabeledFormTextFieldViewSnapshotTests/testAppearance_STPLabeledFormTextFieldView_defaultAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPLabeledMultiFormTextFieldViewSnapshotTests/testAppearance_STPLabeledMultiFormTextFieldView_defaultAppearance@2x.png b/Tests/ReferenceImages_64/STPLabeledMultiFormTextFieldViewSnapshotTests/testAppearance_STPLabeledMultiFormTextFieldView_defaultAppearance@2x.png
index b312596540d..185af53a487 100644
Binary files a/Tests/ReferenceImages_64/STPLabeledMultiFormTextFieldViewSnapshotTests/testAppearance_STPLabeledMultiFormTextFieldView_defaultAppearance@2x.png and b/Tests/ReferenceImages_64/STPLabeledMultiFormTextFieldViewSnapshotTests/testAppearance_STPLabeledMultiFormTextFieldView_defaultAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsLargeTitle@2x.png b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsLargeTitle@2x.png
index 2e0684ea807..689769714ef 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsLargeTitle@2x.png and b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsLargeTitle@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsSmallTitle@2x.png b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsSmallTitle@2x.png
index 4efcbd157ef..58ca57cf365 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsSmallTitle@2x.png and b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushPaymentOptionsSmallTitle@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressLargeTitle@2x.png b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressLargeTitle@2x.png
index 786b3893b14..c49ae346822 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressLargeTitle@2x.png and b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressLargeTitle@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressSmallTitle@2x.png b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressSmallTitle@2x.png
index 113d1bc0e3d..e8198c99cf7 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressSmallTitle@2x.png and b/Tests/ReferenceImages_64/STPPaymentContextSnapshotTests/testPushShippingAddressSmallTitle@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testChinese@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testChinese@2x.png
index 5c950001023..b675a1dcb59 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testChinese@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testChinese@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testDutch@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testDutch@2x.png
index 6183d16f739..0ae64a56470 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testDutch@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testDutch@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testEnglish@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testEnglish@2x.png
index 4efcbd157ef..58ca57cf365 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testEnglish@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testEnglish@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testFrench@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testFrench@2x.png
index b74c13b94f1..a831b26cd6e 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testFrench@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testFrench@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testGerman@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testGerman@2x.png
index 962fa4053d5..b2c88d099e7 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testGerman@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testGerman@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testItalian@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testItalian@2x.png
index 39f2651a69f..cb119d5d06a 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testItalian@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testItalian@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testJapanese@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testJapanese@2x.png
index 91a482e13fa..17c46d372d1 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testJapanese@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testJapanese@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testSpanish@2x.png b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testSpanish@2x.png
index 9ec958801ec..f8a696521b3 100644
Binary files a/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testSpanish@2x.png and b/Tests/ReferenceImages_64/STPPaymentOptionsViewControllerLocalizationTests/testSpanish@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testDefaultAppearance_STPViewWithSeparator_defaultAppearance@2x.png b/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testDefaultAppearance_STPViewWithSeparator_defaultAppearance@2x.png
index 6de8c27ed7e..eaa8ec0e70b 100644
Binary files a/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testDefaultAppearance_STPViewWithSeparator_defaultAppearance@2x.png and b/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testDefaultAppearance_STPViewWithSeparator_defaultAppearance@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testHiddenTopSeparator_STPViewWithSeparator_hiddenTopSeparator@2x.png b/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testHiddenTopSeparator_STPViewWithSeparator_hiddenTopSeparator@2x.png
index 759745bb4e0..e0f93b818cc 100644
Binary files a/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testHiddenTopSeparator_STPViewWithSeparator_hiddenTopSeparator@2x.png and b/Tests/ReferenceImages_64/STPSTPViewWithSeparatorSnapshotTests/testHiddenTopSeparator_STPViewWithSeparator_hiddenTopSeparator@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_contact@2x.png
index 5fdcf1d69ee..1e8fa1e2c6e 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_delivery@2x.png
index b2f7ec9273c..dd260484c9c 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_shipping@2x.png
index 16c158b8526..4c4a505be98 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testChinese_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_contact@2x.png
index 721976634ec..d1a8e2592a7 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_delivery@2x.png
index a1de6869e62..737896c1ab8 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_shipping@2x.png
index 2422f8ee3bd..9ece4be8109 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testDutch_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_contact@2x.png
index 113d1bc0e3d..e8198c99cf7 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_delivery@2x.png
index 4361c42b4a5..d5c2270c3f6 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_shipping@2x.png
index 19ac01e53b1..9ff9b24a912 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testEnglish_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_contact@2x.png
index 9072661c1bf..21977511bd3 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_delivery@2x.png
index 1152392cc80..38704113e82 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_shipping@2x.png
index 16f4aba2188..32f87c03a29 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testFrench_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_contact@2x.png
index dd57ca599b7..389c7d286e1 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_delivery@2x.png
index 562dbf01e85..db0637eefa0 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_shipping@2x.png
index 4c1f03b1534..ed8b9204511 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testGerman_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_contact@2x.png
index 46e8f1341c3..a66dc3924a3 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_delivery@2x.png
index 344f30c476d..3396c300ce4 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_shipping@2x.png
index 3c0156281de..e57a7be3c49 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testItalian_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_contact@2x.png
index ee369dee173..003fa3c1b5e 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_delivery@2x.png
index f7cdf31beed..892cb79177d 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_shipping@2x.png
index ab0adbf48df..04c3ade2356 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testJapanese_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_contact@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_contact@2x.png
index a99f93d1cab..e46887eed4a 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_contact@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_contact@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_delivery@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_delivery@2x.png
index 3ac193d9aa5..932fba6c795 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_delivery@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_delivery@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_shipping@2x.png b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_shipping@2x.png
index b1fd2be29c9..d8070c2ac48 100644
Binary files a/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_shipping@2x.png and b/Tests/ReferenceImages_64/STPShippingAddressViewControllerLocalizationTests/testSpanish_shipping@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testChinese@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testChinese@2x.png
index c473dedaef0..06a11f81d6b 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testChinese@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testChinese@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testDutch@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testDutch@2x.png
index 7db44b7ac00..c99eedca215 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testDutch@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testDutch@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testEnglish@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testEnglish@2x.png
index 3a800e55492..cb2aac4069f 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testEnglish@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testEnglish@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testFrench@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testFrench@2x.png
index c7d950f092a..0c1aae8670a 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testFrench@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testFrench@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testGerman@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testGerman@2x.png
index 10af3412cc7..31970398863 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testGerman@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testGerman@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testItalian@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testItalian@2x.png
index 71fed961ec5..9d377f3c15c 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testItalian@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testItalian@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testJapanese@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testJapanese@2x.png
index a63f43c9289..2ec7b88953d 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testJapanese@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testJapanese@2x.png differ
diff --git a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testSpanish@2x.png b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testSpanish@2x.png
index b1da76f5635..47e6f1e1fad 100644
Binary files a/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testSpanish@2x.png and b/Tests/ReferenceImages_64/STPShippingMethodsViewControllerLocalizationTests/testSpanish@2x.png differ
diff --git a/Tests/Tests/STPAddCardViewControllerLocalizationTests.m b/Tests/Tests/STPAddCardViewControllerLocalizationTests.m
index 34622ab6b96..c9176f36d38 100644
--- a/Tests/Tests/STPAddCardViewControllerLocalizationTests.m
+++ b/Tests/Tests/STPAddCardViewControllerLocalizationTests.m
@@ -16,7 +16,6 @@
#import "STPAddressViewModel.h"
#import "STPAddressFieldTableViewCell.h"
#import "STPBundleLocator.h"
-#import "STPCardIOProxy.h"
#import "STPFixtures.h"
#import "STPLocalizationUtils.h"
#import "STPLocalizationUtils+STPTestAdditions.h"
@@ -38,15 +37,12 @@ @implementation STPAddCardViewControllerLocalizationTests
//}
- (void)performSnapshotTestForLanguage:(NSString *)language delivery:(BOOL)delivery {
- id mockCardIOProxy = OCMClassMock([STPCardIOProxy class]);
- OCMStub([mockCardIOProxy isCardIOAvailable]).andReturn(YES);
-
STPPaymentConfiguration *config = [STPFixtures paymentConfiguration];
config.companyName = @"Test Company";
config.requiredBillingAddressFields = STPBillingAddressFieldsFull;
config.additionalPaymentOptions = STPPaymentOptionTypeDefault;
config.shippingType = (delivery) ? STPShippingTypeDelivery : STPShippingTypeShipping;
-
+ config.cardScanningEnabled = YES;
[STPLocalizationUtils overrideLanguageTo:language];
STPAddCardViewController *addCardVC = [[STPAddCardViewController alloc] initWithConfiguration:config
diff --git a/Tests/Tests/STPPaymentCardTextFieldTest.m b/Tests/Tests/STPPaymentCardTextFieldTest.m
index 70af450cf25..ecfc009956b 100644
--- a/Tests/Tests/STPPaymentCardTextFieldTest.m
+++ b/Tests/Tests/STPPaymentCardTextFieldTest.m
@@ -75,7 +75,11 @@ - (void)testIntrinsicContentSize {
XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.width, 259, 0.1);
textField.font = [UIFont fontWithName:@"Avenir" size:44];
- XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.height, 61, 0.1);
+ if (@available(iOS 13.0, *)) {
+ XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.height, 62, 0.1);
+ } else {
+ XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.height, 61, 0.1);
+ }
XCTAssertEqualWithAccuracy(textField.intrinsicContentSize.width, 478, 0.1);
}
diff --git a/Tests/Tests/STPPaymentConfigurationTest.m b/Tests/Tests/STPPaymentConfigurationTest.m
index 7cff94a0c8f..83f34824262 100644
--- a/Tests/Tests/STPPaymentConfigurationTest.m
+++ b/Tests/Tests/STPPaymentConfigurationTest.m
@@ -39,6 +39,7 @@ - (void)testInit {
XCTAssertEqualObjects(paymentConfiguration.companyName, @"applicationName");
XCTAssertNil(paymentConfiguration.appleMerchantIdentifier);
XCTAssert(paymentConfiguration.canDeletePaymentOptions);
+ XCTAssert(!paymentConfiguration.cardScanningEnabled);
}
- (void)testApplePayEnabledSatisfied {
@@ -115,6 +116,7 @@ - (void)testCopyWithZone {
paymentConfigurationA.companyName = @"companyName";
paymentConfigurationA.appleMerchantIdentifier = @"appleMerchantIdentifier";
paymentConfigurationA.canDeletePaymentOptions = NO;
+ paymentConfigurationA.cardScanningEnabled = YES;
STPPaymentConfiguration *paymentConfigurationB = [paymentConfigurationA copy];
XCTAssertNotEqual(paymentConfigurationA, paymentConfigurationB);
@@ -134,6 +136,7 @@ - (void)testCopyWithZone {
NSSet *availableCountries = [NSSet setWithArray:@[@"US", @"CA", @"BT"]];
XCTAssertEqualObjects(paymentConfigurationB.availableCountries, availableCountries);
XCTAssertEqual(paymentConfigurationA.canDeletePaymentOptions, paymentConfigurationB.canDeletePaymentOptions);
+ XCTAssertEqual(paymentConfigurationA.cardScanningEnabled, paymentConfigurationB.cardScanningEnabled);
}
@end
diff --git a/ci_scripts/run_tests.sh b/ci_scripts/run_tests.sh
index d96ebd439af..c70d3ae2d09 100755
--- a/ci_scripts/run_tests.sh
+++ b/ci_scripts/run_tests.sh
@@ -29,15 +29,15 @@ if [[ "${carthage_exit_code}" != 0 ]]; then
die "Executing carthage failed with status code: ${carthage_exit_code}"
fi
-# Execute tests (iPhone 7 @ iOS 12.4)
-info "Executing tests (iPhone 7 @ iOS 12.4)..."
+# Execute tests (iPhone 8 @ iOS 13.6)
+info "Executing tests (iPhone 8 @ iOS 13.6)..."
xcodebuild clean test \
-workspace "Stripe.xcworkspace" \
-scheme "StripeiOS" \
-configuration "Debug" \
-sdk "iphonesimulator" \
- -destination "platform=iOS Simulator,name=iPhone 7,OS=12.4" \
+ -destination "platform=iOS Simulator,name=iPhone 8,OS=13.6" \
| xcpretty
exit_code="${PIPESTATUS[0]}"