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

Add credit card implementation #503

Merged
merged 25 commits into from Dec 4, 2018

Conversation

cdolm92
Copy link
Contributor

@cdolm92 cdolm92 commented Nov 30, 2018

📲 What

Adds the ability for user to save a new credit card payment using Stripe.

🤔 Why

Allowing users to add a new credit card payment increases the manageability of payment information in the app.

🛠 How

We built a custom form using the STPPaymentCardTextField and STPAPIClientclass. The STPAPIClientclass is used to handle the tokenizing the details.

We first create an STPCardParams instance with the payment details provided in the STPPaymentCardTextField and cardholderNameTextField. The STPAPIClient class then converts that into an STPToken instance where we pass the tokenID and stripeID to the CreatePaymentSourceInput() . More details on Stripe's Custom iOS Integration Guide
.

👀 See

Add New Card No Error Add New Card w/ Error
add-credit-cardgif add-credit-card-invalid

✅ Acceptance criteria

Saving New Payment Method No Error

Test Environment: Staged w/ Test Cards and Tokens

  • When Add New Card Screen Appears Keyboard should appear w/ Next return key
  • Enter name and check that capitalization happens after tapping spacebar in the Cardholder name field
  • When the Next return key is tapped cursor should be placed in the credit card info field
  • Type credit card information. Here are some Test Cards and Tokens to test with
  • Check that save button is enabled when both fields are filled and credit card information is valid
  • tap Save
  • 1. Keyboard should dismiss
  • 2. Loader on the top right navigation item should show
  • 3. Add New Card screen should dismiss
  • Payment Methods Screen appears with success message banner on the bottom of screen that reads "Got it! Your changes have been saved"
  • I should see the new credit card in the list of credit cards

Saving New Payment Method w/ Error

Test Environment: Production w/ Test Cards and Tokens

  • When Add New Card Screen Appears Keyboard should appear w/ Next return key
  • Enter name and check that capitalization happens after tapping spacebar in the Cardholder name field
  • When the Next return key is tapped cursor should be placed in the credit card info field
  • Type credit card information. Here are some Test Cards and Tokens to test with
  • Check that save button is enabled when both fields are filled and credit card information is valid
  • tap Save
  • 1. Keyboard should dismiss
  • 2. Loader on the top right navigation item should show
  • An error message banner on the bottom of screen that reads "Your card was declined." should appear.
  • Tap Cancel
  • Payment Methods Screen should appear and there should not be a new credit card in the list.

Todo

  • Add ACs and GIF for Saving New Payment Method w/ Error on this PR
  • Investigate 🤔PaymentMethodsViewModelTests failures.
  • Update Why explanation 🤷🏾‍♀️

@IBOutlet private weak var cardholderNameLabel: UILabel!
@IBOutlet private weak var cardholderNameTextField: UITextField!
@IBOutlet private weak var paymentField: STPPaymentCardTextField!
@IBOutlet private weak var paymentTextField: STPPaymentCardTextField!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paymentTextField sounds too general here and it's obvious that fields here pertain to credit card information so going to rename this to creditCardTextField instead.

self.saveButtonIsEnabled = Signal.combineLatest(
cardholderName.map { !$0.isEmpty },
self.paymentInfoIsValidProperty.signal
).map { cardholderName, validation in cardholderName && validation }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renaming tocardholderNameFieldNotEmpty would be good here.


let paymentInput = Signal.combineLatest(cardholderName, paymentDetails)
.map { cardholderName, paymentInfo in
(cardholderName, paymentInfo.0, paymentInfo.1, paymentInfo.2, paymentInfo.3) }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renaming paymentInfo to creditCardDetails

var addNewCardSuccess: Signal<String, NoError> { get }
var cardholderNameBecomeFirstResponder: Signal<Void, NoError> { get }
var dismissKeyboard: Signal<Void, NoError> { get }
var paymentDetails: Signal<(String, String, Int, Int, String), NoError> { get }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renaming to creditCardDetails

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decided not to. I now think that payment details is fine.

Copy link
Contributor

@Scollaco Scollaco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome job, @cdolm92 🌟 I just left a few minor comments.

}

