Skip to content

Commit

Permalink
Add FPX to Basic Integration (#1390)
Browse files Browse the repository at this point in the history
* Add FPX to Basic Integration

* Fix comments and custom integration

* Fix entitlements

* Fix bank icons

* Fix test

* Add analytics, fix up table view to handle 0 cards with APMs

* Fix CollectionView to not lose items on reload

* Fix Apple Pay appearing when not supported

* Update based on feedback

* Fix previous bad fix, the individual responsible has been sacked
  • Loading branch information
davidme-stripe authored Sep 27, 2019
1 parent f953403 commit a1df69a
Show file tree
Hide file tree
Showing 43 changed files with 530 additions and 104 deletions.
2 changes: 1 addition & 1 deletion Example/Custom Integration/ApplePayExampleViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ - (void)_createAndConfirmPaymentIntentWithPaymentMethod:(STPPaymentMethod *)paym
[[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams
withAuthenticationContext:self
completion:paymentHandlerCompletion];
}];
} additionalParameters:nil];
}

- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ - (void)pay {
break;
}
}];
}];
} additionalParameters:nil];
}

@end
2 changes: 1 addition & 1 deletion Example/Custom Integration/FPXExampleViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ - (void)payWithBankAccount:(STPPaymentMethodParams *)paymentMethodParams {
break;
}
}];
}];
} additionalParameters:@"country=my"];
}

