Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update postal code logic #1479

Merged
merged 18 commits into from
Feb 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode11.2
osx_image: xcode11.3
branches:
only:
- master
Expand Down
8 changes: 4 additions & 4 deletions Example/Basic Integration/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class SettingsViewController: UITableViewController {

private var theme: Theme = .Default
private var additionalPaymentOptions: STPPaymentOptionType = .default
private var requiredBillingAddressFields: RequiredBillingAddressFields = .None
private var requiredBillingAddressFields: RequiredBillingAddressFields = .PostalCode
private var requiredShippingAddressFields: RequiredShippingAddressFields = .PostalAddressPhone
private var shippingType: ShippingType = .Shipping
private var country: Country = .US
Expand Down Expand Up @@ -217,14 +217,14 @@ class SettingsViewController: UITableViewController {

fileprivate enum RequiredBillingAddressFields: String {
case None = "None"
case Zip = "Zip"
case PostalCode = "Postal code"
case Name = "Name"
case Full = "Full"

init(row: Int) {
switch row {
case 0: self = .None
case 1: self = .Zip
case 1: self = .PostalCode
case 2: self = .Name
default: self = .Full
}
Expand All @@ -233,7 +233,7 @@ class SettingsViewController: UITableViewController {
var stpBillingAddressFields: STPBillingAddressFields {
switch self {
case .None: return .none
case .Zip: return .zip
case .PostalCode: return .postalCode
case .Name: return .name
case .Full: return .full
}
Expand Down
238 changes: 235 additions & 3 deletions Example/BasicIntegrationUITests/BasicIntegrationUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class BasicIntegrationUITests: XCTestCase {

func disableAddressEntry(_ app: XCUIApplication) {
app.navigationBars["Emoji Apparel"].buttons["Settings"].tap()
app.tables.children(matching: .cell).element(boundBy: 10).staticTexts["None"].tap()
let noneButton = app.tables.children(matching: .cell).element(boundBy: 10).staticTexts["None"]
waitToAppear(noneButton)
noneButton.tap()
app.navigationBars["Settings"].buttons["Done"].tap()
}

Expand Down Expand Up @@ -115,6 +117,7 @@ class BasicIntegrationUITests: XCTestCase {
app.scrollViews.otherElements.buttons["Continue"].tap()
let success = app.alerts["Success"].buttons["OK"]
waitToAppear(success)

success.tap()
}

Expand Down Expand Up @@ -151,12 +154,12 @@ class BasicIntegrationUITests: XCTestCase {
let cardNumberField = tablesQuery.textFields["card number"]
let cvcField = tablesQuery.textFields["CVC"]
let expirationDateField = tablesQuery.textFields["expiration date"]
let zipField = tablesQuery.textFields["ZIP"]
cardNumberField.tap()
cardNumberField.typeText("4000000000000069")
expirationDateField.tap()
expirationDateField.typeText("02/28")
cvcField.tap()
cvcField.typeText("223")
zipField.typeText("90210")

let addcardviewcontrollernavbardonebuttonidentifierButton = app.navigationBars["Add a Card"].buttons["AddCardViewControllerNavBarDoneButtonIdentifier"]
addcardviewcontrollernavbardonebuttonidentifierButton.tap()
Expand Down Expand Up @@ -236,3 +239,232 @@ class BasicIntegrationUITests: XCTestCase {
XCTAssertFalse(visa.isSelected)
}
}

class FrenchAndBelizeBasicIntegrationUITests: XCTestCase {

override func setUp() {
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false

// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
let app = XCUIApplication()
let stripePublishableKey = "pk_test_6Q7qTzl8OkUj5K5ArgayVsFD00Sa5AHMj3"
let backendBaseURL = "https://stripe-mobile-test-backend-17.herokuapp.com/"
app.launchArguments.append(contentsOf: ["-StripePublishableKey", stripePublishableKey, "-StripeBackendBaseURL", backendBaseURL, "-AppleLanguages", "(fr)", "-AppleLocale", "en_BZ"])
app.launch()
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

func disableAddressEntry(_ app: XCUIApplication) {
app.navigationBars["Emoji Apparel"].buttons["Settings"].tap()
app.tables.children(matching: .cell).element(boundBy: 10).staticTexts["None"].tap()
waitToAppear(app.navigationBars["Settings"].buttons["OK"])
app.navigationBars["Settings"].buttons["OK"].tap()
}

func selectItems(_ app: XCUIApplication) {
let cellsQuery = app.collectionViews.cells
cellsQuery.otherElements.containing(.staticText, identifier:"👠").element.tap()
app.collectionViews.staticTexts["👞"].tap()
cellsQuery.otherElements.containing(.staticText, identifier:"👗").children(matching: .other).element(boundBy: 0).tap()
}

func waitToAppear(_ target: Any?) {
let exists = NSPredicate(format: "exists == 1")
expectation(for: exists, evaluatedWith: target, handler: nil)
waitForExpectations(timeout: 60.0, handler: nil)
}

func testSimpleTransaction() {
let app = XCUIApplication()
disableAddressEntry(app)
selectItems(app)

app.buttons["Buy Now"].tap()
app.tables.otherElements.containing(.staticText, identifier:"Pay from").children(matching: .button).element.tap()
let visa = app.tables.staticTexts["Visa se terminant par 4242"]
waitToAppear(visa)
visa.tap()
app.buttons["Buy"].tap()
let success = app.alerts["Success"].buttons["OK"]
waitToAppear(success)
success.tap()
}

func test3DS1() {
let app = XCUIApplication()
disableAddressEntry(app)
selectItems(app)

let buyNowButton = app.buttons["Buy Now"]
buyNowButton.tap()

app.tables.otherElements.containing(.staticText, identifier:"Pay from").children(matching: .button).element.tap()
let visa3063 = app.tables.staticTexts["Visa se terminant par 3063"]
waitToAppear(visa3063)
visa3063.tap()

let buyButton = app.buttons["Buy"]
buyButton.tap()

let webViewsQuery = app.webViews
let completeAuth = webViewsQuery.buttons["COMPLETE AUTHENTICATION"]
waitToAppear(completeAuth)
completeAuth.tap()
let successButton = app.alerts["Success"].buttons["OK"]
waitToAppear(successButton)
successButton.tap()
buyButton.tap()

let failAuth = webViewsQuery.buttons["FAIL AUTHENTICATION"]
waitToAppear(failAuth)
failAuth.tap()
let errorButton = app.alerts["Error"].buttons["OK"]
waitToAppear(errorButton)
errorButton.tap()
}

func test3DS2() {
let app = XCUIApplication()
disableAddressEntry(app)
selectItems(app)

let buyNowButton = app.buttons["Buy Now"]
buyNowButton.tap()
app.tables.otherElements.containing(.staticText, identifier:"Pay from").children(matching: .button).element.tap()
let visa = app.tables.staticTexts["Visa se terminant par 3220"]
waitToAppear(visa)
visa.tap()
app.buttons["Buy"].tap()

let elementsQuery = app.scrollViews.otherElements
let learnMore = elementsQuery.staticTexts["Learn more about authentication"]
waitToAppear(learnMore)
learnMore.tap()
elementsQuery.staticTexts["Need help?"].tap()
app.scrollViews.otherElements.buttons["Continue"].tap()
let success = app.alerts["Success"].buttons["OK"]
waitToAppear(success)
success.tap()
}

func testPopApplePaySheet() {
let app = XCUIApplication()
disableAddressEntry(app)
selectItems(app)

let buyNowButton = app.buttons["Buy Now"]
buyNowButton.tap()

let tablesQuery = app.tables
tablesQuery.otherElements.containing(.staticText, identifier:"Pay from").children(matching: .button).element.tap()
let applePay = tablesQuery.staticTexts["Apple Pay"]
waitToAppear(applePay)
applePay.tap()
app.buttons["Buy"].tap()
}

func testCCEntry() {
let app = XCUIApplication()
disableAddressEntry(app)
selectItems(app)

let buyNowButton = app.buttons["Buy Now"]
buyNowButton.tap()
let tablesQuery = app.tables
tablesQuery.otherElements.containing(.staticText, identifier:"Pay from").children(matching: .button).element.tap()

let addButton = app.tables.staticTexts["Ajouter une nouvelle carte..."]
waitToAppear(addButton)
addButton.tap()

let cardNumberField = tablesQuery.textFields["numéro de carte"]
let cvcField = tablesQuery.textFields["Code CVC"]
let expirationDateField = tablesQuery.textFields["date d\'expiration"]
cardNumberField.tap()
cardNumberField.typeText("4000000000000069")
expirationDateField.typeText("02/28")
cvcField.typeText("223")

let addcardviewcontrollernavbardonebuttonidentifierButton = app.navigationBars["Ajouter une carte"].buttons["AddCardViewControllerNavBarDoneButtonIdentifier"]
addcardviewcontrollernavbardonebuttonidentifierButton.tap()
app.alerts["Votre carte est arrivée à expiration."].buttons["OK"].tap()
cardNumberField.tap()
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: 4)
cardNumberField.typeText(deleteString)
cardNumberField.typeText("0341")
addcardviewcontrollernavbardonebuttonidentifierButton.tap()
let buyButton = app.buttons["Buy"]
waitToAppear(buyButton)
buyButton.tap()
let errorButton = app.alerts["Error"].buttons["OK"]
waitToAppear(errorButton)
errorButton.tap()
}

func testPaymentOptionsDefault() {
// Note that the example backend creates a new Customer every time you start the app
// A STPPaymentOptionsVC w/o a selected card...
let app = XCUIApplication()
disableAddressEntry(app)
selectItems(app)
let buyNowButton = app.buttons["Buy Now"]
buyNowButton.tap()
let tablesQuery = app.tables
let payFromButton = tablesQuery.otherElements.containing(.staticText, identifier:"Pay from").children(matching: .button).element
payFromButton.tap()

// ...preselects Apple Pay by default
let applePay = tablesQuery.cells["Apple Pay"]
waitToAppear(applePay)
XCTAssertTrue(applePay.isSelected)

// Selecting another payment method...
let visa = tablesQuery.cells["Visa se terminant par 3220"]
visa.tap()

// ...and resetting the PaymentOptions VC...
// Note that STPPaymentContext clears its cache and refetches every time it's initialized, which happens whenever CheckoutViewController is pushed on
app.navigationBars["Checkout"].buttons["Products"].tap()
buyNowButton.tap()
payFromButton.tap()

// ...should keep the 3220 card selected
XCTAssertTrue(visa.isSelected)
XCTAssertFalse(applePay.isSelected)

// Reselecting Apple Pay...
applePay.tap()

// ...and resetting the PaymentOptions VC...
app.navigationBars["Checkout"].buttons["Products"].tap()
buyNowButton.tap()
payFromButton.tap()

// ...should keep Apple Pay selected
XCTAssertTrue(applePay.isSelected)
XCTAssertFalse(visa.isSelected)

// Selecting another payment method...
visa.tap()

// ...and logging out...
app.navigationBars["Checkout"].buttons["Products"].tap()
app.navigationBars["Emoji Apparel"].buttons["Settings"].tap()
app.tables.children(matching: .cell).element(boundBy: 16).staticTexts["Log out"].tap()
app.navigationBars["Settings"].buttons["OK"].tap()

// ...and going back to PaymentOptionsVC...
buyNowButton.tap()
payFromButton.tap()

// ..should not retain the visa default
waitToAppear(applePay)
XCTAssertTrue(applePay.isSelected)
XCTAssertFalse(visa.isSelected)
}
}

15 changes: 13 additions & 2 deletions Example/UI Examples/BrowseViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import Stripe
class BrowseViewController: UITableViewController, STPAddCardViewControllerDelegate, STPPaymentOptionsViewControllerDelegate, STPShippingAddressViewControllerDelegate {

enum Demo: Int {
static let count = 6
static let count = 7
case STPPaymentCardTextField
case STPAddCardViewController
case STPAddCardViewControllerWithAddress
case STPPaymentOptionsViewController
case STPPaymentOptionsFPXViewController
case STPShippingInfoViewController
Expand All @@ -23,7 +24,8 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
var title: String {
switch self {
case .STPPaymentCardTextField: return "Card Field"
case .STPAddCardViewController: return "Card Form with Billing Address"
case .STPAddCardViewController: return "Card Form"
case .STPAddCardViewControllerWithAddress: return "Card Form with Billing Address"
case .STPPaymentOptionsViewController: return "Payment Option Picker"
case .STPPaymentOptionsFPXViewController: return "Payment Option Picker (With FPX)"
case .STPShippingInfoViewController: return "Shipping Info Form"
Expand All @@ -35,6 +37,7 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
switch self {
case .STPPaymentCardTextField: return "STPPaymentCardTextField"
case .STPAddCardViewController: return "STPAddCardViewController"
case .STPAddCardViewControllerWithAddress: return "STPAddCardViewController"
case .STPPaymentOptionsViewController: return "STPPaymentOptionsViewController"
case .STPPaymentOptionsFPXViewController: return "STPPaymentOptionsViewController"
case .STPShippingInfoViewController: return "STPShippingInfoViewController"
Expand Down Expand Up @@ -86,6 +89,14 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg
navigationController.navigationBar.stp_theme = theme
present(navigationController, animated: true, completion: nil)
case .STPAddCardViewController:
let config = STPPaymentConfiguration()
let viewController = STPAddCardViewController(configuration: config, theme: theme)
viewController.apiClient = MockAPIClient()
viewController.delegate = self
let navigationController = UINavigationController(rootViewController: viewController)
navigationController.navigationBar.stp_theme = theme
present(navigationController, animated: true, completion: nil)
case .STPAddCardViewControllerWithAddress:
let config = STPPaymentConfiguration()
config.requiredBillingAddressFields = .full
let viewController = STPAddCardViewController(configuration: config, theme: theme)
Expand Down
9 changes: 8 additions & 1 deletion MIGRATING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Migration Guides

### Migrating from versions < 19.0.0

#### `publishableKey` and `stripeAccount` changes
* Deprecates `publishableKey` and `stripeAccount` properties of `STPPaymentConfiguration`.
* If you used `STPPaymentConfiguration.sharedConfiguration` to set `publishableKey` and/or `stripeAccount`, use `STPAPIClient.sharedClient` instead.
* If you passed a STPPaymentConfiguration instance to an SDK component, you should instead create an STPAPIClient, set publishableKey on it, and set the SDK component's APIClient property.
Expand Down Expand Up @@ -42,6 +42,13 @@ We recommend you do the following:
// ...or use it directly to make API requests with `stripeAccount` set:
connectedAccountAPIClient.createToken(withCard:...) // e.g. if you are using Tokens + Charges
```
#### Postal code changes
* The user's postal code is now collected by default in countries that support postal codes. We always recommend collecting a postal code to increase card acceptance rates and reduce fraud. To disable this behavior:
* For STPPaymentContext and other full-screen UI, set your `STPPaymentConfiguration`'s `.requiredBillingAddressFields` to `STPBillingAddressFieldsNone`.
* For a PKPaymentRequest, set `.requiredBillingContactFields` to an empty set. If your app supports iOS 10, also set `.requiredBillingAddressFields` to `PKAddressFieldNone`.
* For STPPaymentCardView, set `.postalCodeEntryEnabled` to `NO`.
* Users may now enter spaces, dashes, and uppercase letters into the postal code field in situations where the user has not explicitly selected a country. This allows users with non-US addreses to enter their postal code.
* `STPBillingAddressFieldsZip` has been renamed to `STPBillingAddressFieldsPostalCode`.

### Migrating from versions < 18.0.0
* Some error messages from the Payment Intents API are now localized to the user's display language. If your application's logic depends on specific `message` strings from the Stripe API, please use the error [`code`](https://stripe.com/docs/error-codes) instead.
Expand Down
3 changes: 2 additions & 1 deletion Stripe/NSCharacterSet+Stripe.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

+ (instancetype)stp_asciiDigitCharacterSet;
+ (instancetype)stp_invertedAsciiDigitCharacterSet;

+ (instancetype)stp_postalCodeCharacterSet;
+ (instancetype)stp_invertedPostalCodeCharacterSet;

@end

Expand Down
Loading