@objc func cardholderNameTextFieldChanged(_ textField: UITextField) {
self.viewModel.inputs.cardholderNameChanged(textField.text ?? "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I would make cardholderName optional and simply pass textField.text. This way make the viewModel responsible for taking care of this logic.

self.viewModel.inputs.paymentInfo(valid: textField.isValid)
}

func stripeToken(cardholderName: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make this func private? Also, I would rename it. It's not clear of what's the function about until we see its implementation. Here I would rename it to createStripeToken(...

self.viewModel.inputs.cardholderNameTextFieldReturn()
}

func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, could you add internal keyword here?

.map { cardholderName, creditCardDetails in
(cardholderName, creditCardDetails.0, creditCardDetails.1, creditCardDetails.2, creditCardDetails.3) }

let tryAddCardAction = self.saveButtonTappedProperty.signal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using this just to dismiss the keyboard and to hide/show the activity indicator, it's ok to use the saveButtonTappedProperty, it gets easier to read this way

}
}

func testBecomeFirstResponderAndSaveEnabled() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test could be broken up in 2 pieces: one for becomeFirstResponder part and the other for saveEnabled

self.saveButtonIsEnabled.assertValues([false, false, true], "Enabled when form is valid.")
}

func testSetPublishableKey_CardInfoInValid() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think InValid here should be Invalid

@@ -123,7 +123,7 @@ internal final class AddNewCardViewController: UIViewController, STPPaymentCardT
self.viewModel.outputs.paymentDetails
.observeForUI()
.observeValues { [weak self] cardholderName, cardNumber, expMonth, expYear, cvc in
self?.stripeToken(cardholderName: cardholderName,
self?.createStripeToken(cardholderName: cardholderName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alignment is off here

@cdolm92 cdolm92 merged commit bcaf066 into feature-payment-methods Dec 4, 2018
@cdolm92 cdolm92 deleted the add-credit-card-implementation branch December 4, 2018 22:10
justinswart added a commit that referenced this pull request Feb 15, 2019
* New card screen ui (#471)

* add card view controller

* transition to add new card and uibarbuttons

* settings style applied to add new card screen

* changed keyboard style for textfields

* view controller tests

* placeholder for cardholder name textfield

* Record new screenshots

* new strings, added strings as place holder and text label

* pr feedback - inferring type for cancel button tintcolor

* pr feedback - keypath, no more lenses

* swiftlint fix

* new snapshots

* Payment methods (#457)

* Stripe element add new card (#473)

* New card screen ui (#471)

* add card view controller

* transition to add new card and uibarbuttons

* settings style applied to add new card screen

* changed keyboard style for textfields

* view controller tests

* placeholder for cardholder name textfield

* Record new screenshots

* new strings, added strings as place holder and text label

* pr feedback - inferring type for cancel button tintcolor

* pr feedback - keypath, no more lenses

* swiftlint fix

* new snapshots

* new stppaymentcardtextfield showing

* wip - placeholder colors

* snapshot tests

* Email undeliverable/unverified (#478)

* Adding emailIsVerified

* Renaming GraphUserEmail

* ViewModel tests

* ChangeEmailViewController tests

* Strings, swiftlint

* Strings & new screenshots

* Cleanup

* PR comments

* PR updates

* Test naming

* pr feedback

* new snapshots

* new placeholder colors

* new snapshots

* Delete payment methods (#479)

* Add payment method deletion

* Test datasource

* Test view model

* Clean-up and address PR feedback

* Rename credit card -> payment method

* Fix test

* Add credit card implementation (#503)

* Email undeliverable/unverified (#478)

* Adding emailIsVerified

* Renaming GraphUserEmail

* ViewModel tests

* ChangeEmailViewController tests

* Strings, swiftlint

* Strings & new screenshots

* Cleanup

* PR comments

* PR updates

* Test naming

* Use correct function to log events in crash logs (#481)

* Use correct function to log events in crash logs

* Fix indentation

* changed ui colors for textfield text and font size of text label

* vm and work on enabling save

* wip- get stripe token

* wip- payment source mutation

* wip- keyboard response

* wip- getting a stripe error here

* wip- error fix w/ publishable key, error banner showing

* wip- saving with error and keyboard functionality

* wip- updating card immediately

* wip

* wip - ACs met

* wip - refactor in view model, made IDs testable, begane VM tests

* wip -refactor on vm/vmtest

* wip -refactor deleted comments on vmt

* wip -snapshot tests

* swiftlint fixes

* renaming/refactor, corrected paymentmethodstests

* pr feedback

* swiftlint fixes

* changed function name

* indentation

* Payment methods event tracking (#496)

* Reverted code that deleted SettingsNewsletters from Storyboard.swift

* swiftlint

* Reverted code to instantiate settings newsletters vc on tests

* Alphabetized storyboard enum

* Settings payments colors (#530)

* Unsupported Credit Cards (#561)

* Viewmodel logic for unsupported cards

* Tests and accessibility fixes

* Simplify add card button

* Screenshots and string updates

* Cleanup design and remove logs

* Improving comment message

* Removing filter debug builds

* Addressing PR comments

* Updating payment methods screenshots

* Screenshots

* Possible fix for alignment issues

* Moving iOS 10 handling to output signal closure

* Remove line

* Renaming

* Last cleanup

* Strings & Asset update

* Switching from unsupported to supported

* Zipcode field in Add New Card screen (#566)

* Viewmodel logic for unsupported cards

* Tests and accessibility fixes

* Simplify add card button

* Screenshots and string updates

* Cleanup design and remove logs

* Improving comment message

* Removing filter debug builds

* Addressing PR comments

* Updating payment methods screenshots

* Screenshots

* Possible fix for alignment issues

* Moving iOS 10 handling to output signal closure

* Remove line

* Renaming

* Last cleanup

* Shared styled form field

* Adding functionality for zipcode form field

* More functionality

* View model & screenshot tests

* Swiftlint

* Cleanup

* Improving test and cleanup

* PR feedback and autocapitalizing zipcode

* Regenerating ChangePassword screenshots

* [Payment methods] CVC bug fix (#574)

* wip

* wip

* wip

* wip-swiftlint

* moved CreatePaymentSourceEnvelope to its own file, Mockservice

* swiftlint fix

* deleted unnecesary debugging code

* new paymentsource temploate and vm tests

* swiftlint fix

* fixed vm test

* PR feedback

* update schema

* [Payment methods] Minor bug fixes (#579)

* tableViewIsEditing false and show banner after dismiss

* Swiftlint

* Adding StripePublishableKey to example file

* Update publishableKey test

* Spacing

* Point-free helper

* [Payment Methods] Update padding and image view size (#581)

* Update padding and image view size

* Update stack view's spacing

* Update snapshots

* Use layoutMargins to set the padding

* [Payment methods] Disable edit button if no payment methods (#586)

* [Payment methods] Bugs & visual fixes (#578)

* Adding custom UITableViewHeader and always calling reload data on viewDidLoad

* Updating screenshots

* Cleanup autolayout warnings

* Screenshots

* PR comments

* No autolayout for footer view

* Cleanup

* Remove intrinsicContentSize

* Tableview appears without delay fix (#587)

* Fix header/footer auto sizing

* Update header background color

* Update snapshots

* Screenshots again

* Updating constraint priority to resolve ambiguity

* [Payment methods] Refresh payment methods table view properly (#588)

* Refresh payment methods table view properly

* Remove reference to weak self

* Rename delegate methods

* [Payment methods] Refetch payment methods on viewDidLoad or explicitly (by delegate) (#591)

* Do not refresh on viewWillAppear

* Use optional string to avoid unnecessary initializer

* Bring back viewWillAppear signal to better reflect view controller's lifecycle state

* [Payment methods] A11y - Credit card name (#593)

* Add card name to the a11y label

* Add comment

* [Payment Methods] Design Fixes (#592)

* Design fixes

* Fresh screenshots ✨

* Remove recordMode

* Deleting stale screenshots

* Re-add accidentally deleted line

* update strings

* [Payment Methods] Support optional card type and error handling for payment methods (#596)

* Optional card type, payment methods load error

* Optional support for imageName

* Fix accessibility label

* Fix merge conflict spacing

* Swiftlint

* Implementing PR suggestions

* Fixed bug that shows wrong expiration date (#595)

* [Payment Methods] Add New Card Design Fixes (#594)

* error message present and return key to done

* removed error message banner for incomplete payment details

* new snapshots for correct insets

* [Payment methods] Optimistically disable edit button on card deletion (#589)

* Disable edit button optimistically, use stored cards in result from card deletion

* Fix issue where edit button is not re-enabled upon the failed deletion of the last card.

* Remove unnecessary .init

* Add skipRepeats() to editButtonIsEnabled signal.

* Fixing potential reference cycles (#598)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants