add standalone shipping address view controller
This commit is contained in:
parent
84911420c3
commit
80dc7f0c8d
|
@ -9,7 +9,7 @@
|
|||
import UIKit
|
||||
import Stripe
|
||||
|
||||
class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
||||
class CheckoutViewController: UIViewController, STPPaymentContextDelegate, STPShippingAddressViewControllerDelegate {
|
||||
|
||||
// 1) To get started with this demo, first head to https://dashboard.stripe.com/account/apikeys
|
||||
// and copy your "Test Publishable Key" (it looks like pk_test_abcdef) into the line below.
|
||||
|
@ -33,11 +33,15 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
|
||||
let theme: STPTheme
|
||||
let paymentRow: CheckoutRowView
|
||||
let shippingRow: CheckoutRowView
|
||||
let totalRow: CheckoutRowView
|
||||
let buyButton: BuyButton
|
||||
let rowHeight: CGFloat = 44
|
||||
let productImage = UILabel()
|
||||
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
|
||||
let numberFormatter: NumberFormatter
|
||||
let shippingString: String
|
||||
let shippingVC: STPShippingAddressViewController
|
||||
var product = ""
|
||||
var paymentInProgress: Bool = false {
|
||||
didSet {
|
||||
|
@ -60,11 +64,6 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
self.product = product
|
||||
self.productImage.text = product
|
||||
self.theme = settings.theme
|
||||
self.paymentRow = CheckoutRowView(title: "Payment", detail: "Select Payment",
|
||||
theme: settings.theme)
|
||||
self.totalRow = CheckoutRowView(title: "Total", detail: "", tappable: false,
|
||||
theme: settings.theme)
|
||||
self.buyButton = BuyButton(enabled: true, theme: settings.theme)
|
||||
MyAPIClient.sharedClient.baseURLString = self.backendBaseURL
|
||||
|
||||
// This code is included here for the sake of readability, but in your application you should set up your configuration and theme earlier, preferably in your App Delegate.
|
||||
|
@ -73,6 +72,8 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
config.appleMerchantIdentifier = self.appleMerchantID
|
||||
config.companyName = self.companyName
|
||||
config.requiredBillingAddressFields = settings.requiredBillingAddressFields
|
||||
config.requiredShippingAddressFields = settings.requiredShippingAddressFields
|
||||
config.shippingType = settings.shippingType
|
||||
config.additionalPaymentMethods = settings.additionalPaymentMethods
|
||||
config.smsAutofillDisabled = !settings.smsAutofillEnabled
|
||||
|
||||
|
@ -81,12 +82,37 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
theme: settings.theme)
|
||||
let userInformation = STPUserInformation()
|
||||
paymentContext.prefilledInformation = userInformation
|
||||
|
||||
paymentContext.paymentAmount = price
|
||||
paymentContext.paymentCurrency = self.paymentCurrency
|
||||
|
||||
self.paymentContext = paymentContext
|
||||
|
||||
self.paymentRow = CheckoutRowView(title: "Payment", detail: "Select Payment",
|
||||
theme: settings.theme)
|
||||
var shippingString = "Contact"
|
||||
if config.requiredShippingAddressFields.contains(.postalAddress) {
|
||||
shippingString = config.shippingType == .shipping ? "Shipping" : "Delivery"
|
||||
}
|
||||
self.shippingString = shippingString
|
||||
self.shippingRow = CheckoutRowView(title: self.shippingString,
|
||||
detail: "Enter \(self.shippingString) Info",
|
||||
theme: settings.theme)
|
||||
self.totalRow = CheckoutRowView(title: "Total", detail: "", tappable: false,
|
||||
theme: settings.theme)
|
||||
self.buyButton = BuyButton(enabled: true, theme: settings.theme)
|
||||
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.numberStyle = .currency
|
||||
numberFormatter.usesGroupingSeparator = true
|
||||
self.numberFormatter = numberFormatter
|
||||
let shippingVC = STPShippingAddressViewController()
|
||||
self.shippingVC = shippingVC
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
shippingVC.delegate = self
|
||||
self.paymentContext.delegate = self
|
||||
paymentContext.hostViewController = self
|
||||
}
|
||||
|
@ -106,15 +132,20 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
self.productImage.font = UIFont.systemFont(ofSize: 70)
|
||||
self.view.addSubview(self.totalRow)
|
||||
self.view.addSubview(self.paymentRow)
|
||||
self.view.addSubview(self.shippingRow)
|
||||
self.view.addSubview(self.productImage)
|
||||
self.view.addSubview(self.buyButton)
|
||||
self.view.addSubview(self.activityIndicator)
|
||||
self.activityIndicator.alpha = 0
|
||||
self.buyButton.addTarget(self, action: #selector(didTapBuy), for: .touchUpInside)
|
||||
self.totalRow.detail = "$\(self.paymentContext.paymentAmount/100).00"
|
||||
self.totalRow.detail = self.numberFormatter.string(from: NSNumber(value: Float(self.paymentContext.paymentAmount)/100))!
|
||||
self.paymentRow.onTap = { [weak self] _ in
|
||||
self?.paymentContext.pushPaymentMethodsViewController()
|
||||
}
|
||||
self.shippingRow.onTap = { [weak self] _ in
|
||||
guard let strongSelf = self else { return }
|
||||
strongSelf.navigationController?.pushViewController(strongSelf.shippingVC, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
|
@ -125,7 +156,9 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
y: self.productImage.bounds.height/2.0 + rowHeight)
|
||||
self.paymentRow.frame = CGRect(x: 0, y: self.productImage.frame.maxY + rowHeight,
|
||||
width: width, height: rowHeight)
|
||||
self.totalRow.frame = CGRect(x: 0, y: self.paymentRow.frame.maxY,
|
||||
self.shippingRow.frame = CGRect(x: 0, y: self.paymentRow.frame.maxY,
|
||||
width: width, height: rowHeight)
|
||||
self.totalRow.frame = CGRect(x: 0, y: self.shippingRow.frame.maxY,
|
||||
width: width, height: rowHeight)
|
||||
self.buyButton.frame = CGRect(x: 0, y: 0, width: 88, height: 44)
|
||||
self.buyButton.center = CGPoint(x: width/2.0, y: self.totalRow.frame.maxY + rowHeight*1.5)
|
||||
|
@ -137,6 +170,8 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
self.paymentContext.requestPayment()
|
||||
}
|
||||
|
||||
// MARK: STPPaymentContextDelegate
|
||||
|
||||
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) {
|
||||
MyAPIClient.sharedClient.completeCharge(paymentResult, amount: self.paymentContext.paymentAmount,
|
||||
completion: completion)
|
||||
|
@ -162,8 +197,6 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: STPPaymentContextDelegate
|
||||
|
||||
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
|
||||
self.paymentRow.loading = paymentContext.loading
|
||||
if let paymentMethod = paymentContext.selectedPaymentMethod {
|
||||
|
@ -172,6 +205,7 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
else {
|
||||
self.paymentRow.detail = "Select Payment"
|
||||
}
|
||||
self.totalRow.detail = self.numberFormatter.string(from: NSNumber(value: Float(self.paymentContext.paymentAmount)/100))!
|
||||
}
|
||||
|
||||
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
|
||||
|
@ -193,4 +227,36 @@ class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
|
|||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: STPShippingAddressViewControllerDelegate
|
||||
|
||||
func shippingAddressViewControllerDidCancel(_ addressViewController: STPShippingAddressViewController) {
|
||||
let _ = self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
func shippingAddressViewController(_ addressViewController: STPShippingAddressViewController, didEnter address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock) {
|
||||
let shippingMethod1 = PKShippingMethod()
|
||||
shippingMethod1.amount = 0
|
||||
shippingMethod1.label = "UPS Ground"
|
||||
shippingMethod1.detail = "Arrives in 3-5 days"
|
||||
shippingMethod1.identifier = "123"
|
||||
let shippingMethod2 = PKShippingMethod()
|
||||
shippingMethod2.amount = 5.99
|
||||
shippingMethod2.label = "FedEx"
|
||||
shippingMethod2.detail = "Arrives tomorrow"
|
||||
shippingMethod2.identifier = "456"
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
completion(nil, [shippingMethod1, shippingMethod2])
|
||||
}
|
||||
}
|
||||
|
||||
func shippingAddressViewController(_ addressViewController: STPShippingAddressViewController, didFinishWith address: STPAddress, shippingMethod method: PKShippingMethod?) {
|
||||
if let shippingMethod = method {
|
||||
self.shippingRow.detail = shippingMethod.label
|
||||
}
|
||||
else {
|
||||
self.shippingRow.detail = "Enter \(self.shippingString) Info"
|
||||
}
|
||||
self.shippingVC.dismiss(withHostViewController: self)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ struct Settings {
|
|||
let theme: STPTheme
|
||||
let additionalPaymentMethods: STPPaymentMethodType
|
||||
let requiredBillingAddressFields: STPBillingAddressFields
|
||||
let requiredShippingAddressFields: PKAddressField
|
||||
let shippingType: STPShippingType
|
||||
let smsAutofillEnabled: Bool
|
||||
}
|
||||
|
||||
|
@ -21,18 +23,24 @@ class SettingsViewController: UITableViewController {
|
|||
return Settings(theme: self.theme.stpTheme,
|
||||
additionalPaymentMethods: self.applePay.enabled ? .all : STPPaymentMethodType(),
|
||||
requiredBillingAddressFields: self.requiredBillingAddressFields.stpBillingAddressFields,
|
||||
requiredShippingAddressFields: self.requiredShippingAddressFields.pkAddressFields,
|
||||
shippingType: self.shippingType.stpShippingType,
|
||||
smsAutofillEnabled: self.smsAutofill.enabled)
|
||||
}
|
||||
|
||||
fileprivate var theme: Theme = .Default
|
||||
fileprivate var applePay: Switch = .Enabled
|
||||
fileprivate var requiredBillingAddressFields: RequiredBillingAddressFields = .None
|
||||
fileprivate var requiredShippingAddressFields: RequiredShippingAddressFields = .PostalAddressPhone
|
||||
fileprivate var shippingType: ShippingType = .Shipping
|
||||
fileprivate var smsAutofill: Switch = .Enabled
|
||||
|
||||
fileprivate enum Section: String {
|
||||
case Theme = "Theme"
|
||||
case ApplePay = "Apple Pay"
|
||||
case RequiredBillingAddressFields = "Required Billing Address Fields"
|
||||
case RequiredShippingAddressFields = "Required Shipping Address Fields"
|
||||
case ShippingType = "Shipping Type"
|
||||
case SMSAutofill = "SMS Autofill"
|
||||
case Session = "Session"
|
||||
|
||||
|
@ -41,7 +49,9 @@ class SettingsViewController: UITableViewController {
|
|||
case 0: self = .Theme
|
||||
case 1: self = .ApplePay
|
||||
case 2: self = .RequiredBillingAddressFields
|
||||
case 3: self = .SMSAutofill
|
||||
case 3: self = .RequiredShippingAddressFields
|
||||
case 4: self = .ShippingType
|
||||
case 5: self = .SMSAutofill
|
||||
default: self = .Session
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +135,64 @@ class SettingsViewController: UITableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
private enum RequiredShippingAddressFields: String {
|
||||
case None = "None"
|
||||
case Phone = "Phone"
|
||||
case Email = "Email"
|
||||
case NameEmail = "(Name|Email)"
|
||||
case PostalAddress = "PostalAddress"
|
||||
case PostalAddressPhone = "(PostalAddress|Phone)"
|
||||
case All = "All"
|
||||
|
||||
init(row: Int) {
|
||||
switch row {
|
||||
case 0: self = .None
|
||||
case 1: self = .Phone
|
||||
case 2: self = .Email
|
||||
case 3: self = .NameEmail
|
||||
case 4: self = .PostalAddress
|
||||
case 5: self = .PostalAddressPhone
|
||||
default: self = .All
|
||||
}
|
||||
}
|
||||
|
||||
var pkAddressFields: PKAddressField {
|
||||
switch self {
|
||||
case .None: return []
|
||||
case .Phone: return .phone
|
||||
case .Email: return .email
|
||||
case .NameEmail:
|
||||
if #available(iOS 8.3, *) {
|
||||
return [.name, .email]
|
||||
} else {
|
||||
return [.email]
|
||||
}
|
||||
case .PostalAddress: return .postalAddress
|
||||
case .PostalAddressPhone: return [.postalAddress, .phone]
|
||||
case .All: return .all
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum ShippingType: String {
|
||||
case Shipping = "Shipping"
|
||||
case Delivery = "Delivery"
|
||||
|
||||
init(row: Int) {
|
||||
switch row {
|
||||
case 0: self = .Shipping
|
||||
default: self = .Delivery
|
||||
}
|
||||
}
|
||||
|
||||
var stpShippingType: STPShippingType {
|
||||
switch self {
|
||||
case .Shipping: return .shipping
|
||||
case .Delivery: return .delivery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(style: .grouped)
|
||||
}
|
||||
|
@ -140,7 +208,7 @@ class SettingsViewController: UITableViewController {
|
|||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 5
|
||||
return 7
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
@ -148,6 +216,8 @@ class SettingsViewController: UITableViewController {
|
|||
case .Theme: return 3
|
||||
case .ApplePay: return 2
|
||||
case .RequiredBillingAddressFields: return 3
|
||||
case .RequiredShippingAddressFields: return 7
|
||||
case .ShippingType: return 2
|
||||
case .SMSAutofill: return 2
|
||||
case .Session: return 1
|
||||
}
|
||||
|
@ -172,6 +242,14 @@ class SettingsViewController: UITableViewController {
|
|||
let value = RequiredBillingAddressFields(row: (indexPath as NSIndexPath).row)
|
||||
cell.textLabel?.text = value.rawValue
|
||||
cell.accessoryType = value == self.requiredBillingAddressFields ? .checkmark : .none
|
||||
case .RequiredShippingAddressFields:
|
||||
let value = RequiredShippingAddressFields(row: indexPath.row)
|
||||
cell.textLabel?.text = value.rawValue
|
||||
cell.accessoryType = value == self.requiredShippingAddressFields ? .checkmark : .none
|
||||
case .ShippingType:
|
||||
let value = ShippingType(row: indexPath.row)
|
||||
cell.textLabel?.text = value.rawValue
|
||||
cell.accessoryType = value == self.shippingType ? .checkmark : .none
|
||||
case .SMSAutofill:
|
||||
let value = Switch(row: (indexPath as NSIndexPath).row)
|
||||
cell.textLabel?.text = value.rawValue
|
||||
|
@ -192,6 +270,10 @@ class SettingsViewController: UITableViewController {
|
|||
self.applePay = Switch(row: (indexPath as NSIndexPath).row)
|
||||
case .RequiredBillingAddressFields:
|
||||
self.requiredBillingAddressFields = RequiredBillingAddressFields(row: (indexPath as NSIndexPath).row)
|
||||
case .RequiredShippingAddressFields:
|
||||
self.requiredShippingAddressFields = RequiredShippingAddressFields(row: (indexPath as NSIndexPath).row)
|
||||
case .ShippingType:
|
||||
self.shippingType = ShippingType(row: (indexPath as NSIndexPath).row)
|
||||
case .SMSAutofill:
|
||||
self.smsAutofill = Switch(row: (indexPath as NSIndexPath).row)
|
||||
case .Session:
|
||||
|
|
|
@ -433,6 +433,24 @@
|
|||
C1363BB81D7633D800EB82B4 /* STPPaymentMethodTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C1363BB51D7633D800EB82B4 /* STPPaymentMethodTableViewCell.h */; };
|
||||
C1363BB91D7633D800EB82B4 /* STPPaymentMethodTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C1363BB61D7633D800EB82B4 /* STPPaymentMethodTableViewCell.m */; };
|
||||
C1363BBA1D7633D800EB82B4 /* STPPaymentMethodTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C1363BB61D7633D800EB82B4 /* STPPaymentMethodTableViewCell.m */; };
|
||||
C15993231D8807930047950D /* stp_shipping_form.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993201D8807930047950D /* stp_shipping_form.png */; };
|
||||
C15993241D8807930047950D /* stp_shipping_form@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993211D8807930047950D /* stp_shipping_form@2x.png */; };
|
||||
C15993251D8807930047950D /* stp_shipping_form@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993221D8807930047950D /* stp_shipping_form@3x.png */; };
|
||||
C15993281D8808490047950D /* STPShippingAddressViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C15993261D8808490047950D /* STPShippingAddressViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C159932A1D88084D0047950D /* STPShippingAddressViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C15993261D8808490047950D /* STPShippingAddressViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C15993331D8808680047950D /* STPShippingAddressViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C159932C1D8808680047950D /* STPShippingAddressViewController.m */; };
|
||||
C15993361D8808680047950D /* STPShippingMethodsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C159932F1D8808680047950D /* STPShippingMethodsViewController.h */; };
|
||||
C15993371D8808680047950D /* STPShippingMethodsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C15993301D8808680047950D /* STPShippingMethodsViewController.m */; };
|
||||
C15993381D8808680047950D /* STPShippingMethodTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C15993311D8808680047950D /* STPShippingMethodTableViewCell.h */; };
|
||||
C15993391D8808680047950D /* STPShippingMethodTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C15993321D8808680047950D /* STPShippingMethodTableViewCell.m */; };
|
||||
C159933A1D8808880047950D /* STPShippingAddressViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C159932C1D8808680047950D /* STPShippingAddressViewController.m */; };
|
||||
C159933D1D8808970047950D /* STPShippingMethodsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C159932F1D8808680047950D /* STPShippingMethodsViewController.h */; };
|
||||
C159933F1D88089B0047950D /* STPShippingMethodsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C15993301D8808680047950D /* STPShippingMethodsViewController.m */; };
|
||||
C15993401D88089E0047950D /* STPShippingMethodTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C15993311D8808680047950D /* STPShippingMethodTableViewCell.h */; };
|
||||
C15993411D8808A10047950D /* STPShippingMethodTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C15993321D8808680047950D /* STPShippingMethodTableViewCell.m */; };
|
||||
C15993451D8829C00047950D /* stp_shipping_form.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993201D8807930047950D /* stp_shipping_form.png */; };
|
||||
C15993461D8829C00047950D /* stp_shipping_form@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993211D8807930047950D /* stp_shipping_form@2x.png */; };
|
||||
C15993471D8829C00047950D /* stp_shipping_form@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C15993221D8807930047950D /* stp_shipping_form@3x.png */; };
|
||||
C16F66AB1CA21BAC006A21B5 /* STPFormTextFieldTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */; };
|
||||
C1717DB11CC00ED60009CF4A /* STPAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = C1080F471CBECF7B007B2D89 /* STPAddress.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C17A030D1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C17A030B1CBEE7A2006C819F /* STPAddressFieldTableViewCell.h */; };
|
||||
|
@ -906,6 +924,15 @@
|
|||
C1363BAE1D76337400EB82B4 /* stp_icon_checkmark@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_icon_checkmark@3x.png"; sourceTree = "<group>"; };
|
||||
C1363BB51D7633D800EB82B4 /* STPPaymentMethodTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPPaymentMethodTableViewCell.h; sourceTree = "<group>"; };
|
||||
C1363BB61D7633D800EB82B4 /* STPPaymentMethodTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPPaymentMethodTableViewCell.m; sourceTree = "<group>"; };
|
||||
C15993201D8807930047950D /* stp_shipping_form.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = stp_shipping_form.png; sourceTree = "<group>"; };
|
||||
C15993211D8807930047950D /* stp_shipping_form@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_shipping_form@2x.png"; sourceTree = "<group>"; };
|
||||
C15993221D8807930047950D /* stp_shipping_form@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "stp_shipping_form@3x.png"; sourceTree = "<group>"; };
|
||||
C15993261D8808490047950D /* STPShippingAddressViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STPShippingAddressViewController.h; path = PublicHeaders/STPShippingAddressViewController.h; sourceTree = "<group>"; };
|
||||
C159932C1D8808680047950D /* STPShippingAddressViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPShippingAddressViewController.m; sourceTree = "<group>"; };
|
||||
C159932F1D8808680047950D /* STPShippingMethodsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPShippingMethodsViewController.h; sourceTree = "<group>"; };
|
||||
C15993301D8808680047950D /* STPShippingMethodsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPShippingMethodsViewController.m; sourceTree = "<group>"; };
|
||||
C15993311D8808680047950D /* STPShippingMethodTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPShippingMethodTableViewCell.h; sourceTree = "<group>"; };
|
||||
C15993321D8808680047950D /* STPShippingMethodTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPShippingMethodTableViewCell.m; sourceTree = "<group>"; };
|
||||
C16A4CDB1D36B19B001F46D2 /* MockSTPCheckoutAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockSTPCheckoutAPIClient.h; sourceTree = "<group>"; };
|
||||
C16A4CDC1D36B19B001F46D2 /* MockSTPCheckoutAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MockSTPCheckoutAPIClient.m; sourceTree = "<group>"; };
|
||||
C16F66AA1CA21BAC006A21B5 /* STPFormTextFieldTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPFormTextFieldTest.m; sourceTree = "<group>"; };
|
||||
|
@ -1111,6 +1138,9 @@
|
|||
04E39F621CED3B0100AF3B96 /* stp_icon_chevron_right_small.png */,
|
||||
04E39F631CED3B0100AF3B96 /* stp_icon_chevron_right_small@2x.png */,
|
||||
04E39F641CED3B0100AF3B96 /* stp_icon_chevron_right_small@3x.png */,
|
||||
C15993201D8807930047950D /* stp_shipping_form.png */,
|
||||
C15993211D8807930047950D /* stp_shipping_form@2x.png */,
|
||||
C15993221D8807930047950D /* stp_shipping_form@3x.png */,
|
||||
);
|
||||
path = Images;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1323,6 +1353,12 @@
|
|||
F1852F921D80B6EC00367C86 /* STPStringUtils.m */,
|
||||
F1C7B8D21DBECF2400D9F6F0 /* STPDispatchFunctions.h */,
|
||||
F1C7B8D11DBECF2400D9F6F0 /* STPDispatchFunctions.m */,
|
||||
C15993261D8808490047950D /* STPShippingAddressViewController.h */,
|
||||
C159932C1D8808680047950D /* STPShippingAddressViewController.m */,
|
||||
C159932F1D8808680047950D /* STPShippingMethodsViewController.h */,
|
||||
C15993301D8808680047950D /* STPShippingMethodsViewController.m */,
|
||||
C15993311D8808680047950D /* STPShippingMethodTableViewCell.h */,
|
||||
C15993321D8808680047950D /* STPShippingMethodTableViewCell.m */,
|
||||
);
|
||||
name = Stripe;
|
||||
path = Tests/../Stripe;
|
||||
|
@ -1489,6 +1525,7 @@
|
|||
04F94DA11D229F12004FC826 /* STPAddressFieldTableViewCell.h in Headers */,
|
||||
04EBC75A1B7533C300A0E6AE /* STPCardValidator.h in Headers */,
|
||||
04F94DA61D229F27004FC826 /* STPCardTuple.h in Headers */,
|
||||
C159933D1D8808970047950D /* STPShippingMethodsViewController.h in Headers */,
|
||||
04F94DCD1D22A22F004FC826 /* UIViewController+Stripe_KeyboardAvoiding.h in Headers */,
|
||||
04A4C3981C4F2C8600B3B290 /* NSString+Stripe_CardBrands.h in Headers */,
|
||||
04A488431CA3580700506E53 /* UINavigationController+Stripe_Completion.h in Headers */,
|
||||
|
@ -1510,6 +1547,7 @@
|
|||
049E84E61A605EF0000B66CD /* STPAPIClient.h in Headers */,
|
||||
04F94DBD1D229F95004FC826 /* UITableViewCell+Stripe_Borders.h in Headers */,
|
||||
049E84E91A605EF0000B66CD /* STPBankAccount.h in Headers */,
|
||||
C15993401D88089E0047950D /* STPShippingMethodTableViewCell.h in Headers */,
|
||||
04B31DD51D08E6E200EF1631 /* STPCustomer.h in Headers */,
|
||||
04633AFB1CD1299B009D4FB5 /* NSString+Stripe.h in Headers */,
|
||||
04B31E001D131D9000EF1631 /* STPRememberMePaymentCell.h in Headers */,
|
||||
|
@ -1554,6 +1592,7 @@
|
|||
04F94DC51D22A1FD004FC826 /* STPCheckoutAccountLookup.h in Headers */,
|
||||
04A4883E1CA3568800506E53 /* STPBlocks.h in Headers */,
|
||||
045D71211CEFA57000F6CD65 /* UIViewController+Stripe_Promises.h in Headers */,
|
||||
C159932A1D88084D0047950D /* STPShippingAddressViewController.h in Headers */,
|
||||
049880FD1CED5A2300EA4FFD /* STPPaymentConfiguration.h in Headers */,
|
||||
049A3F9B1CC7DBCC00F57DE7 /* STPPaymentContext.h in Headers */,
|
||||
04F3BB3E1BA89B1200DE235E /* PKPayment+Stripe.h in Headers */,
|
||||
|
@ -1587,6 +1626,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C15993361D8808680047950D /* STPShippingMethodsViewController.h in Headers */,
|
||||
04BC29A91CD9A83600318357 /* STPCheckoutAPIClient.h in Headers */,
|
||||
C11810A71CC6EE840022FB55 /* STPBackendAPIAdapter.h in Headers */,
|
||||
F12C8DC01D63DE9F00ADA0D7 /* STPPaymentContextAmountModel.h in Headers */,
|
||||
|
@ -1601,6 +1641,7 @@
|
|||
04695AD91C77F9EF00E08063 /* STPDelegateProxy.h in Headers */,
|
||||
0433EB491BD06313003912B4 /* NSDictionary+Stripe.h in Headers */,
|
||||
C124A1701CCA968B007D42EE /* STPAnalyticsClient.h in Headers */,
|
||||
C15993281D8808490047950D /* STPShippingAddressViewController.h in Headers */,
|
||||
049A3F7A1CC18D5300F57DE7 /* UIView+Stripe_FirstResponder.h in Headers */,
|
||||
04CDB50E1A5F30A700B854EE /* STPCard.h in Headers */,
|
||||
C1080F491CBECF7B007B2D89 /* STPAddress.h in Headers */,
|
||||
|
@ -1621,6 +1662,7 @@
|
|||
04BC29BD1CDD535700318357 /* STPSwitchTableViewCell.h in Headers */,
|
||||
04CDB5121A5F30A700B854EE /* STPToken.h in Headers */,
|
||||
049952CF1BCF13510088C703 /* STPAPIPostRequest.h in Headers */,
|
||||
C15993381D8808680047950D /* STPShippingMethodTableViewCell.h in Headers */,
|
||||
049A3FB21CC9FEFC00F57DE7 /* UIToolbar+Stripe_InputAccessory.h in Headers */,
|
||||
04E39F521CECF7A100AF3B96 /* STPCardTuple.h in Headers */,
|
||||
04A488331CA34D3000506E53 /* STPEmailAddressValidator.h in Headers */,
|
||||
|
@ -1832,6 +1874,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C15993251D8807930047950D /* stp_shipping_form@3x.png in Resources */,
|
||||
0438EFA81B741C2800D506CC /* stp_card_amex@3x.png in Resources */,
|
||||
0438EFC61B741C2800D506CC /* stp_card_jcb@3x.png in Resources */,
|
||||
0438EFC21B741C2800D506CC /* stp_card_jcb.png in Resources */,
|
||||
|
@ -1892,6 +1935,7 @@
|
|||
0438EFAC1B741C2800D506CC /* stp_card_cvc@2x.png in Resources */,
|
||||
0438EFCC1B741C2800D506CC /* stp_card_mastercard@3x.png in Resources */,
|
||||
F1510BB91D5A782E000731AD /* stp_card_form_applepay@3x.png in Resources */,
|
||||
C15993241D8807930047950D /* stp_shipping_form@2x.png in Resources */,
|
||||
F1510B0B1D5A4C93000731AD /* stp_card_amex_template.png in Resources */,
|
||||
F1510BB31D5A782E000731AD /* stp_card_form_applepay.png in Resources */,
|
||||
0438EFCA1B741C2800D506CC /* stp_card_mastercard@2x.png in Resources */,
|
||||
|
@ -1900,6 +1944,7 @@
|
|||
049A3FA01CC8006800F57DE7 /* stp_icon_add@2x.png in Resources */,
|
||||
042CA1B81B7BD84100AF0DA6 /* stp_card_placeholder_template@3x.png in Resources */,
|
||||
F1510B211D5A4C93000731AD /* stp_card_mastercard_template@3x.png in Resources */,
|
||||
C15993231D8807930047950D /* stp_shipping_form.png in Resources */,
|
||||
0438EFC81B741C2800D506CC /* stp_card_mastercard.png in Resources */,
|
||||
F1510B151D5A4C93000731AD /* stp_card_applepay_template@3x.png in Resources */,
|
||||
04E39F661CED3B0100AF3B96 /* stp_icon_chevron_right_small@2x.png in Resources */,
|
||||
|
@ -1916,6 +1961,7 @@
|
|||
C1B630BB1D1D860100A05285 /* stp_card_amex.png in Resources */,
|
||||
F1510B161D5A4C93000731AD /* stp_card_applepay_template@3x.png in Resources */,
|
||||
C1B630BC1D1D860100A05285 /* stp_card_amex@2x.png in Resources */,
|
||||
C15993471D8829C00047950D /* stp_shipping_form@3x.png in Resources */,
|
||||
F148AC0C1D5E8DF30014FD92 /* stp_icon_add@3x.png in Resources */,
|
||||
F148AC0D1D5E8DF30014FD92 /* stp_icon_chevron_left.png in Resources */,
|
||||
C1363BB31D76337900EB82B4 /* stp_icon_checkmark@2x.png in Resources */,
|
||||
|
@ -1934,6 +1980,7 @@
|
|||
C1B630C11D1D860100A05285 /* stp_card_cvc_amex.png in Resources */,
|
||||
F1510B4F1D5A4CC4000731AD /* stp_card_form_back@2x.png in Resources */,
|
||||
F1510B5C1D5A4CC4000731AD /* stp_icon_add@3x.png in Resources */,
|
||||
C15993461D8829C00047950D /* stp_shipping_form@2x.png in Resources */,
|
||||
C120574C1D676DD400CFBCB8 /* stp_card_diners_template@3x.png in Resources */,
|
||||
C1B630C21D1D860100A05285 /* stp_card_cvc_amex@2x.png in Resources */,
|
||||
C120574F1D676DD400CFBCB8 /* stp_card_form_applepay@3x.png in Resources */,
|
||||
|
@ -1972,6 +2019,7 @@
|
|||
C1B630CF1D1D860100A05285 /* stp_card_mastercard@3x.png in Resources */,
|
||||
F1510B281D5A4C93000731AD /* stp_card_visa_template@3x.png in Resources */,
|
||||
F1510B221D5A4C93000731AD /* stp_card_mastercard_template@3x.png in Resources */,
|
||||
C15993451D8829C00047950D /* stp_shipping_form.png in Resources */,
|
||||
C120574B1D676DD400CFBCB8 /* stp_card_diners_template@2x.png in Resources */,
|
||||
F1510B591D5A4CC4000731AD /* stp_card_applepay@3x.png in Resources */,
|
||||
F1510B5B1D5A4CC4000731AD /* stp_icon_add@2x.png in Resources */,
|
||||
|
@ -2107,6 +2155,7 @@
|
|||
04A4C3901C4F25F900B3B290 /* UIViewController+Stripe_ParentViewController.m in Sources */,
|
||||
04F94DA01D229F0B004FC826 /* STPPostalCodeValidator.m in Sources */,
|
||||
C124A17F1CCAA0C2007D42EE /* NSMutableURLRequest+Stripe.m in Sources */,
|
||||
C15993411D8808A10047950D /* STPShippingMethodTableViewCell.m in Sources */,
|
||||
04F94DAA1D229F36004FC826 /* STPTheme.m in Sources */,
|
||||
0439B98A1C454F97005A1ED5 /* STPPaymentMethodsViewController.m in Sources */,
|
||||
04F94DAE1D229F54004FC826 /* STPColorUtils.m in Sources */,
|
||||
|
@ -2121,12 +2170,14 @@
|
|||
0438EF371B7416BB00D506CC /* STPPaymentCardTextField.m in Sources */,
|
||||
049E84CC1A605DE0000B66CD /* STPAPIClient.m in Sources */,
|
||||
049E84CD1A605DE0000B66CD /* STPFormEncoder.m in Sources */,
|
||||
C159933A1D8808880047950D /* STPShippingAddressViewController.m in Sources */,
|
||||
04F94DA41D229F1C004FC826 /* STPAddressViewModel.m in Sources */,
|
||||
049880FF1CED5A2300EA4FFD /* STPPaymentConfiguration.m in Sources */,
|
||||
04F94DB91D229F86004FC826 /* STPApplePayPaymentMethod.m in Sources */,
|
||||
04B31DE91D09D25F00EF1631 /* STPPaymentMethodsInternalViewController.m in Sources */,
|
||||
F12C8DC51D63DE9F00ADA0D7 /* STPPaymentContextAmountModel.m in Sources */,
|
||||
04633B011CD129CB009D4FB5 /* STPPhoneNumberValidator.m in Sources */,
|
||||
C159933F1D88089B0047950D /* STPShippingMethodsViewController.m in Sources */,
|
||||
04A488451CA3580700506E53 /* UINavigationController+Stripe_Completion.m in Sources */,
|
||||
049E84CF1A605DE0000B66CD /* STPBankAccount.m in Sources */,
|
||||
049E84D01A605DE0000B66CD /* STPCard.m in Sources */,
|
||||
|
@ -2195,11 +2246,14 @@
|
|||
04BC29BE1CDD535700318357 /* STPSwitchTableViewCell.m in Sources */,
|
||||
04E39F6B1CED48D500AF3B96 /* UIBarButtonItem+Stripe.m in Sources */,
|
||||
04CDB5181A5F30A700B854EE /* StripeError.m in Sources */,
|
||||
C15993371D8808680047950D /* STPShippingMethodsViewController.m in Sources */,
|
||||
C1363BB91D7633D800EB82B4 /* STPPaymentMethodTableViewCell.m in Sources */,
|
||||
C15993331D8808680047950D /* STPShippingAddressViewController.m in Sources */,
|
||||
04633B051CD44F1C009D4FB5 /* STPAPIClient+ApplePay.m in Sources */,
|
||||
F1C7B8D31DBECF2400D9F6F0 /* STPDispatchFunctions.m in Sources */,
|
||||
04B31DF41D09F0A800EF1631 /* UIViewController+Stripe_NavigationItemProxy.m in Sources */,
|
||||
04BC29A51CD8697900318357 /* STPTheme.m in Sources */,
|
||||
C15993391D8808680047950D /* STPShippingMethodTableViewCell.m in Sources */,
|
||||
049A3F8A1CC73C7100F57DE7 /* STPPaymentContext.m in Sources */,
|
||||
0426B9731CEAE3EB006AC8DD /* UITableViewCell+Stripe_Borders.m in Sources */,
|
||||
04BC29B21CD9AAA800318357 /* STPCheckoutAccount.m in Sources */,
|
||||
|
|
|
@ -93,6 +93,7 @@ typedef NS_ENUM(NSUInteger, STPBillingAddressFields) {
|
|||
#pragma clang diagnostic pop
|
||||
|
||||
- (BOOL)containsRequiredFields:(STPBillingAddressFields)requiredFields;
|
||||
- (BOOL)containsRequiredPKFields:(PKAddressField)requiredFields;
|
||||
|
||||
+ (PKAddressField)applePayAddressFieldsFromBillingAddressFields:(STPBillingAddressFields)billingAddressFields; FAUXPAS_IGNORED_ON_LINE(APIAvailability);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <PassKit/PassKit.h>
|
||||
|
||||
@class STPToken;
|
||||
|
||||
|
@ -47,3 +48,12 @@ typedef void (^STPErrorBlock)(NSError * __nullable error);
|
|||
* @param error The error returned from the response, or nil in one occurs. @see StripeError.h for possible values.
|
||||
*/
|
||||
typedef void (^STPTokenCompletionBlock)(STPToken * __nullable token, NSError * __nullable error);
|
||||
|
||||
/**
|
||||
* A callback to be run with a validation result and shipping methods for a
|
||||
* shipping address.
|
||||
*
|
||||
* @param shippingValidationError If the shipping address is invalid, an error describing the issue with the address. Will be nil if the address is valid.
|
||||
* @param shippingMethods The shipping methods available for the address.
|
||||
*/
|
||||
typedef void (^STPShippingMethodsCompletionBlock)(NSError * __nullable shippingValidationError, NSArray<PKShippingMethod *>* __nonnull shippingMethods);
|
||||
|
|
|
@ -11,6 +11,21 @@
|
|||
#import "STPPaymentMethod.h"
|
||||
#import "STPTheme.h"
|
||||
|
||||
/**
|
||||
* These values control the labels used in the shipping info collection form.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, STPShippingType) {
|
||||
/**
|
||||
* Shipping the purchase to the provided address using a third-party
|
||||
* shipping company.
|
||||
*/
|
||||
STPShippingTypeShipping,
|
||||
/**
|
||||
* Delivering the purchase by the seller.
|
||||
*/
|
||||
STPShippingTypeDelivery,
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
|
@ -46,6 +61,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
@property(nonatomic)STPBillingAddressFields requiredBillingAddressFields;
|
||||
|
||||
/**
|
||||
* The billing address fields the user must fill out when prompted for their shipping info.
|
||||
*/
|
||||
@property(nonatomic)PKAddressField requiredShippingAddressFields;
|
||||
|
||||
/**
|
||||
* The type of shipping for this purchase. This property sets the labels displayed when the user is prompted for shipping info, and whether they should also be asked to select a shipping method. The default value is STPShippingTypeShipping.
|
||||
*/
|
||||
@property(nonatomic)STPShippingType shippingType;
|
||||
|
||||
/**
|
||||
* The name of your company, for displaying to the user during payment flows. 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.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// STPShippingAddressViewController.h
|
||||
// Stripe
|
||||
//
|
||||
// Created by Ben Guo on 8/29/16.
|
||||
// Copyright © 2016 Stripe, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <PassKit/PassKit.h>
|
||||
#import "STPTheme.h"
|
||||
#import "STPPaymentContext.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol STPShippingAddressViewControllerDelegate;
|
||||
|
||||
/** This view controller contains a shipping address collection form. It renders a right bar button item that submits the form, so it must be shown inside a `UINavigationController`. Depending on your configuration's shippingType, the view controller may present a shipping method selection form after the user enters an address.
|
||||
*/
|
||||
@interface STPShippingAddressViewController : UIViewController
|
||||
|
||||
/**
|
||||
* A convenience initializer; equivalent to calling `initWithConfiguration:[STPPaymentConfiguration sharedConfiguration] theme:[STPTheme defaultTheme] currency:nil shippingAddress:nil selectedShippingMethod:nil`.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Initializes a new `STPShippingAddressCardViewController` with the provided parameters.
|
||||
*
|
||||
* @param configuration The configuration to use (this determines the required shipping address fields and shipping type). @see STPPaymentConfiguration
|
||||
* @param theme The theme to use to inform the view controller's visual appearance. @see STPTheme
|
||||
* @param currency The currency to use when displaying amounts for shipping methods. The default is USD.
|
||||
* @param shippingAddress If set, the shipping address view controller will be pre-filled with this address. @see STPAddress
|
||||
* @param selectedShippingMethod If set, the shipping methods view controller will use this method as the selected shipping method. If `selectedShippingMethod` is nil, the first shipping method in the array of methods returned by your delegate will be selected.
|
||||
*/
|
||||
- (instancetype)initWithConfiguration:(STPPaymentConfiguration *)configuration
|
||||
theme:(STPTheme *)theme
|
||||
currency:(nullable NSString *)currency
|
||||
shippingAddress:(nullable STPAddress *)shippingAddress
|
||||
selectedShippingMethod:(nullable PKShippingMethod *)selectedShippingMethod;
|
||||
|
||||
/**
|
||||
* The view controller's delegate. This must be set before showing the view controller in order for it to work properly. @see STPShippingAddressViewControllerDelegate
|
||||
*/
|
||||
@property(nonatomic, weak) id<STPShippingAddressViewControllerDelegate> delegate;
|
||||
|
||||
/**
|
||||
* If you're pushing `STPShippingAddressViewController` onto an existing `UINavigationController`'s stack, you should use this method to dismiss it, since it may have pushed an additional shipping method view controller onto the navigation controller's stack.
|
||||
*
|
||||
* @param hostViewController the view controller that the shipping address view controller was pushed onto.
|
||||
*/
|
||||
- (void)dismissWithHostViewController:(UIViewController *)hostViewController;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* An `STPShippingAddressViewControllerDelegate` is notified when an `STPShippingAddressViewController` receives an address, completes with an address, or is cancelled.
|
||||
*/
|
||||
@protocol STPShippingAddressViewControllerDelegate <NSObject>
|
||||
|
||||
/**
|
||||
* Called when the user cancels entering a shipping address. You should dismiss (or pop) the view controller at this point.
|
||||
*
|
||||
* @param addressViewController the view controller that has been cancelled
|
||||
*/
|
||||
- (void)shippingAddressViewControllerDidCancel:(STPShippingAddressViewController *)addressViewController;
|
||||
|
||||
/**
|
||||
* This is called when the user enters a shipping address and taps next. You should validate the address and determine what shipping methods are available, and call the `completion` block when finished. If an error occurrs, call the `completion` block with the error. Otherwise, call the `completion` block with a nil error and an array of available shipping methods. If you don't need to collect a shipping method, you may pass an empty array.
|
||||
*
|
||||
* @param addressViewController the view controller where the address was entered
|
||||
* @param address the address that was entered. @see STPAddress
|
||||
* @param completion call this callback when you're done validating the address and determining available shipping methods.
|
||||
*/
|
||||
- (void)shippingAddressViewController:(STPShippingAddressViewController *)addressViewController
|
||||
didEnterAddress:(STPAddress *)address
|
||||
completion:(STPShippingMethodsCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
* This is called when the user selects a shipping method. If no shipping methods are given, or if the shipping type doesn't require a shipping method, this will be called after the user has a shipping address and your validation has succeeded. After updating your app with the user's shipping info, you should dismiss (or pop) the view controller. Note that if `shippingMethod` is non-nil, there will be an additional shipping methods view controller on the navigation controller's stack.
|
||||
*
|
||||
* @param addressViewController the view controller where the address was entered
|
||||
* @param address the address that was entered. @see STPAddress
|
||||
* @param shippingMethod the shipping method that was selected.
|
||||
*/
|
||||
- (void)shippingAddressViewController:(STPShippingAddressViewController *)addressViewController
|
||||
didFinishWithAddress:(STPAddress *)address
|
||||
shippingMethod:(nullable PKShippingMethod *)method;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -37,3 +37,4 @@
|
|||
#import "STPPaymentConfiguration.h"
|
||||
#import "STPAPIResponseDecodable.h"
|
||||
#import "STPFormEncodable.h"
|
||||
#import "STPShippingAddressViewController.h"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -9,6 +9,8 @@
|
|||
#import "STPAddress.h"
|
||||
#import "STPCardValidator.h"
|
||||
#import "STPPostalCodeValidator.h"
|
||||
#import "STPEmailAddressValidator.h"
|
||||
#import "STPPhoneNumberValidator.h"
|
||||
|
||||
@implementation STPAddress
|
||||
|
||||
|
@ -82,6 +84,23 @@
|
|||
return containsFields;
|
||||
}
|
||||
|
||||
- (BOOL)containsRequiredPKFields:(PKAddressField)requiredFields {
|
||||
BOOL containsFields = YES;
|
||||
if (requiredFields & PKAddressFieldName) {
|
||||
containsFields = containsFields && [self.name length] > 0;
|
||||
}
|
||||
if (requiredFields & PKAddressFieldEmail) {
|
||||
containsFields = containsFields && [STPEmailAddressValidator stringIsValidEmailAddress:self.email];
|
||||
}
|
||||
if (requiredFields & PKAddressFieldPhone) {
|
||||
containsFields = containsFields && [STPPhoneNumberValidator stringIsValidPhoneNumber:self.phone];
|
||||
}
|
||||
if (requiredFields & PKAddressFieldPostalAddress) {
|
||||
containsFields = containsFields && [self hasValidPostalAddress];
|
||||
}
|
||||
return containsFields;
|
||||
}
|
||||
|
||||
- (BOOL)hasValidPostalAddress {
|
||||
return (self.line1.length > 0
|
||||
&& self.city.length > 0
|
||||
|
|
|
@ -48,6 +48,7 @@ typedef NS_ENUM(NSInteger, STPAddressFieldType) {
|
|||
@property(nonatomic, weak, readonly) STPFormTextField *textField;
|
||||
@property(nonatomic, copy) NSString *contents;
|
||||
@property(nonatomic)STPTheme *theme;
|
||||
@property(nonatomic, assign) BOOL lastInList;
|
||||
|
||||
- (void)delegateCountryCodeDidChange:(NSString *)countryCode;
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
@property(nonatomic, weak)id<STPAddressFieldTableViewCellDelegate>delegate;
|
||||
@property(nonatomic, strong) NSString *ourCountryCode;
|
||||
@property(nonatomic, assign) STPPostalCodeType postalCodeType;
|
||||
@property(nonatomic, assign) BOOL lastInList;
|
||||
@end
|
||||
|
||||
@implementation STPAddressFieldTableViewCell
|
||||
|
@ -81,9 +80,6 @@
|
|||
_lastInList = lastInList;
|
||||
_type = type;
|
||||
self.textField.text = contents;
|
||||
if (!lastInList) {
|
||||
self.textField.returnKeyType = UIReturnKeyNext;
|
||||
}
|
||||
|
||||
NSString *ourCountryCode = nil;
|
||||
if ([self.delegate respondsToSelector:@selector(addressFieldTableViewCountryCode)]) {
|
||||
|
@ -104,8 +100,19 @@
|
|||
[self updateAppearance];
|
||||
}
|
||||
|
||||
- (void)setLastInList:(BOOL)lastInList {
|
||||
_lastInList = lastInList;
|
||||
[self updateTextFieldsAndCaptions];
|
||||
}
|
||||
|
||||
- (void)updateTextFieldsAndCaptions {
|
||||
self.textField.placeholder = [self placeholderForAddressField:self.type];
|
||||
if (!self.lastInList) {
|
||||
self.textField.returnKeyType = UIReturnKeyNext;
|
||||
}
|
||||
else {
|
||||
self.textField.returnKeyType = UIReturnKeyDefault;
|
||||
}
|
||||
switch (self.type) {
|
||||
case STPAddressFieldTypeName:
|
||||
self.textField.keyboardType = UIKeyboardTypeDefault;
|
||||
|
@ -134,6 +141,9 @@
|
|||
if (!self.lastInList) {
|
||||
self.textField.inputAccessoryView = self.inputAccessoryToolbar;
|
||||
}
|
||||
else {
|
||||
self.textField.inputAccessoryView = nil;
|
||||
}
|
||||
break;
|
||||
case STPAddressFieldTypeCountry:
|
||||
self.textField.keyboardType = UIKeyboardTypeDefault;
|
||||
|
@ -160,6 +170,9 @@
|
|||
if (!self.lastInList) {
|
||||
self.textField.inputAccessoryView = self.inputAccessoryToolbar;
|
||||
}
|
||||
else {
|
||||
self.textField.inputAccessoryView = nil;
|
||||
}
|
||||
break;
|
||||
case STPAddressFieldTypeEmail:
|
||||
self.textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
@property(nonatomic, readonly)BOOL isValid;
|
||||
|
||||
- (instancetype)initWithRequiredBillingFields:(STPBillingAddressFields)requiredBillingAddressFields;
|
||||
- (instancetype)initWithRequiredShippingFields:(PKAddressField)requiredShippingAddressFields;
|
||||
- (STPAddressFieldTableViewCell *)cellAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
#import "STPPostalCodeValidator.h"
|
||||
|
||||
@interface STPAddressViewModel()<STPAddressFieldTableViewCellDelegate>
|
||||
|
||||
@property(nonatomic)BOOL isBillingAddress;
|
||||
@property(nonatomic)STPBillingAddressFields requiredBillingAddressFields;
|
||||
@property(nonatomic)PKAddressField requiredShippingAddressFields;
|
||||
@property(nonatomic)NSArray<STPAddressFieldTableViewCell *> *addressCells;
|
||||
@property(nonatomic)BOOL showingPostalCodeCell;
|
||||
@end
|
||||
|
@ -24,9 +25,8 @@
|
|||
- (instancetype)initWithRequiredBillingFields:(STPBillingAddressFields)requiredBillingAddressFields {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_isBillingAddress = YES;
|
||||
_requiredBillingAddressFields = requiredBillingAddressFields;
|
||||
_addressFieldTableViewCountryCode = [[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleCountryCode];
|
||||
|
||||
switch (requiredBillingAddressFields) {
|
||||
case STPBillingAddressFieldsNone:
|
||||
_addressCells = @[];
|
||||
|
@ -48,28 +48,68 @@
|
|||
];
|
||||
break;
|
||||
}
|
||||
|
||||
[self updatePostalCodeCellIfNecessary];
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequiredShippingFields:(PKAddressField)requiredShippingAddressFields {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_isBillingAddress = NO;
|
||||
_requiredShippingAddressFields = requiredShippingAddressFields;
|
||||
NSMutableArray *cells = [NSMutableArray new];
|
||||
if (requiredShippingAddressFields & PKAddressFieldName) {
|
||||
[cells addObject:[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeName contents:@"" lastInList:NO delegate:self]];
|
||||
}
|
||||
if (requiredShippingAddressFields & PKAddressFieldEmail) {
|
||||
[cells addObject:[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeEmail contents:@"" lastInList:NO delegate:self]];
|
||||
}
|
||||
if (requiredShippingAddressFields & PKAddressFieldPostalAddress) {
|
||||
NSMutableArray *postalCells = [@[
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeName contents:@"" lastInList:NO delegate:self],
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeLine1 contents:@"" lastInList:NO delegate:self],
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeLine2 contents:@"" lastInList:NO delegate:self],
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeCity contents:@"" lastInList:NO delegate:self],
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeState contents:@"" lastInList:NO delegate:self],
|
||||
// Postal code cell will be added later if necessary
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeCountry contents:_addressFieldTableViewCountryCode lastInList:NO delegate:self],
|
||||
] mutableCopy];
|
||||
if (requiredShippingAddressFields & PKAddressFieldName) {
|
||||
[postalCells removeObjectAtIndex:0];
|
||||
}
|
||||
[cells addObjectsFromArray:postalCells];
|
||||
}
|
||||
if (requiredShippingAddressFields & PKAddressFieldPhone) {
|
||||
[cells addObject:[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypePhone contents:@"" lastInList:NO delegate:self]];
|
||||
}
|
||||
STPAddressFieldTableViewCell *lastCell = [cells lastObject];
|
||||
if (lastCell != nil) {
|
||||
lastCell.lastInList = YES;
|
||||
}
|
||||
_addressCells = [cells copy];
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commonInit {
|
||||
_addressFieldTableViewCountryCode = [[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleCountryCode];
|
||||
[self updatePostalCodeCellIfNecessary];
|
||||
}
|
||||
|
||||
- (void)updatePostalCodeCellIfNecessary {
|
||||
STPPostalCodeType postalCodeType = [STPPostalCodeValidator postalCodeTypeForCountryCode:_addressFieldTableViewCountryCode];
|
||||
BOOL shouldBeShowingPostalCode = (postalCodeType != STPCountryPostalCodeTypeNotRequired);
|
||||
if (shouldBeShowingPostalCode && !self.showingPostalCodeCell) {
|
||||
switch (self.requiredBillingAddressFields) {
|
||||
case STPBillingAddressFieldsNone:
|
||||
// Do nothing
|
||||
break;
|
||||
case STPBillingAddressFieldsZip:
|
||||
if (self.isBillingAddress && self.requiredBillingAddressFields == STPBillingAddressFieldsZip) {
|
||||
self.addressCells = @[
|
||||
[[STPAddressFieldTableViewCell alloc] initWithType:STPAddressFieldTypeZip contents:@"" lastInList:YES delegate:self]
|
||||
];
|
||||
[self.delegate addressViewModel:self addedCellAtIndex:0];
|
||||
[self.delegate addressViewModelDidChange:self];
|
||||
break;
|
||||
case STPBillingAddressFieldsFull: {
|
||||
}
|
||||
else if (self.containsStateAndPostalFields) {
|
||||
// Add after STPAddressFieldTypeState
|
||||
NSUInteger stateFieldIndex = [self.addressCells indexOfObjectPassingTest:^BOOL(STPAddressFieldTableViewCell * _Nonnull obj, NSUInteger __unused idx, BOOL * _Nonnull __unused stop) {
|
||||
return (obj.type == STPAddressFieldTypeState);
|
||||
|
@ -85,22 +125,15 @@
|
|||
[self.delegate addressViewModel:self addedCellAtIndex:zipFieldIndex];
|
||||
[self.delegate addressViewModelDidChange:self];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (!shouldBeShowingPostalCode && self.showingPostalCodeCell) {
|
||||
switch (self.requiredBillingAddressFields) {
|
||||
case STPBillingAddressFieldsNone:
|
||||
// Do nothing
|
||||
break;
|
||||
case STPBillingAddressFieldsZip:
|
||||
if (self.isBillingAddress && self.requiredBillingAddressFields == STPBillingAddressFieldsZip) {
|
||||
self.addressCells = @[];
|
||||
[self.delegate addressViewModel:self removedCellAtIndex:0];
|
||||
[self.delegate addressViewModelDidChange:self];
|
||||
break;
|
||||
case STPBillingAddressFieldsFull: {
|
||||
}
|
||||
else if (self.containsStateAndPostalFields) {
|
||||
NSUInteger zipFieldIndex = [self.addressCells indexOfObjectPassingTest:^BOOL(STPAddressFieldTableViewCell * _Nonnull obj, NSUInteger __unused idx, BOOL * _Nonnull __unused stop) {
|
||||
return (obj.type == STPAddressFieldTypeZip);
|
||||
}];
|
||||
|
@ -112,13 +145,20 @@
|
|||
[self.delegate addressViewModel:self removedCellAtIndex:zipFieldIndex];
|
||||
[self.delegate addressViewModelDidChange:self];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.showingPostalCodeCell = shouldBeShowingPostalCode;
|
||||
}
|
||||
|
||||
- (BOOL)containsStateAndPostalFields {
|
||||
if (self.isBillingAddress) {
|
||||
return self.requiredBillingAddressFields == STPBillingAddressFieldsFull;
|
||||
}
|
||||
else {
|
||||
return (self.requiredShippingAddressFields & PKAddressFieldPostalAddress) == PKAddressFieldPostalAddress;
|
||||
}
|
||||
}
|
||||
|
||||
- (STPAddressFieldTableViewCell *)cellAtIndex:(NSInteger)index {
|
||||
return self.addressCells[index];
|
||||
}
|
||||
|
@ -140,8 +180,13 @@
|
|||
}
|
||||
|
||||
- (BOOL)isValid {
|
||||
if (self.isBillingAddress) {
|
||||
return [self.address containsRequiredFields:self.requiredBillingAddressFields];
|
||||
}
|
||||
else {
|
||||
return [self.address containsRequiredPKFields:self.requiredShippingAddressFields];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAddressFieldTableViewCountryCode:(NSString *)addressFieldTableViewCountryCode {
|
||||
if (addressFieldTableViewCountryCode.length > 0 // ignore if someone passing in nil or empty and keep our current setup
|
||||
|
|
|
@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
+ (UIImage *)largeCardFrontImage;
|
||||
+ (UIImage *)largeCardBackImage;
|
||||
+ (UIImage *)largeCardApplePayImage;
|
||||
+ (UIImage *)largeShippingImage;
|
||||
|
||||
+ (UIImage *)safeImageNamed:(NSString *)imageName
|
||||
templateIfAvailable:(BOOL)templateIfAvailable;
|
||||
|
|
|
@ -98,6 +98,10 @@
|
|||
return [self safeImageNamed:@"stp_card_form_applepay" templateIfAvailable:YES];
|
||||
}
|
||||
|
||||
+ (UIImage *)largeShippingImage {
|
||||
return [self safeImageNamed:@"stp_shipping_form" templateIfAvailable:YES];
|
||||
}
|
||||
|
||||
+ (UIImage *)safeImageNamed:(NSString *)imageName
|
||||
templateIfAvailable:(BOOL)templateIfAvailable {
|
||||
FAUXPAS_IGNORED_IN_METHOD(APIAvailability);
|
||||
|
|
|
@ -32,8 +32,10 @@
|
|||
if (self) {
|
||||
_additionalPaymentMethods = STPPaymentMethodTypeAll;
|
||||
_requiredBillingAddressFields = STPBillingAddressFieldsNone;
|
||||
_requiredShippingAddressFields = PKAddressFieldNone;
|
||||
_companyName = [NSBundle stp_applicationName];
|
||||
_smsAutofillDisabled = NO;
|
||||
_shippingType = STPShippingTypeShipping;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -43,6 +45,8 @@
|
|||
copy.publishableKey = self.publishableKey;
|
||||
copy.additionalPaymentMethods = self.additionalPaymentMethods;
|
||||
copy.requiredBillingAddressFields = self.requiredBillingAddressFields;
|
||||
copy.requiredShippingAddressFields = self.requiredShippingAddressFields;
|
||||
copy.shippingType = self.shippingType;
|
||||
copy.companyName = self.companyName;
|
||||
copy.appleMerchantIdentifier = self.appleMerchantIdentifier;
|
||||
copy.smsAutofillDisabled = self.smsAutofillDisabled;
|
||||
|
|
|
@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@interface STPPhoneNumberValidator : NSObject
|
||||
|
||||
+ (BOOL)stringIsValidPartialPhoneNumber:(NSString *)string;
|
||||
+ (BOOL)stringIsValidPhoneNumber:(NSString *)string;
|
||||
+ (BOOL)stringIsValidPhoneNumber:(nullable NSString *)string;
|
||||
+ (BOOL)stringIsValidPartialPhoneNumber:(NSString *)string
|
||||
forCountryCode:(nullable NSString *)countryCode;
|
||||
+ (BOOL)stringIsValidPhoneNumber:(NSString *)string
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
}
|
||||
|
||||
+ (BOOL)stringIsValidPhoneNumber:(NSString *)string {
|
||||
if (!string) {
|
||||
return NO;
|
||||
}
|
||||
return [self stringIsValidPhoneNumber:string forCountryCode:nil];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
//
|
||||
// STPShippingAddressViewController.m
|
||||
// Stripe
|
||||
//
|
||||
// Created by Ben Guo on 8/29/16.
|
||||
// Copyright © 2016 Stripe, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "STPShippingAddressViewController.h"
|
||||
#import "STPTheme.h"
|
||||
#import "UIBarButtonItem+Stripe.h"
|
||||
#import "UIViewController+Stripe_NavigationItemProxy.h"
|
||||
#import "STPAddressViewModel.h"
|
||||
#import "STPPaymentActivityIndicatorView.h"
|
||||
#import "STPImageLibrary+Private.h"
|
||||
#import "STPColorUtils.h"
|
||||
#import "UIViewController+Stripe_KeyboardAvoiding.h"
|
||||
#import "UIViewController+Stripe_ParentViewController.h"
|
||||
#import "NSArray+Stripe_BoundSafe.h"
|
||||
#import "UITableViewCell+Stripe_Borders.h"
|
||||
#import "STPAddress.h"
|
||||
#import "STPLocalizationUtils.h"
|
||||
#import "STPShippingMethodsViewController.h"
|
||||
#import "STPPaymentContext+Private.h"
|
||||
#import "UINavigationController+Stripe_Completion.h"
|
||||
|
||||
@interface STPShippingAddressViewController ()<STPAddressViewModelDelegate, UITableViewDelegate, UITableViewDataSource, STPShippingMethodsViewControllerDelegate>
|
||||
@property(nonatomic)STPPaymentConfiguration *configuration;
|
||||
@property(nonatomic)NSString *currency;
|
||||
@property(nonatomic)STPTheme *theme;
|
||||
@property(nonatomic)PKShippingMethod *selectedShippingMethod;
|
||||
@property(nonatomic, weak)UITableView *tableView;
|
||||
@property(nonatomic, weak)UIImageView *imageView;
|
||||
@property(nonatomic)UIBarButtonItem *nextItem;
|
||||
@property(nonatomic)UIBarButtonItem *backItem;
|
||||
@property(nonatomic)UIBarButtonItem *cancelItem;
|
||||
@property(nonatomic)BOOL loading;
|
||||
@property(nonatomic)STPPaymentActivityIndicatorView *activityIndicator;
|
||||
@property(nonatomic)STPAddressViewModel *addressViewModel;
|
||||
@end
|
||||
|
||||
@implementation STPShippingAddressViewController
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithConfiguration:[STPPaymentConfiguration sharedConfiguration] theme:[STPTheme defaultTheme] currency:nil shippingAddress:nil selectedShippingMethod:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(STPPaymentConfiguration *)configuration
|
||||
theme:(STPTheme *)theme
|
||||
currency:(NSString *)currency
|
||||
shippingAddress:(STPAddress *)shippingAddress
|
||||
selectedShippingMethod:(PKShippingMethod *)selectedShippingMethod {
|
||||
self = [super initWithNibName:nil bundle:nil];
|
||||
if (self) {
|
||||
_configuration = configuration;
|
||||
_currency = currency ?: @"usd";
|
||||
_theme = theme;
|
||||
_selectedShippingMethod = selectedShippingMethod;
|
||||
_addressViewModel = [[STPAddressViewModel alloc] initWithRequiredShippingFields:configuration.requiredShippingAddressFields];
|
||||
_addressViewModel.delegate = self;
|
||||
_addressViewModel.address = shippingAddress;
|
||||
self.title = [self titleForShippingType:self.configuration.shippingType];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
|
||||
tableView.sectionHeaderHeight = 30;
|
||||
[self.view addSubview:tableView];
|
||||
self.tableView = tableView;
|
||||
self.backItem = [UIBarButtonItem stp_backButtonItemWithTitle:STPLocalizedString(@"Back", @"Text for back button")
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(cancel:)];
|
||||
self.cancelItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
|
||||
target:self
|
||||
action:@selector(cancel:)];
|
||||
UIBarButtonItem *nextItem;
|
||||
switch (self.configuration.shippingType) {
|
||||
case STPShippingTypeShipping:
|
||||
nextItem = [[UIBarButtonItem alloc] initWithTitle:STPLocalizedString(@"Next", @"Text for next button")
|
||||
style:UIBarButtonItemStyleDone
|
||||
target:self
|
||||
action:@selector(next:)];
|
||||
break;
|
||||
case STPShippingTypeDelivery:
|
||||
nextItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
|
||||
target:self
|
||||
action:@selector(next:)];
|
||||
break;
|
||||
}
|
||||
self.nextItem = nextItem;
|
||||
self.stp_navigationItemProxy.rightBarButtonItem = nextItem;
|
||||
self.stp_navigationItemProxy.rightBarButtonItem.enabled = NO;
|
||||
|
||||
UIImageView *imageView = [[UIImageView alloc] initWithImage:[STPImageLibrary largeShippingImage]];
|
||||
imageView.contentMode = UIViewContentModeCenter;
|
||||
imageView.frame = CGRectMake(0, 0, self.view.bounds.size.width, imageView.bounds.size.height + (57 * 2));
|
||||
self.imageView = imageView;
|
||||
self.tableView.tableHeaderView = imageView;
|
||||
|
||||
self.activityIndicator = [[STPPaymentActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20.0f, 20.0f)];
|
||||
|
||||
tableView.dataSource = self;
|
||||
tableView.delegate = self;
|
||||
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(endEditing)]];
|
||||
[self updateAppearance];
|
||||
[self updateDoneButton];
|
||||
}
|
||||
|
||||
- (void)endEditing {
|
||||
[self.view endEditing:NO];
|
||||
}
|
||||
|
||||
- (void)updateAppearance {
|
||||
self.view.backgroundColor = self.theme.primaryBackgroundColor;
|
||||
[self.nextItem stp_setTheme:self.theme];
|
||||
[self.cancelItem stp_setTheme:self.theme];
|
||||
[self.backItem stp_setTheme:self.theme];
|
||||
self.tableView.allowsSelection = NO;
|
||||
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
self.tableView.backgroundColor = self.theme.primaryBackgroundColor;
|
||||
if ([STPColorUtils colorIsBright:self.theme.primaryBackgroundColor]) {
|
||||
self.tableView.indicatorStyle = UIScrollViewIndicatorStyleBlack;
|
||||
} else {
|
||||
self.tableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
|
||||
}
|
||||
self.imageView.tintColor = self.theme.accentColor;
|
||||
self.activityIndicator.tintColor = self.theme.accentColor;
|
||||
for (STPAddressFieldTableViewCell *cell in self.addressViewModel.addressCells) {
|
||||
cell.theme = self.theme;
|
||||
}
|
||||
[self setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
self.stp_navigationItemProxy.leftBarButtonItem = [self stp_isAtRootOfNavigationController] ? self.cancelItem : self.backItem;
|
||||
[self.tableView reloadData];
|
||||
if (self.navigationController.navigationBar.translucent) {
|
||||
CGFloat insetTop = CGRectGetMaxY(self.navigationController.navigationBar.frame);
|
||||
self.tableView.contentInset = UIEdgeInsetsMake(insetTop, 0, 0, 0);
|
||||
self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
|
||||
} else {
|
||||
self.tableView.contentInset = UIEdgeInsetsZero;
|
||||
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
|
||||
}
|
||||
CGPoint offset = self.tableView.contentOffset;
|
||||
offset.y = -self.tableView.contentInset.top;
|
||||
self.tableView.contentOffset = offset;
|
||||
}
|
||||
|
||||
-(void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
[self stp_beginObservingKeyboardAndInsettingScrollView:self.tableView
|
||||
onChangeBlock:nil];
|
||||
[[self firstEmptyField] becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (UIResponder *)firstEmptyField {
|
||||
for (STPAddressFieldTableViewCell *cell in self.addressViewModel.addressCells) {
|
||||
if (cell.contents.length == 0) {
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
self.tableView.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[self.view endEditing:YES];
|
||||
}
|
||||
|
||||
- (void)setLoading:(BOOL)loading {
|
||||
if (loading == _loading) {
|
||||
return;
|
||||
}
|
||||
_loading = loading;
|
||||
[self.stp_navigationItemProxy setHidesBackButton:loading animated:YES];
|
||||
self.stp_navigationItemProxy.leftBarButtonItem.enabled = !loading;
|
||||
self.activityIndicator.animating = loading;
|
||||
if (loading) {
|
||||
[self.tableView endEditing:YES];
|
||||
UIBarButtonItem *loadingItem = [[UIBarButtonItem alloc] initWithCustomView:self.activityIndicator];
|
||||
[self.stp_navigationItemProxy setRightBarButtonItem:loadingItem animated:YES];
|
||||
} else {
|
||||
[self.stp_navigationItemProxy setRightBarButtonItem:self.nextItem animated:YES];
|
||||
}
|
||||
for (UITableViewCell *cell in self.addressViewModel.addressCells) {
|
||||
cell.userInteractionEnabled = !loading;
|
||||
[UIView animateWithDuration:0.1f animations:^{
|
||||
cell.alpha = loading ? 0.7f : 1.0f;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel:(__unused id)sender {
|
||||
[self.delegate shippingAddressViewControllerDidCancel:self];
|
||||
}
|
||||
|
||||
- (void)next:(__unused id)sender {
|
||||
STPAddress *address = self.addressViewModel.address;
|
||||
switch (self.configuration.shippingType) {
|
||||
case STPShippingTypeShipping: {
|
||||
self.loading = YES;
|
||||
[self.delegate shippingAddressViewController:self didEnterAddress:address completion:^(NSError *shippingValidationError, NSArray<PKShippingMethod *> * _Nonnull shippingMethods) {
|
||||
self.loading = NO;
|
||||
if (shippingValidationError == nil) {
|
||||
if ([shippingMethods count] > 0) {
|
||||
STPShippingMethodsViewController *nextViewController = [[STPShippingMethodsViewController alloc] initWithShippingMethods:shippingMethods
|
||||
selectedShippingMethod:self.selectedShippingMethod
|
||||
currency:self.currency
|
||||
theme:self.theme];
|
||||
nextViewController.delegate = self;
|
||||
[self.navigationController pushViewController:nextViewController animated:YES];
|
||||
}
|
||||
else {
|
||||
[self.delegate shippingAddressViewController:self
|
||||
didFinishWithAddress:address
|
||||
shippingMethod:nil];
|
||||
}
|
||||
}
|
||||
else {
|
||||
[self handleShippingValidationError:shippingValidationError];
|
||||
}
|
||||
}];
|
||||
break;
|
||||
}
|
||||
case STPShippingTypeDelivery:
|
||||
[self.delegate shippingAddressViewController:self
|
||||
didFinishWithAddress:address
|
||||
shippingMethod:nil];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateDoneButton {
|
||||
self.stp_navigationItemProxy.rightBarButtonItem.enabled = self.addressViewModel.isValid;
|
||||
}
|
||||
|
||||
- (void)handleShippingValidationError:(NSError *)error {
|
||||
[[self firstEmptyField] becomeFirstResponder];
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:error.localizedDescription
|
||||
message:error.localizedFailureReason
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:STPLocalizedString(@"OK", @"ok button")
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)dismissWithHostViewController:(UIViewController *)hostViewController {
|
||||
[self.navigationController stp_popToViewController:hostViewController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - STPAddressViewModelDelegate
|
||||
|
||||
- (void)addressViewModel:(__unused STPAddressViewModel *)addressViewModel addedCellAtIndex:(NSUInteger)index {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
|
||||
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
- (void)addressViewModel:(__unused STPAddressViewModel *)addressViewModel removedCellAtIndex:(NSUInteger)index {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
|
||||
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
- (void)addressViewModelDidChange:(__unused STPAddressViewModel *)addressViewModel {
|
||||
[self updateDoneButton];
|
||||
}
|
||||
|
||||
#pragma mark - UITableView
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(__unused UITableView *)tableView numberOfRowsInSection:(__unused NSInteger)section {
|
||||
return self.addressViewModel.addressCells.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(__unused UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [self.addressViewModel.addressCells stp_boundSafeObjectAtIndex:indexPath.row];
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.contentView.backgroundColor = self.theme.secondaryBackgroundColor;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
BOOL topRow = (indexPath.row == 0);
|
||||
BOOL bottomRow = ([self tableView:tableView numberOfRowsInSection:indexPath.section] - 1 == indexPath.row);
|
||||
[cell stp_setBorderColor:self.theme.tertiaryBackgroundColor];
|
||||
[cell stp_setTopBorderHidden:!topRow];
|
||||
[cell stp_setBottomBorderHidden:!bottomRow];
|
||||
[cell stp_setFakeSeparatorColor:self.theme.quaternaryBackgroundColor];
|
||||
[cell stp_setFakeSeparatorLeftInset:15.0f];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(__unused UITableView *)tableView heightForFooterInSection:(__unused NSInteger)section {
|
||||
return 0.01f;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(__unused UITableView *)tableView viewForFooterInSection:(__unused NSInteger)section {
|
||||
return [UIView new];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(__unused UITableView *)tableView heightForHeaderInSection:(__unused NSInteger)section {
|
||||
return 0.01f;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(__unused UITableView *)tableView viewForHeaderInSection:(__unused NSInteger)section {
|
||||
return [UIView new];
|
||||
}
|
||||
|
||||
- (NSString *)titleForShippingType:(STPShippingType)type {
|
||||
if (self.configuration.requiredShippingAddressFields & PKAddressFieldPostalAddress) {
|
||||
switch (type) {
|
||||
case STPShippingTypeShipping:
|
||||
return STPLocalizedString(@"Shipping", @"Title for shipping info form");
|
||||
break;
|
||||
case STPShippingTypeDelivery:
|
||||
return STPLocalizedString(@"Delivery", @"Title for delivery info form");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return STPLocalizedString(@"Contact", @"Title for contact info form");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - STPShippingMethodsViewControllerDelegate
|
||||
|
||||
- (void)shippingMethodsViewController:(__unused STPShippingMethodsViewController *)methodsViewController
|
||||
didFinishWithShippingMethod:(PKShippingMethod *)method {
|
||||
[self.delegate shippingAddressViewController:self
|
||||
didFinishWithAddress:self.addressViewModel.address
|
||||
shippingMethod:method];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// STPShippingMethodTableViewCell.h
|
||||
// Stripe
|
||||
//
|
||||
// Created by Ben Guo on 8/30/16.
|
||||
// Copyright © 2016 Stripe, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <PassKit/PassKit.h>
|
||||
#import "STPTheme.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface STPShippingMethodTableViewCell : UITableViewCell
|
||||
@property(nonatomic)STPTheme *theme;
|
||||
- (void)setShippingMethod:(PKShippingMethod *)method currency:(NSString *)currency;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// STPShippingMethodTableViewCell.m
|
||||
// Stripe
|
||||
//
|
||||
// Created by Ben Guo on 8/30/16.
|
||||
// Copyright © 2016 Stripe, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "STPShippingMethodTableViewCell.h"
|
||||
#import "STPImageLibrary+Private.h"
|
||||
#import "STPLocalizationUtils.h"
|
||||
#import "NSDecimalNumber+Stripe_Currency.h"
|
||||
|
||||
@interface STPShippingMethodTableViewCell ()
|
||||
@property(nonatomic, weak) UILabel *titleLabel;
|
||||
@property(nonatomic, weak) UILabel *subtitleLabel;
|
||||
@property(nonatomic, weak) UILabel *amountLabel;
|
||||
@property(nonatomic, weak) UIImageView *checkmarkIcon;
|
||||
@property(nonatomic)PKShippingMethod *shippingMethod;
|
||||
@property(nonatomic) NSNumberFormatter *numberFormatter;
|
||||
@end
|
||||
|
||||
@implementation STPShippingMethodTableViewCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self) {
|
||||
_theme = [STPTheme new];
|
||||
UILabel *titleLabel = [UILabel new];
|
||||
_titleLabel = titleLabel;
|
||||
UILabel *subtitleLabel = [UILabel new];
|
||||
_subtitleLabel = subtitleLabel;
|
||||
UILabel *amountLabel = [UILabel new];
|
||||
_amountLabel = amountLabel;
|
||||
UIImageView *checkmarkIcon = [[UIImageView alloc] initWithImage:[STPImageLibrary checkmarkIcon]];
|
||||
_checkmarkIcon = checkmarkIcon;
|
||||
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
|
||||
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||
formatter.usesGroupingSeparator = YES;
|
||||
_numberFormatter = formatter;
|
||||
[self.contentView addSubview:titleLabel];
|
||||
[self.contentView addSubview:subtitleLabel];
|
||||
[self.contentView addSubview:amountLabel];
|
||||
[self.contentView addSubview:checkmarkIcon];
|
||||
[self updateAppearance];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTheme:(STPTheme *)theme {
|
||||
_theme = theme;
|
||||
[self updateAppearance];
|
||||
}
|
||||
|
||||
- (void)setShippingMethod:(PKShippingMethod *)method currency:(NSString *)currency {
|
||||
_shippingMethod = method;
|
||||
self.titleLabel.text = method.label;
|
||||
self.subtitleLabel.text = method.detail;
|
||||
NSMutableDictionary<NSString *,NSString *>*localeInfo = [@{NSLocaleCurrencyCode: currency} mutableCopy];
|
||||
localeInfo[NSLocaleLanguageCode] = [[NSLocale preferredLanguages] firstObject];
|
||||
NSString *localeID = [NSLocale localeIdentifierFromComponents:localeInfo];
|
||||
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:localeID];
|
||||
self.numberFormatter.locale = locale;
|
||||
NSInteger amount = [method.amount stp_amountWithCurrency:currency];
|
||||
if (amount == 0) {
|
||||
self.amountLabel.text = STPLocalizedString(@"Free", @"Label for free shipping method");
|
||||
}
|
||||
else {
|
||||
NSDecimalNumber *number = [NSDecimalNumber stp_decimalNumberWithAmount:amount
|
||||
currency:currency];
|
||||
self.amountLabel.text = [self.numberFormatter stringFromNumber:number];
|
||||
}
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setSelected:(BOOL)selected {
|
||||
[super setSelected:selected];
|
||||
[self updateAppearance];
|
||||
}
|
||||
|
||||
- (void)updateAppearance {
|
||||
self.contentView.backgroundColor = self.theme.secondaryBackgroundColor;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.titleLabel.font = self.theme.font;
|
||||
self.subtitleLabel.font = self.theme.smallFont;
|
||||
self.amountLabel.font = self.theme.font;
|
||||
self.titleLabel.textColor = self.selected ? self.theme.accentColor : self.theme.primaryForegroundColor;
|
||||
self.amountLabel.textColor = self.titleLabel.textColor;
|
||||
self.subtitleLabel.textColor = self.selected ? [self.theme.accentColor colorWithAlphaComponent:0.6f] : self.theme.secondaryForegroundColor;
|
||||
self.checkmarkIcon.tintColor = self.theme.accentColor;
|
||||
self.checkmarkIcon.hidden = !self.selected;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
CGFloat midY = CGRectGetMidY(self.bounds);
|
||||
self.checkmarkIcon.frame = CGRectMake(0, 0, 14, 14);
|
||||
self.checkmarkIcon.center = CGPointMake(CGRectGetWidth(self.bounds) - 15 - CGRectGetMidX(self.checkmarkIcon.bounds), midY);
|
||||
[self.amountLabel sizeToFit];
|
||||
self.amountLabel.center = CGPointMake(CGRectGetMinX(self.checkmarkIcon.frame) - 15 - CGRectGetMidX(self.amountLabel.bounds), midY);
|
||||
CGFloat labelWidth = CGRectGetMinX(self.amountLabel.frame) - 30;
|
||||
[self.titleLabel sizeToFit];
|
||||
self.titleLabel.frame = CGRectMake(15, 8, labelWidth, self.titleLabel.frame.size.height);
|
||||
[self.subtitleLabel sizeToFit];
|
||||
self.subtitleLabel.frame = CGRectMake(15, self.bounds.size.height - 8 - self.subtitleLabel.frame.size.height, labelWidth, self.subtitleLabel.frame.size.height);
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// STPShippingMethodsViewController.h
|
||||
// Stripe
|
||||
//
|
||||
// Created by Ben Guo on 8/29/16.
|
||||
// Copyright © 2016 Stripe, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <PassKit/PassKit.h>
|
||||
#import "STPTheme.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol STPShippingMethodsViewControllerDelegate;
|
||||
|
||||
@interface STPShippingMethodsViewController : UIViewController
|
||||
|
||||
- (instancetype)initWithShippingMethods:(NSArray<PKShippingMethod *>*)methods
|
||||
selectedShippingMethod:(nullable PKShippingMethod *)selectedMethod
|
||||
currency:(NSString *)currency
|
||||
theme:(STPTheme *)theme;
|
||||
|
||||
@property(nonatomic, weak) id<STPShippingMethodsViewControllerDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@protocol STPShippingMethodsViewControllerDelegate <NSObject>
|
||||
|
||||
- (void)shippingMethodsViewController:(STPShippingMethodsViewController *)methodsViewController
|
||||
didFinishWithShippingMethod:(PKShippingMethod *)method;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,185 @@
|
|||
//
|
||||
// STPShippingMethodsViewController.m
|
||||
// Stripe
|
||||
//
|
||||
// Created by Ben Guo on 8/29/16.
|
||||
// Copyright © 2016 Stripe, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "STPShippingMethodsViewController.h"
|
||||
#import "STPLocalizationUtils.h"
|
||||
#import "UIBarButtonItem+Stripe.h"
|
||||
#import "UIViewController+Stripe_NavigationItemProxy.h"
|
||||
#import "STPImageLibrary+Private.h"
|
||||
#import "STPColorUtils.h"
|
||||
#import "UITableViewCell+Stripe_Borders.h"
|
||||
#import "NSArray+Stripe_BoundSafe.h"
|
||||
#import "STPShippingMethodTableViewCell.h"
|
||||
|
||||
static NSString *const STPShippingMethodCellReuseIdentifier = @"STPShippingMethodCellReuseIdentifier";
|
||||
|
||||
@interface STPShippingMethodsViewController () <UITableViewDataSource, UITableViewDelegate>
|
||||
@property(nonatomic)NSArray<PKShippingMethod *>*shippingMethods;
|
||||
@property(nonatomic)PKShippingMethod *selectedShippingMethod;
|
||||
@property(nonatomic)STPTheme *theme;
|
||||
@property(nonatomic)NSString *currency;
|
||||
@property(nonatomic, weak)UITableView *tableView;
|
||||
@property(nonatomic, weak)UIImageView *imageView;
|
||||
@property(nonatomic)UIBarButtonItem *doneItem;
|
||||
@property(nonatomic)UIBarButtonItem *backItem;
|
||||
@end
|
||||
|
||||
@implementation STPShippingMethodsViewController
|
||||
|
||||
- (instancetype)initWithShippingMethods:(NSArray<PKShippingMethod *>*)methods
|
||||
selectedShippingMethod:(PKShippingMethod *)selectedMethod
|
||||
currency:(NSString *)currency
|
||||
theme:(STPTheme *)theme {
|
||||
self = [super initWithNibName:nil bundle:nil];
|
||||
if (self) {
|
||||
_shippingMethods = methods;
|
||||
if (selectedMethod != nil && [methods indexOfObject:selectedMethod] != NSNotFound) {
|
||||
_selectedShippingMethod = selectedMethod;
|
||||
}
|
||||
else {
|
||||
_selectedShippingMethod = [methods stp_boundSafeObjectAtIndex:0];
|
||||
}
|
||||
_theme = theme;
|
||||
_currency = currency;
|
||||
self.title = STPLocalizedString(@"Shipping", @"Title for shipping info form");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
|
||||
[tableView registerClass:[STPShippingMethodTableViewCell class] forCellReuseIdentifier:STPShippingMethodCellReuseIdentifier];
|
||||
tableView.sectionHeaderHeight = 30;
|
||||
[self.view addSubview:tableView];
|
||||
self.tableView = tableView;
|
||||
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(done:)];
|
||||
self.doneItem = doneItem;
|
||||
self.backItem = [UIBarButtonItem stp_backButtonItemWithTitle:STPLocalizedString(@"Back", @"Text for back button") style:UIBarButtonItemStylePlain target:self action:@selector(cancel:)];
|
||||
self.stp_navigationItemProxy.rightBarButtonItem = doneItem;
|
||||
UIImageView *imageView = [[UIImageView alloc] initWithImage:[STPImageLibrary largeShippingImage]];
|
||||
imageView.contentMode = UIViewContentModeCenter;
|
||||
imageView.frame = CGRectMake(0, 0, self.view.bounds.size.width, imageView.bounds.size.height + (57 * 2));
|
||||
self.imageView = imageView;
|
||||
self.tableView.tableHeaderView = imageView;
|
||||
tableView.dataSource = self;
|
||||
tableView.delegate = self;
|
||||
[self updateAppearance];
|
||||
}
|
||||
|
||||
- (void)updateAppearance {
|
||||
self.view.backgroundColor = self.theme.primaryBackgroundColor;
|
||||
[self.doneItem stp_setTheme:self.theme];
|
||||
self.tableView.allowsSelection = YES;
|
||||
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
self.tableView.backgroundColor = self.theme.primaryBackgroundColor;
|
||||
if ([STPColorUtils colorIsBright:self.theme.primaryBackgroundColor]) {
|
||||
self.tableView.indicatorStyle = UIScrollViewIndicatorStyleBlack;
|
||||
} else {
|
||||
self.tableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
|
||||
}
|
||||
self.imageView.tintColor = self.theme.accentColor;
|
||||
for (UITableViewCell *cell in [self.tableView visibleCells]) {
|
||||
STPShippingMethodTableViewCell *shippingCell = (STPShippingMethodTableViewCell *)cell;
|
||||
[shippingCell setTheme:self.theme];
|
||||
}
|
||||
[self setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self.tableView reloadData];
|
||||
self.stp_navigationItemProxy.leftBarButtonItem = self.backItem;
|
||||
if (self.navigationController.navigationBar.translucent) {
|
||||
CGFloat insetTop = CGRectGetMaxY(self.navigationController.navigationBar.frame);
|
||||
self.tableView.contentInset = UIEdgeInsetsMake(insetTop, 0, 0, 0);
|
||||
self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
|
||||
} else {
|
||||
self.tableView.contentInset = UIEdgeInsetsZero;
|
||||
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
|
||||
}
|
||||
CGPoint offset = self.tableView.contentOffset;
|
||||
offset.y = -self.tableView.contentInset.top;
|
||||
self.tableView.contentOffset = offset;
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
self.tableView.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
- (void)cancel:(__unused id)sender {
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)done:(__unused id)sender {
|
||||
[self.delegate shippingMethodsViewController:self didFinishWithShippingMethod:self.selectedShippingMethod];
|
||||
}
|
||||
|
||||
#pragma mark - UITableView
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(__unused UITableView *)tableView numberOfRowsInSection:(__unused NSInteger)section {
|
||||
return self.shippingMethods.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
STPShippingMethodTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:STPShippingMethodCellReuseIdentifier forIndexPath:indexPath];
|
||||
PKShippingMethod *method = [self.shippingMethods stp_boundSafeObjectAtIndex:indexPath.row];
|
||||
cell.theme = self.theme;
|
||||
[cell setShippingMethod:method currency:self.currency];
|
||||
cell.selected = [method.identifier isEqualToString:self.selectedShippingMethod.identifier];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
BOOL topRow = (indexPath.row == 0);
|
||||
BOOL bottomRow = ([self tableView:tableView numberOfRowsInSection:indexPath.section] - 1 == indexPath.row);
|
||||
[cell stp_setBorderColor:self.theme.tertiaryBackgroundColor];
|
||||
[cell stp_setTopBorderHidden:!topRow];
|
||||
[cell stp_setBottomBorderHidden:!bottomRow];
|
||||
[cell stp_setFakeSeparatorColor:self.theme.quaternaryBackgroundColor];
|
||||
[cell stp_setFakeSeparatorLeftInset:15.0f];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(__unused UITableView *)tableView heightForRowAtIndexPath:(__unused NSIndexPath *)indexPath {
|
||||
return 57;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(__unused UITableView *)tableView heightForFooterInSection:(__unused NSInteger)section {
|
||||
return 27.0f;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(__unused NSInteger)section {
|
||||
return tableView.sectionHeaderHeight;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(__unused UITableView *)tableView viewForHeaderInSection:(__unused NSInteger)section {
|
||||
UILabel *label = [UILabel new];
|
||||
label.font = self.theme.smallFont;
|
||||
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
||||
style.firstLineHeadIndent = 15;
|
||||
NSDictionary *attributes = @{NSParagraphStyleAttributeName: style};
|
||||
label.textColor = self.theme.secondaryForegroundColor;
|
||||
label.attributedText = [[NSAttributedString alloc] initWithString:STPLocalizedString(@"Shipping Method", @"Label for shipping method form") attributes:attributes];
|
||||
return label;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
self.selectedShippingMethod = [self.shippingMethods stp_boundSafeObjectAtIndex:indexPath.row];
|
||||
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
}
|
||||
|
||||
@end
|
|
@ -137,4 +137,29 @@
|
|||
XCTAssertTrue([address containsRequiredFields:STPBillingAddressFieldsFull]);
|
||||
}
|
||||
|
||||
- (void)testContainsRequiredPKFields {
|
||||
STPAddress *address = [STPAddress new];
|
||||
XCTAssertTrue([address containsRequiredPKFields:PKAddressFieldNone]);
|
||||
XCTAssertFalse([address containsRequiredPKFields:PKAddressFieldAll]);
|
||||
|
||||
address.name = @"John Smith";
|
||||
XCTAssertTrue([address containsRequiredPKFields:PKAddressFieldName]);
|
||||
XCTAssertFalse([address containsRequiredPKFields:PKAddressFieldEmail]);
|
||||
|
||||
address.email = @"john@example.com";
|
||||
XCTAssertTrue([address containsRequiredPKFields:PKAddressFieldEmail|PKAddressFieldName]);
|
||||
XCTAssertFalse([address containsRequiredPKFields:PKAddressFieldAll]);
|
||||
|
||||
address.phone = @"5555555555";
|
||||
XCTAssertTrue([address containsRequiredPKFields:PKAddressFieldEmail|PKAddressFieldName|PKAddressFieldPhone]);
|
||||
XCTAssertFalse([address containsRequiredPKFields:PKAddressFieldAll]);
|
||||
|
||||
address.country = @"US";
|
||||
address.line1 = @"55 John St";
|
||||
address.city = @"New York";
|
||||
address.state = @"NY";
|
||||
address.postalCode = @"12345";
|
||||
XCTAssertTrue([address containsRequiredPKFields:PKAddressFieldAll]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue