Skip to content

Commit

Permalink
Adds us_bank_account PaymentMethod bindings (#827)
Browse files Browse the repository at this point in the history
* us bank account

* Adds option to pass custom API version to our testing backend

* Adds tests for us_bank_account

* Adds example view controller

* Changelog

* * Removes unneeded product param from example
* Adds "(ACH)" to docstrings
* Removes extra space in docstrings
* Removes dead paymentintent confirmation test method signature
* Moves fulfills to end of completion block

* Add new strings

* uncomment test

* Adds support for verifying microdeposits (#871)

* Adds support for verifying microdeposits

* Update Stripe/STPAPIClient+Payments.swift

Co-authored-by: Mel <[email protected]>

* Update Stripe/STPAPIClient+Payments.swift

Co-authored-by: Mel <[email protected]>

* Update Stripe/STPAPIClient+Payments.swift

Co-authored-by: Mel <[email protected]>

* Update Stripe/STPAPIClient+Payments.swift

Co-authored-by: Mel <[email protected]>

* Less repetition in verify code

* Fun with generics

* Whitespace cleanup

* Adds support for payment_method_options[us_bank_account] (#877)

* Adds support for payment_method_options[us_bank_account]

* Update PMO form encoding test

* Update Stripe/STPConfirmPaymentMethodOptions.swift

Co-authored-by: Yuki <[email protected]>

* Update Stripe/STPConfirmUSBankAccountOptions.swift

Co-authored-by: Yuki <[email protected]>

Co-authored-by: Yuki <[email protected]>

Co-authored-by: Mel <[email protected]>
Co-authored-by: Yuki <[email protected]>

* Fix build

Co-authored-by: Mel <[email protected]>
Co-authored-by: Yuki <[email protected]>
  • Loading branch information
3 people authored Mar 18, 2022
1 parent 4b6d20e commit fd1543d
Show file tree
Hide file tree
Showing 30 changed files with 1,432 additions and 104 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 21.13.0 2022-03-15
* [Changed] Binary framework distribution now requires Xcode 13. Carthage users using Xcode 12 need to add the `--no-use-binaries` flag.
* [Added] `us_bank_account` PaymentMethod is now supported.

### PaymentSheet
* [Fixed] Fixed potential crash when using PaymentSheet custom flow with SwiftUI.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
361962592450DA280025D60B /* GiropayExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 361962582450DA280025D60B /* GiropayExampleViewController.m */; };
362AA09E269E4F59000DE28F /* StripeCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 362AA09D269E4F59000DE28F /* StripeCore.framework */; };
362AA09F269E4F60000DE28F /* StripeCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 362AA09D269E4F59000DE28F /* StripeCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
36345D5D27D0384D00D2445D /* USBankAccountExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36345D5C27D0384D00D2445D /* USBankAccountExampleViewController.swift */; };
364FC2D024201F62002879EB /* AUBECSDebitExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 364FC2CF24201F62002879EB /* AUBECSDebitExampleViewController.m */; };
366F93B0225FF2A2005CFBF6 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 366F93AF225FF2A2005CFBF6 /* README.md */; };
36B6CB5A234BE3FA00331C38 /* PaymentExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36B6CB59234BE3FA00331C38 /* PaymentExampleViewController.m */; };
Expand Down Expand Up @@ -101,6 +102,7 @@
361962572450DA280025D60B /* GiropayExampleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GiropayExampleViewController.h; sourceTree = "<group>"; };
361962582450DA280025D60B /* GiropayExampleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GiropayExampleViewController.m; sourceTree = "<group>"; };
362AA09D269E4F59000DE28F /* StripeCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = StripeCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
36345D5C27D0384D00D2445D /* USBankAccountExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USBankAccountExampleViewController.swift; sourceTree = "<group>"; };
364FC2CE24201F62002879EB /* AUBECSDebitExampleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AUBECSDebitExampleViewController.h; sourceTree = "<group>"; };
364FC2CF24201F62002879EB /* AUBECSDebitExampleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AUBECSDebitExampleViewController.m; sourceTree = "<group>"; };
366F93AF225FF2A2005CFBF6 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
Expand Down Expand Up @@ -239,6 +241,7 @@
0FC7C95525B1127000E99D3E /* AfterpayClearpayExampleViewController.swift */,
61C5FA6F27225B1100D73313 /* KlarnaExampleViewController.swift */,
6B48785727BDDB9000B7632D /* AffirmExampleViewController.swift */,
36345D5C27D0384D00D2445D /* USBankAccountExampleViewController.swift */,
);
path = "Non-Card Payment Examples";
sourceTree = "<group>";
Expand Down Expand Up @@ -370,6 +373,7 @@
04533E8D1A687F5D00C7E52E /* main.m in Sources */,
31A8934D230F6ABD007ABE37 /* FPXExampleViewController.m in Sources */,
36B6CB5D234BEB8400331C38 /* SEPADebitExampleViewController.m in Sources */,
36345D5D27D0384D00D2445D /* USBankAccountExampleViewController.swift in Sources */,
69A6C306246E63A2005FF304 /* EPSExampleViewController.m in Sources */,
C12C50DD1E57B3C800EC6D58 /* BrowseExamplesViewController.m in Sources */,
3171D89A24DDECD80038A218 /* SofortExampleViewController.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 21;
return 22;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Expand Down Expand Up @@ -114,6 +114,9 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
case 20:
cell.textLabel.text = @"Affirm (PaymentMethods)";
break;
case 21:
cell.textLabel.text = @"US Bank Account";
break;
}
return cell;
}
Expand Down Expand Up @@ -254,6 +257,12 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
viewController = exampleVC;
break;
}
case 21: {
USBankAccountExampleViewController *exampleVC = [USBankAccountExampleViewController new];
exampleVC.delegate = self;
viewController = exampleVC;
break;
}
}
[self.navigationController pushViewController:viewController animated:YES];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//
// USBankAccountExampleViewController.swift
// Non-Card Payment Examples
//
// Created by Cameron Sabol on 3/2/22.
// Copyright © 2022 Stripe. All rights reserved.
//

import UIKit

@_spi(STP) import StripeCore // TODO(csabol): Remove before GA

class USBankAccountExampleViewController: UIViewController {
@objc weak var delegate: ExampleViewControllerDelegate?
var inProgress: Bool = false {
didSet {
navigationController?.navigationBar.isUserInteractionEnabled = !inProgress
payButton.isEnabled = !inProgress
inProgress
? activityIndicatorView.startAnimating() : activityIndicatorView.stopAnimating()
fieldsStackView.isUserInteractionEnabled = !inProgress
}
}

// UI
lazy var nameField: UITextField = {
let textField = UITextField()
textField.autocorrectionType = .no
textField.borderStyle = .roundedRect
textField.placeholder = "Name"
return textField
}()
lazy var emailField: UITextField = {
let textField = UITextField()
textField.autocorrectionType = .no
textField.autocapitalizationType = .none
textField.borderStyle = .roundedRect
textField.placeholder = "Email"
return textField
}()
lazy var accountNumberField: UITextField = {
let textField = UITextField()
textField.borderStyle = .roundedRect
textField.placeholder = "Account number"
textField.keyboardType = .numberPad
return textField
}()
lazy var routingNumberField: UITextField = {
let textField = UITextField()
textField.borderStyle = .roundedRect
textField.placeholder = "Routing number"
textField.keyboardType = .numberPad
return textField
}()
lazy var accountTypeSelector: UISegmentedControl = UISegmentedControl(items: ["checking", "savings"])
lazy var accountHolderTypeSelector: UISegmentedControl = UISegmentedControl(items: ["individual", "company"])
lazy var fieldsStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [nameField,
emailField,
accountNumberField,
routingNumberField,
accountTypeSelector,
accountHolderTypeSelector])
stackView.axis = .vertical
stackView.alignment = .leading
stackView.spacing = 4
return stackView
}()
lazy var activityIndicatorView = {
return UIActivityIndicatorView(style: .gray)
}()
lazy var payButton: UIButton = {
let button = UIButton(type: .roundedRect)
button.setTitle("Pay with US Bank Account", for: .normal)
button.addTarget(self, action: #selector(didTapPayButton), for: .touchUpInside)
return button
}()

override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "US Bank Account"
[payButton, activityIndicatorView].forEach { subview in
view.addSubview(subview)
subview.translatesAutoresizingMaskIntoConstraints = false
}
fieldsStackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(fieldsStackView)

let mandateLabel = UILabel()
mandateLabel.numberOfLines = 0
mandateLabel.font = .preferredFont(forTextStyle: .caption1)
mandateLabel.text = """
By clicking Pay with US Bank Account, you authorize Non-Card Payment Examples to debit the bank account specified above for any amount owed for charges arising from your use of Non-Card Payment Examples’ services and/or purchase of products from Non-Card Payment Examples, pursuant to Non-Card Payment Examples’ website and terms, until this authorization is revoked. You may amend or cancel this authorization at any time by providing notice to Non-Card Payment Examples with 30 (thirty) days notice.
If you use Non-Card Payment Examples’ services or purchase additional products periodically pursuant to Non-Card Payment Examples’ terms, you authorize Non-Card Payment Examples to debit your bank account periodically. Payments that fall outside of the regular debits authorized above will only be debited after your authorization is obtained.
"""
mandateLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mandateLabel)

let constraints = [
fieldsStackView.topAnchor.constraint(equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor, multiplier: 1),
fieldsStackView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: fieldsStackView.trailingAnchor, multiplier: 1),

nameField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor),
emailField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor),
accountNumberField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor),
routingNumberField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor),

mandateLabel.topAnchor.constraint(equalToSystemSpacingBelow: fieldsStackView.bottomAnchor, multiplier: 1),
mandateLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: mandateLabel.trailingAnchor, multiplier: 1),


payButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
payButton.topAnchor.constraint(equalToSystemSpacingBelow: mandateLabel.bottomAnchor, multiplier: 1),

activityIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
activityIndicatorView.centerYAnchor.constraint(equalTo: payButton.centerYAnchor),
]
NSLayoutConstraint.activate(constraints)
}

@objc func didTapPayButton() {
guard STPAPIClient.shared.publishableKey != nil else {
delegate?.exampleViewController(
self, didFinishWithMessage: "Please set a Stripe Publishable Key in Constants.m")
return
}
inProgress = true
pay()
}

}

extension USBankAccountExampleViewController {
@objc func pay() {
// 1. Create a US Bank Account PaymentIntent
MyAPIClient.shared().createPaymentIntent(
completion: { [self] (result, clientSecret, error) in
guard let clientSecret = clientSecret else {
self.delegate?.exampleViewController(self, didFinishWithError: error)
return
}
// 2. Collect payment method params information
let usBankAccountParams = STPPaymentMethodUSBankAccountParams()
usBankAccountParams.accountType = accountTypeSelector.titleForSegment(at: accountTypeSelector.selectedSegmentIndex) == "checking" ? .checking : .savings
usBankAccountParams.accountHolderType = accountHolderTypeSelector.titleForSegment(at: accountHolderTypeSelector.selectedSegmentIndex) == "individual" ? .individual : .company
usBankAccountParams.accountNumber = accountNumberField.text
usBankAccountParams.routingNumber = routingNumberField.text

let billingDetails = STPPaymentMethodBillingDetails()
billingDetails.name = nameField.text
billingDetails.email = emailField.text

let paymentMethodParams = STPPaymentMethodParams(usBankAccount: usBankAccountParams,
billingDetails: billingDetails,
metadata: nil)
let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret)
paymentIntentParams.paymentMethodParams = paymentMethodParams
paymentIntentParams.returnURL = "payments-example://safepay/"

// 3. Confirm payment
STPAPIClient.shared.betas.insert("us_bank_account_beta=v2") // TODO(csabol): Remove before GA
STPPaymentHandler.shared().confirmPayment(
paymentIntentParams, with: self
) { (status, intent, error) in
switch status {
case .canceled:
self.delegate?.exampleViewController(
self, didFinishWithMessage: "Cancelled")
case .failed:
self.delegate?.exampleViewController(self, didFinishWithError: error)
case .succeeded:
self.delegate?.exampleViewController(
self, didFinishWithMessage: "Payment successfully initiated. Will fulfill after microdeposit verification")
@unknown default:
fatalError()
}
}
}, additionalParameters: "supported_payment_methods=us_bank_account")
}
}

extension USBankAccountExampleViewController: STPAuthenticationContext {
func authenticationPresentingViewController() -> UIViewController {
self
}
}
Loading

0 comments on commit fd1543d

Please sign in to comment.