- (void)bankSelectionViewController:(nonnull STPBankSelectionViewController *)bankViewController didCreatePaymentMethodParams:(STPPaymentMethodParams *)paymentMethodParams {
Expand Down
3 changes: 2 additions & 1 deletion Example/Custom Integration/MyAPIClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ typedef void (^STPCreateSetupIntentCompletionHandler)(MyAPIClientResult status,
method signature is the most interesting part: you need some way to ask *your* backend to create
a PaymentIntent with the correct properties, and then it needs to pass the client secret back.
@param additionalParameters additional parameters to pass to the example backend
@param completion completion block called with status of backend call & the client secret if successful.
@see https://stripe.com/docs/payments/payment-intents/ios
*/
- (void)createPaymentIntentWithCompletion:(STPPaymentIntentCreationHandler)completion;
- (void)createPaymentIntentWithCompletion:(STPPaymentIntentCreationHandler)completion additionalParameters:(NSString * _Nullable)additionalParameters;

#pragma mark - PaymentIntents (manual confirmation)

Expand Down
7 changes: 5 additions & 2 deletions Example/Custom Integration/MyAPIClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ - (void)_callOnMainThread:(void (^)(void))block {
method signature is the most interesting part: you need some way to ask *your* backend to create
a PaymentIntent with the correct properties, and then it needs to pass the client secret back.
@param amount Amount to charge the customer
@param additionalParameters additional parameters to pass to the example backend
@param completion completion block called with status of backend call & the client secret if successful.
*/
- (void)createPaymentIntentWithCompletion:(STPPaymentIntentCreationHandler)completion {
- (void)createPaymentIntentWithCompletion:(STPPaymentIntentCreationHandler)completion additionalParameters:(NSString *)additionalParameters {
if (!BackendBaseURL) {
NSError *error = [NSError errorWithDomain:@"MyAPIClientErrorDomain"
code:0
Expand All @@ -64,6 +64,9 @@ - (void)createPaymentIntentWithCompletion:(STPPaymentIntentCreationHandler)compl
// example-ios-backend allows passing metadata through to Stripe
@"B3E611D1-5FA1-4410-9CEC-00958A5126CB"
];
if (additionalParameters != nil) {
postBody = [postBody stringByAppendingFormat:@"&%@", additionalParameters];
}
NSData *data = [postBody dataUsingEncoding:NSUTF8StringEncoding];

NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
Expand Down
26 changes: 20 additions & 6 deletions Example/Standard Integration/BrowseProductsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ import UIKit
struct Product {
let emoji: String
let price: Int

var priceText: String {
return "$\(price/100).00"
}
}

class BrowseProductsViewController: UICollectionViewController {
Expand All @@ -41,7 +37,7 @@ class BrowseProductsViewController: UICollectionViewController {
var shoppingCart = [Product]() {
didSet {
let price = shoppingCart.reduce(0) { result, product in result + product.price }
buyButton.priceLabel.text = "$\(price/100).00"
buyButton.priceLabel.text = numberFormatter.string(from: NSNumber(value: Float(price)/100))!
let enabled = price > 0
if enabled == buyButton.isEnabled {
return
Expand All @@ -55,6 +51,13 @@ class BrowseProductsViewController: UICollectionViewController {
}, completion: nil)
}
}

var numberFormatter : NumberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.usesGroupingSeparator = true
return numberFormatter
}()

let settingsVC = SettingsViewController()

Expand Down Expand Up @@ -88,6 +91,8 @@ class BrowseProductsViewController: UICollectionViewController {
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Products", style: .plain, target: nil, action: nil)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Settings", style: .plain, target: self, action: #selector(showSettings))

self.numberFormatter.locale = self.settingsVC.settings.currencyLocale

collectionView?.register(EmojiCell.self, forCellWithReuseIdentifier: "Cell")
collectionView?.allowsMultipleSelection = true

Expand Down Expand Up @@ -127,6 +132,14 @@ class BrowseProductsViewController: UICollectionViewController {
self.navigationController?.navigationBar.titleTextAttributes = titleAttributes
self.navigationItem.leftBarButtonItem?.setTitleTextAttributes(buttonAttributes, for: UIControl.State())
self.navigationItem.backBarButtonItem?.setTitleTextAttributes(buttonAttributes, for: UIControl.State())

self.numberFormatter.locale = self.settingsVC.settings.currencyLocale
self.view.setNeedsLayout()
let selectedItems = self.collectionView.indexPathsForSelectedItems ?? []
self.collectionView.reloadData()
for item in selectedItems {
self.collectionView.selectItem(at: item, animated: false, scrollPosition: [])
}
}

@objc func showSettings() {
Expand Down Expand Up @@ -168,7 +181,8 @@ extension BrowseProductsViewController: UICollectionViewDelegateFlowLayout {
}

let product = self.productsAndPrices[indexPath.item]
cell.configure(with: product)

cell.configure(with: product, numberFormatter: self.numberFormatter)
return cell
}

Expand Down
21 changes: 9 additions & 12 deletions Example/Standard Integration/CheckoutViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class CheckoutViewController: UIViewController {

// These values will be shown to the user when they purchase with Apple Pay.
let companyName = "Emoji Apparel"
let paymentCurrency = "usd"
let paymentCurrency: String

let paymentContext: STPPaymentContext

Expand All @@ -40,6 +40,7 @@ class CheckoutViewController: UIViewController {
let rowHeight: CGFloat = 52
let activityIndicator = UIActivityIndicatorView(style: .gray)
let numberFormatter: NumberFormatter
let country: String
var products: [Product]
var paymentInProgress: Bool = false {
didSet {
Expand Down Expand Up @@ -83,7 +84,9 @@ class CheckoutViewController: UIViewController {
config.requiredShippingAddressFields = settings.requiredShippingAddressFields
config.shippingType = settings.shippingType
config.additionalPaymentOptions = settings.additionalPaymentOptions

self.country = settings.country
self.paymentCurrency = settings.currency

let customerContext = STPCustomerContext(keyProvider: MyAPIClient.sharedClient)
let paymentContext = STPPaymentContext(customerContext: customerContext,
configuration: config,
Expand Down Expand Up @@ -131,13 +134,8 @@ See https://stripe.com/docs/testing.
}
self.totalRow = CheckoutRowView(title: "Total", detail: "", tappable: false)
self.buyButton = BuyButton(enabled: false, title: "Buy")
var localeComponents: [String: String] = [
NSLocale.Key.currencyCode.rawValue: self.paymentCurrency,
]
localeComponents[NSLocale.Key.languageCode.rawValue] = NSLocale.preferredLanguages.first
let localeID = NSLocale.localeIdentifier(fromComponents: localeComponents)
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: localeID)
numberFormatter.locale = settings.currencyLocale
numberFormatter.numberStyle = .currency
numberFormatter.usesGroupingSeparator = true
self.numberFormatter = numberFormatter
Expand Down Expand Up @@ -261,15 +259,14 @@ extension CheckoutViewController: STPPaymentContextDelegate {
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock) {

// Create the PaymentIntent on the backend
// To speed this up, create the PaymentIntent earlier in the checkout flow and update it as necessary (e.g. when the cart subtotal updates or when shipping fees and taxes are calculated, instead of re-creating a PaymentIntent for every payment attempt.
MyAPIClient.sharedClient.createPaymentIntent(products: self.products, shippingMethod: paymentContext.selectedShippingMethod) { result in
MyAPIClient.sharedClient.createPaymentIntent(products: self.products, shippingMethod: paymentContext.selectedShippingMethod, country: self.country) { result in
switch result {
case .success(let clientSecret):
// Confirm the PaymentIntent
let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret)
paymentIntentParams.paymentMethodId = paymentResult.paymentMethod.stripeId
paymentIntentParams.configure(with: paymentResult)
paymentIntentParams.returnURL = "payments-example://stripe-redirect"
STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: paymentContext) { status, paymentIntent, error in
switch status {
Expand Down Expand Up @@ -399,7 +396,7 @@ extension CheckoutViewController: UITableViewDelegate, UITableViewDataSource {
}

let product = self.products[indexPath.item]
cell.configure(with: product)
cell.configure(with: product, numberFormatter: self.numberFormatter)
cell.selectionStyle = .none
return cell
}
Expand Down
4 changes: 2 additions & 2 deletions Example/Standard Integration/EmojiCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ class EmojiCell: UICollectionViewCell {
fatalError()
}

public func configure(with product: Product) {
priceLabel.text = product.priceText
public func configure(with product: Product, numberFormatter: NumberFormatter) {
priceLabel.text = numberFormatter.string(from: NSNumber(value: Float(product.price)/100))!
emojiLabel.text = product.emoji
}

Expand Down
4 changes: 2 additions & 2 deletions Example/Standard Integration/EmojiCheckoutCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class EmojiCheckoutCell: UITableViewCell {
])
}

public func configure(with product: Product) {
priceLabel.text = product.priceText
public func configure(with product: Product, numberFormatter: NumberFormatter) {
priceLabel.text = numberFormatter.string(from: NSNumber(value: Float(product.price)/100))!
emojiLabel.text = product.emoji
detailLabel.text = product.emoji.unicodeScalars.first?.properties.name?.localizedCapitalized
}
Expand Down
3 changes: 2 additions & 1 deletion Example/Standard Integration/MyAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider {
}
}

func createPaymentIntent(products: [Product], shippingMethod: PKShippingMethod?, completion: @escaping ((Result<String, Error>) -> Void)) {
func createPaymentIntent(products: [Product], shippingMethod: PKShippingMethod?, country: String? = nil, completion: @escaping ((Result<String, Error>) -> Void)) {
let url = self.baseURL.appendingPathComponent("create_payment_intent")
var params: [String: Any] = [
"metadata": [
Expand All @@ -45,6 +45,7 @@ class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider {
if let shippingMethod = shippingMethod {
params["shipping"] = shippingMethod.identifier
}
params["country"] = country
let jsonData = try? JSONSerialization.data(withJSONObject: params)
var request = URLRequest(url: url)
request.httpMethod = "POST"
Expand Down
Loading

0 comments on commit a1df69a

Please sign in to comment.