diff --git a/Example/Stripe iOS Example (Simple).xcodeproj/project.pbxproj b/Example/Stripe iOS Example (Simple).xcodeproj/project.pbxproj index 339e538e1a..e34aca14f0 100644 --- a/Example/Stripe iOS Example (Simple).xcodeproj/project.pbxproj +++ b/Example/Stripe iOS Example (Simple).xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 042CA41D1A685E8D00D778E7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 042CA4141A685E8D00D778E7 /* AppDelegate.swift */; }; 042CA4201A685E8D00D778E7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 042CA4191A685E8D00D778E7 /* Images.xcassets */; }; + 04BC29A01CD81D3900318357 /* BrowseProductsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BC299F1CD81D3900318357 /* BrowseProductsViewController.swift */; }; 04D0761D1A69E66F00094431 /* Stripe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04D076191A69C14700094431 /* Stripe.framework */; }; 04D0761E1A69E66F00094431 /* Stripe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 04D076191A69C14700094431 /* Stripe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C124A18B1CCACAA1007D42EE /* CheckoutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C124A18A1CCACAA1007D42EE /* CheckoutViewController.swift */; }; @@ -38,6 +39,7 @@ 042CA4191A685E8D00D778E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 042CA41A1A685E8D00D778E7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 04823F781A6849200098400B /* Stripe iOS Example (Simple).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Stripe iOS Example (Simple).app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 04BC299F1CD81D3900318357 /* BrowseProductsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowseProductsViewController.swift; sourceTree = ""; }; 04D075D91A69B82B00094431 /* Stripe iOS Example (Simple).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Stripe iOS Example (Simple).entitlements"; sourceTree = ""; }; 04D076191A69C14700094431 /* Stripe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Stripe.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C124A18A1CCACAA1007D42EE /* CheckoutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckoutViewController.swift; sourceTree = ""; }; @@ -67,6 +69,7 @@ C188F2A51CC1A338003A524B /* MyAPIClient.swift */, C1B83CBE1CCE6FEE00F0790B /* CheckoutView.swift */, C124A18A1CCACAA1007D42EE /* CheckoutViewController.swift */, + 04BC299F1CD81D3900318357 /* BrowseProductsViewController.swift */, C1C154331CD296CB0036AA63 /* Networking.swift */, C124A18C1CCACC92007D42EE /* CheckoutRowView.swift */, C1B83CBC1CCE6C0700F0790B /* Buttons.swift */, @@ -182,6 +185,7 @@ files = ( C1C154341CD296CB0036AA63 /* Networking.swift in Sources */, C124A18B1CCACAA1007D42EE /* CheckoutViewController.swift in Sources */, + 04BC29A01CD81D3900318357 /* BrowseProductsViewController.swift in Sources */, C188F2A61CC1A338003A524B /* MyAPIClient.swift in Sources */, C1B83CBF1CCE6FEE00F0790B /* CheckoutView.swift in Sources */, C1B83CBD1CCE6C0700F0790B /* Buttons.swift in Sources */, diff --git a/Example/Stripe iOS Example (Simple)/AppDelegate.swift b/Example/Stripe iOS Example (Simple)/AppDelegate.swift index 95e9e077e6..fc76eb0582 100644 --- a/Example/Stripe iOS Example (Simple)/AppDelegate.swift +++ b/Example/Stripe iOS Example (Simple)/AppDelegate.swift @@ -13,7 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - let rootVC = CheckoutViewController() + let rootVC = BrowseProductsViewController() let navigationController = UINavigationController(rootViewController: rootVC) let window = UIWindow() window.rootViewController = navigationController; diff --git a/Example/Stripe iOS Example (Simple)/BrowseProductsViewController.swift b/Example/Stripe iOS Example (Simple)/BrowseProductsViewController.swift new file mode 100644 index 0000000000..13124fd332 --- /dev/null +++ b/Example/Stripe iOS Example (Simple)/BrowseProductsViewController.swift @@ -0,0 +1,59 @@ +// +// BrowseProductsViewController.swift +// Stripe iOS Example (Simple) +// +// Created by Jack Flintermann on 5/2/16. +// Copyright © 2016 Stripe. All rights reserved. +// + +import UIKit + +class BrowseProductsViewController: UITableViewController { + + let productsAndPrices = [ + "👕": 2000, + "👖": 4000, + "👗": 3000, + "👞": 700, + "👟": 600, + "👠": 1000, + "👡": 2000, + "👢": 2500, + "👒": 800, + "👙": 3000, + "💄": 2000, + "🎩": 5000, + "👛": 5500, + "👜": 6000, + "🕶": 2000, + "👚": 2500, + ] + + override func viewDidLoad() { + super.viewDidLoad() + self.navigationItem.title = "Emoji Apparel" + self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Products", style: .Plain, target: nil, action: nil) + } + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.productsAndPrices.count + } + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("Cell") ?? UITableViewCell(style: .Value1, reuseIdentifier: "Cell") + let product = Array(self.productsAndPrices.keys)[indexPath.row] + let price = self.productsAndPrices[product]! + cell.textLabel?.text = product + cell.detailTextLabel?.text = "$\(price/100).00" + cell.accessoryType = .DisclosureIndicator + return cell + } + + override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + let product = Array(self.productsAndPrices.keys)[indexPath.row] + let price = self.productsAndPrices[product]! + let checkoutViewController = CheckoutViewController(product: product, price: price) + self.navigationController?.pushViewController(checkoutViewController, animated: true) + } + +} diff --git a/Example/Stripe iOS Example (Simple)/CheckoutViewController.swift b/Example/Stripe iOS Example (Simple)/CheckoutViewController.swift index ff04a18d3d..b27b0fa617 100644 --- a/Example/Stripe iOS Example (Simple)/CheckoutViewController.swift +++ b/Example/Stripe iOS Example (Simple)/CheckoutViewController.swift @@ -31,29 +31,29 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate { let appleMerchantID: String? = nil // These values will be shown to the user when they purchase with Apple Pay. - let merchantName = "Emoji Apparel" + let companyName = "Emoji Apparel" let paymentAmount = 1000 // this amount is in cents. let paymentCurrency = "usd" let myAPIClient: MyAPIClient let paymentContext: STPPaymentContext - let products = ["👕", "👖", "👗", "👞", "👟", "👠", "👡", "👢", - "👒", "👙", "💄", "🎩", "👛", "👜", "🕶", "👚"] let checkoutView = CheckoutView() - - override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { + + init(product: String, price: Int) { + self.checkoutView.product = product Stripe.setDefaultPublishableKey(self.stripePublishableKey) self.myAPIClient = MyAPIClient(baseURL: self.backendBaseURL, - customerID: self.customerID) + customerID: self.customerID) let paymentContext = STPPaymentContext(APIAdapter: self.myAPIClient) paymentContext.appleMerchantIdentifier = self.appleMerchantID paymentContext.paymentAmount = self.paymentAmount paymentContext.paymentCurrency = self.paymentCurrency - paymentContext.merchantName = self.merchantName + paymentContext.companyName = self.companyName paymentContext.requiredBillingAddressFields = .Zip self.paymentContext = paymentContext super.init(nibName: nil, bundle: nil) self.paymentContext.delegate = self + self.paymentContext.paymentAmount = price } required init?(coder aDecoder: NSCoder) { @@ -66,30 +66,28 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate { override func viewDidLoad() { super.viewDidLoad() - let index = Int(arc4random_uniform(UInt32(self.products.count))) - self.checkoutView.product = self.products[index] self.navigationController?.navigationBar.translucent = false self.navigationItem.title = "Emoji Apparel" self.checkoutView.buyButton.addTarget(self, action: #selector(didTapBuy), forControlEvents: .TouchUpInside) self.checkoutView.totalRow.detail = "$10.00" self.checkoutView.paymentRow.onTap = { _ in - guard self.checkConstants() else { return } - if let navController = self.navigationController { self.paymentContext.pushPaymentMethodsViewControllerOntoNavigationController(navController) } } } - + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + self.paymentContext.willAppear() + } + override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) - guard self.checkConstants() else { return } - self.paymentContext.didAppear() } func didTapBuy() { - guard self.checkConstants() else { return } self.checkoutView.paymentInProgress = true self.paymentContext.requestPaymentFromViewController( @@ -144,27 +142,15 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate { func paymentContext(paymentContext: STPPaymentContext, didFailToLoadWithError error: NSError) { let alertController = UIAlertController( - title: "Error Retrieving Cards", + title: "Error", message: error.localizedDescription, preferredStyle: .Alert ) - let action = UIAlertAction(title: "OK", style: .Default, handler: nil) + let action = UIAlertAction(title: "OK", style: .Default, handler: { action in + self.navigationController?.popViewControllerAnimated(true) + }) alertController.addAction(action) self.presentViewController(alertController, animated: true, completion: nil) } - func checkConstants() -> Bool { - if self.stripePublishableKey.containsString("#") - || self.stripePublishableKey.utf16.count == 0 { - let alertController = UIAlertController(title: "No Stripe Publishable Key", - message: "Please set stripePublishableKey to your account's test publishable key in CheckoutViewController.swift", - preferredStyle: .Alert) - let action = UIAlertAction(title: "OK", style: .Default, handler: nil) - alertController.addAction(action) - self.presentViewController(alertController, animated: true, completion: nil) - return false - } - return true - } - } diff --git a/Example/Stripe iOS Example (Simple)/MyAPIClient.swift b/Example/Stripe iOS Example (Simple)/MyAPIClient.swift index 70a5d331aa..4f89fbafe0 100644 --- a/Example/Stripe iOS Example (Simple)/MyAPIClient.swift +++ b/Example/Stripe iOS Example (Simple)/MyAPIClient.swift @@ -30,12 +30,24 @@ class MyAPIClient: NSObject, STPBackendAPIAdapter { guard let json = data?.JSON else { return nil } if let cardsJSON = json["cards"] as? [[String: AnyObject]] { let selectedCardJSON = json["selected_card"] as? [String: AnyObject] - let selectedCard = STPCard.decodedObjectFromAPIResponse(selectedCardJSON) - let cards = cardsJSON.flatMap { STPCard.decodedObjectFromAPIResponse($0) } + let selectedCard = decodeCard(selectedCardJSON) + let cards = cardsJSON.flatMap(decodeCard) return (selectedCard, cards) } return nil } + + func decodeCard(json: [String: AnyObject]?) -> STPCard? { + guard let json = json, + cardID = json["id"] as? String, + brand = json["brand"] as? String, + last4 = json["last4"] as? String, + expMonth = json["exp_month"] as? UInt, + expYear = json["exp_year"] as? UInt, + funding = json["funding"] as? String + else { return nil } + return STPCard(ID: cardID, brand: STPCard.brandFromString(brand), last4: last4, expMonth: expMonth, expYear: expYear, funding: STPCard.fundingFromString(funding)) + } func decodeResponse(response: NSURLResponse?, error: NSError?) -> NSError? { if let httpResponse = response as? NSHTTPURLResponse @@ -71,6 +83,13 @@ class MyAPIClient: NSObject, STPBackendAPIAdapter { } @objc func retrieveCards(completion: STPCardCompletionBlock) { + guard let key = Stripe.defaultPublishableKey() where !key.containsString("#") else { + let error = NSError(domain: StripeDomain, code: 50, userInfo: [ + NSLocalizedDescriptionKey: "Please set stripePublishableKey to your account's test publishable key in CheckoutViewController.swift" + ]) + completion(nil, nil, error) + return + } guard let baseURLString = baseURLString, baseURL = NSURL(string: baseURLString), customerID = customerID else { completion(self.inMemoryCards.last, self.inMemoryCards, nil) return diff --git a/Stripe.xcodeproj/project.pbxproj b/Stripe.xcodeproj/project.pbxproj index 0e54ad0c3c..7567317028 100644 --- a/Stripe.xcodeproj/project.pbxproj +++ b/Stripe.xcodeproj/project.pbxproj @@ -442,9 +442,9 @@ 0438EFA11B741C2800D506CC /* stp_card_visa.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = stp_card_visa.png; sourceTree = ""; }; 0438EFA21B741C2800D506CC /* stp_card_visa@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_card_visa@2x.png"; sourceTree = ""; }; 0438EFA31B741C2800D506CC /* stp_card_visa@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_card_visa@3x.png"; sourceTree = ""; }; - 0439B9851C454F97005A1ED5 /* STPPaymentMethodsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPaymentMethodsViewController.h; sourceTree = ""; }; + 0439B9851C454F97005A1ED5 /* STPPaymentMethodsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPaymentMethodsViewController.h; path = PublicHeaders/STPPaymentMethodsViewController.h; sourceTree = ""; }; 0439B9861C454F97005A1ED5 /* STPPaymentMethodsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentMethodsViewController.m; sourceTree = ""; }; - 0451CC421C49AE1C003B2CA6 /* STPPaymentResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPaymentResult.h; sourceTree = ""; }; + 0451CC421C49AE1C003B2CA6 /* STPPaymentResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPaymentResult.h; path = PublicHeaders/STPPaymentResult.h; sourceTree = ""; }; 0451CC431C49AE1C003B2CA6 /* STPPaymentResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentResult.m; sourceTree = ""; }; 0451CC481C49AF0D003B2CA6 /* STPSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPSource.h; sourceTree = ""; }; 045A62AA1B8E7259000165CE /* STPPaymentCardTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentCardTextFieldTest.m; sourceTree = ""; }; @@ -470,7 +470,7 @@ 049A3F791CC18D5300F57DE7 /* UIView+Stripe_FirstResponder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Stripe_FirstResponder.m"; sourceTree = ""; }; 049A3F7C1CC1920A00F57DE7 /* UIViewController+Stripe_KeyboardAvoiding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Stripe_KeyboardAvoiding.h"; sourceTree = ""; }; 049A3F7D1CC1920A00F57DE7 /* UIViewController+Stripe_KeyboardAvoiding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Stripe_KeyboardAvoiding.m"; sourceTree = ""; }; - 049A3F871CC73C7100F57DE7 /* STPPaymentContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPaymentContext.h; sourceTree = ""; }; + 049A3F871CC73C7100F57DE7 /* STPPaymentContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPaymentContext.h; path = PublicHeaders/STPPaymentContext.h; sourceTree = ""; }; 049A3F881CC73C7100F57DE7 /* STPPaymentContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentContext.m; sourceTree = ""; }; 049A3F8F1CC740FF00F57DE7 /* NSDecimalNumber+Stripe_Currency.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDecimalNumber+Stripe_Currency.h"; sourceTree = ""; }; 049A3F901CC740FF00F57DE7 /* NSDecimalNumber+Stripe_Currency.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDecimalNumber+Stripe_Currency.m"; sourceTree = ""; }; @@ -492,7 +492,7 @@ 04A488311CA34D3000506E53 /* STPEmailAddressValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPEmailAddressValidator.h; sourceTree = ""; }; 04A488321CA34D3000506E53 /* STPEmailAddressValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPEmailAddressValidator.m; sourceTree = ""; }; 04A488351CA34DC600506E53 /* STPEmailAddressValidatorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPEmailAddressValidatorTest.m; sourceTree = ""; }; - 04A4883B1CA3568800506E53 /* STPBlocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPBlocks.h; sourceTree = ""; }; + 04A4883B1CA3568800506E53 /* STPBlocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPBlocks.h; path = PublicHeaders/STPBlocks.h; sourceTree = ""; }; 04A488401CA3580700506E53 /* UINavigationController+Stripe_Completion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationController+Stripe_Completion.h"; sourceTree = ""; }; 04A488411CA3580700506E53 /* UINavigationController+Stripe_Completion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationController+Stripe_Completion.m"; sourceTree = ""; }; 04A4C3851C4F25F900B3B290 /* NSArray+Stripe_BoundSafe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Stripe_BoundSafe.h"; sourceTree = ""; }; @@ -506,7 +506,6 @@ 04A58A461BC603BB004E7BC2 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = ""; }; 04A58A471BC603BB004E7BC2 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = ""; }; 04A58A481BC603BB004E7BC2 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = ""; }; - 04A850C91CB4BD7D00B3AD68 /* module_osx.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module_osx.modulemap; sourceTree = ""; }; 04A850E21CB71D9900B3AD68 /* MockSTPAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockSTPAPIClient.h; sourceTree = ""; }; 04A850E31CB71D9900B3AD68 /* MockSTPAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MockSTPAPIClient.m; sourceTree = ""; }; 04A850E41CB71D9900B3AD68 /* MockSTPCoordinatorDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockSTPCoordinatorDelegate.h; sourceTree = ""; }; @@ -561,25 +560,25 @@ 04F39F151AEF2AFE005B926E /* StripeiOSStatic.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StripeiOSStatic.xcconfig; sourceTree = ""; }; 04F39F181AEF2AFE005B926E /* StripeiOSStaticFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StripeiOSStaticFramework.xcconfig; sourceTree = ""; }; 04F3BB3B1BA89B1200DE235E /* PKPayment+Stripe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PKPayment+Stripe.h"; sourceTree = ""; }; - 04F416241CA3639500486FB5 /* STPPaymentCardEntryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPaymentCardEntryViewController.h; sourceTree = ""; }; + 04F416241CA3639500486FB5 /* STPPaymentCardEntryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPaymentCardEntryViewController.h; path = PublicHeaders/STPPaymentCardEntryViewController.h; sourceTree = ""; }; 04F416251CA3639500486FB5 /* STPPaymentCardEntryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentCardEntryViewController.m; sourceTree = ""; }; 04FCFA171BD59A8C00297732 /* STPCategoryLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPCategoryLoader.h; sourceTree = ""; }; 11C74B9B164043050071C2CA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 4A0D74F918F6106100966D7B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - C1080F471CBECF7B007B2D89 /* STPAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPAddress.h; sourceTree = ""; }; + C1080F471CBECF7B007B2D89 /* STPAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPAddress.h; path = PublicHeaders/STPAddress.h; sourceTree = ""; }; C1080F481CBECF7B007B2D89 /* STPAddress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddress.m; sourceTree = ""; }; C1080F4B1CBED48A007B2D89 /* STPAddressTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPAddressTests.m; sourceTree = ""; }; C1080F4D1CBEDE7F007B2D89 /* STPShippingEntryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPShippingEntryViewController.h; sourceTree = ""; }; C1080F4E1CBEDE7F007B2D89 /* STPShippingEntryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPShippingEntryViewController.m; sourceTree = ""; }; - C11810851CC6AF4C0022FB55 /* STPPaymentMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPaymentMethod.h; sourceTree = ""; }; - C11810871CC6B00D0022FB55 /* STPApplePayPaymentMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPApplePayPaymentMethod.h; sourceTree = ""; }; + C11810851CC6AF4C0022FB55 /* STPPaymentMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPPaymentMethod.h; path = PublicHeaders/STPPaymentMethod.h; sourceTree = ""; }; + C11810871CC6B00D0022FB55 /* STPApplePayPaymentMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPApplePayPaymentMethod.h; path = PublicHeaders/STPApplePayPaymentMethod.h; sourceTree = ""; }; C11810881CC6B00D0022FB55 /* STPApplePayPaymentMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPApplePayPaymentMethod.m; sourceTree = ""; }; - C118108B1CC6B07B0022FB55 /* STPCardPaymentMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPCardPaymentMethod.h; sourceTree = ""; }; + C118108B1CC6B07B0022FB55 /* STPCardPaymentMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPCardPaymentMethod.h; path = PublicHeaders/STPCardPaymentMethod.h; sourceTree = ""; }; C118108C1CC6B07B0022FB55 /* STPCardPaymentMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPCardPaymentMethod.m; sourceTree = ""; }; C11810931CC6C4700022FB55 /* PKPaymentAuthorizationViewController+Stripe_Blocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PKPaymentAuthorizationViewController+Stripe_Blocks.h"; sourceTree = ""; }; C11810941CC6C4700022FB55 /* PKPaymentAuthorizationViewController+Stripe_Blocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PKPaymentAuthorizationViewController+Stripe_Blocks.m"; sourceTree = ""; }; C11810981CC6D46D0022FB55 /* NSDecimalNumber+StripeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDecimalNumber+StripeTest.m"; sourceTree = ""; }; - C11810A61CC6E2160022FB55 /* STPBackendAPIAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STPBackendAPIAdapter.h; sourceTree = ""; }; + C11810A61CC6E2160022FB55 /* STPBackendAPIAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = STPBackendAPIAdapter.h; path = PublicHeaders/STPBackendAPIAdapter.h; sourceTree = ""; }; C11810B01CC7D3EB0022FB55 /* UIFont+Stripe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIFont+Stripe.h"; sourceTree = ""; }; C11810B11CC7D3EB0022FB55 /* UIFont+Stripe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIFont+Stripe.m"; sourceTree = ""; }; C11810B41CC7D6880022FB55 /* UIColor+Stripe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Stripe.h"; sourceTree = ""; }; @@ -873,7 +872,6 @@ 049A3F971CC76A2400F57DE7 /* NSBundle+Stripe_AppName.h */, 049A3F981CC76A2400F57DE7 /* NSBundle+Stripe_AppName.m */, C124A17B1CCAA0C2007D42EE /* NSMutableURLRequest+Stripe.m */, - 04A850C91CB4BD7D00B3AD68 /* module_osx.modulemap */, ); name = Stripe; path = Tests/../Stripe; diff --git a/Stripe.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Stripe.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/Stripe.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Stripe/STPAddress.h b/Stripe/PublicHeaders/STPAddress.h similarity index 58% rename from Stripe/STPAddress.h rename to Stripe/PublicHeaders/STPAddress.h index 945bd99bda..e7b326cf98 100644 --- a/Stripe/STPAddress.h +++ b/Stripe/PublicHeaders/STPAddress.h @@ -16,22 +16,72 @@ #import #import +/** + * What set of billing address information you need to collect from your user. + */ typedef NS_ENUM(NSUInteger, STPBillingAddressFields) { + /** + * No billing address information + */ STPBillingAddressFieldsNone, + /** + * Just request the user's billing ZIP code + */ STPBillingAddressFieldsZip, + /** + * Request the user's full billing address + */ STPBillingAddressFieldsFull, }; +/** + * STPAddress wraps ABRecordRef and CNContact objects returned by iOS and adapts them to the Stripe API. + */ @interface STPAddress : NSObject +/** + * The user's full name (e.g. "Jane Doe") + */ @property (nonatomic, strong) NSString *name; + +/** + * The first line of the user's street address (e.g. "123 Fake St") + */ @property (nonatomic, strong) NSString *line1; + +/** + * The apartment, floor number, etc of the user's street address (e.g. "Apartment 1A") + */ @property (nonatomic, strong) NSString *line2; + +/** + * The city in which the user resides (e.g. "San Francisco") + */ @property (nonatomic, strong) NSString *city; + +/** + * The state in which the user resides (e.g. "CA") + */ @property (nonatomic, strong) NSString *state; + +/** + * The postal code in which the user resides (e.g. "90210") + */ @property (nonatomic, strong) NSString *postalCode; + +/** + * The ISO country code of the address (e.g. "US") + */ @property (nonatomic, strong) NSString *country; + +/** + * The phone number of the address (e.g. "8885551212") + */ @property (nonatomic, strong) NSString *phone; + +/** + * The email of the address (e.g. "jane@doe.com") + */ @property (nonatomic, strong) NSString *email; #pragma clang diagnostic push diff --git a/Stripe/PublicHeaders/STPApplePayPaymentMethod.h b/Stripe/PublicHeaders/STPApplePayPaymentMethod.h new file mode 100644 index 0000000000..866de285e5 --- /dev/null +++ b/Stripe/PublicHeaders/STPApplePayPaymentMethod.h @@ -0,0 +1,16 @@ +// +// STPApplePayPaymentMethod.h +// Stripe +// +// Created by Ben Guo on 4/19/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + +#import +#import "STPPaymentMethod.h" + +/** + * An empty class representing that the user wishes to pay via Apple Pay. This can be checked on an STPPaymentContext, e.g. if ([paymentContext.selectedPaymentMethod isKindOfClass:[STPApplePayPaymentMethod class]]) { // don't ask the user for their card number; they want to pay with apple pay. } + */ +@interface STPApplePayPaymentMethod : NSObject +@end diff --git a/Stripe/PublicHeaders/STPBackendAPIAdapter.h b/Stripe/PublicHeaders/STPBackendAPIAdapter.h new file mode 100644 index 0000000000..908f049d3b --- /dev/null +++ b/Stripe/PublicHeaders/STPBackendAPIAdapter.h @@ -0,0 +1,55 @@ +// +// STPBackendAPIAdapter.h +// Stripe +// +// Created by Jack Flintermann on 1/12/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + +#import +#import +#import "STPAddress.h" + +@class STPCard, STPToken; + +/** + * Call this block after you're done fetching a customer on your server, returning their cards and selected card to your iOS application, and parsing them into STPCard objects. For more information, see the `retrieveSources` documentation. + * + * @param selectedCard the user's currently selected card + * @param cards the user's list of available cards + * @param error any error that occurred while communicating with your server + */ +typedef void (^STPCardCompletionBlock)(STPCard * __nullable selectedCard, NSArray* __nullable cards, NSError * __nullable error); + +typedef void (^STPAddressCompletionBlock)(STPAddress * __nullable address, NSError * __nullable error); + +/** + * You should make your application's API client conform to this interface in order to use it with an STPPaymentContext. It provides a "bridge" from the prebuilt UI we expose (such as STPPaymentMethodsViewController) to your backend to fetch the information it needs to power those views. To see examples of implementing these APIs, see MyAPIClient.swift in our example project and https://github.com/stripe/example-ios-backend . + */ +@protocol STPBackendAPIAdapter + +/** + * Retrieve the cards to be displayed inside a payment context. On your backend, retrieve the Stripe Customer associated with your currently logged-in user (see https://stripe.com/docs/api#retrieve_customer ), and return all of their `sources` where `object` == `card`. Also specifically return the user's currently selected card. (For an example Ruby implementation of this API, see https://github.com/stripe/example-ios-backend/blob/master/web.rb#L40 ). Back in your iOS app, after you've called this API, create an STPCard object for the user's selected card (see [STPCard cardWithID:brand:last4:expMonth:expYear:funding]) and the array of their cards, and call `completion` with this information. See MyAPIClient.swift in our example project to see this in action. + * + * @see STPCard + * @param completion call this callback when you're done fetching and parsing the above information from your server. For example, completion(selectedCard, cards, nil) (if your call succeeds) or completion(nil, nil, error) if an error is returned. + */ +- (void)retrieveCards:(nonnull STPCardCompletionBlock)completion; + +/** + * Adds a card token to a customer. On your backend, retrieve the Stripe Customer associated with your logged-in user. Then, call the Create Card method on that customer as described at https://stripe.com/docs/api#create_card (for an example Ruby implementation of this API, see https://github.com/stripe/example-ios-backend/blob/master/web.rb#L60 ). Then, return their cards and selected card as described in the `retrieveCards` documentation and call `completion` with them. + * + * @param token the Stripe token representing the user's newly-added credit card. + * @param completion call this callback when you're done adding the token to the customer on your server. For example, completion(selectedCard, cards, nil) (if your call succeeds) or completion(nil, nil, error) if an error is returned. + */ +- (void)addToken:(nonnull STPToken *)token completion:(nonnull STPCardCompletionBlock)completion; + +/** + * Change a customer's default_source to be the provided card. On your backend, retrieve the Stripe Customer associated with your logged-in user. Then, call the Customer Update method as described at https://stripe.com/docs/api#update_customer , specifying default_source to be the provided card (for an example Ruby implementation of this API, see https://github.com/stripe/example-ios-backend/blob/master/web.rb#L82 ). Then, return their cards and selected card as described in the `retrieveCards` documentation and call `completion` with them. + * + * @param card The newly-selected default source for the user. + * @param completion call this callback when you're done selecting the new default source for the customer on your server. For example, completion(selectedCard, cards, nil) (if your call succeeds) or completion(nil, nil, error) if an error is returned. + */ +- (void)selectCard:(nonnull STPCard *)card completion:(nonnull STPCardCompletionBlock)completion; + +@end diff --git a/Stripe/PublicHeaders/STPBlocks.h b/Stripe/PublicHeaders/STPBlocks.h new file mode 100644 index 0000000000..d249a3fdea --- /dev/null +++ b/Stripe/PublicHeaders/STPBlocks.h @@ -0,0 +1,40 @@ +// +// STPBlocks.h +// Stripe +// +// Created by Jack Flintermann on 3/23/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + +#import +#import "STPSource.h" + +/** + * An enum representing the status of a payment requested from the user. + */ +typedef NS_ENUM(NSUInteger, STPPaymentStatus) { + /** + * The payment succeeded. + */ + STPPaymentStatusSuccess, + /** + * The payment failed due to an unforeseen error, such as the user's Internet connection being offline. + */ + STPPaymentStatusError, + /** + * The user cancelled the payment (for example, by hitting "cancel" in the Apple Pay dialog). + */ + STPPaymentStatusUserCancellation, +}; + +/** + * An empty block, called with no arguments, returning nothing. + */ +typedef void (^STPVoidBlock)(); + +/** + * A block that may optionally be called with an error. + * + * @param error The error that occurred, if any. + */ +typedef void (^STPErrorBlock)(NSError * __nullable error); diff --git a/Stripe/PublicHeaders/STPCard.h b/Stripe/PublicHeaders/STPCard.h index 53c4495439..5e584959f1 100644 --- a/Stripe/PublicHeaders/STPCard.h +++ b/Stripe/PublicHeaders/STPCard.h @@ -29,9 +29,41 @@ typedef NS_ENUM(NSInteger, STPCardFundingType) { @interface STPCard : STPCardParams /** - * The card's number. This will be nil for cards retrieved from the Stripe API. + * A factory method to build an STPCard from a Stripe API response. + * + * @param cardID The Stripe ID of the card, e.g. card_185iQx4JYtv6MPZKfcuXwkOx + * @param brand The brand of the card (e.g. "Visa". To obtain this enum value from a string, use [STPCardBrand brandFromString:string]; + * @param last4 The last 4 digits of the card, e.g. 4242 + * @param expMonth The card's expiration month, 1-indexed (i.e. 1 = January) + * @param expYear The card's expiration year + * @param funding The card's funding type (credit, debit, or prepaid). To obtain this enum value from a string, use [STPCardBrand fundingFromString:string]. + * + * @return an STPCard instance populated with the provided values. */ ++ (nonnull instancetype)cardWithID:(nonnull NSString *)cardID + brand:(STPCardBrand)brand + last4:(nonnull NSString *)last4 + expMonth:(NSUInteger)expMonth + expYear:(NSUInteger)expYear + funding:(STPCardFundingType)funding; +/** + * This parses a string representing a card's brand into the appropriate STPCardBrand enum value, i.e. [STPCard brandFromString:@"American Express"] == STPCardBrandAmex + * + * @param string a string representing the card's brand as returned from the Stripe API + * + * @return an enum value mapped to that string. If the string is unrecognized, returns STPCardBrandUnknown. + */ ++ (STPCardBrand)brandFromString:(nonnull NSString *)string; + +/** + * This parses a string representing a card's funding type into the appropriate STPCardFundingType enum value, i.e. [STPCard fundingFromString:@"prepaid"] == STPCardFundingTypePrepaid. + * + * @param string a string representing the card's funding type as returned from the Stripe API + * + * @return an enum value mapped to that string. If the string is unrecognized, returns STPCardFundingTypeOther. + */ ++ (STPCardFundingType)fundingFromString:(nonnull NSString *)string; /** * The last 4 digits of the card. @@ -44,7 +76,7 @@ typedef NS_ENUM(NSInteger, STPCardFundingType) { @property (nonatomic, readonly, nullable) NSString *dynamicLast4; /** - * The card's expiration month. + * The card's expiration month. 1-indexed (i.e. 1 == January) */ @property (nonatomic) NSUInteger expMonth; diff --git a/Stripe/STPCardPaymentMethod.h b/Stripe/PublicHeaders/STPCardPaymentMethod.h similarity index 72% rename from Stripe/STPCardPaymentMethod.h rename to Stripe/PublicHeaders/STPCardPaymentMethod.h index df729d4718..968cd5a733 100644 --- a/Stripe/STPCardPaymentMethod.h +++ b/Stripe/PublicHeaders/STPCardPaymentMethod.h @@ -13,8 +13,14 @@ NS_ASSUME_NONNULL_BEGIN @class STPCard; +/** + * This represents a payment method backed by a specific card, as opposed to for example Apple Pay. + */ @interface STPCardPaymentMethod : NSObject +/** + * The underlying card the user has selected. + */ @property (nonatomic, readonly) STPCard *card; - (instancetype)initWithCard:(STPCard *)card; diff --git a/Stripe/PublicHeaders/STPPaymentCardEntryViewController.h b/Stripe/PublicHeaders/STPPaymentCardEntryViewController.h new file mode 100644 index 0000000000..9bfc3c158c --- /dev/null +++ b/Stripe/PublicHeaders/STPPaymentCardEntryViewController.h @@ -0,0 +1,44 @@ +// +// STPPaymentCardEntryViewController.h +// Stripe +// +// Created by Jack Flintermann on 3/23/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + +#import +#import "STPBlocks.h" +#import "STPCardParams.h" +#import "STPAPIClient.h" +#import "STPAddress.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This callback will be yielded with two arguments: a Stripe token (may be nil, if the user cancels the form) and another callback. If the token is present, you can submit it to your backend API to either create a charge or attach it to a customer. When that is done, call tokenCompletion with any error that occurred (it'll be shown to the user and they'll be able to resubmit the form). If your backend API call succeeds, you should dismiss the view controller and move the user to the next step of your UI. + * + * @param token A valid Stripe token, or nil if the user presses the "cancel" button. + * @param tokenCompletion A callback to call once you're done passing the token to your server. + */ +typedef void (^STPPaymentCardEntryBlock)(STPToken * __nullable token, STPErrorBlock tokenCompletion); + +/** This view controller contains a credit card entry form that the user can fill out. On submission, it will use the Stripe API to convert the user's card details to a Stripe token. It renders a right bar button item that submits the form, so it must be shown inside a UINavigationController. + */ +@interface STPPaymentCardEntryViewController : UIViewController + +/** + * Returns a new view controller with a credit card form. + * + * @param apiClient An API client to communicate with Stripe. You can pass [STPAPIClient sharedClient] here. + * @param requiredBillingAddressFields The billing address fields the user must fill out in order for the form to validate. These fields will all be present on the returned token from Stripe. See https://stripe.com/docs/api#create_card_token for more information. + * @param completion A block that will be called when the user has successfully submitted their information, or pressed cancel. If they submit their information, the token parameter will be a valid STPToken object. If they cancel, it will be nil. When this is called, the user will see a spinner on the form. This gives your application time to send the token to your backend and add it to a customer or complete a charge. When you're done, call the `tokenCompletion` parameter with whether or not your call succeeded. + * + * @return a view controller that you can either embed in a UINavigationController and present modally, or push onto an existing UINavigationController stack. + */ +- (instancetype)initWithAPIClient:(STPAPIClient *)apiClient + requiredBillingAddressFields:(STPBillingAddressFields)requiredBillingAddressFields + completion:(STPPaymentCardEntryBlock)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/PublicHeaders/STPPaymentContext.h b/Stripe/PublicHeaders/STPPaymentContext.h new file mode 100644 index 0000000000..94b33946ca --- /dev/null +++ b/Stripe/PublicHeaders/STPPaymentContext.h @@ -0,0 +1,210 @@ +// +// STPPaymentContext.h +// Stripe +// +// Created by Jack Flintermann on 4/20/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + + +#import +#import "STPPaymentMethod.h" +#import "STPBlocks.h" +#import "STPAddress.h" + +NS_ASSUME_NONNULL_BEGIN + +@class STPPaymentContext, STPAPIClient; +@protocol STPBackendAPIAdapter, STPPaymentMethod; + +/** + * This is invoked by an STPPaymentContext when it's done obtaining a valid `source` with which to complete a payment. Inside this block, you should make a call to your backend API to make a charge with that source, and invoke the sourceCompletion block when that is done. + * + * @param paymentMethod the type of payment method that the user selected. + * @param source the source that the user selected - this might be a card that is already attached to a customer (e.g. "card_abc123") or an Apple Pay token (e.g. "tok_def456") but either way you can pass this value directly into the Charge Create API: https://stripe.com/docs/api#create_charge + * @param sourceCompletion call this block when you're done creating a charge on your backend. If it succeeded, call sourceCompletion(nil). If it failed with an errorm call sourceCompletion(error). + */ +typedef void (^STPSourceHandlerBlock)(STPPaymentMethodType paymentMethod, id __nonnull source, STPErrorBlock __nonnull sourceCompletion); + +/** + * This is invoked by an STPPaymentContext when a payment is completed. + * + * @param status the status of the payment - STPPaymentStatusSuccess if it succeeded, STPPaymentStatusError if it failed with an error (in which case the `error` parameter will be non-nil), STPPaymentStatusUserCanceled if the user canceled the payment. + * @param error an error that occurred when finishing the payment, if any. + * @see the documentation for requestPaymentFromViewController in STPPaymentContext + */ +typedef void (^STPPaymentCompletionBlock)(STPPaymentStatus status, NSError * __nullable error); + +/** + * Implement STPPaymentContextDelegate to get notified when a payment context's contents change. In practice, if your app has a "checkout page view controller", that is a good candidate to implement this protocol. + */ +@protocol STPPaymentContextDelegate + +/** + * Called when the payment context begins loading. You could tell a UIActivityIndicatorView to start animating when this is called. + * + * @param paymentContext the payment context that began loading. + */ +- (void)paymentContextDidBeginLoading:(STPPaymentContext *)paymentContext; + +/** + * Called when the payment context encounters an error when fetching its initial set of data. If you're showing the user a checkout page, you could dismiss the checkout page when this is called and present the error to the user. To make it harder to get your UI into an inconsistent state, this won't be called until you've called -didAppear on the payment context. + * + * @param paymentContext the payment context that encountered the error + * @param error the error that was encountered + */ +- (void)paymentContext:(STPPaymentContext *)paymentContext didFailToLoadWithError:(NSError *)error; + +/** + * Called when the payment context is done loading. You could tell a UIActivityIndicatorView to stop animating when this is called. + * + * @param paymentContext the payment context that finished loading. + */ +- (void)paymentContextDidEndLoading:(STPPaymentContext *)paymentContext; + +/** + * This is called every time the contents of the payment context change. When this is called, you should update your app's UI to reflect the current state of the payment context. For example, if you have a checkout page with a "selected payment method" row, you should update its payment method with `paymentContext.selectedPaymentMethod.label`. If that checkout page has a "buy" button, you should enable/disable it depending on the result of [paymentContext isReadyForPayment]. + * + * @param paymentContext the payment context that changed + */ +- (void)paymentContextDidChange:(STPPaymentContext *)paymentContext; + +@end + +/** + An STPPaymentContext keeps track of all of the state around a payment. It will manage fetching a user's saved payment methods, tracking any information they select, and prompting them for required additional information before completing their purchase. It can be used to power your application's "payment confirmation" page with just a few lines of code. It requires an "API Adapter" to communicate with your backend API to retrieve and modify a customer's payment methods - see STPBackendAPIAdapter.h for how to implement this. You can also see CheckoutViewController.swift in our example app to see STPPaymentContext in action. + */ +@interface STPPaymentContext : NSObject + +/** + * This is a shortcut for calling -initWithAPIAdapter:apiAdapter publishableKey:[Stripe defaultPublishableKey] supportedPaymentMethods:STPPaymentMethodTypeAll. + */ +- (instancetype)initWithAPIAdapter:(id)apiAdapter; + +/** + * Initializes a new payment context. + * + * @param apiAdapter The API adapter that will be used to talk to your backend API. + * @param publishableKey The publishable key the payment context will use to tokenize your user's payment details. @see STPAPIClient.h + * @param supportedPaymentMethods An enum value representing which payment methods you will accept from your user. Unless you have a very specific reason not to, you should set this to STPPaymentMethodTypeAll. + * + * @return a new payment context. + */ +- (instancetype)initWithAPIAdapter:(id)apiAdapter + publishableKey:(NSString *)publishableKey + supportedPaymentMethods:(STPPaymentMethodType)supportedPaymentMethods; + +/** + * The API client used by the payment context to create tokens. + */ +@property(nonatomic, readonly)STPAPIClient *apiClient; + +/** + * The API adapter that will be used to talk to your backend API. + */ +@property(nonatomic, readonly)id apiAdapter; + +/** + * An enum value representing which payment methods you will accept from your user. Unless you have a very specific reason not to, you should set this to STPPaymentMethodTypeAll. + */ +@property(nonatomic, readonly)STPPaymentMethodType supportedPaymentMethods; + +/** + * The billing address fields the user must fill out in order for the form to validate. These fields will all be present on the returned token from Stripe. See https://stripe.com/docs/api#create_card_token for more information. + */ +@property(nonatomic)STPBillingAddressFields requiredBillingAddressFields; + +/** + * This delegate will be notified when the payment context's contents change. @see STPPaymentContextDelegate + */ +@property(nonatomic, weak, nullable)id delegate; + +/** + * Whether or not the payment context is currently loading information from the network. + */ +@property(nonatomic, readonly, getter=isLoading)BOOL loading; + +/** + * The user's currently selected payment method. May be nil. + */ +@property(nonatomic, readonly, nullable)id selectedPaymentMethod; + +/** + * The available payment methods the user can choose between. May be nil. + */ +@property(nonatomic, readonly, nullable)NSArray> *paymentMethods; + +/** + * The amount of money you're requesting from the user, in the smallest currency unit for the selected currency. For example, to indicate $10 USD, use 1000 (i.e. 1000 cents). For more information see https://stripe.com/docs/api#charge_object-amount . This value must be present and greater than zero in order for Apple Pay to be automatically enabled. + */ +@property(nonatomic)NSInteger paymentAmount; + +/** + * The three-letter currency code for the currency of the payment (i.e. USD, GBP, JPY, etc). Defaults to USD. + */ +@property(nonatomic)NSString *paymentCurrency; + +/** + * The name of your company, for displaying to the user during the payment flow. For example, when using Apple Pay, the payment sheet's final line item will read "PAY {companyName}". This defaults to the name of your iOS application. + */ +@property(nonatomic)NSString *companyName; + +/** + * The Apple Merchant Identifier to use during Apple Pay transactions. To create one of these, see our guide at https://stripe.com/docs/mobile/applepay . You must set this to a valid identifier in order to automatically enable Apple Pay. + */ +@property(nonatomic, nullable)NSString *appleMerchantIdentifier; + +/** + * You must call this method on your payment context when the view controller it's powering is about to appear. This tells the payment context to begin fetching the data it needs. A good time to call this is in the -viewWillAppear: method of that view controller. + */ +- (void)willAppear; + +/** + * You must call this method on your payment context when the view controller it's powering has finished appearing. A good time to call this is in the -viewDidAppear: method of that view controller. + */ +- (void)didAppear; + +/** + * This creates, configures, and appropriately presents an STPPaymentMethodsViewController on top of the view controller that you specify. It'll be dismissed automatically when the user is done selecting their payment method. + * + * @param viewController the view controller on which to present the STPPaymentMethodsViewController + */ +- (void)presentPaymentMethodsViewControllerOnViewController:(UIViewController *)viewController; + +/** + * This creates, configures, and appropriately pushes an STPPaymentMethodsViewController onto the navigation stack of the view controller that you specify. It'll be popped automatically when the user is done selecting their payment method. + * + * @param navigationController the view controller on which to present the STPPaymentMethodsViewController + */ +- (void)pushPaymentMethodsViewControllerOntoNavigationController:(UINavigationController *)navigationController; + +/** + * Whether or not the payment context contains all of the information it needs to complete a payment. For example, you can use the result of this method to enable/disable the "buy" button on a checkout page. + */ +- (BOOL)isReadyForPayment; + +/** + * Attempts to finalize the payment. This may need to present some supplemental UI to the user. For instance, if they've selected Apple Pay as their payment method, calling this method will show the payment sheet. If the user has a card on file, this will use that without presenting any additional UI. You should create a charge with the provided source in the `sourceHandler` block, and update your UI in the `completion` block. For an example of this, see CheckoutViewController.swift in our example app. + * + * @param fromViewController The view controller that is onscreen when this method is invoked. The payment context may call `presentViewController` on this view controller to show any required additional UI. + * @param sourceHandler This block will yield you the `source` the user has selected, along with the type of payment method they've chosen. You should go to your backend API with this `source` and make a charge to complete the payment, and once that's done call `sourceCompletion` with any error that occurred (or none, if the charge succeeded). + * @param completion This will be called after the payment is done and all necessary UI has been dismissed. You should inspect the `status` of the payment and behave appropriately. For example: if it's STPPaymentStatusSuccess, show the user a receipt. If it's STPPaymentStatusError, inform the user of the error. If it's STPPaymentStatusUserCanceled, do nothing. + */ +- (void)requestPaymentFromViewController:(UIViewController *)fromViewController + sourceHandler:(STPSourceHandlerBlock)sourceHandler + completion:(STPPaymentCompletionBlock)completion; + + +@end + +// These are internal methods and are subject to change - don't call them in your application's code. + +typedef void (^STPAddTokenBlock)(id __nullable paymentMethod, NSError * __nullable error); + +@interface STPPaymentContext(Internal) +- (void)onSuccess:(STPVoidBlock)completion; +- (void)addToken:(STPToken *)token completion:(STPAddTokenBlock)completion; +- (void)selectPaymentMethod:(id)paymentMethod; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/PublicHeaders/STPPaymentMethod.h b/Stripe/PublicHeaders/STPPaymentMethod.h new file mode 100644 index 0000000000..e748d364b9 --- /dev/null +++ b/Stripe/PublicHeaders/STPPaymentMethod.h @@ -0,0 +1,44 @@ +// +// STPPaymentMethod.h +// Stripe +// +// Created by Ben Guo on 4/19/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + +#import + +/** + * This represents all of the payment methods available to your user when configuring an STPPaymentContext. + */ +typedef NS_OPTIONS(NSUInteger, STPPaymentMethodType) { + /** + * The user is allowed to pay with Apple Pay (if it's configured and available on their device). + */ + STPPaymentMethodTypeApplePay = 1 << 0, + /** + * The user is allowed to pay with a card. + */ + STPPaymentMethodTypeCard = 1 << 1, + /** + * The user can use any available payment method to pay. + */ + STPPaymentMethodTypeAll = STPPaymentMethodTypeApplePay | STPPaymentMethodTypeCard +}; + +/** + * This protocol represents a payment method that a user can select and use to pay. Currently the only classes that conform to it are STPCardPaymentMethod (which represents that the user wants to pay with a specific card) and STPApplePayPaymentMethod (which represents that the user wants to pay with Apple Pay). + */ +@protocol STPPaymentMethod + +/** + * An image representing the payment method. For example, the Visa logo for a Visa card, or the Apple Pay logo. + */ +@property (nonatomic, readonly) UIImage *image; + +/** + * A string describing the payment method, such as "Apple Pay" or "Visa 4242". + */ +@property (nonatomic, readonly) NSString *label; + +@end diff --git a/Stripe/PublicHeaders/STPPaymentMethodsViewController.h b/Stripe/PublicHeaders/STPPaymentMethodsViewController.h new file mode 100644 index 0000000000..0aed99f0a2 --- /dev/null +++ b/Stripe/PublicHeaders/STPPaymentMethodsViewController.h @@ -0,0 +1,61 @@ +// +// STPSourceListViewController.h +// Stripe +// +// Created by Jack Flintermann on 1/12/16. +// Copyright © 2016 Stripe, Inc. All rights reserved. +// + +#import +#import "STPPaymentMethod.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol STPPaymentMethod; +@class STPPaymentContext, STPPaymentMethodsViewController; + +/** + * An STPPaymentMethodsViewControllerDelegate responds when a user selects a payment method from (or cancels) an STPPaymentMethodsViewController. In both of these instances, you should dismiss the view controller (either by popping it off the navigation stack, or dismissing it). If you are popping it off of a UINavigationController stack, be aware that it may have already pushed additional view controllers (such as an STPPaymentCardEntryViewController) onto the stack, so don't call -popViewControllerAnimated: on your UINavigationController. Instead, call -popToViewController: on your navigation controller, with the view controller that was behind the STPPaymentMethodsViewController as the first argument. + */ +@protocol STPPaymentMethodsViewControllerDelegate + +/** + * This is called when the user either makes a selection, or adds a new card. Before this is done, the view controller will update the selected payment method on its payment context. + * + * @param paymentMethodsViewController the view controller in question + * @param paymentMethod the selected payment method + */ +- (void)paymentMethodsViewController:(STPPaymentMethodsViewController *)paymentMethodsViewController + didSelectPaymentMethod:(id)paymentMethod; + +/** + * This is called when the user taps "cancel". + * + * @param paymentMethodsViewController the view controller that has been cancelled + */ +- (void)paymentMethodsViewControllerDidCancel:(STPPaymentMethodsViewController *)paymentMethodsViewController; + +@end + +/** + * This view controller presents a list of payment method options to the user, which they can select between. They can also add and remove credit cards from the list. It must be displayed inside a UINavigationController, so you can either create a UINavigationController with an STPPaymentMethodsViewController as the rootViewController and then present the UINavigationController, or push a new STPPaymentMethodsViewController onto an existing UINavigationController's stack. You can also have STPPaymentContext do this for you automatically, by calling presentPaymentMethodsViewControllerOnViewController: or pushPaymentMethodsViewControllerOntoNavigationController: on it. + */ +@interface STPPaymentMethodsViewController : UIViewController + +@property(nonatomic, readonly)STPPaymentContext *paymentContext; +@property(nonatomic, weak, nullable, readonly)iddelegate; + +/** + * Creates a new payment methods view controller. + * + * @param paymentContext A payment context to power the view controller's view. The paymentContext will in turn use its backend API adapter to fetch the information it needs from your application. + * @param delegate A delegate that will be notified when the user selects a payment method or cancels their selection. @see STPPaymentMethodsViewControllerDelegate + * + * @return an initialized view controller. + */ +- (instancetype)initWithPaymentContext:(STPPaymentContext *)paymentContext + delegate:(id)delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe/STPPaymentResult.h b/Stripe/PublicHeaders/STPPaymentResult.h similarity index 100% rename from Stripe/STPPaymentResult.h rename to Stripe/PublicHeaders/STPPaymentResult.h diff --git a/Stripe/PublicHeaders/Stripe.h b/Stripe/PublicHeaders/Stripe.h index a84ad514d8..4f8f3dbb62 100644 --- a/Stripe/PublicHeaders/Stripe.h +++ b/Stripe/PublicHeaders/Stripe.h @@ -17,12 +17,10 @@ #import "STPCardValidator.h" #import "STPToken.h" #import "STPBlocks.h" - #import "Stripe+ApplePay.h" #import "STPAPIClient+ApplePay.h" #import "STPPaymentCardTextField.h" #import "STPAddress.h" - #import "STPPaymentCardEntryViewController.h" #import "STPPaymentMethodsViewController.h" #import "STPPaymentContext.h" diff --git a/Stripe/STPApplePayPaymentMethod.h b/Stripe/STPApplePayPaymentMethod.h deleted file mode 100644 index 0a879b73de..0000000000 --- a/Stripe/STPApplePayPaymentMethod.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// STPApplePayPaymentMethod.h -// Stripe -// -// Created by Ben Guo on 4/19/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - -#import -#import "STPPaymentMethod.h" - -@interface STPApplePayPaymentMethod : NSObject - -@end diff --git a/Stripe/STPBackendAPIAdapter.h b/Stripe/STPBackendAPIAdapter.h deleted file mode 100644 index fa193b6a36..0000000000 --- a/Stripe/STPBackendAPIAdapter.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// STPBackendAPIAdapter.h -// Stripe -// -// Created by Jack Flintermann on 1/12/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - -#import -#import -#import "STPAddress.h" - -@class STPCard, STPToken; - -typedef void (^STPCardCompletionBlock)(STPCard * __nullable selectedCard, NSArray* __nullable cards, NSError * __nullable error); -typedef void (^STPAddressCompletionBlock)(STPAddress * __nullable address, NSError * __nullable error); - -@protocol STPBackendAPIAdapter - -- (void)retrieveCards:(nonnull STPCardCompletionBlock)completion; -- (void)addToken:(nonnull STPToken *)token completion:(nonnull STPCardCompletionBlock)completion; -- (void)selectCard:(nonnull STPCard *)card completion:(nonnull STPCardCompletionBlock)completion; - -@end diff --git a/Stripe/STPBlocks.h b/Stripe/STPBlocks.h deleted file mode 100644 index 206cf478ba..0000000000 --- a/Stripe/STPBlocks.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// STPBlocks.h -// Stripe -// -// Created by Jack Flintermann on 3/23/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - -#import -#import "STPSource.h" - -typedef NS_ENUM(NSUInteger, STPPaymentStatus) { - STPPaymentStatusSuccess, - STPPaymentStatusError, - STPPaymentStatusUserCancellation, -}; - -typedef void (^STPVoidBlock)(); -typedef void (^STPErrorBlock)(NSError * __nullable error); diff --git a/Stripe/STPCard.m b/Stripe/STPCard.m index c6cdae09d0..7c2717a1b6 100644 --- a/Stripe/STPCard.m +++ b/Stripe/STPCard.m @@ -33,6 +33,57 @@ @dynamic number, cvc, expMonth, expYear, currency, name, addressLine1, addressLine2, addressCity, addressState, addressZip, addressCountry; ++ (nonnull instancetype)cardWithID:(nonnull NSString *)stripeID + brand:(STPCardBrand)brand + last4:(nonnull NSString *)last4 + expMonth:(NSUInteger)expMonth + expYear:(NSUInteger)expYear + funding:(STPCardFundingType)funding { + STPCard *card = [self new]; + card.cardId = stripeID; + card.brand = brand; + card.last4 = last4; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + card.expMonth = expMonth; + card.expYear = expYear; +#pragma clang diagnostic pop + card.funding = funding; + return card; +} + ++ (STPCardBrand)brandFromString:(nonnull NSString *)string { + NSString *brand = [string lowercaseString]; + if ([brand isEqualToString:@"visa"]) { + return STPCardBrandVisa; + } else if ([brand isEqualToString:@"american express"]) { + return STPCardBrandAmex; + } else if ([brand isEqualToString:@"mastercard"]) { + return STPCardBrandMasterCard; + } else if ([brand isEqualToString:@"discover"]) { + return STPCardBrandDiscover; + } else if ([brand isEqualToString:@"jcb"]) { + return STPCardBrandJCB; + } else if ([brand isEqualToString:@"diners club"]) { + return STPCardBrandDinersClub; + } else { + return STPCardBrandUnknown; + } +} + ++ (STPCardFundingType)fundingFromString:(nonnull NSString *)string { + NSString *funding = [string lowercaseString]; + if ([funding isEqualToString:@"credit"]) { + return STPCardFundingTypeCredit; + } else if ([funding isEqualToString:@"debit"]) { + return STPCardFundingTypeDebit; + } else if ([funding isEqualToString:@"prepaid"]) { + return STPCardFundingTypePrepaid; + } else { + return STPCardFundingTypeOther; + } +} + - (instancetype)init { self = [super init]; if (self) { @@ -105,31 +156,9 @@ card.last4 = dict[@"last4"]; card.dynamicLast4 = dict[@"dynamic_last4"]; NSString *brand = [dict[@"brand"] lowercaseString]; - if ([brand isEqualToString:@"visa"]) { - card.brand = STPCardBrandVisa; - } else if ([brand isEqualToString:@"american express"]) { - card.brand = STPCardBrandAmex; - } else if ([brand isEqualToString:@"mastercard"]) { - card.brand = STPCardBrandMasterCard; - } else if ([brand isEqualToString:@"discover"]) { - card.brand = STPCardBrandDiscover; - } else if ([brand isEqualToString:@"jcb"]) { - card.brand = STPCardBrandJCB; - } else if ([brand isEqualToString:@"diners club"]) { - card.brand = STPCardBrandDinersClub; - } else { - card.brand = STPCardBrandUnknown; - } + card.brand = [STPCard brandFromString:brand]; NSString *funding = dict[@"funding"]; - if ([funding.lowercaseString isEqualToString:@"credit"]) { - card.funding = STPCardFundingTypeCredit; - } else if ([funding.lowercaseString isEqualToString:@"debit"]) { - card.funding = STPCardFundingTypeDebit; - } else if ([funding.lowercaseString isEqualToString:@"prepaid"]) { - card.funding = STPCardFundingTypePrepaid; - } else { - card.funding = STPCardFundingTypeOther; - } + card.funding = [STPCard fundingFromString:funding]; card.fingerprint = dict[@"fingerprint"]; card.country = dict[@"country"]; card.currency = dict[@"currency"]; diff --git a/Stripe/STPPaymentCardEntryViewController.h b/Stripe/STPPaymentCardEntryViewController.h deleted file mode 100644 index 430ea50d40..0000000000 --- a/Stripe/STPPaymentCardEntryViewController.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// STPPaymentCardEntryViewController.h -// Stripe -// -// Created by Jack Flintermann on 3/23/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - -#import -#import "STPBlocks.h" -#import "STPCardParams.h" -#import "STPAPIClient.h" -#import "STPAddress.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^STPPaymentCardEntryBlock)(STPToken * __nullable token, STPErrorBlock tokenCompletion); - -@interface STPPaymentCardEntryViewController : UIViewController - -- (instancetype)initWithAPIClient:(STPAPIClient *)apiClient - requiredBillingAddressFields:(STPBillingAddressFields)requiredBillingAddressFields - completion:(STPPaymentCardEntryBlock)completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Stripe/STPPaymentContext.h b/Stripe/STPPaymentContext.h deleted file mode 100644 index 4060e2f491..0000000000 --- a/Stripe/STPPaymentContext.h +++ /dev/null @@ -1,74 +0,0 @@ -// -// STPPaymentContext.h -// Stripe -// -// Created by Jack Flintermann on 4/20/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - - -#import -#import "STPPaymentMethod.h" -#import "STPBlocks.h" -#import "STPAddress.h" - -NS_ASSUME_NONNULL_BEGIN - -@class STPPaymentContext, STPAPIClient; -@protocol STPBackendAPIAdapter, STPPaymentMethod; - -typedef void (^STPSourceHandlerBlock)(STPPaymentMethodType paymentMethod, id __nonnull source, STPErrorBlock __nonnull completion); -typedef void (^STPPaymentCompletionBlock)(STPPaymentStatus status, NSError * __nullable error); - -@protocol STPPaymentContextDelegate - -- (void)paymentContextDidBeginLoading:(STPPaymentContext *)paymentContext; -- (void)paymentContext:(STPPaymentContext *)paymentContext didFailToLoadWithError:(NSError *)error; -- (void)paymentContextDidEndLoading:(STPPaymentContext *)paymentContext; -- (void)paymentContextDidChange:(STPPaymentContext *)paymentContext; - -@end - -@interface STPPaymentContext : NSObject - -- (instancetype)initWithAPIAdapter:(id)apiAdapter; - -- (instancetype)initWithAPIAdapter:(id)apiAdapter - apiClient:(STPAPIClient *)apiClient - supportedPaymentMethods:(STPPaymentMethodType)supportedPaymentMethods; - -@property(nonatomic, readonly)STPAPIClient *apiClient; -@property(nonatomic, readonly)id apiAdapter; -@property(nonatomic, readonly)STPPaymentMethodType supportedPaymentMethods; -@property(nonatomic)STPBillingAddressFields requiredBillingAddressFields; - -@property(nonatomic, weak, nullable)id delegate; -@property(nonatomic, readonly, getter=isLoading)BOOL loading; -@property(nonatomic, readonly, nullable)id selectedPaymentMethod; -@property(nonatomic, readonly, nullable)NSArray> *paymentMethods; - -@property(nonatomic)NSInteger paymentAmount; -@property(nonatomic, nullable)NSString *paymentCurrency; -@property(nonatomic, nullable)NSString *merchantName; -@property(nonatomic, nullable)NSString *appleMerchantIdentifier; - -- (void)didAppear; -- (void)presentPaymentMethodsViewControllerOnViewController:(UIViewController *)viewController; -- (void)pushPaymentMethodsViewControllerOntoNavigationController:(UINavigationController *)navigationController; -- (BOOL)isReadyForPayment; -- (void)requestPaymentFromViewController:(UIViewController *)fromViewController - sourceHandler:(STPSourceHandlerBlock)sourceHandler - completion:(STPPaymentCompletionBlock)completion; - - -@end - -typedef void (^STPAddTokenBlock)(id __nullable paymentMethod, NSError * __nullable error); - -@interface STPPaymentContext(Internal) -- (void)onSuccess:(STPVoidBlock)completion; -- (void)addToken:(STPToken *)token completion:(STPAddTokenBlock)completion; -- (void)selectPaymentMethod:(id)paymentMethod; -@end - -NS_ASSUME_NONNULL_END diff --git a/Stripe/STPPaymentContext.m b/Stripe/STPPaymentContext.m index 9ec1657122..7dab4324c5 100644 --- a/Stripe/STPPaymentContext.m +++ b/Stripe/STPPaymentContext.m @@ -36,6 +36,7 @@ @property(nonatomic)STPPaymentMethodType supportedPaymentMethods; @property(nonatomic, readwrite, getter=isLoading)BOOL loading; @property(nonatomic)STPPromise *initialLoadingPromise; +@property(nonatomic)STPPromise *didAppearPromise; @property(nonatomic)id selectedPaymentMethod; @property(nonatomic)NSArray> *paymentMethods; @@ -44,40 +45,45 @@ @implementation STPPaymentContext - (instancetype)initWithAPIAdapter:(id)apiAdapter { - return [self initWithAPIAdapter:apiAdapter - apiClient:[STPAPIClient sharedClient] - supportedPaymentMethods:STPPaymentMethodTypeAll]; + return [self initWithAPIAdapter:apiAdapter publishableKey:[Stripe defaultPublishableKey] supportedPaymentMethods:STPPaymentMethodTypeAll]; } - (instancetype)initWithAPIAdapter:(id)apiAdapter - apiClient:(STPAPIClient *)apiClient + publishableKey:(NSString *)publishableKey supportedPaymentMethods:(STPPaymentMethodType)supportedPaymentMethods { self = [super init]; if (self) { _apiAdapter = apiAdapter; - _apiClient = apiClient; + _apiClient = [[STPAPIClient alloc] initWithPublishableKey:publishableKey]; _supportedPaymentMethods = supportedPaymentMethods; _paymentCurrency = @"USD"; - _merchantName = [NSBundle stp_applicationName]; - __weak typeof(self) weakSelf = self; + _companyName = [NSBundle stp_applicationName]; + _didAppearPromise = [STPPromise new]; + __weak typeof(self) weakself = self; _initialLoadingPromise = [[[STPPromise new] onSuccess:^(STPCardTuple *tuple) { - weakSelf.paymentMethods = [weakSelf parsePaymentMethods:tuple.cards]; + weakself.paymentMethods = [weakself parsePaymentMethods:tuple.cards]; if (tuple.selectedCard) { - weakSelf.selectedPaymentMethod = [[STPCardPaymentMethod alloc] initWithCard:tuple.selectedCard]; + weakself.selectedPaymentMethod = [[STPCardPaymentMethod alloc] initWithCard:tuple.selectedCard]; } else if ([self applePaySupported]) { - weakSelf.selectedPaymentMethod = [STPApplePayPaymentMethod new]; + weakself.selectedPaymentMethod = [STPApplePayPaymentMethod new]; } }] onFailure:^(NSError * _Nonnull error) { - [weakSelf.delegate paymentContext:weakSelf didFailToLoadWithError:error]; + [weakself.didAppearPromise onSuccess:^(__unused id value) { + [weakself.delegate paymentContext:weakself didFailToLoadWithError:error]; + }]; }]; } return self; } -- (void)didAppear { +- (void)willAppear { [self performInitialLoad]; } +- (void)didAppear { + [self.didAppearPromise succeed:[NSObject new]]; +} + - (void)performInitialLoad { if (self.loading || self.initialLoadingPromise.completed) { return; @@ -276,7 +282,7 @@ PKPaymentRequest *paymentRequest = [Stripe paymentRequestWithMerchantIdentifier:self.appleMerchantIdentifier]; NSDecimalNumber *amount = [NSDecimalNumber stp_decimalNumberWithAmount:self.paymentAmount currency:self.paymentCurrency]; - PKPaymentSummaryItem *totalItem = [PKPaymentSummaryItem summaryItemWithLabel:self.merchantName + PKPaymentSummaryItem *totalItem = [PKPaymentSummaryItem summaryItemWithLabel:self.companyName amount:amount]; paymentRequest.paymentSummaryItems = @[totalItem]; paymentRequest.requiredBillingAddressFields = [STPAddress applePayAddressFieldsFromBillingAddressFields:self.requiredBillingAddressFields]; diff --git a/Stripe/STPPaymentMethod.h b/Stripe/STPPaymentMethod.h deleted file mode 100644 index a6ec172589..0000000000 --- a/Stripe/STPPaymentMethod.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// STPPaymentMethod.h -// Stripe -// -// Created by Ben Guo on 4/19/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - -#import - -typedef NS_OPTIONS(NSUInteger, STPPaymentMethodType) { - STPPaymentMethodTypeApplePay = 1 << 0, - STPPaymentMethodTypeCard = 1 << 1, - STPPaymentMethodTypeAll = STPPaymentMethodTypeApplePay | STPPaymentMethodTypeCard -}; - -@protocol STPPaymentMethod - -@property (nonatomic, readonly) UIImage *image; -@property (nonatomic, readonly) NSString *label; - -@end diff --git a/Stripe/STPPaymentMethodsViewController.h b/Stripe/STPPaymentMethodsViewController.h deleted file mode 100644 index 5ad9b0681a..0000000000 --- a/Stripe/STPPaymentMethodsViewController.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// STPSourceListViewController.h -// Stripe -// -// Created by Jack Flintermann on 1/12/16. -// Copyright © 2016 Stripe, Inc. All rights reserved. -// - -#import -#import "STPPaymentMethod.h" - -NS_ASSUME_NONNULL_BEGIN - -@protocol STPPaymentMethod; -@class STPPaymentContext, STPPaymentMethodsViewController; - -@protocol STPPaymentMethodsViewControllerDelegate - -- (void)paymentMethodsViewController:(STPPaymentMethodsViewController *)paymentMethodsViewController - didSelectPaymentMethod:(id)paymentMethod; -- (void)paymentMethodsViewControllerDidCancel:(STPPaymentMethodsViewController *)paymentMethodsViewController; - -@end - -@interface STPPaymentMethodsViewController : UIViewController - -@property(nonatomic, readonly)STPPaymentContext *paymentContext; - -- (instancetype)initWithPaymentContext:(STPPaymentContext *)paymentContext - delegate:(id)delegate; -@property(nonatomic, weak, nullable, readonly)iddelegate; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Stripe/STPPaymentMethodsViewController.m b/Stripe/STPPaymentMethodsViewController.m index 1e4faf6bf3..09035e50b6 100644 --- a/Stripe/STPPaymentMethodsViewController.m +++ b/Stripe/STPPaymentMethodsViewController.m @@ -76,7 +76,6 @@ static NSInteger STPPaymentMethodAddCardSection = 1; self.navigationItem.title = NSLocalizedString(@"Choose Payment", nil); self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Back", nil) style:UIBarButtonItemStylePlain target:nil action:nil]; - [self.paymentContext didAppear]; __weak typeof(self) weakself = self; [self.paymentContext onSuccess:^{ [UIView animateWithDuration:0.2 animations:^{ @@ -93,6 +92,7 @@ static NSInteger STPPaymentMethodAddCardSection = 1; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView reloadData]; + [self.paymentContext willAppear]; NSDictionary *titleTextAttributes = @{NSFontAttributeName:[UIFont stp_navigationBarFont]}; self.navigationController.navigationBar.titleTextAttributes = titleTextAttributes; if ([self stp_isTopNavigationController]) {