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 FPX to Basic Integration #1390

Merged
merged 10 commits into from
Sep 27, 2019
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;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this going to convert to swift strangely? I'd probalby expect createPaymentIntentWithAdditionalParameters:(NSString * _Nullable)additionalParameters completion:(STPPaymentIntentCreationHandler)completion;

I'd also probably expect additionalParameters to be an NSDictionary but since this is sample backend code don't feel strongly on either of these

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that was roughly my feeling. It should take a dictionary and convert all the parameters properly, but this was a quick change to make an Objective-C-only sample app work, so meh. I'm more worried about how this additional parameter adds incremental complexity to our sample app.


#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
}()
Copy link
Contributor

Choose a reason for hiding this comment

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

👏


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