Browse Source

pod Stripe

Abel 1 year ago
parent
commit
2bfe5c25b1
100 changed files with 35341 additions and 33565 deletions
  1. 28608 33565
      Pods/Pods.xcodeproj/project.pbxproj
  2. 21 0
      Pods/Stripe/LICENSE
  3. 154 0
      Pods/Stripe/README.md
  4. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/Cards/stp_card_form_amex_cvc@3x.png
  5. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/Cards/stp_card_form_back@3x.png
  6. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/Cards/stp_card_form_front@3x.png
  7. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_affin_bank@3x.png
  8. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_alliance_bank@3x.png
  9. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_ambank@3x.png
  10. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bank_islam@3x.png
  11. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bank_muamalat@3x.png
  12. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bank_rakyat@3x.png
  13. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bsn@3x.png
  14. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_cimb@3x.png
  15. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_hong_leong_bank@3x.png
  16. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_hsbc@3x.png
  17. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_kfh@3x.png
  18. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_maybank2e@3x.png
  19. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_maybank2u@3x.png
  20. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_ocbc@3x.png
  21. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_public_bank@3x.png
  22. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_rhb@3x.png
  23. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_standard_chartered@3x.png
  24. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_uob@3x.png
  25. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_fpx_big_logo@3x.png
  26. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_fpx_logo@3x.png
  27. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_icon_add@3x.png
  28. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_icon_bank@3x.png
  29. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_icon_checkmark@3x.png
  30. BIN
      Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_shipping_form@3x.png
  31. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/bg-BG.lproj/Localizable.strings
  32. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ca-ES.lproj/Localizable.strings
  33. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/cs-CZ.lproj/Localizable.strings
  34. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/da.lproj/Localizable.strings
  35. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/de.lproj/Localizable.strings
  36. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/el-GR.lproj/Localizable.strings
  37. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/en-GB.lproj/Localizable.strings
  38. 60 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/en.lproj/Localizable.strings
  39. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/es-419.lproj/Localizable.strings
  40. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/es.lproj/Localizable.strings
  41. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/et-EE.lproj/Localizable.strings
  42. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fi.lproj/Localizable.strings
  43. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fil.lproj/Localizable.strings
  44. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fr-CA.lproj/Localizable.strings
  45. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fr.lproj/Localizable.strings
  46. 37 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/hr.lproj/Localizable.strings
  47. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/hu.lproj/Localizable.strings
  48. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/id.lproj/Localizable.strings
  49. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/it.lproj/Localizable.strings
  50. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ja.lproj/Localizable.strings
  51. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ko.lproj/Localizable.strings
  52. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/lt-LT.lproj/Localizable.strings
  53. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/lv-LV.lproj/Localizable.strings
  54. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ms-MY.lproj/Localizable.strings
  55. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/mt.lproj/Localizable.strings
  56. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/nb.lproj/Localizable.strings
  57. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/nl.lproj/Localizable.strings
  58. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/nn-NO.lproj/Localizable.strings
  59. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/pl-PL.lproj/Localizable.strings
  60. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/pt-BR.lproj/Localizable.strings
  61. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/pt-PT.lproj/Localizable.strings
  62. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ro-RO.lproj/Localizable.strings
  63. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ru.lproj/Localizable.strings
  64. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/sk-SK.lproj/Localizable.strings
  65. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/sl-SI.lproj/Localizable.strings
  66. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/sv.lproj/Localizable.strings
  67. 0 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/tk.lproj/Localizable.strings
  68. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/tr.lproj/Localizable.strings
  69. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/vi.lproj/Localizable.strings
  70. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/zh-HK.lproj/Localizable.strings
  71. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/zh-Hans.lproj/Localizable.strings
  72. 39 0
      Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/zh-Hant.lproj/Localizable.strings
  73. 63 0
      Pods/Stripe/Stripe/StripeiOS/Source/Enums+CustomStringConvertible.swift
  74. 30 0
      Pods/Stripe/Stripe/StripeiOS/Source/PKAddPaymentPassRequest+Stripe_Error.swift
  75. 187 0
      Pods/Stripe/Stripe/StripeiOS/Source/PKPaymentAuthorizationViewController+Stripe_Blocks.swift
  76. 178 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAPIClient+BasicUI.swift
  77. 40 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAPIClient+PushProvisioning.swift
  78. 970 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAddCardViewController.swift
  79. 212 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAddress+BasicUI.swift
  80. 509 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAddressFieldTableViewCell.swift
  81. 434 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAddressViewModel.swift
  82. 84 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAnalyticsClient+BasicUI.swift
  83. 28 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPAnalyticsClient+Payments.swift
  84. 98 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPApplePayContextDelegate.swift
  85. 51 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPApplePayPaymentOption.swift
  86. 86 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPBackendAPIAdapter.swift
  87. 124 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPBankSelectionTableViewCell.swift
  88. 300 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPBankSelectionViewController.swift
  89. 44 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPBlocks.swift
  90. 77 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCameraView.swift
  91. 30 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCard+BasicUI.swift
  92. 518 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCardScanner.swift
  93. 67 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCardScannerTableViewCell.swift
  94. 9 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCardValidationState.swift
  95. 62 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCoreScrollViewController.swift
  96. 54 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCoreTableViewController.swift
  97. 162 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCoreViewController.swift
  98. 419 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPCustomerContext.swift
  99. 104 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPEphemeralKey.swift
  100. 0 0
      Pods/Stripe/Stripe/StripeiOS/Source/STPEphemeralKeyManager.swift

File diff suppressed because it is too large
+ 28608 - 33565
Pods/Pods.xcodeproj/project.pbxproj


+ 21 - 0
Pods/Stripe/LICENSE

@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2011- Stripe, Inc. (https://stripe.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

File diff suppressed because it is too large
+ 154 - 0
Pods/Stripe/README.md


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/Cards/stp_card_form_amex_cvc@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/Cards/stp_card_form_back@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/Cards/stp_card_form_front@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_affin_bank@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_alliance_bank@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_ambank@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bank_islam@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bank_muamalat@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bank_rakyat@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_bsn@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_cimb@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_hong_leong_bank@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_hsbc@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_kfh@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_maybank2e@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_maybank2u@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_ocbc@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_public_bank@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_rhb@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_standard_chartered@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_bank_fpx_uob@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_fpx_big_logo@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/FPX/stp_fpx_logo@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_icon_add@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_icon_bank@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_icon_checkmark@3x.png


BIN
Pods/Stripe/Stripe/StripeiOS/Resources/Images/stp_shipping_form@3x.png


+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/bg-BG.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Офлайн";
+
+"3D Secure" = "3D Secure удостоверяване";
+
+"Add New Card…" = "Добавяне на нова карта...";
+
+"Add a Card" = "Добавяне на карта";
+
+"Apt." = "Aп.";
+
+"Contact" = "Контакт";
+
+"Delivery" = "Доставка";
+
+"Delivery Address" = "Адрес за доставка";
+
+"Free" = "Безплатно";
+
+"Invalid Shipping Address" = "Невалиден адрес за доставка";
+
+"Loading…" = "Зареждане...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Следващо";
+
+"Online Banking (FPX)" = "Онлайн банкиране (FPX)";
+
+"Payment Method" = "Метод на плащане";
+
+"Shipping" = "Доставка";
+
+"Shipping Method" = "Метод на доставка";
+
+"Use Billing" = "Да се използва адреса за фактуриране";
+
+"Use Delivery" = "Да се използва адреса за доставка";
+
+"Use Shipping" = "Да се използва адреса за изпращане";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ca-ES.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Fora de línia";
+
+"3D Secure" = "3D Segur";
+
+"Add New Card…" = "Afegir nova targeta...";
+
+"Add a Card" = "Afegir una targeta";
+
+"Apt." = "Apt.";
+
+"Contact" = "Contacte";
+
+"Delivery" = "Lliurament";
+
+"Delivery Address" = "Adreça de lliurament";
+
+"Free" = "Sense cost";
+
+"Invalid Shipping Address" = "Adreça d'enviament invàlida";
+
+"Loading…" = "Carregant...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Següent";
+
+"Online Banking (FPX)" = "Banca Online (FPX)";
+
+"Payment Method" = "Mètode de pagament";
+
+"Shipping" = "Enviament";
+
+"Shipping Method" = "Mètode d'enviament";
+
+"Use Billing" = "Fer servir Facturació";
+
+"Use Delivery" = "Fer servir Lliurament";
+
+"Use Shipping" = "Fes servir Enviament";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/cs-CZ.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Přidat novou kartu";
+
+"Add a Card" = "Přidat kartu";
+
+"Apt." = "Byt";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Dodávka";
+
+"Delivery Address" = "Dodací adresa";
+
+"Free" = "Zdarma";
+
+"Invalid Shipping Address" = "Neplatná dodací adresa";
+
+"Loading…" = "Načítání...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Další";
+
+"Online Banking (FPX)" = "Online bankovnictví (FPX)";
+
+"Payment Method" = "Způsob platby";
+
+"Shipping" = "Doprava";
+
+"Shipping Method" = "Způsob dopravy";
+
+"Use Billing" = "Použít fakturaci";
+
+"Use Delivery" = "Použít dodání";
+
+"Use Shipping" = "Použít dopravu";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/da.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Tilføj nyt kort …";
+
+"Add a Card" = "Tilføj et kort";
+
+"Apt." = "Lejlighed";
+
+"Contact" = "Kontaktperson";
+
+"Delivery" = "Levering";
+
+"Delivery Address" = "Leveringsadresse";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Ugyldig forsendelsesadresse";
+
+"Loading…" = "Indlæser ...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Næste";
+
+"Online Banking (FPX)" = "Onlinebanking (FPX)";
+
+"Payment Method" = "Betalingsmetode";
+
+"Shipping" = "Forsendelse";
+
+"Shipping Method" = "Forsendelsesmetode";
+
+"Use Billing" = "Brug faktureringsadresse";
+
+"Use Delivery" = "Brug leveringsadresse";
+
+"Use Shipping" = "Brug forsendelsesadresse";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/de.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – offline";
+
+"3D Secure" = "Secure";
+
+"Add New Card…" = "Neue Karte hinzufügen…";
+
+"Add a Card" = "Karte hinzufügen";
+
+"Apt." = "Adresszeile 2";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Lieferinformationen";
+
+"Delivery Address" = "Lieferadresse";
+
+"Free" = "Kostenlos";
+
+"Invalid Shipping Address" = "Ungültige Versandadresse";
+
+"Loading…" = "Wird geladen...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Weiter";
+
+"Online Banking (FPX)" = "Online-Banking (FPX)";
+
+"Payment Method" = "Zahlungsmethode";
+
+"Shipping" = "Versand";
+
+"Shipping Method" = "Versandmethode";
+
+"Use Billing" = "Rechnungsadresse verwenden";
+
+"Use Delivery" = "Lieferadresse verwenden";
+
+"Use Shipping" = "Versandadresse verwenden";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/el-GR.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Εκτός σύνδεσης";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Προσθήκη νέας κάρτας…";
+
+"Add a Card" = "Προσθήκη κάρτας";
+
+"Apt." = "Διαμ.";
+
+"Contact" = "Επικοινωνία";
+
+"Delivery" = "Παράδοση";
+
+"Delivery Address" = "Διεύθυνση παράδοσης";
+
+"Free" = "Δωρεάν";
+
+"Invalid Shipping Address" = "Μη έγκυρη διεύθυνση αποστολής";
+
+"Loading…" = "Φόρτωση…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Επόμενο";
+
+"Online Banking (FPX)" = "Τραπεζικές υπηρεσίες μέσω διαδικτύου (FPX)";
+
+"Payment Method" = "Μέθοδος πληρωμής";
+
+"Shipping" = "Αποστολή";
+
+"Shipping Method" = "Μέθοδος αποστολής";
+
+"Use Billing" = "Χρήση χρέωσης";
+
+"Use Delivery" = "Χρήση παράδοσης";
+
+"Use Shipping" = "Χρήση αποστολής";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/en-GB.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ — Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Add New Card…";
+
+"Add a Card" = "Add a Card";
+
+"Apt." = "Apt.";
+
+"Contact" = "Contact";
+
+"Delivery" = "Delivery";
+
+"Delivery Address" = "Delivery Address";
+
+"Free" = "Free";
+
+"Invalid Shipping Address" = "Invalid Shipping Address";
+
+"Loading…" = "Loading…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Next";
+
+"Online Banking (FPX)" = "Online Banking (FPX)";
+
+"Payment Method" = "Payment Method";
+
+"Shipping" = "Shipping";
+
+"Shipping Method" = "Shipping Method";
+
+"Use Billing" = "Use Billing";
+
+"Use Delivery" = "Use Delivery";
+
+"Use Shipping" = "Use Shipping";

+ 60 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/en.lproj/Localizable.strings

@@ -0,0 +1,60 @@
+/* Bank name when bank is offline for maintenance. */
+"%@ - Offline" = "%@ - Offline";
+
+/* Source type brand name */
+"3D Secure" = "3D Secure";
+
+/* Title for Add a Card view */
+"Add a Card" = "Add a Card";
+
+/* Button to add a new credit card. */
+"Add New Card…" = "Add New Card…";
+
+/* Caption for Apartment/Address line 2 field on address form */
+"Apt." = "Apt.";
+
+/* Title for contact info form */
+"Contact" = "Contact";
+
+/* Title for delivery info form */
+"Delivery" = "Delivery";
+
+/* Title for delivery address entry section */
+"Delivery Address" = "Delivery Address";
+
+/* Label for free shipping method */
+"Free" = "Free";
+
+/* Shipping form error message */
+"Invalid Shipping Address" = "Invalid Shipping Address";
+
+/* Title for screen when data is still loading from the network. */
+"Loading…" = "Loading…";
+
+/* Source type brand name */
+"Multibanco" = "Multibanco";
+
+/* Button to move to the next text entry field */
+"Next" = "Next";
+
+/* Button to pay with a Bank Account (using FPX). */
+"Online Banking (FPX)" = "Online Banking (FPX)";
+
+/* Title for Payment Method screen */
+"Payment Method" = "Payment Method";
+
+/* Title for shipping info form */
+"Shipping" = "Shipping";
+
+/* Label for shipping method form */
+"Shipping Method" = "Shipping Method";
+
+/* Button to fill shipping address from billing address. */
+"Use Billing" = "Use Billing";
+
+/* Button to fill billing address from delivery address. */
+"Use Delivery" = "Use Delivery";
+
+/* Button to fill billing address from shipping address. */
+"Use Shipping" = "Use Shipping";
+

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/es-419.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Fuera de línea";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Agregar nueva tarjeta...";
+
+"Add a Card" = "Agrega una tarjeta";
+
+"Apt." = "Apto.";
+
+"Contact" = "Contacto";
+
+"Delivery" = "Entrega";
+
+"Delivery Address" = "Dirección de entrega";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Dirección de envío inválida";
+
+"Loading…" = "Cargando...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Siguiente";
+
+"Online Banking (FPX)" = "Banca electrónica (FPX)";
+
+"Payment Method" = "Método de pago";
+
+"Shipping" = "Envío";
+
+"Shipping Method" = "Método de envío";
+
+"Use Billing" = "Usar la de facturación";
+
+"Use Delivery" = "Usar la de entrega";
+
+"Use Shipping" = "Usar la de envío";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/es.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Fuera de línea";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Añadir tarjeta nueva…";
+
+"Add a Card" = "Añade una tarjeta";
+
+"Apt." = "Piso";
+
+"Contact" = "Contacto";
+
+"Delivery" = "Entrega";
+
+"Delivery Address" = "Dirección de entrega";
+
+"Free" = "Gratuito";
+
+"Invalid Shipping Address" = "Dirección de envío no válida";
+
+"Loading…" = "Cargando…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Siguiente";
+
+"Online Banking (FPX)" = "Banca electrónica (FPX)";
+
+"Payment Method" = "Método de pago";
+
+"Shipping" = "Envío";
+
+"Shipping Method" = "Método de envío";
+
+"Use Billing" = "Usar la de facturación";
+
+"Use Delivery" = "Usar la de entrega";
+
+"Use Shipping" = "Usar la de envío";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/et-EE.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Võrguühenduseta";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Lisa uus kaart ...";
+
+"Add a Card" = "Kaardi lisamine";
+
+"Apt." = "Korter";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Tarnimine";
+
+"Delivery Address" = "Tarneaadress";
+
+"Free" = "Tasuta";
+
+"Invalid Shipping Address" = "Kehtetu tarneaadress";
+
+"Loading…" = "Laadimine ...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Järgmine";
+
+"Online Banking (FPX)" = "Internetipank (FPX)";
+
+"Payment Method" = "Makseviis";
+
+"Shipping" = "Tarnimine";
+
+"Shipping Method" = "Tarneviis";
+
+"Use Billing" = "Kasuta arveldusaadressi";
+
+"Use Delivery" = "Kasuta saaja aadressi";
+
+"Use Shipping" = "Kasuta tarneaadressi";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fi.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ Offline-tila";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Lisää uusi kortti...";
+
+"Add a Card" = "Lisää kortti";
+
+"Apt." = "Asunto";
+
+"Contact" = "Yhteystiedot";
+
+"Delivery" = "Toimitus";
+
+"Delivery Address" = "Toimitusosoite";
+
+"Free" = "Maksuton";
+
+"Invalid Shipping Address" = "Toimitusosoite ei kelpaa";
+
+"Loading…" = "Lataa...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Seuraava";
+
+"Online Banking (FPX)" = "Verkkopankki (FPX)";
+
+"Payment Method" = "Maksutapa";
+
+"Shipping" = "Toimitus";
+
+"Shipping Method" = "Toimitustapa";
+
+"Use Billing" = "Käytä laskutusosoitetta";
+
+"Use Delivery" = "Käytä toimitusosoitetta";
+
+"Use Shipping" = "Käytä lähetysosoitetta";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fil.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Magdagdag ng Bagong Kard...";
+
+"Add a Card" = "Magdagdag ng isang Kard";
+
+"Apt." = "Apt.";
+
+"Contact" = "Kontak";
+
+"Delivery" = "Pagpapadala";
+
+"Delivery Address" = "Adres ng Paghahatarin";
+
+"Free" = "Libre";
+
+"Invalid Shipping Address" = "Adres ng Nagpadala ay di balido";
+
+"Loading…" = "Naglo-load...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Susunod";
+
+"Online Banking (FPX)" = "Online Banking (FPX)";
+
+"Payment Method" = "Paraan ng Pagbabayad";
+
+"Shipping" = "Pagpapadala";
+
+"Shipping Method" = "Paraan ng Pagpapadala";
+
+"Use Billing" = "Gamitin ang Billing";
+
+"Use Delivery" = "Gamitin ang Padadalhan";
+
+"Use Shipping" = "Gamitin ang Nagpadala";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fr-CA.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Hors ligne";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Ajouter une carte…";
+
+"Add a Card" = "Ajouter une carte";
+
+"Apt." = "Nº d'app.";
+
+"Contact" = "Contact";
+
+"Delivery" = "Livraison";
+
+"Delivery Address" = "Adresse de livraison";
+
+"Free" = "Gratuit";
+
+"Invalid Shipping Address" = "Adresse de livraison non valide";
+
+"Loading…" = "Chargement…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Suivant";
+
+"Online Banking (FPX)" = "Services bancaires en ligne (FPX)";
+
+"Payment Method" = "Moyen de paiement";
+
+"Shipping" = "Livraison";
+
+"Shipping Method" = "Mode de livraison";
+
+"Use Billing" = "Utiliser l'adresse de facturation";
+
+"Use Delivery" = "Utiliser l'adresse de livraison";
+
+"Use Shipping" = "Utiliser l'adresse de livraison";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/fr.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Hors ligne";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Ajouter une nouvelle carte...";
+
+"Add a Card" = "Ajouter une carte";
+
+"Apt." = "Numéro d'appartement";
+
+"Contact" = "Contact";
+
+"Delivery" = "Livraison";
+
+"Delivery Address" = "Adresse de livraison";
+
+"Free" = "Gratuit";
+
+"Invalid Shipping Address" = "Adresse de livraison incorrecte";
+
+"Loading…" = "Chargement en cours...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Suivant";
+
+"Online Banking (FPX)" = "Compte bancaire (FPX)";
+
+"Payment Method" = "Moyen de paiement";
+
+"Shipping" = "Expédition";
+
+"Shipping Method" = "Mode de livraison";
+
+"Use Billing" = "Utiliser l'adresse de facturation";
+
+"Use Delivery" = "Utiliser l'adresse de livraison";
+
+"Use Shipping" = "Utiliser l'adresse de livraison";

+ 37 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/hr.lproj/Localizable.strings

@@ -0,0 +1,37 @@
+"%@ - Offline" = "%@ - Izvan mreže";
+
+"Add New Card…" = "Dodaj novu karticu...";
+
+"Add a Card" = "Dodaj karticu";
+
+"Apt." = "Stan br.";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Dostava";
+
+"Delivery Address" = "Adresa za dostavu";
+
+"Free" = "Besplatno";
+
+"Invalid Shipping Address" = "Adresa za otpremu nije valjana";
+
+"Loading…" = "Učitava se...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Sljedeće";
+
+"Online Banking (FPX)" = "Online Banking (FPX)";
+
+"Payment Method" = "Način plaćanja";
+
+"Shipping" = "Otprema";
+
+"Shipping Method" = "Način otpreme";
+
+"Use Billing" = "Koristi istu adresu kao i za naplatu";
+
+"Use Delivery" = "Koristi istu adresu kao i za dostavu";
+
+"Use Shipping" = "Koristi istu adresu kao i za otpremu";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/hu.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Új kártya hozzáadása...";
+
+"Add a Card" = "Kártya hozzáadása";
+
+"Apt." = "Házszám";
+
+"Contact" = "Kapcsolat";
+
+"Delivery" = "Szállítás";
+
+"Delivery Address" = "Szállítási cím";
+
+"Free" = "Ingyenes";
+
+"Invalid Shipping Address" = "Érvénytelen szállítási cím";
+
+"Loading…" = "Betöltés...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Tovább";
+
+"Online Banking (FPX)" = "Online bankolás (FPX)";
+
+"Payment Method" = "Fizetési mód";
+
+"Shipping" = "Szállítás";
+
+"Shipping Method" = "Szállítási mód";
+
+"Use Billing" = "Számlázási használata";
+
+"Use Delivery" = "Szállítási használata";
+
+"Use Shipping" = "Szállítási használata";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/id.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Tambahkan Kartu Baru...";
+
+"Add a Card" = "Tambahkan sebuah Kartu";
+
+"Apt." = "Apt.";
+
+"Contact" = "Kontak";
+
+"Delivery" = "Pengiriman";
+
+"Delivery Address" = "Alamat Pengiriman";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Alamat Pengiriman Tidak Valid";
+
+"Loading…" = "Memuat…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Berikutnya";
+
+"Online Banking (FPX)" = "Perbankan Online (FPX)";
+
+"Payment Method" = "Metode Pembayaran";
+
+"Shipping" = "Pengiriman";
+
+"Shipping Method" = "Metode Pengiriman";
+
+"Use Billing" = "Gunakan Tagihan";
+
+"Use Delivery" = "Gunakan Pengiriman";
+
+"Use Shipping" = "Gunakan Pengiriman";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/it.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Aggiungi nuova carta…";
+
+"Add a Card" = "Aggiungi una carta";
+
+"Apt." = "Int.";
+
+"Contact" = "Contatto";
+
+"Delivery" = "Consegna";
+
+"Delivery Address" = "Indirizzo di consegna";
+
+"Free" = "Gratuita";
+
+"Invalid Shipping Address" = "Indirizzo di spedizione non valido";
+
+"Loading…" = "Caricamento...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Avanti";
+
+"Online Banking (FPX)" = "Online Banking (FPX)";
+
+"Payment Method" = "Modalità di pagamento";
+
+"Shipping" = "Spedizione";
+
+"Shipping Method" = "Modalità di spedizione";
+
+"Use Billing" = "Usa indirizzo di fatturazione";
+
+"Use Delivery" = "Usa indirizzo di consegna";
+
+"Use Shipping" = "Usa indirizzo di spedizione";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ja.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - オフライン";
+
+"3D Secure" = "3D セキュア";
+
+"Add New Card…" = "新しいクレジットカードを追加...";
+
+"Add a Card" = "カードを追加";
+
+"Apt." = "建物名";
+
+"Contact" = "連絡先";
+
+"Delivery" = "配達先情報";
+
+"Delivery Address" = "配達先住所";
+
+"Free" = "無料";
+
+"Invalid Shipping Address" = "配送先住所が無効です";
+
+"Loading…" = "ロード中...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "次へ";
+
+"Online Banking (FPX)" = "オンラインバンキング (FPX)";
+
+"Payment Method" = "お支払い方法";
+
+"Shipping" = "配送先情報";
+
+"Shipping Method" = "配送方法";
+
+"Use Billing" = "請求先を使用";
+
+"Use Delivery" = "配達先を使用";
+
+"Use Shipping" = "配送先を使用";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ko.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - 오프라인";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "새 카드 추가...";
+
+"Add a Card" = "카드 추가";
+
+"Apt." = "아파트";
+
+"Contact" = "연락처";
+
+"Delivery" = "배달";
+
+"Delivery Address" = "배달 주소";
+
+"Free" = "무료";
+
+"Invalid Shipping Address" = "잘못된 배송 주소";
+
+"Loading…" = "로드 중...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "다음";
+
+"Online Banking (FPX)" = "온라인 뱅킹(FPX)";
+
+"Payment Method" = "결제 수단";
+
+"Shipping" = "배송";
+
+"Shipping Method" = "배송 방법";
+
+"Use Billing" = "청구 사용";
+
+"Use Delivery" = "배달 사용";
+
+"Use Shipping" = "배송 사용";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/lt-LT.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – neprisijungęs";
+
+"3D Secure" = "3D saugi";
+
+"Add New Card…" = "Pridėti naują kortelę...";
+
+"Add a Card" = "Pridėti kortelę";
+
+"Apt." = "But.";
+
+"Contact" = "Susisiekti";
+
+"Delivery" = "Pristatymas";
+
+"Delivery Address" = "Pristatymo adresas";
+
+"Free" = "Nemokamai";
+
+"Invalid Shipping Address" = "Netinkamas siuntimo adresas";
+
+"Loading…" = "Įkeliama...";
+
+"Multibanco" = "„Multibanco“";
+
+"Next" = "Toliau";
+
+"Online Banking (FPX)" = "Interneto banko paslaugos (FPX)";
+
+"Payment Method" = "Mokėjimo būdas";
+
+"Shipping" = "Siuntimas";
+
+"Shipping Method" = "Siuntimo būdas";
+
+"Use Billing" = "Naudoti sąskaitų siuntimo adresą";
+
+"Use Delivery" = "Naudoti pristatymo adresą";
+
+"Use Shipping" = "Naudoti siuntimo adresą";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/lv-LV.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ — bezsaistē";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Pievienot jaunu karti…";
+
+"Add a Card" = "Kartes pievienošana";
+
+"Apt." = "Dzīv.";
+
+"Contact" = "Kontaktinformācija";
+
+"Delivery" = "Piegāde";
+
+"Delivery Address" = "Piegādes adrese";
+
+"Free" = "Bezmaksas";
+
+"Invalid Shipping Address" = "Nederīga piegādes adrese";
+
+"Loading…" = "Ielādē…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Tālāk";
+
+"Online Banking (FPX)" = "Internetbanka (FPX)";
+
+"Payment Method" = "Maksājuma metode";
+
+"Shipping" = "Piegāde";
+
+"Shipping Method" = "Piegādes metode";
+
+"Use Billing" = "Izmantot norēķinu adresi";
+
+"Use Delivery" = "Izmantot piegādes adresi";
+
+"Use Shipping" = "Izmantot piegādes adresi";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ms-MY.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – Luar Talian";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Tambah Kad Baharu...";
+
+"Add a Card" = "Tambah Kad";
+
+"Apt." = "Apt.";
+
+"Contact" = "Maklumat Hubungan";
+
+"Delivery" = "Penghantaran";
+
+"Delivery Address" = "Alamat Penghantaran";
+
+"Free" = "Percuma";
+
+"Invalid Shipping Address" = "Alamat Pengiriman Tidak Sah";
+
+"Loading…" = "Sedang dimuatkan...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Seterusnya";
+
+"Online Banking (FPX)" = "Perbankan Dalam Talian (FPX)";
+
+"Payment Method" = "Kaedah Pembayaran";
+
+"Shipping" = "Pengiriman";
+
+"Shipping Method" = "Kaedah Pengiriman";
+
+"Use Billing" = "Gunakan Alamat Pengebilan";
+
+"Use Delivery" = "Gunakan Alamat Penghantaran";
+
+"Use Shipping" = "Gunakan Alamat Pengiriman";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/mt.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@- Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Żid Kard Ġdida…";
+
+"Add a Card" = "Żid Kard";
+
+"Apt." = "Appartament";
+
+"Contact" = "Kuntatt";
+
+"Delivery" = "Kunsinna";
+
+"Delivery Address" = "Indirizz tal-Kunsinna";
+
+"Free" = "Bla ħlas";
+
+"Invalid Shipping Address" = "L-Indirizz għat-Trasport tal-Merkanzija Mhux Validu";
+
+"Loading…" = "Qed jillowdja…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Li jmiss";
+
+"Online Banking (FPX)" = "Bankar online";
+
+"Payment Method" = "Metodu tal-Ħlas";
+
+"Shipping" = "Trasport tal-Merkanzija";
+
+"Shipping Method" = "Il-Mod tat-Trasport tal-Merkanzija";
+
+"Use Billing" = "Uża tal-Kont";
+
+"Use Delivery" = "Uża tal-Kunsinna";
+
+"Use Shipping" = "Uża tat-Trasport tal-Merkanzija";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/nb.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – frakoblet";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Legg til nytt kort…";
+
+"Add a Card" = "Legg til kort";
+
+"Apt." = "Leil.";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Leveranse";
+
+"Delivery Address" = "Leveringsadresse";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Ugyldig leveringsadresse";
+
+"Loading…" = "Laster ...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Neste";
+
+"Online Banking (FPX)" = "Banktjenester på nett (FPX)";
+
+"Payment Method" = "Betalingsmåte";
+
+"Shipping" = "Frakt";
+
+"Shipping Method" = "Forsendelsesmetode";
+
+"Use Billing" = "Bruk fakturaadresse";
+
+"Use Delivery" = "Bruk leveringsadresse";
+
+"Use Shipping" = "Bruk forsendelsesadresse";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/nl.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Nieuwe kaart toevoegen…";
+
+"Add a Card" = "Een kaart toevoegen";
+
+"Apt." = "Adresregel 2";
+
+"Contact" = "Contact";
+
+"Delivery" = "Bezorging";
+
+"Delivery Address" = "Bezorgadres";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Ongeldig verzendadres";
+
+"Loading…" = "Laden…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Volgende";
+
+"Online Banking (FPX)" = "Online bankieren (FPX)";
+
+"Payment Method" = "Betaalmethode";
+
+"Shipping" = "Verzending";
+
+"Shipping Method" = "Verzendmethode";
+
+"Use Billing" = "Factuuradres gebruiken";
+
+"Use Delivery" = "Bezorgadres gebruiken";
+
+"Use Shipping" = "Verzendadres gebruiken";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/nn-NO.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Fråkopla";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Legg til nytt kort …";
+
+"Add a Card" = "Legg til kort";
+
+"Apt." = "Leilegheit";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Leveranse";
+
+"Delivery Address" = "Leveringsadresse";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Ugyldig sendingsadresse";
+
+"Loading…" = "Lastar …";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Neste";
+
+"Online Banking (FPX)" = "Nettbank (FPX)";
+
+"Payment Method" = "Betalingsmetode";
+
+"Shipping" = "Sending";
+
+"Shipping Method" = "Sendingsmåte";
+
+"Use Billing" = "Bruk fakturering";
+
+"Use Delivery" = "Bruk leveringsadressa";
+
+"Use Shipping" = "Bruk sendingsadressa";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/pl-PL.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ — offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Dodaj nową kartę…";
+
+"Add a Card" = "Dodaj kartę";
+
+"Apt." = "Nr lokalu";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Dostawa";
+
+"Delivery Address" = "Adres dostawy";
+
+"Free" = "Bezpłatnie";
+
+"Invalid Shipping Address" = "Nieprawidłowy adres dostawy";
+
+"Loading…" = "Ładowanie…";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Dalej";
+
+"Online Banking (FPX)" = "Bankowość elektroniczna (FPX)";
+
+"Payment Method" = "Metoda płatności";
+
+"Shipping" = "Dostawa";
+
+"Shipping Method" = "Metoda dostawy";
+
+"Use Billing" = "Użyj adresu płatności";
+
+"Use Delivery" = "Użyj adresu dostawy";
+
+"Use Shipping" = "Użyj adresu dostawy";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/pt-BR.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Adicionar novo cartão...";
+
+"Add a Card" = "Adicionar um cartão";
+
+"Apt." = "Apt.";
+
+"Contact" = "Contato";
+
+"Delivery" = "Entrega";
+
+"Delivery Address" = "Endereço de entrega";
+
+"Free" = "Grátis";
+
+"Invalid Shipping Address" = "Endereço de envio inválido";
+
+"Loading…" = "Carregando...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Próximo";
+
+"Online Banking (FPX)" = "Online Banking (FPX)";
+
+"Payment Method" = "Forma de pagamento";
+
+"Shipping" = "Dados de entrega";
+
+"Shipping Method" = "Método de entrega";
+
+"Use Billing" = "Usar cobrança";
+
+"Use Delivery" = "Usar entrega";
+
+"Use Shipping" = "Usar envio";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/pt-PT.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Adicionar Novo Cartão...";
+
+"Add a Card" = "Adicionar um cartão";
+
+"Apt." = "Apt.";
+
+"Contact" = "Contacto";
+
+"Delivery" = "Entrega";
+
+"Delivery Address" = "Endereço de entrega";
+
+"Free" = "Gratuito";
+
+"Invalid Shipping Address" = "Endereço de envio inválido";
+
+"Loading…" = "A carregar...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Seguinte";
+
+"Online Banking (FPX)" = "Serviços bancários online (FPX)";
+
+"Payment Method" = "Método de Pagamento";
+
+"Shipping" = "Envio";
+
+"Shipping Method" = "Método de envio";
+
+"Use Billing" = "Utilizar endereço de faturação";
+
+"Use Delivery" = "Utilizar endereço de entrega";
+
+"Use Shipping" = "Utilizar endereço de envio";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ro-RO.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Adăugare card nou...";
+
+"Add a Card" = "Adăugare card";
+
+"Apt." = "Apartament";
+
+"Contact" = "Detalii de contact";
+
+"Delivery" = "Informații privind livrarea";
+
+"Delivery Address" = "Adresa de livrare";
+
+"Free" = "Expediere gratuită";
+
+"Invalid Shipping Address" = "Adresa de expediere nu este validă";
+
+"Loading…" = "Se încarcă...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Următorul";
+
+"Online Banking (FPX)" = "Servicii bancare electronice (FPX)";
+
+"Payment Method" = "Metoda de plată";
+
+"Shipping" = "Expediere";
+
+"Shipping Method" = "Metoda de expediere";
+
+"Use Billing" = "Utilizare adresă de facturare";
+
+"Use Delivery" = "Utilizare adresă de livrare";
+
+"Use Shipping" = "Utilizare adresă de expediere";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/ru.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - не в сети";
+
+"3D Secure" = "Метод 3D Secure";
+
+"Add New Card…" = "Добавить новую карту...";
+
+"Add a Card" = "Добавить карту";
+
+"Apt." = "Кв.";
+
+"Contact" = "Контактные данные";
+
+"Delivery" = "Доставка";
+
+"Delivery Address" = "Адрес доставки";
+
+"Free" = "Бесплатно";
+
+"Invalid Shipping Address" = "Ошибочный адрес доставки";
+
+"Loading…" = "Идет загрузка...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Далее";
+
+"Online Banking (FPX)" = "Онлайн-банкинг (FPX)";
+
+"Payment Method" = "Метод оплаты";
+
+"Shipping" = "Доставка";
+
+"Shipping Method" = "Метод доставки";
+
+"Use Billing" = "Использовать адрес выставления счетов";
+
+"Use Delivery" = "Использовать адрес доставки";
+
+"Use Shipping" = "Использовать адрес доставки";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/sk-SK.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Pridať novú kartu...";
+
+"Add a Card" = "Pridať kartu";
+
+"Apt." = "Byt";
+
+"Contact" = "Kontakt";
+
+"Delivery" = "Dodanie";
+
+"Delivery Address" = "Dodacia adresa";
+
+"Free" = "Zdarma";
+
+"Invalid Shipping Address" = "Neplatná dodacia adresa";
+
+"Loading…" = "Nahrávanie...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Ďalej";
+
+"Online Banking (FPX)" = "Online bankovníctvo (FPX)";
+
+"Payment Method" = "Spôsob platby";
+
+"Shipping" = "Dodanie";
+
+"Shipping Method" = "Spôsob dodania";
+
+"Use Billing" = "Použiť fakturačnú adresu";
+
+"Use Delivery" = "Použiť adresu doručenia";
+
+"Use Shipping" = "Použiť dodaciu adresu";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/sl-SI.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – ni dosegljiva";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Dodaj novo kartico ...";
+
+"Add a Card" = "Dodajte kartico";
+
+"Apt." = "Stanovanje";
+
+"Contact" = "Stik";
+
+"Delivery" = "Dostava";
+
+"Delivery Address" = "Dostavni naslov";
+
+"Free" = "Brezplačno";
+
+"Invalid Shipping Address" = "Neveljaven dostavni naslov";
+
+"Loading…" = "Nalaganje ...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Naprej";
+
+"Online Banking (FPX)" = "Spletno bančništvo (FPX)";
+
+"Payment Method" = "Načina plačila";
+
+"Shipping" = "Dostava";
+
+"Shipping Method" = "Način dostave";
+
+"Use Billing" = "Uporabi naslov plačnika računa";
+
+"Use Delivery" = "Uporabi dostavo";
+
+"Use Shipping" = "Uporabi dostavo";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/sv.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ – Offline";
+
+"3D Secure" = "3D Secure";
+
+"Add New Card…" = "Lägg till nytt kort …";
+
+"Add a Card" = "Lägg till kort";
+
+"Apt." = "Lägenhetsnr.";
+
+"Contact" = "Kontaktinformation";
+
+"Delivery" = "Leverans";
+
+"Delivery Address" = "Leveransadress";
+
+"Free" = "Gratis";
+
+"Invalid Shipping Address" = "Ogiltig leveransadress";
+
+"Loading…" = "Laddar ...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Nästa";
+
+"Online Banking (FPX)" = "Internetbank (FPX)";
+
+"Payment Method" = "Betalningsmetod";
+
+"Shipping" = "Frakt";
+
+"Shipping Method" = "Leveransmetod";
+
+"Use Billing" = "Använd faktureringsadress";
+
+"Use Delivery" = "Använd leveransadress";
+
+"Use Shipping" = "Använd fraktadress";

+ 0 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/tk.lproj/Localizable.strings


+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/tr.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Çevrimdışı";
+
+"3D Secure" = "3D Güvenli";
+
+"Add New Card…" = "Yeni Kart Ekle...";
+
+"Add a Card" = "Kart Ekle";
+
+"Apt." = "Apt.";
+
+"Contact" = "İletişim";
+
+"Delivery" = "Teslimat";
+
+"Delivery Address" = "Teslimat Adresi";
+
+"Free" = "Ücretsiz";
+
+"Invalid Shipping Address" = "Geçersiz Gönderi Adresi";
+
+"Loading…" = "Yükleniyor...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "İleri";
+
+"Online Banking (FPX)" = "Çevrimiçi Bankacılık (FPX)";
+
+"Payment Method" = "Ödeme Metodu";
+
+"Shipping" = "Gönderi";
+
+"Shipping Method" = "Gönderi Metodu";
+
+"Use Billing" = "Fatura Adresini Kullan";
+
+"Use Delivery" = "Teslimat Adresini Kullan";
+
+"Use Shipping" = "Gönderi Adresini Kullan";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/vi.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - Ngoại tuyến";
+
+"3D Secure" = "Bảo mật 3D";
+
+"Add New Card…" = "Thêm thẻ mới...";
+
+"Add a Card" = "Thêm thẻ";
+
+"Apt." = "Căn hộ";
+
+"Contact" = "Liên hệ";
+
+"Delivery" = "Giao hàng";
+
+"Delivery Address" = "Địa chỉ giao hàng";
+
+"Free" = "Miễn phí";
+
+"Invalid Shipping Address" = "Địa chỉ vận chuyển không hợp lệ";
+
+"Loading…" = "Đang tải...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "Tiếp";
+
+"Online Banking (FPX)" = "Ngân hàng Trực tuyến (FPX)";
+
+"Payment Method" = "Phương thức thanh toán";
+
+"Shipping" = "Vận chuyển";
+
+"Shipping Method" = "Phương thức vận chuyển";
+
+"Use Billing" = "Sử dụng Hóa đơn";
+
+"Use Delivery" = "Sử dụng Giao hàng";
+
+"Use Shipping" = "Chọn Vận chuyển";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/zh-HK.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - 離線";
+
+"3D Secure" = "3DS 驗證";
+
+"Add New Card…" = "添加新卡…";
+
+"Add a Card" = "添加卡";
+
+"Apt." = "公寓";
+
+"Contact" = "聯絡資訊";
+
+"Delivery" = "收貨";
+
+"Delivery Address" = "收貨地址";
+
+"Free" = "免費";
+
+"Invalid Shipping Address" = "收货地址無效";
+
+"Loading…" = "正在載入...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "下一步";
+
+"Online Banking (FPX)" = "網上銀行 (FPX)";
+
+"Payment Method" = "支付方式";
+
+"Shipping" = "配送";
+
+"Shipping Method" = "配送方式";
+
+"Use Billing" = "使用帳單地址";
+
+"Use Delivery" = "使用收貨地址";
+
+"Use Shipping" = "使用收货地址";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/zh-Hans.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - 离线";
+
+"3D Secure" = "3DS 验证";
+
+"Add New Card…" = "添加新卡……";
+
+"Add a Card" = "添加卡";
+
+"Apt." = "公寓";
+
+"Contact" = "联系信息";
+
+"Delivery" = "收货信息";
+
+"Delivery Address" = "收货地址";
+
+"Free" = "免费";
+
+"Invalid Shipping Address" = "无效的送货地址";
+
+"Loading…" = "正在加载……";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "下一步";
+
+"Online Banking (FPX)" = "网上银行 (FPX)";
+
+"Payment Method" = "支付方式";
+
+"Shipping" = "配送";
+
+"Shipping Method" = "配送方式";
+
+"Use Billing" = "使用账单地址";
+
+"Use Delivery" = "使用收货地址";
+
+"Use Shipping" = "使用送货地址";

+ 39 - 0
Pods/Stripe/Stripe/StripeiOS/Resources/Localizations/zh-Hant.lproj/Localizable.strings

@@ -0,0 +1,39 @@
+"%@ - Offline" = "%@ - 離線";
+
+"3D Secure" = "3DS 驗證";
+
+"Add New Card…" = "添加新卡...";
+
+"Add a Card" = "新增卡";
+
+"Apt." = "公寓";
+
+"Contact" = "聯絡資訊";
+
+"Delivery" = "寄送";
+
+"Delivery Address" = "寄送地址";
+
+"Free" = "免費";
+
+"Invalid Shipping Address" = "送貨地址無效";
+
+"Loading…" = "正在載入...";
+
+"Multibanco" = "Multibanco";
+
+"Next" = "下一步";
+
+"Online Banking (FPX)" = "網上銀行 (FPX)";
+
+"Payment Method" = "付款方式";
+
+"Shipping" = "送貨";
+
+"Shipping Method" = "送貨方式";
+
+"Use Billing" = "使用帳單地址";
+
+"Use Delivery" = "使用寄送地址";
+
+"Use Shipping" = "使用送貨地址";

+ 63 - 0
Pods/Stripe/Stripe/StripeiOS/Source/Enums+CustomStringConvertible.swift

@@ -0,0 +1,63 @@
+//
+//  Enums+CustomStringConvertible.swift
+//  Stripe
+//
+//  Autogenerated by generate_objc_enum_string_values.rb
+//  Copyright © 2021 Stripe, Inc. All rights reserved.
+//
+
+import SwiftUI
+
+/// :nodoc:
+extension STPBankSelectionMethod: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .FPX:
+            return "FPX"
+        case .unknown:
+            return "unknown"
+        }
+    }
+}
+
+/// :nodoc:
+extension STPBillingAddressFields: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .full:
+            return "full"
+        case .name:
+            return "name"
+        case .none:
+            return "none"
+        case .postalCode:
+            return "postalCode"
+        case .zip:
+            return "zip"
+        }
+    }
+}
+
+/// :nodoc:
+extension STPShippingStatus: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .invalid:
+            return "invalid"
+        case .valid:
+            return "valid"
+        }
+    }
+}
+
+/// :nodoc:
+extension STPShippingType: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .delivery:
+            return "delivery"
+        case .shipping:
+            return "shipping"
+        }
+    }
+}

+ 30 - 0
Pods/Stripe/Stripe/StripeiOS/Source/PKAddPaymentPassRequest+Stripe_Error.swift

@@ -0,0 +1,30 @@
+//
+//  PKAddPaymentPassRequest+Stripe_Error.swift
+//  StripeiOS
+//
+//  Created by Jack Flintermann on 9/29/19.
+//  Copyright © 2019 Stripe, Inc. All rights reserved.
+//
+
+import ObjectiveC
+import PassKit
+
+var stpAddPaymentPassRequest: UInt8 = 0
+
+// This is used to store an error on a PKAddPaymentPassRequest
+// so that STPFakeAddPaymentPassViewController can inspect it for debugging.
+extension PKAddPaymentPassRequest {
+    @objc var stp_error: NSError? {
+        get {
+            return objc_getAssociatedObject(self, &stpAddPaymentPassRequest) as? NSError
+        }
+        set(stp_error) {
+            objc_setAssociatedObject(
+                self,
+                &stpAddPaymentPassRequest,
+                stp_error,
+                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
+            )
+        }
+    }
+}

+ 187 - 0
Pods/Stripe/Stripe/StripeiOS/Source/PKPaymentAuthorizationViewController+Stripe_Blocks.swift

@@ -0,0 +1,187 @@
+//
+//  PKPaymentAuthorizationViewController+Stripe_Blocks.swift
+//  StripeiOS
+//
+//  Created by Ben Guo on 4/19/16.
+//  Copyright © 2016 Stripe, Inc. All rights reserved.
+//
+
+import ObjectiveC
+import PassKit
+@_spi(STP) import StripeCore
+
+typealias STPApplePayPaymentMethodHandlerBlock = (STPPaymentMethod, @escaping STPPaymentStatusBlock)
+    -> Void
+typealias STPPaymentCompletionBlock = (STPPaymentStatus, Error?) -> Void
+typealias STPPaymentSummaryItemCompletionBlock = ([PKPaymentSummaryItem]) -> Void
+typealias STPShippingMethodSelectionBlock = (
+    PKShippingMethod, @escaping STPPaymentSummaryItemCompletionBlock
+) -> Void
+typealias STPShippingAddressValidationBlock = (
+    STPShippingStatus, [PKShippingMethod], [PKPaymentSummaryItem]
+) -> Void
+typealias STPShippingAddressSelectionBlock = (
+    STPAddress, @escaping STPShippingAddressValidationBlock
+) -> Void
+typealias STPPaymentAuthorizationBlock = (PKPayment) -> Void
+extension PKPaymentAuthorizationViewController {
+    class func stp_controller(
+        with paymentRequest: PKPaymentRequest,
+        apiClient: STPAPIClient,
+        onShippingAddressSelection: @escaping STPShippingAddressSelectionBlock,
+        onShippingMethodSelection: @escaping STPShippingMethodSelectionBlock,
+        onPaymentAuthorization: @escaping STPPaymentAuthorizationBlock,
+        onPaymentMethodCreation: @escaping STPApplePayPaymentMethodHandlerBlock,
+        onFinish: @escaping STPPaymentCompletionBlock
+    ) -> Self? {
+        let delegate = STPBlockBasedApplePayDelegate()
+        delegate.apiClient = apiClient
+        delegate.onShippingAddressSelection = onShippingAddressSelection
+        delegate.onShippingMethodSelection = onShippingMethodSelection
+        delegate.onPaymentAuthorization = onPaymentAuthorization
+        delegate.onPaymentMethodCreation = onPaymentMethodCreation
+        delegate.onFinish = onFinish
+        let viewController = self.init(paymentRequest: paymentRequest)
+        viewController?.delegate = delegate
+        if let viewController = viewController {
+            objc_setAssociatedObject(
+                viewController,
+                UnsafeRawPointer(&kSTPBlockBasedApplePayDelegateAssociatedObjectKey),
+                delegate,
+                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
+            )
+        }
+        return viewController
+    }
+}
+
+private var kSTPBlockBasedApplePayDelegateAssociatedObjectKey = 0
+typealias STPApplePayShippingMethodCompletionBlock = (
+    PKPaymentAuthorizationStatus, [PKPaymentSummaryItem]?
+) -> Void
+typealias STPApplePayShippingAddressCompletionBlock = (
+    PKPaymentAuthorizationStatus, [PKShippingMethod]?, [PKPaymentSummaryItem]?
+) -> Void
+class STPBlockBasedApplePayDelegate: NSObject, PKPaymentAuthorizationViewControllerDelegate {
+    var apiClient: STPAPIClient?
+    var onShippingAddressSelection: STPShippingAddressSelectionBlock?
+    var onShippingMethodSelection: STPShippingMethodSelectionBlock?
+    var onPaymentAuthorization: STPPaymentAuthorizationBlock?
+    var onPaymentMethodCreation: STPApplePayPaymentMethodHandlerBlock?
+    var onFinish: STPPaymentCompletionBlock?
+    var lastError: Error?
+    var didSucceed = false
+
+    // Remove all this once we drop iOS 11 support
+    func paymentAuthorizationViewController(
+        _ controller: PKPaymentAuthorizationViewController,
+        didAuthorizePayment payment: PKPayment,
+        completion: @escaping (PKPaymentAuthorizationStatus) -> Void
+    ) {
+        onPaymentAuthorization?(payment)
+
+        let paymentMethodCreateCompletion: ((STPPaymentMethod?, Error?) -> Void)? = {
+            result,
+            paymentMethodCreateError in
+            if let paymentMethodCreateError = paymentMethodCreateError {
+                self.lastError = paymentMethodCreateError
+                completion(.failure)
+                return
+            }
+            guard let result = result else {
+                self.lastError = NSError.stp_genericFailedToParseResponseError()
+                completion(.failure)
+                return
+            }
+            self.onPaymentMethodCreation?(
+                result,
+                { status, error in
+                    if status != .success || error != nil {
+                        self.lastError = error
+                        completion(.failure)
+                        if controller.presentingViewController == nil {
+                            // If we call completion() after dismissing, didFinishWithStatus is NOT called.
+                            self._finish()
+                        }
+                        return
+                    }
+                    self.didSucceed = true
+                    completion(.success)
+                    if controller.presentingViewController == nil {
+                        // If we call completion() after dismissing, didFinishWithStatus is NOT called.
+                        self._finish()
+                    }
+                }
+            )
+        }
+        if let paymentMethodCreateCompletion = paymentMethodCreateCompletion {
+            apiClient?.createPaymentMethod(with: payment, completion: paymentMethodCreateCompletion)
+        }
+    }
+
+    func paymentAuthorizationViewController(
+        _ controller: PKPaymentAuthorizationViewController,
+        didSelect shippingMethod: PKShippingMethod,
+        completion: @escaping (PKPaymentAuthorizationStatus, [PKPaymentSummaryItem]) -> Void
+    ) {
+        onShippingMethodSelection?(
+            shippingMethod,
+            { summaryItems in
+                completion(PKPaymentAuthorizationStatus.success, summaryItems)
+            }
+        )
+    }
+
+    func paymentAuthorizationViewController(
+        _ controller: PKPaymentAuthorizationViewController,
+        didSelectShippingContact contact: PKContact,
+        handler completion: @escaping (PKPaymentRequestShippingContactUpdate) -> Void
+    ) {
+        let stpAddress = STPAddress(pkContact: contact)
+        onShippingAddressSelection?(
+            stpAddress,
+            { status, shippingMethods, summaryItems in
+                if status == .invalid {
+                    let genericShippingError = NSError(
+                        domain: PKPaymentErrorDomain,
+                        code: PKPaymentError.shippingContactInvalidError.rawValue,
+                        userInfo: nil
+                    )
+                    completion(
+                        PKPaymentRequestShippingContactUpdate(
+                            errors: [genericShippingError],
+                            paymentSummaryItems: summaryItems,
+                            shippingMethods: shippingMethods
+                        )
+                    )
+                } else {
+                    completion(
+                        PKPaymentRequestShippingContactUpdate(
+                            errors: nil,
+                            paymentSummaryItems: summaryItems,
+                            shippingMethods: shippingMethods
+                        )
+                    )
+                }
+            }
+        )
+    }
+
+    func paymentAuthorizationViewControllerDidFinish(
+        _ controller: PKPaymentAuthorizationViewController
+    ) {
+        _finish()
+    }
+
+    func _finish() {
+        if didSucceed {
+            onFinish?(.success, nil)
+        } else if let lastError = lastError {
+            onFinish?(.error, lastError)
+        } else {
+            onFinish?(.userCancellation, nil)
+        }
+    }
+}
+
+typealias STPPaymentAuthorizationStatusCallback = (PKPaymentAuthorizationStatus) -> Void

+ 178 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAPIClient+BasicUI.swift

@@ -0,0 +1,178 @@
+//
+//  STPAPIClient+BasicUI.swift
+//  StripeiOS
+//
+//  Created by David Estes on 6/30/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePayments
+
+/// A client for making connections to the Stripe API.
+extension STPAPIClient {
+    /// Initializes an API client with the given configuration.
+    /// - Parameter configuration: The configuration to use.
+    /// - Returns: An instance of STPAPIClient.
+    @available(
+        *,
+        deprecated,
+        message:
+            "This initializer previously configured publishableKey and stripeAccount via the STPPaymentConfiguration instance. This behavior is deprecated; set the STPAPIClient configuration, publishableKey, and stripeAccount properties directly on the STPAPIClient instead."
+    )
+    public convenience init(
+        configuration: STPPaymentConfiguration
+    ) {
+        // For legacy reasons, we'll support this initializer and use the deprecated configuration.{publishableKey, stripeAccount} properties
+        self.init()
+        publishableKey = configuration.publishableKey
+        stripeAccount = configuration.stripeAccount
+    }
+}
+
+extension STPAPIClient {
+    /// The client's configuration.
+    /// Defaults to `STPPaymentConfiguration.shared`.
+    @objc public var configuration: STPPaymentConfiguration {
+        get {
+            if let config = _stored_configuration as? STPPaymentConfiguration {
+                return config
+            } else {
+                return .shared
+            }
+        }
+        set {
+            _stored_configuration = newValue
+        }
+    }
+
+    /// Update a customer with parameters
+    /// - seealso: https://stripe.com/docs/api#update_customer
+    func updateCustomer(
+        withParameters parameters: [String: Any],
+        using ephemeralKey: STPEphemeralKey,
+        completion: @escaping STPCustomerCompletionBlock
+    ) {
+        let endpoint = "\(APIEndpointCustomers)/\(ephemeralKey.customerID ?? "")"
+        APIRequest<STPCustomer>.post(
+            with: self,
+            endpoint: endpoint,
+            additionalHeaders: authorizationHeader(using: ephemeralKey),
+            parameters: parameters
+        ) { object, _, error in
+            completion(object, error)
+        }
+    }
+
+    /// Attach a Payment Method to a customer
+    /// - seealso: https://stripe.com/docs/api/payment_methods/attach
+    func attachPaymentMethod(
+        _ paymentMethodID: String,
+        toCustomerUsing ephemeralKey: STPEphemeralKey,
+        completion: @escaping STPErrorBlock
+    ) {
+        guard let customerID = ephemeralKey.customerID else {
+            assertionFailure()
+            completion(nil)
+            return
+        }
+        let endpoint = "\(APIEndpointPaymentMethods)/\(paymentMethodID)/attach"
+        APIRequest<STPPaymentMethod>.post(
+            with: self,
+            endpoint: endpoint,
+            additionalHeaders: authorizationHeader(using: ephemeralKey),
+            parameters: [
+                "customer": customerID
+            ]
+        ) { _, _, error in
+            completion(error)
+        }
+    }
+
+    /// Detach a Payment Method from a customer
+    /// - seealso: https://stripe.com/docs/api/payment_methods/detach
+    func detachPaymentMethod(
+        _ paymentMethodID: String,
+        fromCustomerUsing ephemeralKey: STPEphemeralKey,
+        completion: @escaping STPErrorBlock
+    ) {
+        let endpoint = "\(APIEndpointPaymentMethods)/\(paymentMethodID)/detach"
+        APIRequest<STPPaymentMethod>.post(
+            with: self,
+            endpoint: endpoint,
+            additionalHeaders: authorizationHeader(using: ephemeralKey),
+            parameters: [:]
+        ) { _, _, error in
+            completion(error)
+        }
+    }
+
+    /// Retrieves a list of Payment Methods attached to a customer.
+    /// @note This only fetches card type Payment Methods
+    func listPaymentMethodsForCustomer(
+        using ephemeralKey: STPEphemeralKey,
+        completion: @escaping STPPaymentMethodsCompletionBlock
+    ) {
+        let header = authorizationHeader(using: ephemeralKey)
+        let params: [String: Any] = [
+            "customer": ephemeralKey.customerID ?? "",
+            "type": "card",
+        ]
+        APIRequest<STPPaymentMethodListDeserializer>.getWith(
+            self,
+            endpoint: APIEndpointPaymentMethods,
+            additionalHeaders: header,
+            parameters: params as [String: Any]
+        ) { deserializer, _, error in
+            if let error = error {
+                completion(nil, error)
+            } else if let paymentMethods = deserializer?.paymentMethods {
+                completion(paymentMethods, nil)
+            }
+        }
+    }
+
+    /// Retrieve a customer
+    /// - seealso: https://stripe.com/docs/api#retrieve_customer
+    func retrieveCustomer(
+        using ephemeralKey: STPEphemeralKey,
+        completion: @escaping STPCustomerCompletionBlock
+    ) {
+        let endpoint = "\(APIEndpointCustomers)/\(ephemeralKey.customerID ?? "")"
+        APIRequest<STPCustomer>.getWith(
+            self,
+            endpoint: endpoint,
+            additionalHeaders: authorizationHeader(using: ephemeralKey),
+            parameters: [:]
+        ) { object, _, error in
+            completion(object, error)
+        }
+    }
+
+    // MARK: FPX
+    /// Retrieves the online status of the FPX banks from the Stripe API.
+    /// - Parameter completion:  The callback to run with the returned FPX bank list, or an error.
+    func retrieveFPXBankStatus(
+        withCompletion completion: @escaping STPFPXBankStatusCompletionBlock
+    ) {
+        APIRequest<STPFPXBankStatusResponse>.getWith(
+            self,
+            endpoint: APIEndpointFPXStatus,
+            parameters: [
+                "account_holder_type": "individual"
+            ]
+        ) { statusResponse, _, error in
+            completion(statusResponse, error)
+        }
+    }
+
+    // MARK: Helpers
+
+    /// A helper method that returns the Authorization header to use for API requests. If ephemeralKey is nil, uses self.publishableKey instead.
+    func authorizationHeader(using ephemeralKey: STPEphemeralKey? = nil) -> [String: String] {
+        return authorizationHeader(using: ephemeralKey?.secret)
+    }
+}
+
+private let APIEndpointFPXStatus = "fpx/bank_statuses"

+ 40 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAPIClient+PushProvisioning.swift

@@ -0,0 +1,40 @@
+//
+//  STPAPIClient+PushProvisioning.swift
+//  StripeiOS
+//
+//  Created by Jack Flintermann on 9/27/18.
+//  Copyright © 2018 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePayments
+@_spi(STP) import StripePaymentsUI
+
+typealias STPPushProvisioningDetailsCompletionBlock = (STPPushProvisioningDetails?, Error?) -> Void
+extension STPAPIClient {
+    func retrievePushProvisioningDetails(
+        with params: STPPushProvisioningDetailsParams,
+        ephemeralKey: STPEphemeralKey,
+        completion: @escaping STPPushProvisioningDetailsCompletionBlock
+    ) {
+
+        let endpoint = "issuing/cards/\(params.cardId)/push_provisioning_details"
+        let parameters = [
+            "ios": [
+                "certificates": params.certificatesBase64,
+                "nonce": params.nonceHex,
+                "nonce_signature": params.nonceSignatureHex,
+            ] as [String: Any],
+        ]
+
+        APIRequest<STPPushProvisioningDetails>.getWith(
+            self,
+            endpoint: endpoint,
+            additionalHeaders: authorizationHeader(using: ephemeralKey),
+            parameters: parameters
+        ) { details, _, error in
+            completion(details, error)
+        }
+    }
+}

+ 970 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAddCardViewController.swift

@@ -0,0 +1,970 @@
+//
+//  STPAddCardViewController.swift
+//  StripeiOS
+//
+//  Created by Jack Flintermann on 3/23/16.
+//  Copyright © 2016 Stripe, Inc. All rights reserved.
+//
+
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePayments
+@_spi(STP) import StripePaymentsUI
+@_spi(STP) import StripeUICore
+import UIKit
+
+/// 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`.
+public class STPAddCardViewController: STPCoreTableViewController, STPAddressViewModelDelegate,
+    STPCardScannerDelegate, STPPaymentCardTextFieldDelegate, UITableViewDelegate,
+    UITableViewDataSource
+{
+
+    /// A convenience initializer; equivalent to calling `init(configuration: STPPaymentConfiguration.shared, theme: STPTheme.defaultTheme)`.
+    @objc
+    public convenience init() {
+        self.init(configuration: STPPaymentConfiguration.shared, theme: STPTheme.defaultTheme)
+    }
+
+    /// Initializes a new `STPAddCardViewController` with the provided configuration and theme. Don't forget to set the `delegate` property after initialization.
+    /// - Parameters:
+    ///   - configuration: The configuration to use (this determines the Stripe publishable key to use, the required billing address fields, whether or not to use SMS autofill, etc). - seealso: STPPaymentConfiguration
+    ///   - theme:         The theme to use to inform the view controller's visual appearance. - seealso: STPTheme
+    @objc(initWithConfiguration:theme:)
+    public init(
+        configuration: STPPaymentConfiguration,
+        theme: STPTheme
+    ) {
+        addressViewModel = STPAddressViewModel(
+            requiredBillingFields: configuration.requiredBillingAddressFields,
+            availableCountries: configuration.availableCountries
+        )
+        super.init(theme: theme)
+        commonInit(with: configuration)
+    }
+
+    /// The view controller's delegate. This must be set before showing the view controller in order for it to work properly. - seealso: STPAddCardViewControllerDelegate
+    @objc public weak var delegate: STPAddCardViewControllerDelegate?
+    /// You can set this property to pre-fill any information you've already collected from your user. - seealso: STPUserInformation.h
+    @objc public var prefilledInformation: STPUserInformation? {
+        didSet {
+            if let address = prefilledInformation?.billingAddress {
+                addressViewModel.address = address
+            }
+        }
+    }
+
+    private var _customFooterView: UIView?
+    /// Provide this view controller with a footer view.
+    /// When the footer view needs to be resized, it will be sent a
+    /// `sizeThatFits:` call. The view should respond correctly to this method in order
+    /// to be sized and positioned properly.
+    @objc public var customFooterView: UIView? {
+        get {
+            _customFooterView
+        }
+        set(footerView) {
+            _customFooterView = footerView
+            _configureFooterView()
+        }
+    }
+
+    func _configureFooterView() {
+        if isViewLoaded, let footerView = _customFooterView {
+            let size = footerView.sizeThatFits(
+                CGSize(width: view.bounds.size.width, height: CGFloat.greatestFiniteMagnitude)
+            )
+            footerView.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
+
+            tableView?.tableFooterView = footerView
+        }
+    }
+
+    /// The API Client to use to make requests.
+    /// Defaults to `STPAPIClient.shared`
+    public var apiClient: STPAPIClient = STPAPIClient.shared
+
+    /// Use init: or initWithConfiguration:theme:
+    required init(
+        theme: STPTheme?
+    ) {
+        let configuration = STPPaymentConfiguration.shared
+        addressViewModel = STPAddressViewModel(
+            requiredBillingFields: configuration.requiredBillingAddressFields,
+            availableCountries: configuration.availableCountries
+        )
+        super.init(theme: theme)
+    }
+
+    /// Use init: or initWithConfiguration:theme:
+    required init(
+        nibName nibNameOrNil: String?,
+        bundle nibBundleOrNil: Bundle?
+    ) {
+        let configuration = STPPaymentConfiguration.shared
+        addressViewModel = STPAddressViewModel(
+            requiredBillingFields: configuration.requiredBillingAddressFields,
+            availableCountries: configuration.availableCountries
+        )
+        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+    }
+
+    /// Use init: or initWithConfiguration:theme:
+    required init?(
+        coder aDecoder: NSCoder
+    ) {
+        let configuration = STPPaymentConfiguration.shared
+        addressViewModel = STPAddressViewModel(
+            requiredBillingFields: configuration.requiredBillingAddressFields,
+            availableCountries: configuration.availableCountries
+        )
+        super.init(coder: aDecoder)
+    }
+
+    private var _alwaysEnableDoneButton = false
+    @objc var alwaysEnableDoneButton: Bool {
+        get {
+            _alwaysEnableDoneButton
+        }
+        set(alwaysEnableDoneButton) {
+            if alwaysEnableDoneButton != _alwaysEnableDoneButton {
+                _alwaysEnableDoneButton = alwaysEnableDoneButton
+                updateDoneButton()
+            }
+        }
+    }
+    private var configuration: STPPaymentConfiguration?
+    @objc var shippingAddress: STPAddress?
+    private var hasUsedShippingAddress = false
+    private weak var cardImageView: UIImageView?
+    private var doneItem: UIBarButtonItem?
+    private var cardHeaderView: STPSectionHeaderView?
+
+    @available(macCatalyst 14.0, *)
+    private var cardScanner: STPCardScanner? {
+        get {
+            _cardScanner as? STPCardScanner
+        }
+        set {
+            _cardScanner = newValue
+        }
+    }
+
+    /// Storage for `cardScanner`.
+    private var _cardScanner: NSObject?
+
+    @available(macCatalyst 14.0, *)
+    private var scannerCell: STPCardScannerTableViewCell? {
+        get {
+            _scannerCell as? STPCardScannerTableViewCell
+        }
+        set {
+            _scannerCell = newValue
+        }
+    }
+
+    /// Storage for `scannerCell`.
+    private var _scannerCell: NSObject?
+
+    private var _isScanning = false
+    private var isScanning: Bool {
+        get {
+            _isScanning
+        }
+        set(isScanning) {
+            if _isScanning == isScanning {
+                return
+            }
+            _isScanning = isScanning
+
+            cardHeaderView?.button?.isEnabled = !isScanning
+            let indexPath = IndexPath(
+                row: 0,
+                section: STPPaymentCardSection.stpPaymentCardScannerSection.rawValue
+            )
+            tableView?.beginUpdates()
+            if isScanning {
+                tableView?.insertRows(at: [indexPath], with: .automatic)
+            } else {
+                tableView?.deleteRows(at: [indexPath], with: .automatic)
+            }
+            tableView?.endUpdates()
+            if isScanning {
+                tableView?.scrollToRow(at: indexPath, at: .middle, animated: true)
+            }
+            updateInputAccessoryVisiblity()
+        }
+    }
+    private var addressHeaderView: STPSectionHeaderView?
+    var paymentCell: STPPaymentCardTextFieldCell?
+
+    private var _loading = false
+    @objc var loading: Bool {
+        get {
+            _loading
+        }
+        set(loading) {
+            if loading == _loading {
+                return
+            }
+            _loading = loading
+            stp_navigationItemProxy?.setHidesBackButton(loading, animated: true)
+            stp_navigationItemProxy?.leftBarButtonItem?.isEnabled = !loading
+            activityIndicator?.animating = loading
+            if loading {
+                tableView?.endEditing(true)
+                var loadingItem: UIBarButtonItem?
+                if let activityIndicator = activityIndicator {
+                    loadingItem = UIBarButtonItem(customView: activityIndicator)
+                }
+                stp_navigationItemProxy?.setRightBarButton(loadingItem, animated: true)
+                cardHeaderView?.buttonHidden = true
+            } else {
+                stp_navigationItemProxy?.setRightBarButton(doneItem, animated: true)
+                cardHeaderView?.buttonHidden = false
+            }
+            var cells = addressViewModel.addressCells as [UITableViewCell]
+
+            if let paymentCell = paymentCell {
+                cells.append(paymentCell)
+            }
+            for cell in cells {
+                cell.isUserInteractionEnabled = !loading
+                UIView.animate(
+                    withDuration: 0.1,
+                    animations: {
+                        cell.alpha = loading ? 0.7 : 1.0
+                    }
+                )
+            }
+        }
+    }
+    private var activityIndicator: STPPaymentActivityIndicatorView?
+    private weak var lookupActivityIndicator: STPPaymentActivityIndicatorView?
+    var addressViewModel: STPAddressViewModel
+    private var inputAccessoryToolbar: UIToolbar?
+    private var lookupSucceeded = false
+    private var scannerCompleteAnimationTimer: Timer?
+
+    @objc(commonInitWithConfiguration:) func commonInit(with configuration: STPPaymentConfiguration)
+    {
+        STPAnalyticsClient.sharedClient.addClass(
+            toProductUsageIfNecessary: STPAddCardViewController.self
+        )
+
+        self.configuration = configuration
+        shippingAddress = nil
+        hasUsedShippingAddress = false
+        addressViewModel.delegate = self
+        title = STPLocalizedString("Add a Card", "Title for Add a Card view")
+
+        if #available(macCatalyst 14.0, *) {
+            cardScanner = STPCardScanner()
+        }
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        estimatedHeightForRowAt indexPath: IndexPath
+    ) -> CGFloat {
+        return 44.0
+    }
+
+    @objc override func createAndSetupViews() {
+        super.createAndSetupViews()
+
+        let doneItem = UIBarButtonItem(
+            barButtonSystemItem: .done,
+            target: self,
+            action: #selector(nextPressed(_:))
+        )
+        self.doneItem = doneItem
+        stp_navigationItemProxy?.rightBarButtonItem = doneItem
+        updateDoneButton()
+
+        stp_navigationItemProxy?.leftBarButtonItem?.accessibilityIdentifier =
+            "AddCardViewControllerNavBarCancelButtonIdentifier"
+        stp_navigationItemProxy?.rightBarButtonItem?.accessibilityIdentifier =
+            "AddCardViewControllerNavBarDoneButtonIdentifier"
+
+        let cardImageView = UIImageView(image: STPLegacyImageLibrary.largeCardFrontImage())
+        cardImageView.contentMode = .center
+        cardImageView.frame = CGRect(
+            x: 0,
+            y: 0,
+            width: view.bounds.size.width,
+            height: cardImageView.bounds.size.height + (57 * 2)
+        )
+        self.cardImageView = cardImageView
+        tableView?.tableHeaderView = cardImageView
+
+        let paymentCell = STPPaymentCardTextFieldCell(
+            style: .default,
+            reuseIdentifier: "STPAddCardViewControllerPaymentCardTextFieldCell"
+        )
+        paymentCell.paymentField?.delegate = self
+        if configuration?.requiredBillingAddressFields == .postalCode {
+            // If postal code collection is enabled, move the postal code field into the card entry field.
+            // Otherwise, this will be picked up by the billing address fields below.
+            paymentCell.paymentField?.postalCodeEntryEnabled = true
+        }
+        self.paymentCell = paymentCell
+
+        activityIndicator = STPPaymentActivityIndicatorView(
+            frame: CGRect(x: 0, y: 0, width: 20.0, height: 20.0)
+        )
+
+        inputAccessoryToolbar = UIToolbar.stp_inputAccessoryToolbar(
+            withTarget: self,
+            action: #selector(paymentFieldNextTapped)
+        )
+        inputAccessoryToolbar?.stp_setEnabled(false)
+        updateInputAccessoryVisiblity()
+        tableView?.dataSource = self
+        tableView?.delegate = self
+        tableView?.reloadData()
+        if let address = prefilledInformation?.billingAddress {
+            addressViewModel.address = address
+        }
+
+        let addressHeaderView = STPSectionHeaderView()
+        addressHeaderView.theme = theme
+        addressHeaderView.title = String.Localized.billing_address
+        switch configuration?.shippingType {
+        case .shipping:
+            addressHeaderView.button?.setTitle(
+                STPLocalizedString(
+                    "Use Shipping",
+                    "Button to fill billing address from shipping address."
+                ),
+                for: .normal
+            )
+        case .delivery:
+            addressHeaderView.button?.setTitle(
+                STPLocalizedString(
+                    "Use Delivery",
+                    "Button to fill billing address from delivery address."
+                ),
+                for: .normal
+            )
+        default:
+            break
+        }
+        addressHeaderView.button?.addTarget(
+            self,
+            action: #selector(useShippingAddress(_:)),
+            for: .touchUpInside
+        )
+        let requiredFields = configuration?.requiredBillingAddressFields ?? .none
+        let needsAddress = requiredFields != .none && !addressViewModel.isValid
+        let buttonVisible =
+            needsAddress && shippingAddress?.containsContent(for: requiredFields) != nil
+            && !hasUsedShippingAddress
+        addressHeaderView.buttonHidden = !buttonVisible
+        addressHeaderView.setNeedsLayout()
+        self.addressHeaderView = addressHeaderView
+        let cardHeaderView = STPSectionHeaderView()
+        cardHeaderView.theme = theme
+        cardHeaderView.title = STPPaymentMethodType.card.displayName
+        cardHeaderView.buttonHidden = true
+        self.cardHeaderView = cardHeaderView
+
+        // re-set the custom footer view if it was added before we loaded
+        _configureFooterView()
+
+        view.addGestureRecognizer(
+            UITapGestureRecognizer(target: self, action: #selector(endEditing))
+        )
+
+        setUpCardScanningIfAvailable()
+
+        STPAnalyticsClient.sharedClient.clearAdditionalInfo()
+    }
+
+    /// :nodoc:
+    @objc
+    public override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+
+        // Resetting it re-calculates the size based on new view width
+        // UITableView requires us to call setter again to actually pick up frame
+        // change on footers
+        if tableView?.tableFooterView != nil {
+            customFooterView = tableView?.tableFooterView
+        }
+    }
+
+    func setUpCardScanningIfAvailable() {
+        if #available(macCatalyst 14.0, *) {
+            if !STPCardScanner.cardScanningAvailable || configuration?.cardScanningEnabled != true {
+                return
+            }
+            let scannerCell = STPCardScannerTableViewCell()
+            self.scannerCell = scannerCell
+
+            let cardScanner = STPCardScanner(delegate: self)
+            cardScanner.cameraView = scannerCell.cameraView
+            self.cardScanner = cardScanner
+
+            cardHeaderView?.buttonHidden = false
+            cardHeaderView?.button?.setTitle(
+                String.Localized.scan_card_title_capitalization,
+                for: .normal
+            )
+            cardHeaderView?.button?.addTarget(
+                self,
+                action: #selector(scanCard),
+                for: .touchUpInside
+            )
+            cardHeaderView?.setNeedsLayout()
+        }
+    }
+
+    @available(macCatalyst 14.0, *)
+    @objc func scanCard() {
+        view.endEditing(true)
+        isScanning = true
+        cardScanner?.start()
+    }
+
+    @objc func endEditing() {
+        view.endEditing(false)
+    }
+
+    /// :nodoc:
+    @objc
+    public override func updateAppearance() {
+        super.updateAppearance()
+
+        view.backgroundColor = theme.primaryBackgroundColor
+
+        let navBarTheme = navigationController?.navigationBar.stp_theme ?? theme
+        doneItem?.stp_setTheme(navBarTheme)
+        tableView?.allowsSelection = false
+
+        cardImageView?.tintColor = theme.accentColor
+        activityIndicator?.tintColor = theme.accentColor
+
+        paymentCell?.theme = theme
+        cardHeaderView?.theme = theme
+        addressHeaderView?.theme = theme
+        for cell in addressViewModel.addressCells {
+            cell.theme = theme
+        }
+        setNeedsStatusBarAppearanceUpdate()
+    }
+
+    /// :nodoc:
+    @objc
+    public override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        stp_beginObservingKeyboardAndInsettingScrollView(
+            tableView,
+            onChange: nil
+        )
+        firstEmptyField()?.becomeFirstResponder()
+    }
+
+    func firstEmptyField() -> UIResponder? {
+
+        if paymentCell?.isEmpty != nil {
+            return paymentCell!
+        }
+
+        for cell in addressViewModel.addressCells {
+            if cell.contents?.count ?? 0 == 0 {
+                return cell
+            }
+        }
+        return nil
+    }
+
+    /// :nodoc:
+    @objc
+    public override func handleCancelTapped(_ sender: Any?) {
+        delegate?.addCardViewControllerDidCancel(self)
+    }
+
+    @objc func nextPressed(_ sender: Any?) {
+        loading = true
+        guard let cardParams = paymentCell?.paymentField?.paymentMethodParams.card else {
+            return
+        }
+        // Create and return a Payment Method
+        let billingDetails = STPPaymentMethodBillingDetails()
+        if configuration?.requiredBillingAddressFields == .postalCode {
+            let address = STPAddress()
+            address.postalCode = paymentCell?.paymentField?.postalCode
+            billingDetails.address = STPPaymentMethodAddress(address: address)
+        } else {
+            billingDetails.address = STPPaymentMethodAddress(address: addressViewModel.address)
+            billingDetails.email = addressViewModel.address.email
+            billingDetails.name = addressViewModel.address.name
+            billingDetails.phone = addressViewModel.address.phone
+        }
+        let paymentMethodParams = STPPaymentMethodParams(
+            card: cardParams,
+            billingDetails: billingDetails,
+            metadata: nil
+        )
+        apiClient.createPaymentMethod(with: paymentMethodParams) {
+            paymentMethod,
+            createPaymentMethodError in
+            if let createPaymentMethodError = createPaymentMethodError {
+                self.handleError(createPaymentMethodError)
+            } else {
+                if let paymentMethod = paymentMethod {
+                    self.delegate?.addCardViewController(
+                        self,
+                        didCreatePaymentMethod: paymentMethod
+                    ) {
+                        attachToCustomerError in
+                        stpDispatchToMainThreadIfNecessary({
+                            if let attachToCustomerError = attachToCustomerError {
+                                self.handleError(attachToCustomerError)
+                            } else {
+                                self.loading = false
+                            }
+                        })
+                    }
+                }
+            }
+        }
+    }
+
+    func handleError(_ error: Error) {
+        loading = false
+        firstEmptyField()?.becomeFirstResponder()
+
+        let alertController = UIAlertController(
+            title: error.localizedDescription,
+            message: (error as NSError).localizedFailureReason,
+            preferredStyle: .alert
+        )
+
+        alertController.addAction(
+            UIAlertAction(
+                title: String.Localized.ok,
+                style: .cancel,
+                handler: nil
+            )
+        )
+
+        present(alertController, animated: true)
+    }
+
+    func updateDoneButton() {
+        stp_navigationItemProxy?.rightBarButtonItem?.isEnabled =
+            (paymentCell?.paymentField?.isValid ?? false && addressViewModel.isValid)
+            || alwaysEnableDoneButton
+    }
+
+    func updateInputAccessoryVisiblity() {
+        // The inputAccessoryToolbar switches from the paymentCell to the first address field.
+        // It should only be shown when there *is* an address field. This compensates for the lack
+        // of a 'Return' key on the number pad used for paymentCell entry
+        let hasAddressCells = (addressViewModel.addressCells.count) > 0
+        paymentCell?.inputAccessoryView = hasAddressCells ? inputAccessoryToolbar : nil
+    }
+
+    // MARK: - STPPaymentCardTextField
+    @objc
+    public func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
+        inputAccessoryToolbar?.stp_setEnabled(textField.isValid)
+        updateDoneButton()
+    }
+
+    @objc func paymentFieldNextTapped() {
+        _ = addressViewModel.addressCells.stp_boundSafeObject(at: 0)?.becomeFirstResponder()
+    }
+
+    @objc
+    public func paymentCardTextFieldWillEndEditing(forReturn textField: STPPaymentCardTextField) {
+        paymentFieldNextTapped()
+    }
+
+    @objc
+    public func paymentCardTextFieldDidBeginEditingCVC(_ textField: STPPaymentCardTextField) {
+        let isAmex = STPCardValidator.brand(forNumber: textField.cardNumber ?? "") == .amex
+        var newImage: UIImage?
+        var animationTransition: UIView.AnimationOptions
+
+        if isAmex {
+            newImage = STPLegacyImageLibrary.largeCardAmexCVCImage()
+            animationTransition = .transitionCrossDissolve
+        } else {
+            newImage = STPLegacyImageLibrary.largeCardBackImage()
+            animationTransition = .transitionFlipFromRight
+        }
+
+        if let cardImageView = cardImageView {
+            UIView.transition(
+                with: cardImageView,
+                duration: 0.2,
+                options: animationTransition,
+                animations: {
+                    self.cardImageView?.image = newImage
+                }
+            )
+        }
+    }
+
+    @objc
+    public func paymentCardTextFieldDidEndEditingCVC(_ textField: STPPaymentCardTextField) {
+        let isAmex = STPCardValidator.brand(forNumber: textField.cardNumber ?? "") == .amex
+        let animationTransition: UIView.AnimationOptions =
+            isAmex ? .transitionCrossDissolve : .transitionFlipFromLeft
+
+        if let cardImageView = cardImageView {
+            UIView.transition(
+                with: cardImageView,
+                duration: 0.2,
+                options: animationTransition,
+                animations: {
+                    self.cardImageView?.image = STPLegacyImageLibrary.largeCardFrontImage()
+                }
+            )
+        }
+    }
+
+    @objc
+    public func paymentCardTextFieldDidBeginEditing(_ textField: STPPaymentCardTextField) {
+        if #available(macCatalyst 14.0, *) {
+            cardScanner?.stop()
+        }
+    }
+
+    // MARK: - STPAddressViewModelDelegate
+    func addressViewModel(_ addressViewModel: STPAddressViewModel, addedCellAt index: Int) {
+        let indexPath = IndexPath(
+            row: index,
+            section: STPPaymentCardSection.stpPaymentCardBillingAddressSection.rawValue
+        )
+        tableView?.insertRows(at: [indexPath], with: .automatic)
+        updateInputAccessoryVisiblity()
+    }
+
+    func addressViewModel(_ addressViewModel: STPAddressViewModel, removedCellAt index: Int) {
+        let indexPath = IndexPath(
+            row: Int(index),
+            section: STPPaymentCardSection.stpPaymentCardBillingAddressSection.rawValue
+        )
+        tableView?.deleteRows(at: [indexPath], with: .automatic)
+        updateInputAccessoryVisiblity()
+    }
+
+    func addressViewModelDidChange(_ addressViewModel: STPAddressViewModel) {
+        updateDoneButton()
+    }
+
+    func addressViewModelWillUpdate(_ addressViewModel: STPAddressViewModel) {
+        tableView?.beginUpdates()
+    }
+
+    func addressViewModelDidUpdate(_ addressViewModel: STPAddressViewModel) {
+        tableView?.endUpdates()
+    }
+
+    // MARK: - UITableView
+    /// :nodoc:
+    @objc
+    public func numberOfSections(in tableView: UITableView) -> Int {
+        return 3
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if section == STPPaymentCardSection.stpPaymentCardNumberSection.rawValue {
+            return 1
+        } else if section == STPPaymentCardSection.stpPaymentCardScannerSection.rawValue {
+            return isScanning ? 1 : 0
+        } else if section == STPPaymentCardSection.stpPaymentCardBillingAddressSection.rawValue {
+            return addressViewModel.addressCells.count
+        }
+        return 0
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        cellForRowAt indexPath: IndexPath
+    ) -> UITableViewCell {
+        var cell: UITableViewCell?
+        switch indexPath.section {
+        case STPPaymentCardSection.stpPaymentCardNumberSection.rawValue:
+            cell = paymentCell
+        case STPPaymentCardSection.stpPaymentCardScannerSection.rawValue:
+            if #available(macCatalyst 14.0, *) {
+                cell = scannerCell
+            } else {
+                return UITableViewCell()
+            }
+        case STPPaymentCardSection.stpPaymentCardBillingAddressSection.rawValue:
+            cell = addressViewModel.addressCells.stp_boundSafeObject(at: indexPath.row)
+        default:
+            return UITableViewCell()  // won't be called; exists to make the static analyzer happy
+        }
+        cell?.backgroundColor = theme.secondaryBackgroundColor
+        cell?.contentView.backgroundColor = UIColor.clear
+        return cell!
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        willDisplay cell: UITableViewCell,
+        forRowAt indexPath: IndexPath
+    ) {
+        let topRow = indexPath.row == 0
+        let bottomRow =
+            self.tableView(tableView, numberOfRowsInSection: indexPath.section) - 1 == indexPath.row
+        cell.stp_setBorderColor(theme.tertiaryBackgroundColor)
+        cell.stp_setTopBorderHidden(!topRow)
+        cell.stp_setBottomBorderHidden(!bottomRow)
+        cell.stp_setFakeSeparatorColor(theme.quaternaryBackgroundColor)
+        cell.stp_setFakeSeparatorLeftInset(15.0)
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        heightForFooterInSection section: Int
+    )
+        -> CGFloat
+    {
+        if self.tableView(tableView, numberOfRowsInSection: section) == 0 {
+            return 0.01
+        }
+        return 27.0
+    }
+
+    /// :nodoc:
+    @objc
+    public override func tableView(
+        _ tableView: UITableView,
+        heightForHeaderInSection section: Int
+    ) -> CGFloat {
+        let fittingSize = CGSize(
+            width: view.bounds.size.width,
+            height: CGFloat.greatestFiniteMagnitude
+        )
+        let numberOfRows = self.tableView(tableView, numberOfRowsInSection: section)
+        if section == STPPaymentCardSection.stpPaymentCardNumberSection.rawValue {
+            return cardHeaderView?.sizeThatFits(fittingSize).height ?? 0.0
+        } else if section == STPPaymentCardSection.stpPaymentCardBillingAddressSection.rawValue
+            && numberOfRows != 0
+        {
+            return addressHeaderView?.sizeThatFits(fittingSize).height ?? 0.0
+        } else if section == STPPaymentCardSection.stpPaymentCardScannerSection.rawValue {
+            return 0.01
+        } else if numberOfRows != 0 {
+            return tableView.sectionHeaderHeight
+        }
+        return 0.01
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        viewForHeaderInSection section: Int
+    )
+        -> UIView?
+    {
+        if self.tableView(tableView, numberOfRowsInSection: section) == 0 {
+            return UIView()
+        } else {
+            if section == STPPaymentCardSection.stpPaymentCardNumberSection.rawValue {
+                return cardHeaderView
+            } else if section == STPPaymentCardSection.stpPaymentCardBillingAddressSection.rawValue
+            {
+                return addressHeaderView
+            }
+        }
+        return nil
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        viewForFooterInSection section: Int
+    )
+        -> UIView?
+    {
+        return UIView()
+    }
+
+    @objc func useShippingAddress(_ sender: UIButton) {
+        tableView?.beginUpdates()
+        addressViewModel.address = shippingAddress ?? STPAddress()
+        hasUsedShippingAddress = true
+        firstEmptyField()?.becomeFirstResponder()
+        UIView.animate(
+            withDuration: 0.2,
+            animations: {
+                self.addressHeaderView?.buttonHidden = true
+            }
+        )
+        tableView?.endUpdates()
+    }
+
+    // MARK: - STPCardScanner
+    /// :nodoc:
+    @objc
+    public override func viewWillTransition(
+        to size: CGSize,
+        with coordinator: UIViewControllerTransitionCoordinator
+    ) {
+        super.viewWillTransition(to: size, with: coordinator)
+        let orientation = UIDevice.current.orientation
+        if orientation.isPortrait || orientation.isLandscape {
+            if #available(macCatalyst 14.0, *) {
+                cardScanner?.deviceOrientation = orientation
+            }
+        }
+        if isScanning {
+            let indexPath = IndexPath(
+                row: 0,
+                section: STPPaymentCardSection.stpPaymentCardScannerSection.rawValue
+            )
+            DispatchQueue.main.async(execute: {
+                self.tableView?.scrollToRow(at: indexPath, at: .middle, animated: true)
+            })
+        }
+    }
+
+    static let cardScannerKSTPCardScanAnimationTime: TimeInterval = 0.04
+
+    @available(macCatalyst 14.0, *)
+    func cardScanner(
+        _ scanner: STPCardScanner,
+        didFinishWith cardParams: STPPaymentMethodCardParams?,
+        error: Error?
+    ) {
+        if let error = error {
+            handleError(error)
+        }
+        if let cardParams = cardParams {
+            view.isUserInteractionEnabled = false
+            paymentCell?.paymentField?.inputView = UIView() as? UIInputView
+            var i = 0
+            scannerCompleteAnimationTimer = Timer.scheduledTimer(
+                withTimeInterval: STPAddCardViewController.cardScannerKSTPCardScanAnimationTime,
+                repeats: true,
+                block: { timer in
+                    i += 1
+                    let newParams = STPPaymentMethodCardParams()
+                    guard let number = cardParams.number else {
+                        timer.invalidate()
+                        self.view.isUserInteractionEnabled = false
+                        return
+                    }
+                    if i < number.count {
+                        newParams.number = String(
+                            number[...number.index(number.startIndex, offsetBy: i)]
+                        )
+                    } else {
+                        newParams.number = number
+                    }
+                    self.paymentCell?.paymentField?.paymentMethodParams = STPPaymentMethodParams(
+                        card: newParams,
+                        billingDetails: nil,
+                        metadata: nil
+                    )
+                    if i > number.count {
+                        self.paymentCell?.paymentField?.paymentMethodParams =
+                            STPPaymentMethodParams(
+                                card: cardParams,
+                                billingDetails: nil,
+                                metadata: nil
+                            )
+                        self.isScanning = false
+                        self.paymentCell?.paymentField?.inputView = nil
+                        // Force the inputView to reload by asking the text field to resign/become first responder:
+                        _ = self.paymentCell?.paymentField?.resignFirstResponder()
+                        _ = self.paymentCell?.paymentField?.becomeFirstResponder()
+                        timer.invalidate()
+                        self.view.isUserInteractionEnabled = true
+                    }
+                }
+            )
+        } else {
+            isScanning = false
+        }
+    }
+
+}
+
+/// An `STPAddCardViewControllerDelegate` is notified when an `STPAddCardViewController`
+/// successfully creates a card token or is cancelled. It has internal error-handling
+/// logic, so there's no error case to deal with.
+@objc public protocol STPAddCardViewControllerDelegate: NSObjectProtocol {
+    /// Called when the user cancels adding a card. You should dismiss (or pop) the
+    /// view controller at this point.
+    /// - Parameter addCardViewController: the view controller that has been cancelled
+    func addCardViewControllerDidCancel(_ addCardViewController: STPAddCardViewController)
+
+    /// This is called when the user successfully adds a card and Stripe returns a
+    /// Payment Method.
+    /// You should send the PaymentMethod to your backend to store it on a customer, and then
+    /// call the provided `completion` block when that call is finished. If an error
+    /// occurs while talking to your backend, call `completion(error)`, otherwise,
+    /// dismiss (or pop) the view controller.
+    /// - Parameters:
+    ///   - addCardViewController: the view controller that successfully created a token
+    ///   - paymentMethod:         the Payment Method that was created. - seealso: STPPaymentMethod
+    ///   - completion:            call this callback when you're done sending the token to your backend
+    @objc func addCardViewController(
+        _ addCardViewController: STPAddCardViewController,
+        didCreatePaymentMethod paymentMethod: STPPaymentMethod,
+        completion: @escaping STPErrorBlock
+    )
+
+    // MARK: - Deprecated
+
+    /// This method is deprecated as of v16.0.0 (https://github.com/stripe/stripe-ios/blob/master/MIGRATING.md#migrating-from-versions--1600).
+    /// To use this class, migrate your integration from Charges to PaymentIntents. See https://stripe.com/docs/payments/payment-intents/migration/charges#read
+    @available(
+        *,
+        deprecated,
+        message:
+            "Use addCardViewController(_:didCreatePaymentMethod:completion:) instead and migrate your integration to PaymentIntents. See https://stripe.com/docs/payments/payment-intents/migration/charges#read",
+        renamed: "addCardViewController(_:didCreatePaymentMethod:completion:)"
+    )
+    @objc optional func addCardViewController(
+        _ addCardViewController: STPAddCardViewController,
+        didCreateToken token: STPToken,
+        completion: STPErrorBlock
+    )
+    /// This method is deprecated as of v16.0.0 (https://github.com/stripe/stripe-ios/blob/master/MIGRATING.md#migrating-from-versions--1600).
+    /// To use this class, migrate your integration from Charges to PaymentIntents. See https://stripe.com/docs/payments/payment-intents/migration/charges#read
+    @available(
+        *,
+        deprecated,
+        message:
+            "Use addCardViewController(_:didCreatePaymentMethod:completion:) instead and migrate your integration to PaymentIntents. See https://stripe.com/docs/payments/payment-intents/migration/charges#read",
+        renamed: "addCardViewController(_:didCreatePaymentMethod:completion:)"
+    )
+    @objc optional func addCardViewController(
+        _ addCardViewController: STPAddCardViewController,
+        didCreateSource source: STPSource,
+        completion: STPErrorBlock
+    )
+}
+
+private let STPPaymentCardCellReuseIdentifier = "STPPaymentCardCellReuseIdentifier"
+enum STPPaymentCardSection: Int {
+    case stpPaymentCardNumberSection = 0
+    case stpPaymentCardScannerSection = 1
+    case stpPaymentCardBillingAddressSection = 2
+}
+
+/// :nodoc:
+@_spi(STP) extension STPAddCardViewController: STPAnalyticsProtocol {
+    @_spi(STP) public static let stp_analyticsIdentifier = "STPAddCardViewController"
+}

+ 212 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAddress+BasicUI.swift

@@ -0,0 +1,212 @@
+//
+//  STPAddress+BasicUI.swift
+//  StripeiOS
+//
+//  Created by David Estes on 6/30/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+import PassKit
+@_spi(STP) import StripePayments
+@_spi(STP) import StripePaymentsUI
+@_spi(STP) import StripeUICore
+
+/// What set of billing address information you need to collect from your user.
+///
+/// @note If the user is from a country that does not use zip/postal codes,
+/// the user may not be asked for one regardless of this setting.
+@objc
+public enum STPBillingAddressFields: UInt {
+    /// No billing address information
+    case none
+    /// Just request the user's billing postal code
+    case postalCode
+    /// Request the user's full billing address
+    case full
+    /// Just request the user's billing name
+    case name
+    /// Just request the user's billing ZIP (synonym for STPBillingAddressFieldsZip)
+    @available(*, deprecated, message: "Use STPBillingAddressFields.postalCode instead")
+    case zip
+}
+
+extension STPAddress {
+
+    /// Checks if this STPAddress has the level of valid address information
+    /// required by the passed in setting.
+    /// - Parameter requiredFields: The required level of billing address information to
+    /// check against.
+    /// - Returns: YES if this address contains at least the necessary information,
+    /// NO otherwise.
+    @objc
+    public func containsRequiredFields(_ requiredFields: STPBillingAddressFields) -> Bool {
+        switch requiredFields {
+        case .none:
+            return true
+        case .postalCode:
+            return STPPostalCodeValidator.validationState(
+                forPostalCode: postalCode,
+                countryCode: country
+            ) == .valid
+        case .full:
+            return hasValidPostalAddress()
+        case .name:
+            return (name?.count ?? 0) > 0
+        default:
+            fatalError()
+        }
+    }
+
+    /// Checks if this STPAddress has any content (possibly invalid) in any of the
+    /// desired billing address fields.
+    /// Where `containsRequiredFields:` validates that this STPAddress contains valid data in
+    /// all of the required fields, this method checks for the existence of *any* data.
+    /// For example, if `desiredFields` is `STPBillingAddressFieldsZip`, this will check
+    /// if the postalCode is empty.
+    /// Note: When `desiredFields == STPBillingAddressFieldsNone`, this method always returns
+    /// NO.
+    /// @parameter desiredFields The billing address information the caller is interested in.
+    /// - Returns: YES if there is any data in this STPAddress that's relevant for those fields.
+    @objc(containsContentForBillingAddressFields:)
+    public func containsContent(for desiredFields: STPBillingAddressFields) -> Bool {
+        switch desiredFields {
+        case .none:
+            return false
+        case .postalCode:
+            return (postalCode?.count ?? 0) > 0
+        case .full:
+            return hasPartialPostalAddress()
+        case .name:
+            return (name?.count ?? 0) > 0
+        default:
+            fatalError()
+        }
+    }
+
+    /// Checks if this STPAddress has the level of valid address information
+    /// required by the passed in setting.
+    /// Note: When `requiredFields == nil`, this method always returns
+    /// YES.
+    /// - Parameter requiredFields: The required shipping address information to check against.
+    /// - Returns: YES if this address contains at least the necessary information,
+    /// NO otherwise.
+    @objc
+    public func containsRequiredShippingAddressFields(
+        _ requiredFields: Set<STPContactField>?
+    )
+        -> Bool
+    {
+        guard let requiredFields = requiredFields else {
+            return true
+        }
+        var containsFields = true
+
+        if requiredFields.contains(.name) {
+            containsFields = containsFields && (name?.count ?? 0) > 0
+        }
+        if requiredFields.contains(.emailAddress) {
+            containsFields =
+                containsFields && STPEmailAddressValidator.stringIsValidEmailAddress(email)
+        }
+        if requiredFields.contains(.phoneNumber) {
+            containsFields =
+                containsFields
+                && STPPhoneNumberValidator.stringIsValidPhoneNumber(
+                    phone ?? "",
+                    forCountryCode: country
+                )
+        }
+        if requiredFields.contains(.postalAddress) {
+            containsFields = containsFields && hasValidPostalAddress()
+        }
+        return containsFields
+    }
+
+    /// Checks if this STPAddress has any content (possibly invalid) in any of the
+    /// desired shipping address fields.
+    /// Where `containsRequiredShippingAddressFields:` validates that this STPAddress
+    /// contains valid data in all of the required fields, this method checks for the
+    /// existence of *any* data.
+    /// Note: When `desiredFields == nil`, this method always returns
+    /// NO.
+    /// @parameter desiredFields The shipping address information the caller is interested in.
+    /// - Returns: YES if there is any data in this STPAddress that's relevant for those fields.
+    @objc
+    public func containsContent(
+        forShippingAddressFields desiredFields: Set<STPContactField>?
+    )
+        -> Bool
+    {
+        guard let desiredFields = desiredFields else {
+            return false
+        }
+        return (desiredFields.contains(.name) && (name?.count ?? 0) > 0)
+            || (desiredFields.contains(.emailAddress) && (email?.count ?? 0) > 0)
+            || (desiredFields.contains(.phoneNumber) && (phone?.count ?? 0) > 0)
+            || (desiredFields.contains(.postalAddress) && hasPartialPostalAddress())
+    }
+
+    /// Converts an STPBillingAddressFields enum value into the closest equivalent
+    /// representation of PKContactField options
+    /// - Parameter billingAddressFields: Stripe billing address fields enum value to convert.
+    /// - Returns: The closest representation of the billing address requirement as
+    /// a PKContactField value.
+    @objc(applePayContactFieldsFromBillingAddressFields:)
+    public class func applePayContactFields(
+        from billingAddressFields: STPBillingAddressFields
+    )
+        -> Set<PKContactField>
+    {
+        switch billingAddressFields {
+        case .none:
+            return Set<PKContactField>([])
+        case .postalCode, .full:
+            return Set<PKContactField>([.name, .postalAddress])
+        case .name:
+            return Set<PKContactField>([.name])
+        case .zip:
+            return Set()
+        @unknown default:
+            fatalError()
+        }
+    }
+
+    /// Converts a set of STPContactField values into the closest equivalent
+    /// representation of PKContactField options
+    /// - Parameter contactFields: Stripe contact fields values to convert.
+    /// - Returns: The closest representation of the contact fields as
+    /// a PKContactField value.
+    @objc
+    public class func pkContactFields(
+        fromStripeContactFields contactFields: Set<STPContactField>?
+    ) -> Set<PKContactField>? {
+        guard let contactFields = contactFields else {
+            return nil
+        }
+
+        var pkFields: Set<PKContactField> = Set()
+        let stripeToPayKitContactMap: [STPContactField: PKContactField] = [
+            STPContactField.postalAddress: PKContactField.postalAddress,
+            STPContactField.emailAddress: PKContactField.emailAddress,
+            STPContactField.phoneNumber: PKContactField.phoneNumber,
+            STPContactField.name: PKContactField.name,
+        ]
+
+        for contactField in contactFields {
+            if let convertedField = stripeToPayKitContactMap[contactField] {
+                pkFields.insert(convertedField)
+            }
+        }
+        return pkFields
+    }
+
+    private func hasValidPostalAddress() -> Bool {
+        return (line1?.count ?? 0) > 0 && (city?.count ?? 0) > 0 && (country?.count ?? 0) > 0
+            && ((state?.count ?? 0) > 0 || !(country == "US"))
+            && (STPPostalCodeValidator.validationState(
+                forPostalCode: postalCode,
+                countryCode: country
+            ) == .valid)
+    }
+}

+ 509 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAddressFieldTableViewCell.swift

@@ -0,0 +1,509 @@
+//
+//  STPAddressFieldTableViewCell.swift
+//  StripeiOS
+//
+//  Created by Ben Guo on 4/13/16.
+//  Copyright © 2016 Stripe, Inc. All rights reserved.
+//
+
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePaymentsUI
+@_spi(STP) import StripeUICore
+import UIKit
+
+enum STPAddressFieldType: Int {
+    case name
+    case line1
+    case line2
+    case city
+    case state
+    case zip
+    case country
+    case email
+    case phone
+}
+
+protocol STPAddressFieldTableViewCellDelegate: AnyObject {
+    func addressFieldTableViewCellDidUpdateText(_ cell: STPAddressFieldTableViewCell)
+
+    func addressFieldTableViewCellDidReturn(_ cell: STPAddressFieldTableViewCell)
+    func addressFieldTableViewCellDidEndEditing(_ cell: STPAddressFieldTableViewCell)
+    var addressFieldTableViewCountryCode: String? { get set }
+    var availableCountries: Set<String>? { get set }
+}
+
+class STPAddressFieldTableViewCell: UITableViewCell, UITextFieldDelegate, UIPickerViewDelegate,
+    UIPickerViewDataSource
+{
+
+    init(
+        type: STPAddressFieldType,
+        contents: String?,
+        lastInList: Bool,
+        delegate: STPAddressFieldTableViewCellDelegate?
+    ) {
+        textField = {
+            if type == .phone {
+                // We have very specific US-based phone formatting that's built into STPFormTextField
+                let formTextField = STPFormTextField()
+                formTextField.preservesContentsOnPaste = false
+                formTextField.selectionEnabled = false
+                return formTextField
+            } else {
+                return STPValidatedTextField()
+            }
+        }()
+
+        super.init(style: .default, reuseIdentifier: nil)
+        self.delegate = delegate
+        theme = STPTheme()
+        _contents = contents
+
+        textField.delegate = self
+        textField.addTarget(
+            self,
+            action: #selector(STPAddressFieldTableViewCell.textFieldTextDidChange(textField:)),
+            for: .editingChanged
+        )
+        contentView.addSubview(textField)
+
+        let toolbar = UIToolbar()
+        let flexibleItem = UIBarButtonItem(
+            barButtonSystemItem: .flexibleSpace,
+            target: nil,
+            action: nil
+        )
+        let nextItem = UIBarButtonItem(
+            title: STPLocalizedString("Next", nil),
+            style: .done,
+            target: self,
+            action: #selector(nextTapped(sender:))
+        )
+        toolbar.items = [flexibleItem, nextItem]
+        inputAccessoryToolbar = toolbar
+
+        var countryCode = NSLocale.autoupdatingCurrent.regionCode
+        var otherCountryCodes = Array(
+            self.delegate?.availableCountries ?? Set(NSLocale.isoCountryCodes)
+        )
+        if otherCountryCodes.contains(countryCode ?? "") {
+            // Remove the current country code to re-add it once we sort the list.
+            otherCountryCodes.removeAll { $0 == countryCode }
+        } else {
+            // If it isn't in the list (if we've been configured to not show that country), don't re-add it.
+            countryCode = nil
+        }
+        let locale = NSLocale.current as NSLocale
+        otherCountryCodes =
+            (otherCountryCodes as NSArray).sortedArray(comparator: { code1, code2 in
+                guard let code1 = code1 as? String, let code2 = code2 as? String else {
+                    return .orderedDescending
+                }
+                let localeID1 = NSLocale.localeIdentifier(fromComponents: [
+                    NSLocale.Key.countryCode.rawValue: code1
+                ])
+                let localeID2 = NSLocale.localeIdentifier(fromComponents: [
+                    NSLocale.Key.countryCode.rawValue: code2
+                ])
+                if let name1 = locale.displayName(forKey: .identifier, value: localeID1),
+                    let name2 = locale.displayName(forKey: .identifier, value: localeID2)
+                {
+                    return name1.compare(name2)
+                } else {
+                    return .orderedDescending
+                }
+            }) as? [String] ?? []
+        if let countryCode = countryCode {
+            countryCodes = ["", countryCode] + otherCountryCodes
+        } else {
+            countryCodes = [""] + otherCountryCodes
+        }
+        let pickerView = UIPickerView()
+        pickerView.dataSource = self
+        pickerView.delegate = self
+        countryPickerView = pickerView
+
+        self.lastInList = lastInList
+        self.type = type
+        self.textField.text = contents
+
+        var ourCountryCode = self.delegate?.addressFieldTableViewCountryCode
+
+        if ourCountryCode == nil {
+            ourCountryCode = countryCode
+        }
+        delegateCountryCodeDidChange(countryCode: ourCountryCode ?? "")
+        updateAppearance()
+
+        self.textField.translatesAutoresizingMaskIntoConstraints = false
+
+        NSLayoutConstraint.activate(
+            [
+                textField.leadingAnchor.constraint(
+                    equalTo: contentView.safeAreaLayoutGuide.leadingAnchor,
+                    constant: 15
+                ),
+                textField.trailingAnchor.constraint(
+                    equalTo: contentView.safeAreaLayoutGuide.trailingAnchor,
+                    constant: -15
+                ),
+                textField.topAnchor.constraint(
+                    equalTo: contentView.safeAreaLayoutGuide.topAnchor,
+                    constant: 1
+                ),
+                contentView.safeAreaLayoutGuide.bottomAnchor.constraint(
+                    greaterThanOrEqualTo: textField.bottomAnchor
+                ),
+                textField.heightAnchor.constraint(greaterThanOrEqualToConstant: 43),
+                inputAccessoryToolbar?.heightAnchor.constraint(equalToConstant: 44),
+            ].compactMap { $0 }
+        )
+    }
+
+    var type: STPAddressFieldType = .name
+    var caption: String? {
+        get {
+            self.textField.placeholder
+        }
+        set {
+            self.textField.placeholder = newValue
+        }
+    }
+
+    private(set) var textField: STPValidatedTextField
+    private var _contents: String?
+    var contents: String? {
+        get {
+            // iOS 11 QuickType completions from textContentType have a space at the end.
+            // This *keeps* that space in the `textField`, but removes leading/trailing spaces from
+            // the logical contents of this field, so they're ignored for validation and persisting
+            return _contents?.trimmingCharacters(in: CharacterSet.whitespaces)
+        }
+        set {
+            _contents = newValue
+            if self.type == .country {
+                self.updateTextFieldsAndCaptions()
+            } else {
+                self.textField.text = contents
+            }
+            if self.textField.isFirstResponder {
+                self.textField.validText = self.potentiallyValidContents
+            } else {
+                self.textField.validText = self.validContents
+            }
+            self.delegate?.addressFieldTableViewCellDidUpdateText(self)
+        }
+    }
+
+    var theme: STPTheme = .defaultTheme {
+        didSet {
+            updateAppearance()
+        }
+    }
+
+    var lastInList: Bool = false {
+        didSet {
+            updateTextFieldsAndCaptions()
+        }
+    }
+
+    private var inputAccessoryToolbar: UIToolbar?
+    private var countryPickerView: UIPickerView?
+    private var countryCodes: [AnyHashable]?
+    private weak var delegate: STPAddressFieldTableViewCellDelegate?
+    private var ourCountryCode: String?
+
+    func updateTextFieldsAndCaptions() {
+        textField.placeholder = placeholder(for: type)
+
+        if !lastInList {
+            textField.returnKeyType = .next
+        } else {
+            textField.returnKeyType = .default
+        }
+        switch type {
+        case .name:
+            textField.keyboardType = .default
+            textField.textContentType = .name
+        case .line1:
+            textField.keyboardType = .numbersAndPunctuation
+            textField.textContentType = .streetAddressLine1
+        case .line2:
+            textField.keyboardType = .numbersAndPunctuation
+            textField.textContentType = .streetAddressLine2
+        case .city:
+            textField.keyboardType = .default
+            textField.textContentType = .addressCity
+        case .state:
+            textField.keyboardType = .default
+            textField.textContentType = .addressState
+        case .zip:
+            textField.keyboardType = .numbersAndPunctuation
+            textField.textContentType = .postalCode
+        case .country:
+            textField.keyboardType = .default
+            // Don't set textContentType for Country, because we don't want iOS to skip the UIPickerView for input
+            textField.inputView = countryPickerView
+            // If we're being set directly to a country we don't allow, add it to the allowed list
+            let countryCodes = self.countryCodes ?? []
+            if let contents = contents,
+                !countryCodes.contains(contents) && NSLocale.isoCountryCodes.contains(contents)
+            {
+                self.countryCodes = countryCodes + [contents]
+            }
+            let index = countryCodes.firstIndex(of: contents ?? "") ?? NSNotFound
+            if index == NSNotFound {
+                textField.text = ""
+            } else {
+                countryPickerView?.selectRow(index, inComponent: 0, animated: false)
+                if let countryPickerView = countryPickerView {
+                    textField.text = pickerView(
+                        countryPickerView,
+                        titleForRow: index,
+                        forComponent: 0
+                    )
+                }
+            }
+            textField.validText = validContents
+        case .phone:
+            self.textField.keyboardType = .numbersAndPunctuation
+            self.textField.textContentType = .telephoneNumber
+            let behavior: STPFormTextFieldAutoFormattingBehavior =
+                (self.countryCodeIsUnitedStates ? .phoneNumbers : .none)
+            (self.textField as? STPFormTextField)?.autoFormattingBehavior = behavior
+        case .email:
+            self.textField.keyboardType = .emailAddress
+            self.textField.textContentType = .emailAddress
+        }
+
+        if !self.lastInList {
+            self.textField.inputAccessoryView = self.inputAccessoryToolbar
+        } else {
+            self.textField.inputAccessoryView = nil
+        }
+        self.textField.accessibilityLabel = self.textField.placeholder
+        self.textField.accessibilityIdentifier = self.accessibilityIdentifierForAddressField(
+            type: self.type
+        )
+    }
+
+    func accessibilityIdentifierForAddressField(type: STPAddressFieldType) -> String {
+        switch type {
+        case .name:
+            return "ShippingAddressFieldTypeNameIdentifier"
+        case .line1:
+            return "ShippingAddressFieldTypeLine1Identifier"
+        case .line2:
+            return "ShippingAddressFieldTypeLine2Identifier"
+        case .city:
+            return "ShippingAddressFieldTypeCityIdentifier"
+        case .state:
+            return "ShippingAddressFieldTypeStateIdentifier"
+        case .zip:
+            return "ShippingAddressFieldTypeZipIdentifier"
+        case .country:
+            return "ShippingAddressFieldTypeCountryIdentifier"
+        case .email:
+            return "ShippingAddressFieldTypeEmailIdentifier"
+        case .phone:
+            return "ShippingAddressFieldTypePhoneIdentifier"
+        }
+    }
+
+    func stateFieldCaption(forCountryCode countryCode: String?) -> String {
+        return StripeSharedStrings.localizedStateString(for: countryCode)
+    }
+
+    func placeholder(for addressFieldType: STPAddressFieldType) -> String {
+        switch addressFieldType {
+        case .name:
+            return String.Localized.name
+        case .line1:
+            return String.Localized.address
+        case .line2:
+            return STPLocalizedString(
+                "Apt.",
+                "Caption for Apartment/Address line 2 field on address form"
+            )
+        case .city:
+            return String.Localized.city
+        case .state:
+            return stateFieldCaption(forCountryCode: self.ourCountryCode)
+        case .zip:
+            return StripeSharedStrings.localizedPostalCodeString(for: self.ourCountryCode)
+        case .country:
+            return String.Localized.country
+        case .email:
+            return String.Localized.email
+        case .phone:
+            return String.Localized.phone
+        }
+    }
+
+    func delegateCountryCodeDidChange(countryCode: String) {
+        if self.type == .country {
+            self.contents = countryCode
+        }
+
+        self.ourCountryCode = countryCode
+        self.updateTextFieldsAndCaptions()
+        self.setNeedsLayout()
+    }
+
+    func updateAppearance() {
+        self.backgroundColor = self.theme.secondaryBackgroundColor
+        self.contentView.backgroundColor = .clear
+        self.textField.placeholderColor = theme.tertiaryForegroundColor
+        self.textField.defaultColor = theme.primaryForegroundColor
+        self.textField.errorColor = self.theme.errorColor
+        self.textField.font = self.theme.font
+        self.setNeedsLayout()
+    }
+
+    var countryCodeIsUnitedStates: Bool {
+        self.ourCountryCode == "US"
+    }
+
+    public override func becomeFirstResponder() -> Bool {
+        return self.textField.becomeFirstResponder()
+    }
+
+    @objc func nextTapped(sender: NSObject) {
+        delegate?.addressFieldTableViewCellDidReturn(self)
+    }
+
+    @objc
+    public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+        self.delegate?.addressFieldTableViewCellDidReturn(self)
+        return false
+    }
+
+    @objc
+    public func textFieldDidEndEditing(_ textField: UITextField) {
+        (textField as? STPFormTextField)?.validText = validContents
+        self.delegate?.addressFieldTableViewCellDidEndEditing(self)
+    }
+
+    public override func accessibilityElementCount() -> Int {
+        return 1
+    }
+
+    public override func accessibilityElement(at index: Int) -> Any? {
+        return textField
+    }
+
+    public override func index(ofAccessibilityElement element: Any) -> Int {
+        return 0
+    }
+
+    @objc func textFieldTextDidChange(textField: STPValidatedTextField) {
+        if self.type != .country {
+            _contents = textField.text
+            if textField.isFirstResponder {
+                textField.validText = self.potentiallyValidContents
+            } else {
+                textField.validText = self.validContents
+            }
+        }
+        self.delegate?.addressFieldTableViewCellDidUpdateText(self)
+    }
+
+    // pragma mark - UITextFieldDelegate
+
+    var validContents: Bool {
+        switch self.type {
+        case .name, .line1, .city, .state, .country:
+            return self.contents?.count ?? 0 > 0
+        case .line2:
+            return true
+        case .zip:
+            return STPPostalCodeValidator.validationState(
+                forPostalCode: self.contents,
+                countryCode: self.ourCountryCode
+            ) == .valid
+        case .email:
+            return STPEmailAddressValidator.stringIsValidEmailAddress(self.contents)
+        case .phone:
+            return STPPhoneNumberValidator.stringIsValidPhoneNumber(
+                self.contents ?? "",
+                forCountryCode: self.ourCountryCode
+            )
+        }
+    }
+
+    var potentiallyValidContents: Bool {
+        switch self.type {
+        case .name, .line1, .city, .state, .country, .line2, .phone:
+            return true
+        case .zip:
+            let validationState = STPPostalCodeValidator.validationState(
+                forPostalCode: self.contents,
+                countryCode: self.ourCountryCode
+            )
+            return validationState == .valid || validationState == .incomplete
+        case .email:
+            return STPEmailAddressValidator.stringIsValidPartialEmailAddress(self.contents)
+        }
+    }
+
+    public func pickerView(
+        _ pickerView: UIPickerView,
+        didSelectRow row: Int,
+        inComponent component: Int
+    ) {
+        guard let countryCode = self.countryCodes?[row] as? String
+        else {
+            return
+        }
+        self.ourCountryCode = countryCode
+        self.contents = self.ourCountryCode
+        textField.text = self.pickerView(pickerView, titleForRow: row, forComponent: component)
+        // UIControlEvent not fired for programmatic changes
+        self.textFieldTextDidChange(textField: textField)
+        self.delegate?.addressFieldTableViewCountryCode = self.ourCountryCode ?? ""
+
+    }
+
+    public func pickerView(
+        _ pickerView: UIPickerView,
+        titleForRow row: Int,
+        forComponent component: Int
+    ) -> String? {
+        guard let countryCode = self.countryCodes?[row] as? String else {
+            return nil
+        }
+        let identifier = Locale.identifier(fromComponents: [
+            NSLocale.Key.countryCode.rawValue: countryCode
+        ])
+        return (NSLocale.autoupdatingCurrent as NSLocale).displayName(
+            forKey: NSLocale.Key(rawValue: NSLocale.Key.identifier.rawValue),
+            value: identifier
+        )
+    }
+
+    public func numberOfComponents(in pickerView: UIPickerView) -> Int {
+        return 1
+    }
+
+    public func pickerView(
+        _ pickerView: UIPickerView,
+        numberOfRowsInComponent component: Int
+    )
+        -> Int
+    {
+        self.countryCodes?.count ?? 0
+    }
+
+    required convenience init?(
+        coder aDecoder: NSCoder
+    ) {
+        assertionFailure("Use initWithType: instead.")
+        self.init(
+            type: .name,
+            contents: nil,
+            lastInList: false,
+            delegate: nil
+        )
+    }
+
+}

+ 434 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAddressViewModel.swift

@@ -0,0 +1,434 @@
+//
+//  STPAddressViewModel.swift
+//  StripeiOS
+//
+//  Created by Jack Flintermann on 4/21/16.
+//  Copyright © 2016 Stripe, Inc. All rights reserved.
+//
+
+import Contacts
+import CoreLocation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePaymentsUI
+import UIKit
+
+protocol STPAddressViewModelDelegate: AnyObject {
+    func addressViewModelDidChange(_ addressViewModel: STPAddressViewModel)
+    func addressViewModel(_ addressViewModel: STPAddressViewModel, addedCellAt index: Int)
+    func addressViewModel(_ addressViewModel: STPAddressViewModel, removedCellAt index: Int)
+    func addressViewModelWillUpdate(_ addressViewModel: STPAddressViewModel)
+    func addressViewModelDidUpdate(_ addressViewModel: STPAddressViewModel)
+}
+
+class STPAddressViewModel: STPAddressFieldTableViewCellDelegate {
+    private(set) var addressCells: [STPAddressFieldTableViewCell] = []
+    weak var delegate: STPAddressViewModelDelegate?
+
+    var addressFieldTableViewCountryCode: String? = Locale.autoupdatingCurrent.regionCode {
+        didSet {
+            updatePostalCodeCellIfNecessary()
+            if let addressFieldTableViewCountryCode = addressFieldTableViewCountryCode {
+                for cell in addressCells {
+                    cell.delegateCountryCodeDidChange(countryCode: addressFieldTableViewCountryCode)
+                }
+            }
+        }
+    }
+
+    var address: STPAddress {
+        get {
+            let address = STPAddress()
+            for cell in addressCells {
+
+                switch cell.type {
+                case .name:
+                    address.name = cell.contents
+                case .line1:
+                    address.line1 = cell.contents
+                case .line2:
+                    address.line2 = cell.contents
+                case .city:
+                    address.city = cell.contents
+                case .state:
+                    address.state = cell.contents
+                case .zip:
+                    address.postalCode = cell.contents
+                case .country:
+                    address.country = cell.contents
+                case .email:
+                    address.email = cell.contents
+                case .phone:
+                    address.phone = cell.contents
+                }
+            }
+            // Prefer to use the contents of STPAddressFieldTypeCountry, but fallback to
+            // `addressFieldTableViewCountryCode` if nil (important for STPBillingAddressFieldsPostalCode)
+            address.country = address.country ?? addressFieldTableViewCountryCode
+            return address
+        }
+        set(address) {
+            if let country = address.country {
+                addressFieldTableViewCountryCode = country
+            }
+
+            for cell in addressCells {
+                switch cell.type {
+                case .name:
+                    cell.contents = address.name
+                case .line1:
+                    cell.contents = address.line1
+                case .line2:
+                    cell.contents = address.line2
+                case .city:
+                    cell.contents = address.city
+                case .state:
+                    cell.contents = address.state
+                case .zip:
+                    cell.contents = address.postalCode
+                case .country:
+                    cell.contents = address.country
+                case .email:
+                    cell.contents = address.email
+                case .phone:
+                    cell.contents = address.phone
+                }
+            }
+        }
+    }
+
+    // The default value of availableCountries is nil, which will allow all known countries.
+    var availableCountries: Set<String>?
+
+    var isValid: Bool {
+        if isBillingAddress {
+            // The AddressViewModel is only for address fields.
+            // Determining whether the postal code is present is up to the
+            // STPCardTextFieldViewModel.
+            if requiredBillingAddressFields == .postalCode {
+                return true
+            } else {
+                return address.containsRequiredFields(requiredBillingAddressFields)
+            }
+
+        } else {
+            if let requiredShippingAddressFields = requiredShippingAddressFields {
+                return address.containsRequiredShippingAddressFields(requiredShippingAddressFields)
+            }
+            return false
+        }
+    }
+
+    // The default value of availableCountries is nil, which will allow all known countries.
+    init(
+        requiredBillingFields requiredBillingAddressFields: STPBillingAddressFields,
+        availableCountries: Set<String>? = nil
+    ) {
+        isBillingAddress = true
+        self.availableCountries = availableCountries
+        self.requiredBillingAddressFields = requiredBillingAddressFields
+        switch requiredBillingAddressFields {
+        case .none:
+            addressCells = []
+        case .zip, .postalCode:
+            addressCells = []  // Postal code cell will be added later if necessary
+        case .full:
+            addressCells = [
+                STPAddressFieldTableViewCell(
+                    type: .name,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .line1,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .line2,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .country,
+                    contents: addressFieldTableViewCountryCode,
+                    lastInList: false,
+                    delegate: self
+                ),
+                // Postal code cell will be added here later if necessary
+                STPAddressFieldTableViewCell(
+                    type: .city,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .state,
+                    contents: "",
+                    lastInList: true,
+                    delegate: self
+                ),
+            ]
+        case .name:
+            addressCells = [
+                STPAddressFieldTableViewCell(
+                    type: .name,
+                    contents: "",
+                    lastInList: true,
+                    delegate: self
+                ),
+            ]
+        default:
+            fatalError()
+        }
+        commonInit()
+    }
+
+    init(
+        requiredShippingFields requiredShippingAddressFields: Set<STPContactField>,
+        availableCountries: Set<String>? = nil
+    ) {
+        isBillingAddress = false
+        self.availableCountries = availableCountries
+        self.requiredShippingAddressFields = requiredShippingAddressFields
+        var cells: [STPAddressFieldTableViewCell] = []
+
+        if requiredShippingAddressFields.contains(STPContactField.name) {
+            cells.append(
+                STPAddressFieldTableViewCell(
+                    type: .name,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                )
+            )
+        }
+        if requiredShippingAddressFields.contains(.emailAddress) {
+            cells.append(
+                STPAddressFieldTableViewCell(
+                    type: .email,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                )
+            )
+        }
+        if requiredShippingAddressFields.contains(STPContactField.postalAddress) {
+            var postalCells = [
+                STPAddressFieldTableViewCell(
+                    type: .name,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .line1,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .line2,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .country,
+                    contents: addressFieldTableViewCountryCode,
+                    lastInList: false,
+                    delegate: self
+                ),
+                // Postal code cell will be added here later if necessary
+                STPAddressFieldTableViewCell(
+                    type: .city,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+                STPAddressFieldTableViewCell(
+                    type: .state,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                ),
+            ]
+            if requiredShippingAddressFields.contains(.name) {
+                postalCells.remove(at: 0)
+            }
+            cells.append(contentsOf: postalCells.compactMap { $0 })
+        }
+        if requiredShippingAddressFields.contains(.phoneNumber) {
+            cells.append(
+                STPAddressFieldTableViewCell(
+                    type: .phone,
+                    contents: "",
+                    lastInList: false,
+                    delegate: self
+                )
+            )
+        }
+        if let lastCell = cells.last {
+            lastCell.lastInList = true
+        }
+        addressCells = cells
+        commonInit()
+    }
+
+    private func cell(at index: Int) -> STPAddressFieldTableViewCell? {
+        guard index > 0,
+            index < addressCells.count
+        else {
+            return nil
+        }
+        return addressCells[index]
+    }
+
+    private var isBillingAddress = false
+    private var requiredBillingAddressFields: STPBillingAddressFields = .none
+    private var requiredShippingAddressFields: Set<STPContactField>?
+    private var showingPostalCodeCell = false
+    private var geocodeInProgress = false
+
+    private func commonInit() {
+        if let countryCode = Locale.autoupdatingCurrent.regionCode {
+            addressFieldTableViewCountryCode = countryCode
+        }
+        updatePostalCodeCellIfNecessary()
+    }
+
+    private func updatePostalCodeCellIfNecessary() {
+        delegate?.addressViewModelWillUpdate(self)
+        let shouldBeShowingPostalCode = STPPostalCodeValidator.postalCodeIsRequired(
+            forCountryCode: addressFieldTableViewCountryCode
+        )
+
+        if shouldBeShowingPostalCode && !showingPostalCodeCell {
+            if containsStateAndPostalFields() {
+                // Add before city
+                let zipFieldIndex = addressCells.firstIndex(where: { $0.type == .city }) ?? 0
+
+                var mutableAddressCells = addressCells
+                mutableAddressCells.insert(
+                    STPAddressFieldTableViewCell(
+                        type: .zip,
+                        contents: "",
+                        lastInList: false,
+                        delegate: self
+                    ),
+                    at: zipFieldIndex
+                )
+                addressCells = mutableAddressCells
+                delegate?.addressViewModel(self, addedCellAt: zipFieldIndex)
+                delegate?.addressViewModelDidChange(self)
+            }
+        } else if !shouldBeShowingPostalCode && showingPostalCodeCell {
+            if containsStateAndPostalFields() {
+                if let zipFieldIndex = addressCells.firstIndex(where: { $0.type == .zip }) {
+
+                    var mutableAddressCells = addressCells
+                    mutableAddressCells.remove(at: zipFieldIndex)
+                    addressCells = mutableAddressCells
+                    delegate?.addressViewModel(self, removedCellAt: zipFieldIndex)
+                    delegate?.addressViewModelDidChange(self)
+                }
+            }
+        }
+        showingPostalCodeCell = shouldBeShowingPostalCode
+        delegate?.addressViewModelDidUpdate(self)
+    }
+
+    private func containsStateAndPostalFields() -> Bool {
+        if isBillingAddress {
+            return requiredBillingAddressFields == .full
+        } else {
+            return requiredShippingAddressFields?.contains(.postalAddress) ?? false
+        }
+    }
+
+    func updateCityAndState(fromZipCodeCell zipCell: STPAddressFieldTableViewCell?) {
+
+        let zipCode = zipCell?.contents
+
+        if geocodeInProgress || zipCode == nil || !(zipCell?.textField.validText ?? false)
+            || !(addressFieldTableViewCountryCode == "US")
+        {
+            return
+        }
+
+        var cityCell: STPAddressFieldTableViewCell?
+        var stateCell: STPAddressFieldTableViewCell?
+        for cell in addressCells {
+            if cell.type == .city {
+                cityCell = cell
+            } else if cell.type == .state {
+                stateCell = cell
+            }
+        }
+
+        if (cityCell == nil && stateCell == nil)
+            || ((cityCell?.contents?.count ?? 0) > 0 || (stateCell?.contents?.count ?? 0) > 0)
+        {
+            // Don't auto fill if either have text already
+            // Or if neither are non-nil
+            return
+        } else {
+            geocodeInProgress = true
+            let geocoder = CLGeocoder()
+
+            let onCompletion: CLGeocodeCompletionHandler = { placemarks, error in
+                stpDispatchToMainThreadIfNecessary({
+                    if (placemarks?.count ?? 0) > 0 && error == nil {
+                        let placemark = placemarks?.first
+                        if (cityCell?.contents?.count ?? 0) == 0
+                            && (stateCell?.contents?.count ?? 0) == 0
+                            && (zipCell?.contents == zipCode)
+                        {
+                            // Check contents again to make sure they're still empty
+                            // And that zipcode hasn't changed to something else
+                            cityCell?.contents = placemark?.locality
+                            stateCell?.contents = placemark?.administrativeArea
+                        }
+                    }
+                    self.geocodeInProgress = false
+                })
+            }
+
+            let address = CNMutablePostalAddress()
+            address.postalCode = zipCode ?? ""
+            address.isoCountryCode = addressFieldTableViewCountryCode ?? ""
+
+            geocoder.geocodePostalAddress(
+                address,
+                completionHandler: onCompletion
+            )
+        }
+    }
+
+    private func cell(after cell: STPAddressFieldTableViewCell?) -> STPAddressFieldTableViewCell? {
+        guard let cell = cell,
+            let cellIndex = addressCells.firstIndex(of: cell),
+            cellIndex + 1 < addressCells.count
+        else {
+            return nil
+        }
+        return addressCells[cellIndex + 1]
+    }
+
+    func addressFieldTableViewCellDidUpdateText(_ cell: STPAddressFieldTableViewCell) {
+        delegate?.addressViewModelDidChange(self)
+    }
+
+    func addressFieldTableViewCellDidReturn(_ cell: STPAddressFieldTableViewCell) {
+        _ = self.cell(after: cell)?.becomeFirstResponder()
+    }
+
+    func addressFieldTableViewCellDidEndEditing(_ cell: STPAddressFieldTableViewCell) {
+        if cell.type == .zip {
+            updateCityAndState(fromZipCodeCell: cell)
+        }
+    }
+
+}

+ 84 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAnalyticsClient+BasicUI.swift

@@ -0,0 +1,84 @@
+//
+//  STPAnalyticsClient+BasicUI.swift
+//  StripeiOS
+//
+//  Created by David Estes on 6/30/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePayments
+
+@objc(STPBasicUIAnalyticsSerializer)
+class STPBasicUIAnalyticsSerializer: NSObject, STPAnalyticsSerializer {
+    static func serializeConfiguration(
+        _ configuration: NSObject
+    ) -> [String:
+        String]
+    {
+        var dictionary: [String: String] = [:]
+        dictionary["publishable_key"] = STPAPIClient.shared.publishableKey ?? "unknown"
+
+        guard let configuration = configuration as? STPPaymentConfiguration else {
+            return dictionary
+        }
+
+        if configuration.applePayEnabled && !configuration.fpxEnabled {
+            dictionary["additional_payment_methods"] = "default"
+        } else if !configuration.applePayEnabled && !configuration.fpxEnabled {
+            dictionary["additional_payment_methods"] = "none"
+        } else if !configuration.applePayEnabled && configuration.fpxEnabled {
+            dictionary["additional_payment_methods"] = "fpx"
+        } else if configuration.applePayEnabled && configuration.fpxEnabled {
+            dictionary["additional_payment_methods"] = "applepay,fpx"
+        }
+
+        switch configuration.requiredBillingAddressFields {
+        case .none:
+            dictionary["required_billing_address_fields"] = "none"
+        case .postalCode:
+            dictionary["required_billing_address_fields"] = "zip"
+        case .full:
+            dictionary["required_billing_address_fields"] = "full"
+        case .name:
+            dictionary["required_billing_address_fields"] = "name"
+        default:
+            fatalError()
+        }
+
+        var shippingFields: [String] = []
+        if let shippingAddressFields = configuration.requiredShippingAddressFields {
+            if shippingAddressFields.contains(.name) {
+                shippingFields.append("name")
+            }
+            if shippingAddressFields.contains(.emailAddress) {
+                shippingFields.append("email")
+            }
+            if shippingAddressFields.contains(.postalAddress) {
+                shippingFields.append("address")
+            }
+            if shippingAddressFields.contains(.phoneNumber) {
+                shippingFields.append("phone")
+            }
+        }
+
+        if shippingFields.isEmpty {
+            shippingFields.append("none")
+        }
+        dictionary["required_shipping_address_fields"] = shippingFields.joined(separator: "_")
+
+        switch configuration.shippingType {
+        case .shipping:
+            dictionary["shipping_type"] = "shipping"
+        case .delivery:
+            dictionary["shipping_type"] = "delivery"
+        @unknown default:
+            break
+        }
+
+        dictionary["company_name"] = configuration.companyName
+        dictionary["apple_merchant_identifier"] = configuration.appleMerchantIdentifier ?? "unknown"
+        return dictionary
+    }
+}

+ 28 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPAnalyticsClient+Payments.swift

@@ -0,0 +1,28 @@
+//
+//  STPAnalyticsClient+Payments.swift
+//  StripeiOS
+//
+//  Created by David Estes on 1/24/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeCore
+
+/// An analytic specific to payments that serializes payment-specific
+/// information into its params.
+@_spi(STP) public protocol PaymentAnalytic: Analytic {
+    var productUsage: Set<String> { get }
+    var additionalParams: [String: Any] { get }
+}
+
+@_spi(STP) extension PaymentAnalytic {
+    public var params: [String: Any] {
+        var params = additionalParams
+
+        params["apple_pay_enabled"] = NSNumber(value: StripeAPI.deviceSupportsApplePay())
+        params["ocr_type"] = PaymentsSDKVariant.ocrTypeString
+        params["pay_var"] = PaymentsSDKVariant.variant
+        return params
+    }
+}

+ 98 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPApplePayContextDelegate.swift

@@ -0,0 +1,98 @@
+//
+//  STPApplePayContextDelegate.swift
+//  StripeiOS
+//
+//  Created by David Estes on 9/15/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+import PassKit
+@_spi(STP) import StripeApplePay
+@_spi(STP) import StripeCore
+
+/// Implement the required methods of this delegate to supply a PaymentIntent to STPApplePayContext and be notified of the completion of the Apple Pay payment.
+/// You may also implement the optional delegate methods to handle shipping methods and shipping address changes e.g. to verify you can ship to the address, or update the payment amount.
+@objc public protocol STPApplePayContextDelegate: _stpinternal_STPApplePayContextDelegateBase {
+    /// Called after the customer has authorized Apple Pay.  Implement this method to call the completion block with the client secret of a PaymentIntent or SetupIntent.
+    /// - Parameters:
+    ///   - paymentMethod:                 The PaymentMethod that represents the customer's Apple Pay payment method.
+    /// If you create the PaymentIntent with confirmation_method=manual, pass `paymentMethod.stripeId` as the payment_method and confirm=true. Otherwise, you can ignore this parameter.
+    ///   - paymentInformation:      The underlying PKPayment created by Apple Pay.
+    /// If you create the PaymentIntent with confirmation_method=manual, you can collect shipping information using its `shippingContact` and `shippingMethod` properties.
+    ///   - completion:                        Call this with the PaymentIntent or SetupIntent client secret, or the error that occurred creating the PaymentIntent or SetupIntent.
+    @objc(applePayContext:didCreatePaymentMethod:paymentInformation:completion:)
+    func applePayContext(
+        _ context: STPApplePayContext,
+        didCreatePaymentMethod paymentMethod: STPPaymentMethod,
+        paymentInformation: PKPayment,
+        completion: @escaping STPIntentClientSecretCompletionBlock
+    )
+
+    /// Called after the Apple Pay sheet is dismissed with the result of the payment.
+    /// Your implementation could stop a spinner and display a receipt view or error to the customer, for example.
+    /// - Parameters:
+    ///   - status: The status of the payment
+    ///   - error: The error that occurred, if any.
+    @objc(applePayContext:didCompleteWithStatus:error:)
+    func applePayContext(
+        _ context: STPApplePayContext,
+        didCompleteWith status: STPPaymentStatus,
+        error: Error?
+    )
+}
+
+/// A helper class used to bridge StripeApplePay.framework with the legacy Stripe.framework objects.
+@objc(STPApplePayContextLegacyHelper)
+class STPApplePayContextLegacyHelper: NSObject {
+    @objc class func performDidCreatePaymentMethod(
+        _ storage: _stpinternal_ApplePayContextDidCreatePaymentMethodStorage
+    ) {
+        let delegate = storage.delegate as! STPApplePayContextDelegate
+        // Convert the PaymentMethod to an STPPaymentMethod:
+        guard
+            let stpPaymentMethod = STPPaymentMethod.decodedObject(
+                fromAPIResponse: storage.paymentMethod.allResponseFields
+            )
+        else {
+            assertionFailure("Failed to convert PaymentMethod to STPPaymentMethod")
+            return
+        }
+        delegate.applePayContext(
+            storage.context,
+            didCreatePaymentMethod: stpPaymentMethod,
+            paymentInformation: storage.paymentInformation,
+            completion: storage.completion
+        )
+    }
+
+    @objc class func performDidComplete(_ storage: _stpinternal_ApplePayContextDidCompleteStorage) {
+        let delegate = storage.delegate as! STPApplePayContextDelegate
+        let stpStatus = STPPaymentStatus(applePayStatus: storage.status)
+
+        // If this is a modern API error, convert it down to a legacy STPError.
+        // This is to avoid changing the API experience for users.
+        // We can re-evaluate this as we release more of the modern API.
+        if let modernError = storage.error as? StripeError {
+            storage.error = NSError.stp_error(from: modernError)
+        }
+
+        delegate.applePayContext(storage.context, didCompleteWith: stpStatus, error: storage.error)
+    }
+
+}
+
+extension STPPaymentStatus {
+    init(
+        applePayStatus: STPApplePayContext.PaymentStatus
+    ) {
+        switch applePayStatus {
+        case .success:
+            self = .success
+        case .error:
+            self = .error
+        case .userCancellation:
+            self = .userCancellation
+        }
+    }
+}

+ 51 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPApplePayPaymentOption.swift

@@ -0,0 +1,51 @@
+//
+//  STPApplePayPaymentOption.swift
+//  StripeiOS
+//
+//  Created by Ben Guo on 4/19/16.
+//  Copyright © 2016 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePaymentsUI
+import UIKit
+
+/// An empty class representing that the user wishes to pay via Apple Pay. This can
+/// be checked on an `STPPaymentContext`, e.g:
+/// ```
+/// if paymentContext.selectedPaymentOption is STPApplePayPaymentOption {
+/// // Don't ask the user for their card number; they want to pay with apple pay.
+/// }
+/// ```
+@objc public class STPApplePayPaymentOption: NSObject, STPPaymentOption {
+    // MARK: - STPPaymentOption
+    @objc public var image: UIImage {
+        return STPImageLibrary.applePayCardImage()
+    }
+
+    @objc public var templateImage: UIImage {
+        // No template for Apple Pay
+        return STPImageLibrary.applePayCardImage()
+    }
+
+    @objc public var label: String {
+        return String.Localized.apple_pay
+    }
+
+    @objc public var isReusable: Bool {
+        return true
+    }
+
+    // MARK: - Equality
+    /// :nodoc:
+    @objc
+    public override func isEqual(_ object: Any?) -> Bool {
+        return object is STPApplePayPaymentOption
+    }
+
+    /// :nodoc:
+    @objc public override var hash: Int {
+        return NSStringFromClass(STPApplePayPaymentOption.self).hash
+    }
+}

+ 86 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPBackendAPIAdapter.swift

@@ -0,0 +1,86 @@
+//
+//  STPBackendAPIAdapter.swift
+//  StripeiOS
+//
+//  Created by Jack Flintermann on 1/12/16.
+//  Copyright © 2016 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+/// A "bridge" from our pre-built UI (`STPPaymentContext`, `STPPaymentOptionsViewController`)
+/// to your backend to fetch Customer-related information needed to power those views.
+/// Typically, you will not need to implement this protocol yourself. You
+/// should instead use `STPCustomerContext`, which implements <STPBackendAPIAdapter>
+/// and manages retrieving and updating a Stripe customer for you.
+/// - seealso: STPCustomerContext.h
+/// If you would prefer retrieving and updating your Stripe customer object via
+/// your own backend instead of using `STPCustomerContext`, you should make your
+/// application's API client conform to this interface.
+@objc public protocol STPBackendAPIAdapter: NSObjectProtocol {
+    /// Retrieve the customer to be displayed inside a payment context.
+    /// If you are not using STPCustomerContext:
+    /// On your backend, retrieve the Stripe customer associated with your currently
+    /// logged-in user ( https://stripe.com/docs/api#retrieve_customer ), and return
+    /// the raw JSON response from the Stripe API. Back in your iOS app, after you've
+    /// called this API, deserialize your API response into an `STPCustomer` object
+    /// (you can use the `STPCustomerDeserializer` class to do this).
+    /// - seealso: STPCard
+    /// - Parameter completion: call this callback when you're done fetching and parsing the above information from your backend. For example, `completion(customer, nil)` (if your call succeeds) or `completion(nil, error)` if an error is returned.
+    func retrieveCustomer(_ completion: STPCustomerCompletionBlock?)
+    /// Retrieves a list of Payment Methods attached to a customer.
+    /// If you are implementing your own <STPBackendAPIAdapter>:
+    /// Call the list method ( https://stripe.com/docs/api/payment_methods/list )
+    /// with the Stripe customer. If this API call succeeds, call `completion(paymentMethods)`
+    /// with the list of PaymentMethods. Otherwise, call `completion(error)` with the error
+    /// that occurred.
+    /// - Parameter completion:  Call this callback with the list of Payment Methods attached to the
+    /// customer.  For example, `completion(paymentMethods)` (if your call succeeds) or
+    /// `completion(error)` if an error is returned.
+    func listPaymentMethodsForCustomer(completion: STPPaymentMethodsCompletionBlock?)
+    /// Adds a Payment Method to a customer.
+    /// If you are implementing your own <STPBackendAPIAdapter>:
+    /// On your backend, retrieve the Stripe customer associated with your logged-in user.
+    /// Then, call the Attach method on the Payment Method with that customer's ID
+    /// ( https://stripe.com/docs/api/payment_methods/attach ). If this API call succeeds,
+    /// call `completion(nil)`. Otherwise, call `completion(error)` with the error that
+    /// occurred.
+    /// - Parameters:
+    ///   - paymentMethod:   A valid Payment Method
+    ///   - completion:      Call this callback when you're done adding the payment method
+    /// to the customer on your backend. For example, `completion(nil)` (if your call succeeds)
+    /// or `completion(error)` if an error is returned.
+    func attachPaymentMethod(toCustomer paymentMethod: STPPaymentMethod, completion: STPErrorBlock?)
+
+    /// Deletes the given Payment Method from the customer.
+    /// If you are implementing your own <STPBackendAPIAdapter>:
+    /// Call the Detach method ( https://stripe.com/docs/api/payment_methods/detach )
+    /// on the Payment Method. If this API call succeeds, call `completion(nil)`.
+    /// Otherwise, call `completion(error)` with the error that occurred.
+    /// - Parameters:
+    ///   - paymentMethod:   The Payment Method to delete from the customer
+    ///   - completion:      Call this callback when you're done deleting the Payment Method
+    /// from the customer on your backend. For example, `completion(nil)` (if your call
+    /// succeeds) or `completion(error)` if an error is returned.
+    @objc optional func detachPaymentMethod(
+        fromCustomer paymentMethod: STPPaymentMethod,
+        completion: STPErrorBlock?
+    )
+    /// Sets the given shipping address on the customer.
+    /// If you are implementing your own <STPBackendAPIAdapter>:
+    /// On your backend, retrieve the Stripe customer associated with your logged-in user.
+    /// Then, call the Customer Update method ( https://stripe.com/docs/api#update_customer )
+    /// specifying shipping to be the given shipping address. If this API call succeeds,
+    /// call `completion(nil)`. Otherwise, call `completion(error)` with the error that occurred.
+    /// - Parameters:
+    ///   - shipping:   The shipping address to set on the customer
+    ///   - completion: call this callback when you're done updating the customer on
+    /// your backend. For example, `completion(nil)` (if your call succeeds) or
+    /// `completion(error)` if an error is returned.
+    /// - seealso: https://stripe.com/docs/api#update_customer
+    @objc optional func updateCustomer(
+        withShippingAddress shipping: STPAddress,
+        completion: STPErrorBlock?
+    )
+}

+ 124 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPBankSelectionTableViewCell.swift

@@ -0,0 +1,124 @@
+//
+//  STPBankSelectionTableViewCell.swift
+//  StripeiOS
+//
+//  Created by David Estes on 8/9/19.
+//  Copyright © 2019 Stripe, Inc. All rights reserved.
+//
+
+import UIKit
+
+class STPBankSelectionTableViewCell: UITableViewCell {
+    func configure(
+        withBank bankBrand: STPFPXBankBrand,
+        theme: STPTheme,
+        selected: Bool,
+        offline: Bool,
+        enabled: Bool
+    ) {
+        bank = bankBrand
+        self.theme = theme
+
+        backgroundColor = theme.secondaryBackgroundColor
+
+        // Left icon
+        leftIcon?.image = STPLegacyImageLibrary.fpxBrandImage(for: bank)
+        leftIcon?.tintColor = primaryColorForPaymentOption(withSelected: selected, enabled: enabled)
+
+        // Title label
+        titleLabel?.font = theme.font
+        titleLabel?.text = STPFPXBank.stringFrom(bank)
+        if offline {
+            let format = STPLocalizedString(
+                "%@ - Offline",
+                "Bank name when bank is offline for maintenance."
+            )
+            titleLabel?.text = String(format: format, STPFPXBank.stringFrom(bank) ?? "")
+        }
+        titleLabel?.textColor = primaryColorForPaymentOption(
+            withSelected: isSelected,
+            enabled: enabled
+        )
+
+        // Loading indicator
+        activityIndicator?.tintColor = theme.accentColor
+        if selected {
+            activityIndicator?.startAnimating()
+        } else {
+            activityIndicator?.stopAnimating()
+        }
+
+        setNeedsLayout()
+    }
+
+    private var bank: STPFPXBankBrand!
+    private var theme: STPTheme = .defaultTheme
+    private var leftIcon: UIImageView?
+    private var titleLabel: UILabel?
+    private var activityIndicator: UIActivityIndicatorView?
+
+    override init(
+        style: UITableViewCell.CellStyle,
+        reuseIdentifier: String?
+    ) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        // Left icon
+        let leftIcon = UIImageView()
+        self.leftIcon = leftIcon
+        contentView.addSubview(leftIcon)
+
+        // Title label
+        let titleLabel = UILabel()
+        self.titleLabel = titleLabel
+        contentView.addSubview(titleLabel)
+
+        // Loading indicator
+        let activityIndicator = UIActivityIndicatorView(style: .medium)
+        self.activityIndicator = activityIndicator
+        self.activityIndicator?.hidesWhenStopped = true
+        contentView.addSubview(activityIndicator)
+    }
+
+    override func layoutSubviews() {
+        super.layoutSubviews()
+
+        let midY = bounds.midY
+        let padding: CGFloat = 15.0
+        let iconWidth: CGFloat = 26.0
+
+        // Left icon
+        leftIcon?.sizeToFit()
+        leftIcon?.center = CGPoint(x: padding + (iconWidth / 2.0), y: midY)
+
+        // Activity indicator
+        activityIndicator?.center = CGPoint(
+            x: bounds.width - padding - (activityIndicator?.bounds.midX ?? 0.0),
+            y: midY
+        )
+
+        // Title label
+        var labelFrame = bounds
+        // not every icon is `iconWidth` wide, but give them all the same amount of space:
+        labelFrame.origin.x = padding + iconWidth + padding
+        labelFrame.size.width =
+            (activityIndicator?.frame.minX ?? 0.0) - padding - labelFrame.origin.x
+        titleLabel?.frame = labelFrame
+    }
+
+    func primaryColorForPaymentOption(withSelected selected: Bool, enabled: Bool) -> UIColor {
+        if selected {
+            return theme.accentColor
+        } else {
+            return
+                (enabled
+                ? theme.primaryForegroundColor
+                : theme.primaryForegroundColor.withAlphaComponent(0.6))
+        }
+    }
+
+    required init?(
+        coder aDecoder: NSCoder
+    ) {
+        super.init(coder: aDecoder)
+    }
+}

+ 300 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPBankSelectionViewController.swift

@@ -0,0 +1,300 @@
+//
+//  STPBankSelectionViewController.swift
+//  StripeiOS
+//
+//  Created by David Estes on 8/9/19.
+//  Copyright © 2019 Stripe, Inc. All rights reserved.
+//
+
+import PassKit
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePayments
+@_spi(STP) import StripePaymentsUI
+import UIKit
+
+/// The payment methods supported by STPBankSelectionViewController.
+@objc public enum STPBankSelectionMethod: Int {
+    /// FPX (Malaysia)
+    case FPX
+    /// An unknown payment method
+    case unknown
+}
+
+/// This view controller displays a list of banks of the specified type, allowing the user to select one to pay from.
+/// Once a bank is selected, it will return a PaymentMethodParams object, which you can use to confirm a PaymentIntent
+/// or inspect to obtain details about the selected bank.
+public class STPBankSelectionViewController: STPCoreTableViewController, UITableViewDataSource,
+    UITableViewDelegate
+{
+    /// A convenience initializer; equivalent to calling `init( bankMethod:bankMethod configuration:STPPaymentConfiguration.shared theme:STPTheme.defaultTheme`.
+    @objc
+    public convenience init(
+        bankMethod: STPBankSelectionMethod
+    ) {
+        self.init(
+            bankMethod: bankMethod,
+            configuration: STPPaymentConfiguration.shared,
+            theme: STPTheme.defaultTheme
+        )
+    }
+
+    @objc public convenience required init(
+        theme: STPTheme?
+    ) {
+        self.init(
+            bankMethod: .FPX,
+            configuration: STPPaymentConfiguration.shared,
+            theme: theme ?? .defaultTheme
+        )
+    }
+
+    /// Initializes a new `STPBankSelectionViewController` with the provided configuration and theme. Don't forget to set the `delegate` property after initialization.
+    /// - Parameters:
+    ///   - bankMethod: The user will be presented with a list of banks for this payment method. STPBankSelectionMethodFPX is currently the only supported payment method.
+    ///   - configuration: The configuration to use. This determines the Stripe publishable key to use when querying metadata about the banks. - seealso: STPPaymentConfiguration
+    ///   - theme:         The theme to use to inform the view controller's visual appearance. - seealso: STPTheme
+    @objc
+    public init(
+        bankMethod: STPBankSelectionMethod,
+        configuration: STPPaymentConfiguration,
+        theme: STPTheme
+    ) {
+        super.init(theme: theme)
+        STPAnalyticsClient.sharedClient.addClass(
+            toProductUsageIfNecessary: STPBankSelectionViewController.self
+        )
+        assert(bankMethod == .FPX, "STPBankSelectionViewController currently only supports FPX.")
+        self.bankMethod = bankMethod
+        self.configuration = configuration
+        selectedBank = .unknown
+        apiClient = STPAPIClient.shared
+        if bankMethod == .FPX {
+            _refreshFPXStatus()
+            NotificationCenter.default.addObserver(
+                self,
+                selector: #selector(_refreshFPXStatus),
+                name: UIApplication.didBecomeActiveNotification,
+                object: nil
+            )
+        }
+        title = String.Localized.bank_account
+    }
+
+    /// The view controller's delegate. This must be set before showing the view controller in order for it to work properly. - seealso: STPBankSelectionViewControllerDelegate
+    @objc public weak var delegate: STPBankSelectionViewControllerDelegate?
+
+    /// The API Client to use to make requests.
+    /// Defaults to `STPAPIClient.shared`
+    public var apiClient: STPAPIClient = .shared
+
+    private var bankMethod: STPBankSelectionMethod = .unknown
+    private var selectedBank: STPFPXBankBrand = .unknown
+    private var configuration: STPPaymentConfiguration?
+    private weak var imageView: UIImageView?
+    private var headerView: STPSectionHeaderView?
+    private var loading = false
+    private var bankStatus: STPFPXBankStatusResponse?
+
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+
+    @objc func _refreshFPXStatus() {
+        apiClient.retrieveFPXBankStatus(withCompletion: { bankStatusResponse, error in
+            if error == nil && bankStatusResponse != nil {
+                if let bankStatusResponse = bankStatusResponse {
+                    self._update(withBankStatus: bankStatusResponse)
+                }
+            }
+        })
+    }
+
+    @objc override func createAndSetupViews() {
+        super.createAndSetupViews()
+
+        tableView?.register(
+            STPBankSelectionTableViewCell.self,
+            forCellReuseIdentifier: STPBankSelectionCellReuseIdentifier
+        )
+
+        tableView?.dataSource = self
+        tableView?.delegate = self
+        tableView?.reloadData()
+    }
+
+    @objc override func updateAppearance() {
+        super.updateAppearance()
+
+        tableView?.reloadData()
+    }
+
+    @objc override func useSystemBackButton() -> Bool {
+        return true
+    }
+
+    func _update(withBankStatus bankStatusResponse: STPFPXBankStatusResponse) {
+        bankStatus = bankStatusResponse
+
+        tableView?.beginUpdates()
+        if let indexPathsForVisibleRows = tableView?.indexPathsForVisibleRows {
+            tableView?.reloadRows(at: indexPathsForVisibleRows, with: .none)
+        }
+        tableView?.endUpdates()
+    }
+
+    // MARK: - UITableView
+
+    /// :nodoc:
+    @objc
+    public func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return STPFPXBankBrand.unknown.rawValue
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        cellForRowAt indexPath: IndexPath
+    ) -> UITableViewCell {
+        let cell =
+            tableView.dequeueReusableCell(
+                withIdentifier: STPBankSelectionCellReuseIdentifier,
+                for: indexPath
+            )
+            as? STPBankSelectionTableViewCell
+        let bankBrand = STPFPXBankBrand(rawValue: indexPath.row)
+        let selected = selectedBank == bankBrand
+        var offline: Bool?
+        if let bankBrand = bankBrand {
+            offline = bankStatus != nil && !(bankStatus?.bankBrandIsOnline(bankBrand) ?? false)
+        }
+        if let bankBrand = bankBrand {
+            cell?.configure(
+                withBank: bankBrand,
+                theme: theme,
+                selected: selected,
+                offline: offline ?? false,
+                enabled: !loading
+            )
+        }
+        return cell!
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        willDisplay cell: UITableViewCell,
+        forRowAt indexPath: IndexPath
+    ) {
+        let topRow = indexPath.row == 0
+        let bottomRow =
+            self.tableView(tableView, numberOfRowsInSection: indexPath.section) - 1 == indexPath.row
+        cell.stp_setBorderColor(theme.tertiaryBackgroundColor)
+        cell.stp_setTopBorderHidden(!topRow)
+        cell.stp_setBottomBorderHidden(!bottomRow)
+        cell.stp_setFakeSeparatorColor(theme.quaternaryBackgroundColor)
+        cell.stp_setFakeSeparatorLeftInset(15.0)
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        heightForFooterInSection section: Int
+    )
+        -> CGFloat
+    {
+        return 27.0
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        shouldHighlightRowAt indexPath: IndexPath
+    )
+        -> Bool
+    {
+        return !loading
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        if loading {
+            return  // Don't allow user interaction if we're currently setting up a payment method
+        }
+        loading = true
+        tableView.deselectRow(at: indexPath, animated: true)
+        let bankIndex = indexPath.row
+        selectedBank = STPFPXBankBrand(rawValue: bankIndex) ?? .unknown
+        tableView.reloadSections(
+            NSIndexSet(index: indexPath.section) as IndexSet,
+            with: .none
+        )
+
+        let fpx = STPPaymentMethodFPXParams()
+        fpx.bank = STPFPXBankBrand(rawValue: bankIndex) ?? .unknown
+        // Create and return a Payment Method Params object
+        let paymentMethodParams = STPPaymentMethodParams(
+            fpx: fpx,
+            billingDetails: nil,
+            metadata: nil
+        )
+        if delegate?.responds(
+            to: #selector(
+                STPBankSelectionViewControllerDelegate.bankSelectionViewController(
+                    _:
+                    didCreatePaymentMethodParams:
+                ))
+        ) ?? false {
+            delegate?.bankSelectionViewController(
+                self,
+                didCreatePaymentMethodParams: paymentMethodParams
+            )
+        }
+    }
+
+    required init?(
+        coder aDecoder: NSCoder
+    ) {
+        super.init(coder: aDecoder)
+    }
+
+    required init(
+        nibName nibNameOrNil: String?,
+        bundle nibBundleOrNil: Bundle?
+    ) {
+        fatalError("init(nibName:bundle:) has not been implemented")
+    }
+}
+
+/// An `STPBankSelectionViewControllerDelegate` is notified when a user selects a bank.
+@objc public protocol STPBankSelectionViewControllerDelegate: NSObjectProtocol {
+    /// This is called when the user selects a bank.
+    /// You can use the returned PaymentMethodParams to confirm a PaymentIntent, or inspect
+    /// it to obtain details about the selected bank.
+    /// Once you're done, you'll want to dismiss (or pop) the view controller.
+    /// - Parameters:
+    ///   - bankViewController:          the view controller that created the PaymentMethodParams
+    ///   - paymentMethodParams:         the PaymentMethodParams that was created. - seealso: STPPaymentMethodParams
+    @objc(bankSelectionViewController:didCreatePaymentMethodParams:)
+    func bankSelectionViewController(
+        _ bankViewController: STPBankSelectionViewController,
+        didCreatePaymentMethodParams paymentMethodParams: STPPaymentMethodParams
+    )
+}
+
+private let STPBankSelectionCellReuseIdentifier = "STPBankSelectionCellReuseIdentifier"
+
+/// :nodoc:
+@_spi(STP) extension STPBankSelectionViewController: STPAnalyticsProtocol {
+    @_spi(STP) public static var stp_analyticsIdentifier = "STPBankSelectionViewController"
+}

+ 44 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPBlocks.swift

@@ -0,0 +1,44 @@
+//
+//  STPBlocks.swift
+//  StripeiOS
+//
+//  Created by David Estes on 6/30/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+import PassKit
+
+/// A callback to be run with a response from the Stripe API containing information about the online status of FPX banks.
+/// - Parameters:
+///   - bankStatusResponse:    The response from Stripe containing the status of the various banks. Will be nil if an error occurs. - seealso: STPFPXBankStatusResponse
+///   - error:                   The error returned from the response, or nil if none occurs.
+typealias STPFPXBankStatusCompletionBlock = (STPFPXBankStatusResponse?, Error?) -> Void
+
+/// These values control the labels used in the shipping info collection form.
+@objc public enum STPShippingType: Int {
+    /// Shipping the purchase to the provided address using a third-party
+    /// shipping company.
+    case shipping
+    /// Delivering the purchase by the seller.
+    case delivery
+}
+
+/// An enum representing the status of a shipping address validation.
+@objc public enum STPShippingStatus: Int {
+    /// The shipping address is valid.
+    case valid
+    /// The shipping address is invalid.
+    case invalid
+}
+
+/// A callback to be run with a validation result and shipping methods for a
+/// shipping address.
+/// - Parameters:
+///   - status: An enum representing whether the shipping address is valid.
+///   - shippingValidationError: If the shipping address is invalid, an error describing the issue with the address. If no error is given and the address is invalid, the default error message will be used.
+///   - shippingMethods: The shipping methods available for the address.
+///   - selectedShippingMethod: The default selected shipping method for the address.
+public typealias STPShippingMethodsCompletionBlock = (
+    STPShippingStatus, Error?, [PKShippingMethod]?, PKShippingMethod?
+) -> Void

+ 77 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCameraView.swift

@@ -0,0 +1,77 @@
+//
+//  STPCameraView.swift
+//  StripeiOS
+//
+//  Created by David Estes on 8/17/20.
+//  Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+import AVFoundation
+import UIKit
+
+@available(macCatalyst 14.0, *)
+class STPCameraView: UIView {
+    private var flashLayer: CALayer?
+
+    var captureSession: AVCaptureSession? {
+        get {
+            return (videoPreviewLayer.session)!
+        }
+        set(captureSession) {
+            videoPreviewLayer.session = captureSession
+        }
+    }
+
+    var videoPreviewLayer: AVCaptureVideoPreviewLayer {
+        return layer as! AVCaptureVideoPreviewLayer
+    }
+
+    func playSnapshotAnimation() {
+        CATransaction.begin()
+        CATransaction.setValue(
+            kCFBooleanTrue,
+            forKey: kCATransactionDisableActions
+        )
+        flashLayer?.frame = CGRect(
+            x: 0,
+            y: 0,
+            width: layer.bounds.size.width,
+            height: layer.bounds.size.height
+        )
+        flashLayer?.opacity = 1.0
+        CATransaction.commit()
+        DispatchQueue.main.async(execute: {
+            let fadeAnim = CABasicAnimation(keyPath: "opacity")
+            fadeAnim.fromValue = NSNumber(value: 1.0)
+            fadeAnim.toValue = NSNumber(value: 0.0)
+            fadeAnim.duration = 1.0
+            self.flashLayer?.add(fadeAnim, forKey: "opacity")
+            self.flashLayer?.opacity = 0.0
+        })
+    }
+
+    override init(
+        frame: CGRect
+    ) {
+        super.init(frame: frame)
+        flashLayer = CALayer()
+        if let flashLayer = flashLayer {
+            layer.addSublayer(flashLayer)
+        }
+        flashLayer?.masksToBounds = true
+        flashLayer?.backgroundColor = UIColor.black.cgColor
+        flashLayer?.opacity = 0.0
+        layer.masksToBounds = true
+        videoPreviewLayer.videoGravity = .resizeAspectFill
+    }
+
+    override class var layerClass: AnyClass {
+        return AVCaptureVideoPreviewLayer.self
+    }
+
+    required init?(
+        coder aDecoder: NSCoder
+    ) {
+        super.init(coder: aDecoder)
+    }
+}

+ 30 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCard+BasicUI.swift

@@ -0,0 +1,30 @@
+//
+//  STPCard+BasicUI.swift
+//  StripeiOS
+//
+//  Created by David Estes on 6/30/22.
+//  Copyright © 2022 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension STPCard: STPPaymentOption {
+    // MARK: - STPPaymentOption
+    @objc public var image: UIImage {
+        return STPImageLibrary.cardBrandImage(for: brand)
+    }
+
+    @objc public var templateImage: UIImage {
+        return STPImageLibrary.templatedBrandImage(for: brand)
+    }
+
+    @objc public var label: String {
+        let brand = STPCard.string(from: self.brand)
+        return "\(brand) \(last4 )"
+    }
+
+    @objc public var isReusable: Bool {
+        return true
+    }
+}

+ 518 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCardScanner.swift

@@ -0,0 +1,518 @@
+//
+//  STPCardScanner.swift
+//  StripeiOS
+//
+//  Created by David Estes on 8/17/20.
+//  Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+import AVFoundation
+import Foundation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePayments
+@_spi(STP) import StripePaymentsUI
+import UIKit
+import Vision
+
+enum STPCardScannerError: Int {
+    /// Camera not available.
+    case cameraNotAvailable
+}
+
+@available(macCatalyst 14.0, *)
+@objc protocol STPCardScannerDelegate: NSObjectProtocol {
+    @objc(cardScanner:didFinishWithCardParams:error:) func cardScanner(
+        _ scanner: STPCardScanner,
+        didFinishWith cardParams: STPPaymentMethodCardParams?,
+        error: Error?
+    )
+}
+
+@available(macCatalyst 14.0, *)
+@objc(STPCardScanner_legacy)
+class STPCardScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate,
+    STPCardScanningProtocol
+{
+    // iOS will kill the app if it tries to request the camera without an NSCameraUsageDescription
+    static let cardScanningAvailableCameraHasUsageDescription = {
+        return
+            (Bundle.main.infoDictionary?["NSCameraUsageDescription"] != nil
+            || Bundle.main.localizedInfoDictionary?["NSCameraUsageDescription"] != nil)
+    }()
+
+    static var cardScanningAvailable: Bool {
+        // Always allow in tests:
+        if NSClassFromString("XCTest") != nil {
+            return true
+        }
+        return cardScanningAvailableCameraHasUsageDescription
+    }
+
+    weak var cameraView: STPCameraView?
+
+    var feedbackGenerator: UINotificationFeedbackGenerator?
+
+    @objc public var deviceOrientation: UIDeviceOrientation {
+        get {
+            return stp_deviceOrientation
+        }
+        set(newDeviceOrientation) {
+            stp_deviceOrientation = newDeviceOrientation
+
+            // This is an optimization for portrait mode: The card will be centered in the screen,
+            // so we can ignore the top and bottom. We'll use the whole frame in landscape.
+            let kSTPCardScanningScreenCenter = CGRect(
+                x: 0,
+                y: CGFloat(0.3),
+                width: 1,
+                height: CGFloat(0.4)
+            )
+
+            // iOS camera image data is returned in LandcapeLeft orientation by default. We'll flip it as needed:
+            switch newDeviceOrientation {
+            case .portraitUpsideDown:
+                videoOrientation = .portraitUpsideDown
+                textOrientation = .left
+                regionOfInterest = kSTPCardScanningScreenCenter
+            case .landscapeLeft:
+                videoOrientation = .landscapeRight
+                textOrientation = .up
+                regionOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
+            case .landscapeRight:
+                videoOrientation = .landscapeLeft
+                textOrientation = .down
+                regionOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
+            case .portrait, .faceUp, .faceDown, .unknown:
+                // swift-format-ignore: NoCasesWithOnlyFallthrough
+                fallthrough
+            default:
+                videoOrientation = .portrait
+                textOrientation = .right
+                regionOfInterest = kSTPCardScanningScreenCenter
+            }
+            cameraView?.videoPreviewLayer.connection?.videoOrientation = videoOrientation
+        }
+    }
+
+    override init() {
+    }
+
+    init(
+        delegate: STPCardScannerDelegate?
+    ) {
+        super.init()
+        self.delegate = delegate
+        captureSessionQueue = DispatchQueue(label: "com.stripe.CardScanning.CaptureSessionQueue")
+        deviceOrientation = UIDevice.current.orientation
+    }
+
+    func start() {
+        if isScanning {
+            return
+        }
+        STPAnalyticsClient.sharedClient.addClass(toProductUsageIfNecessary: STPCardScanner.self)
+        startTime = Date()
+
+        isScanning = true
+        didTimeout = false
+        timeoutStarted = false
+        feedbackGenerator = UINotificationFeedbackGenerator()
+        feedbackGenerator?.prepare()
+
+        captureSessionQueue?.async(execute: {
+            #if targetEnvironment(simulator)
+                // Camera not supported on Simulator
+                self.stopWithError(STPCardScanner.stp_cardScanningError())
+                return
+            #else
+                self.detectedNumbers = NSCountedSet()  // capacity: 5
+                self.detectedExpirations = NSCountedSet()  // capacity: 5
+                self.setupCamera()
+                DispatchQueue.main.async(execute: {
+                    self.cameraView?.captureSession = self.captureSession
+                    self.cameraView?.videoPreviewLayer.connection?.videoOrientation =
+                        self.videoOrientation
+                })
+            #endif
+        })
+    }
+
+    func stop() {
+        stopWithError(nil)
+    }
+
+    private weak var delegate: STPCardScannerDelegate?
+    private var captureDevice: AVCaptureDevice?
+    private var captureSession: AVCaptureSession?
+    private var captureSessionQueue: DispatchQueue?
+    private var videoDataOutput: AVCaptureVideoDataOutput?
+    private var videoDataOutputQueue: DispatchQueue?
+    private var textRequest: VNRecognizeTextRequest?
+    private var isScanning = false
+    private var didTimeout = false
+    private var timeoutStarted = false
+    private var stp_deviceOrientation: UIDeviceOrientation!
+    private var videoOrientation: AVCaptureVideoOrientation!
+    private var textOrientation: CGImagePropertyOrientation!
+    private var regionOfInterest = CGRect.zero
+    private var detectedNumbers: NSCountedSet?
+    private var detectedExpirations: NSCountedSet?
+    private var startTime: Date?
+
+    // MARK: Public
+
+    class func stp_cardScanningError() -> Error {
+        let userInfo = [
+            NSLocalizedDescriptionKey: String.Localized.allow_camera_access,
+            STPError.errorMessageKey: "The camera couldn't be used.",
+        ]
+        return NSError(
+            domain: STPCardScannerErrorDomain,
+            code: STPCardScannerError.cameraNotAvailable.rawValue,
+            userInfo: userInfo
+        )
+    }
+
+    deinit {
+        if isScanning {
+            captureDevice?.unlockForConfiguration()
+            captureSession?.stopRunning()
+        }
+    }
+
+    func stopWithError(_ error: Error?) {
+        if isScanning {
+            finish(with: nil, error: error)
+        }
+    }
+
+    // MARK: Setup
+    func setupCamera() {
+        weak var weakSelf = self
+        textRequest = VNRecognizeTextRequest(completionHandler: { request, error in
+            let strongSelf = weakSelf
+            if !(strongSelf?.isScanning ?? false) {
+                return
+            }
+            if error != nil {
+                strongSelf?.stopWithError(STPCardScanner.stp_cardScanningError())
+                return
+            }
+            strongSelf?.processVNRequest(request)
+        })
+
+        let captureDevice = AVCaptureDevice.default(
+            .builtInWideAngleCamera,
+            for: .video,
+            position: .back
+        )
+        self.captureDevice = captureDevice
+
+        captureSession = AVCaptureSession()
+        captureSession?.sessionPreset = .hd1920x1080
+
+        var deviceInput: AVCaptureDeviceInput?
+        do {
+            if let captureDevice = captureDevice {
+                deviceInput = try AVCaptureDeviceInput(device: captureDevice)
+            }
+        } catch {
+            stopWithError(STPCardScanner.stp_cardScanningError())
+            return
+        }
+
+        if let deviceInput = deviceInput {
+            if captureSession?.canAddInput(deviceInput) ?? false {
+                captureSession?.addInput(deviceInput)
+            } else {
+                stopWithError(STPCardScanner.stp_cardScanningError())
+                return
+            }
+        }
+
+        videoDataOutputQueue = DispatchQueue(label: "com.stripe.CardScanning.VideoDataOutputQueue")
+        videoDataOutput = AVCaptureVideoDataOutput()
+        videoDataOutput?.alwaysDiscardsLateVideoFrames = true
+        videoDataOutput?.setSampleBufferDelegate(self, queue: videoDataOutputQueue)
+
+        // This is the recommended pixel buffer format for Vision:
+        videoDataOutput?.videoSettings = [
+            kCVPixelBufferPixelFormatTypeKey as String:
+                kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
+        ]
+
+        if let videoDataOutput = videoDataOutput {
+            if captureSession?.canAddOutput(videoDataOutput) ?? false {
+                captureSession?.addOutput(videoDataOutput)
+            } else {
+                stopWithError(STPCardScanner.stp_cardScanningError())
+                return
+            }
+        }
+
+        // This improves recognition quality, but means the VideoDataOutput buffers won't match what we're seeing on screen.
+        videoDataOutput?.connection(with: .video)?.preferredVideoStabilizationMode = .auto
+
+        captureSession?.startRunning()
+
+        do {
+            try self.captureDevice?.lockForConfiguration()
+            self.captureDevice?.autoFocusRangeRestriction = .near
+        } catch {
+        }
+    }
+
+    // MARK: Processing
+    func captureOutput(
+        _ output: AVCaptureOutput,
+        didOutput sampleBuffer: CMSampleBuffer,
+        from connection: AVCaptureConnection
+    ) {
+        if !isScanning {
+            return
+        }
+        let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
+        if pixelBuffer == nil {
+            return
+        }
+        textRequest?.recognitionLevel = .accurate
+        textRequest?.usesLanguageCorrection = false
+        textRequest?.regionOfInterest = regionOfInterest
+        var handler: VNImageRequestHandler?
+        if let pixelBuffer = pixelBuffer {
+            handler = VNImageRequestHandler(
+                cvPixelBuffer: pixelBuffer,
+                orientation: textOrientation,
+                options: [:]
+            )
+        }
+        do {
+            try handler?.perform([textRequest].compactMap { $0 })
+        } catch {
+        }
+    }
+
+    func processVNRequest(_ request: VNRequest) {
+        var allNumbers: [String] = []
+        for observation in request.results ?? [] {
+            guard let observation = observation as? VNRecognizedTextObservation else {
+                continue
+            }
+            let candidates = observation.topCandidates(5)
+            let topCandidate = candidates.first?.string
+            if STPCardValidator.sanitizedNumericString(for: topCandidate ?? "").count >= 4 {
+                allNumbers.append(topCandidate ?? "")
+            }
+            for recognizedText in candidates {
+                let possibleNumber = STPCardValidator.sanitizedNumericString(
+                    for: recognizedText.string
+                )
+                // This probably isn't something we're interested in, so don't bother processing it.
+                if possibleNumber.count < 4 {
+                    continue
+                }
+
+                // First strategy: We check if Vision sent us a number in a group on its own. If that fails, we'll try
+                // to catch it later when we iterate over all the numbers.
+                if STPCardValidator.validationState(
+                    forNumber: possibleNumber,
+                    validatingCardBrand: true
+                )
+                    == .valid
+                {
+                    addDetectedNumber(possibleNumber)
+                } else if possibleNumber.count >= 4 && possibleNumber.count <= 6
+                    && STPStringUtils.stringMayContainExpirationDate(recognizedText.string)
+                {
+                    // Try to parse anything that looks like an expiration date.
+                    let expirationString = STPStringUtils.expirationDateString(
+                        from: recognizedText.string
+                    )
+                    let sanitizedExpiration = STPCardValidator.sanitizedNumericString(
+                        for: expirationString ?? ""
+                    )
+                    let month = (sanitizedExpiration as NSString).substring(to: 2)
+                    let year = (sanitizedExpiration as NSString).substring(from: 2)
+
+                    // Ignore expiration dates 10+ years in the future, as they're likely to be incorrect recognitions
+                    let calendar = Calendar(identifier: .gregorian)
+                    let presentYear = calendar.component(.year, from: Date())
+                    let maxYear = (presentYear % 100) + 10
+
+                    if STPCardValidator.validationState(forExpirationYear: year, inMonth: month)
+                        == .valid
+                        && Int(year) ?? 0 < maxYear
+                    {
+                        addDetectedExpiration(sanitizedExpiration)
+                    }
+                }
+            }
+        }
+        // Second strategy: We look for consecutive groups of 4/4/4/4 or 4/6/5
+        // Vision is sending us groups like ["1234 565", "1234 1"], so we'll normalize these into groups with spaces:
+        let allGroups = allNumbers.joined(separator: " ").components(separatedBy: " ")
+        if allGroups.count < 3 {
+            return
+        }
+        for i in 0..<(allGroups.count - 3) {
+            let string1 = allGroups[i]
+            let string2 = allGroups[i + 1]
+            let string3 = allGroups[i + 2]
+            var string4 = ""
+            if i + 3 < allGroups.count {
+                string4 = allGroups[i + 3]
+            }
+            // Then we'll go through each group and build a potential match:
+            let potentialCardString = "\(string1)\(string2)\(string3)\(string4)"
+            let potentialAmexString = "\(string1)\(string2)\(string3)"
+
+            // Then we'll add valid matches. It's okay if we add a number a second time after doing so above, as the success of that first pass means it's more likely to be a good match.
+            if STPCardValidator.validationState(
+                forNumber: potentialCardString,
+                validatingCardBrand: true
+            )
+                == .valid
+            {
+                addDetectedNumber(potentialCardString)
+            } else if STPCardValidator.validationState(
+                forNumber: potentialAmexString,
+                validatingCardBrand: true
+            ) == .valid {
+                addDetectedNumber(potentialAmexString)
+            }
+        }
+    }
+
+    func addDetectedNumber(_ number: String) {
+        detectedNumbers?.add(number)
+
+        // Set a timeout: If we don't get enough scans in the next 1 second, we'll use the best option we have.
+        if !timeoutStarted {
+            timeoutStarted = true
+            weak var weakSelf = self
+            DispatchQueue.main.async(execute: {
+                let strongSelf = weakSelf
+                strongSelf?.cameraView?.playSnapshotAnimation()
+                strongSelf?.feedbackGenerator?.notificationOccurred(.success)
+            })
+            videoDataOutputQueue?.asyncAfter(
+                deadline: DispatchTime.now() + Double(
+                    Int64(kSTPCardScanningTimeout * Double(NSEC_PER_SEC))
+                )
+                    / Double(NSEC_PER_SEC),
+                execute: {
+                    let strongSelf = weakSelf
+                    if strongSelf?.isScanning ?? false {
+                        strongSelf?.didTimeout = true
+                        strongSelf?.finishIfReady()
+                    }
+                }
+            )
+        }
+
+        if (detectedNumbers?.count(for: number) ?? 0) >= kSTPCardScanningMinimumValidScans {
+            finishIfReady()
+        }
+    }
+
+    func addDetectedExpiration(_ expiration: String) {
+        detectedExpirations?.add(expiration)
+        if (detectedExpirations?.count(for: expiration) ?? 0) >= kSTPCardScanningMinimumValidScans {
+            finishIfReady()
+        }
+    }
+
+    // MARK: Completion
+    func finishIfReady() {
+        if !isScanning {
+            return
+        }
+        let detectedNumbers = self.detectedNumbers
+        let detectedExpirations = self.detectedExpirations
+
+        let topNumber = (detectedNumbers?.allObjects as NSArray?)?.sortedArray(comparator: {
+            obj1,
+            obj2 in
+            let c1 = detectedNumbers?.count(for: obj1) ?? 0
+            let c2 = detectedNumbers?.count(for: obj2) ?? 0
+            if c1 < c2 {
+                return .orderedAscending
+            } else if c1 > c2 {
+                return .orderedDescending
+            } else {
+                return .orderedSame
+            }
+        }).last
+        let topExpiration = (detectedExpirations?.allObjects as NSArray?)?.sortedArray(comparator: {
+            obj1,
+            obj2 in
+            let c1 = detectedExpirations?.count(for: obj1) ?? 0
+            let c2 = detectedExpirations?.count(for: obj2) ?? 0
+            if c1 < c2 {
+                return .orderedAscending
+            } else if c1 > c2 {
+                return .orderedDescending
+            } else {
+                return .orderedSame
+            }
+        }).last
+
+        if didTimeout
+            || (((detectedNumbers?.count(for: topNumber ?? 0) ?? 0)
+                >= kSTPCardScanningMinimumValidScans)
+                && ((detectedExpirations?.count(for: topExpiration ?? 0) ?? 0)
+                    >= kSTPCardScanningMinimumValidScans))
+            || ((detectedNumbers?.count(for: topNumber ?? 0) ?? 0) >= kSTPCardScanningMaxValidScans)
+        {
+            let params = STPPaymentMethodCardParams()
+            params.number = topNumber as? String
+            if let topExpiration = topExpiration {
+                params.expMonth = NSNumber(
+                    value: Int((topExpiration as! NSString).substring(to: 2)) ?? 0
+                )
+                params.expYear = NSNumber(
+                    value: Int((topExpiration as! NSString).substring(from: 2)) ?? 0
+                )
+            }
+            finish(with: params, error: nil)
+        }
+    }
+
+    func finish(with params: STPPaymentMethodCardParams?, error: Error?) {
+        var duration: TimeInterval?
+        if let startTime = startTime {
+            duration = Date().timeIntervalSince(startTime)
+        }
+        isScanning = false
+        captureDevice?.unlockForConfiguration()
+        captureSession?.stopRunning()
+
+        DispatchQueue.main.async(execute: {
+            if params == nil {
+                STPAnalyticsClient.sharedClient.logCardScanCancelled(withDuration: duration ?? 0.0)
+            } else {
+                STPAnalyticsClient.sharedClient.logCardScanSucceeded(withDuration: duration ?? 0.0)
+            }
+            self.feedbackGenerator = nil
+
+            self.cameraView?.captureSession = nil
+            self.delegate?.cardScanner(self, didFinishWith: params, error: error)
+        })
+    }
+
+    // MARK: Orientation
+}
+
+// The number of successful scans required for both card number and expiration date before returning a result.
+private let kSTPCardScanningMinimumValidScans = 2
+// If no expiration date is found, we'll return a result after this many successful scans.
+private let kSTPCardScanningMaxValidScans = 3
+// Once one successful scan is found, we'll stop scanning after this many seconds.
+private let kSTPCardScanningTimeout: TimeInterval = 1.0
+let STPCardScannerErrorDomain = "STPCardScannerErrorDomain"
+
+/// :nodoc:
+@available(macCatalyst 14.0, *)
+extension STPCardScanner: STPAnalyticsProtocol {
+    static var stp_analyticsIdentifier = "STPCardScanner"
+}

+ 67 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCardScannerTableViewCell.swift

@@ -0,0 +1,67 @@
+//
+//  STPCardScannerTableViewCell.swift
+//  StripeiOS
+//
+//  Created by David Estes on 8/17/20.
+//  Copyright © 2020 Stripe, Inc. All rights reserved.
+//
+
+import UIKit
+
+@available(macCatalyst 14.0, *)
+class STPCardScannerTableViewCell: UITableViewCell {
+    private(set) weak var cameraView: STPCameraView?
+
+    private var _theme: STPTheme?
+    var theme: STPTheme? {
+        get {
+            _theme
+        }
+        set(theme) {
+            _theme = theme
+            updateAppearance()
+        }
+    }
+
+    let cardSizeRatio: CGFloat = 2.125 / 3.370  // ID-1 card size (in inches)
+
+    override init(
+        style: UITableViewCell.CellStyle,
+        reuseIdentifier: String?
+    ) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        let cameraView = STPCameraView(frame: bounds)
+        contentView.addSubview(cameraView)
+        self.cameraView = cameraView
+        theme = STPTheme.defaultTheme
+        self.cameraView?.translatesAutoresizingMaskIntoConstraints = false
+        contentView.addConstraints(
+            [
+                cameraView.heightAnchor.constraint(
+                    equalTo: cameraView.widthAnchor,
+                    multiplier: cardSizeRatio
+                ),
+                cameraView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
+                cameraView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0),
+                cameraView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0),
+                cameraView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
+            ])
+        updateAppearance()
+    }
+
+    override func layoutSubviews() {
+
+        super.layoutSubviews()
+    }
+
+    @objc func updateAppearance() {
+        // The first few frames of the camera view will be black, so our background should be black too.
+        cameraView?.backgroundColor = UIColor.black
+    }
+
+    required init?(
+        coder aDecoder: NSCoder
+    ) {
+        super.init(coder: aDecoder)
+    }
+}

+ 9 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCardValidationState.swift

@@ -0,0 +1,9 @@
+//
+//  STPCardValidationState.swift
+//  StripeiOS
+//
+//  Created by Jack Flintermann on 8/7/15.
+//  Copyright (c) 2015 Stripe, Inc. All rights reserved.
+//
+
+import Foundation

+ 62 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCoreScrollViewController.swift

@@ -0,0 +1,62 @@
+//
+//  STPCoreScrollViewController.swift
+//  StripeiOS
+//
+//  Created by Brian Dorfman on 1/6/17.
+//  Copyright © 2017 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeUICore
+import UIKit
+
+/// This is the base class for all Stripe scroll view controllers. It is intended
+/// for use only by Stripe classes, you should not subclass it yourself in your app.
+public class STPCoreScrollViewController: STPCoreViewController {
+    /// This returns the scroll view being managed by the view controller
+    @objc public lazy var scrollView: UIScrollView = {
+        createScrollView()
+    }()
+
+    /// This method is used by the base implementation to create the object
+    /// backing the `scrollView` property. Subclasses can override to change the
+    /// type of the scroll view (eg UITableView or UICollectionView instead of
+    /// UIScrollView).
+
+    func createScrollView() -> UIScrollView {
+        return UIScrollView()
+    }
+
+    override func createAndSetupViews() {
+        super.createAndSetupViews()
+        view.addSubview(scrollView)
+    }
+
+    /// :nodoc:
+    @objc
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+
+        scrollView.contentInsetAdjustmentBehavior = .automatic
+    }
+
+    /// :nodoc:
+    @objc
+    public override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        scrollView.frame = view.bounds
+    }
+
+    @objc override func updateAppearance() {
+        super.updateAppearance()
+
+        scrollView.backgroundColor = theme.primaryBackgroundColor
+        scrollView.tintColor = theme.accentColor
+
+        if theme.primaryBackgroundColor.isBright {
+            scrollView.indicatorStyle = .black
+        } else {
+            scrollView.indicatorStyle = .white
+        }
+    }
+}

+ 54 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCoreTableViewController.swift

@@ -0,0 +1,54 @@
+//
+//  STPCoreTableViewController.swift
+//  StripeiOS
+//
+//  Created by Brian Dorfman on 1/6/17.
+//  Copyright © 2017 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+/// This is the base class for all Stripe scroll view controllers. It is intended
+/// for use only by Stripe classes, you should not subclass it yourself in your app.
+/// It inherits from STPCoreScrollViewController and changes the type of the
+/// created scroll view to UITableView, as well as other shared table view logic.
+public class STPCoreTableViewController: STPCoreScrollViewController {
+    /// This points to the same object as `STPCoreScrollViewController`'s `scrollView`
+    /// property but with the type cast to `UITableView`
+
+    @objc public var tableView: UITableView? {
+        return (scrollView as? UITableView)
+    }
+
+    override func createScrollView() -> UIScrollView {
+        let tableView = UITableView(frame: CGRect.zero, style: .grouped)
+        tableView.sectionHeaderHeight = 30
+
+        return tableView
+    }
+
+    /// :nodoc:
+    @objc
+    public override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+
+        tableView?.reloadData()
+    }
+
+    @objc override func updateAppearance() {
+        super.updateAppearance()
+        tableView?.separatorStyle = .none  // handle this with fake separator views for flexibility
+    }
+
+    /// :nodoc:
+    @objc
+    public func tableView(
+        _ tableView: UITableView,
+        heightForHeaderInSection section: Int
+    )
+        -> CGFloat
+    {
+        return 0.01
+    }
+}

+ 162 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCoreViewController.swift

@@ -0,0 +1,162 @@
+//
+//  STPCoreViewController.swift
+//  StripeiOS
+//
+//  Created by Brian Dorfman on 1/6/17.
+//  Copyright © 2017 Stripe, Inc. All rights reserved.
+//
+
+@_spi(STP) import StripeUICore
+import UIKit
+
+/// This is the base class for all Stripe view controllers. It is intended for use
+/// only by Stripe classes, you should not subclass it yourself in your app.
+/// It theming, back/cancel button management, and other shared logic for
+/// Stripe view controllers.
+public class STPCoreViewController: UIViewController {
+    /// A convenience initializer; equivalent to calling `init(theme: STPTheme.defaultTheme)`.
+    @objc
+    public convenience init() {
+        self.init(theme: STPTheme.defaultTheme)
+    }
+
+    /// Initializes a new view controller with the specified theme
+    /// - Parameter theme: The theme to use to inform the view controller's visual appearance. - seealso: STPTheme
+    @objc public required init(
+        theme: STPTheme?
+    ) {
+        super.init(nibName: nil, bundle: nil)
+        commonInit(with: theme)
+    }
+
+    /// Passes through to the default UIViewController behavior for this initializer,
+    /// and then also sets the default theme as in `init`
+    @objc public required override init(
+        nibName nibNameOrNil: String?,
+        bundle nibBundleOrNil: Bundle?
+    ) {
+        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+        commonInit(with: STPTheme.defaultTheme)
+    }
+
+    /// Passes through to the default UIViewController behavior for this initializer,
+    /// and then also sets the default theme as in `init`
+    @objc public required init?(
+        coder aDecoder: NSCoder
+    ) {
+        super.init(coder: aDecoder)
+        commonInit(with: STPTheme.defaultTheme)
+    }
+
+    private var _theme: STPTheme = STPTheme.defaultTheme
+    @objc var theme: STPTheme {
+        get {
+            _theme
+        }
+        set(theme) {
+            _theme = theme
+            updateAppearance()
+        }
+    }
+    @objc var cancelItem: UIBarButtonItem?
+
+    /// All designated initializers funnel through this method to do their setup
+    /// - Parameter theme: Initial theme for this view controller
+    func commonInit(with theme: STPTheme?) {
+        if let theme = theme {
+            _theme = theme
+        } else {
+            _theme = .defaultTheme
+        }
+
+        if !useSystemBackButton() {
+            cancelItem = UIBarButtonItem(
+                barButtonSystemItem: .cancel,
+                target: self,
+                action: #selector(STPAddCardViewController.handleCancelTapped(_:))
+            )
+            cancelItem?.accessibilityIdentifier = "CoreViewControllerCancelIdentifier"
+
+            stp_navigationItemProxy?.leftBarButtonItem = cancelItem
+        }
+
+        NotificationCenter.default.addObserver(
+            self,
+            selector: #selector(STPAddCardViewController.updateAppearance),
+            name: UIContentSizeCategory.didChangeNotification,
+            object: nil
+        )
+    }
+
+    /// Called in viewDidLoad after doing base implementation, before
+    /// calling updateAppearance
+    func createAndSetupViews() {
+        // do nothing
+    }
+
+    // These viewDidX() methods have significant code done
+    // in the base class and super must be called if they are overidden
+    /// :nodoc:
+    @objc
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+
+        createAndSetupViews()
+        updateAppearance()
+    }
+    /// :nodoc:
+    @objc
+    public override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        updateAppearance()
+    }
+    /// :nodoc:
+    @objc
+    public override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        view.endEditing(true)
+    }
+
+    /// Update views based on current STPTheme
+    @objc func updateAppearance() {
+        let navBarTheme = navigationController?.navigationBar.stp_theme ?? theme
+        navigationItem.leftBarButtonItem?.stp_setTheme(navBarTheme)
+        navigationItem.rightBarButtonItem?.stp_setTheme(navBarTheme)
+        cancelItem?.stp_setTheme(navBarTheme)
+
+        view.backgroundColor = theme.primaryBackgroundColor
+
+        setNeedsStatusBarAppearanceUpdate()
+    }
+
+    /// :nodoc:
+    @objc public override var preferredStatusBarStyle: UIStatusBarStyle {
+        let navBarTheme = navigationController?.navigationBar.stp_theme ?? theme
+        return navBarTheme.secondaryBackgroundColor.isBright
+            ? .default
+            : .lightContent
+    }
+
+    /// Called by the automatically-managed back/cancel button
+    /// By default pops the top item off the navigation stack, or if we are the
+    /// root of the navigation controller, dimisses presentation
+    /// - Parameter sender: Sender of the target action, if applicable.
+    @objc func handleCancelTapped(_ sender: Any?) {
+        if stp_isAtRootOfNavigationController() {
+            // if we're the root of the navigation controller, we've been presented modally.
+            presentingViewController?.dismiss(animated: true)
+        } else {
+            // otherwise, we've been pushed onto the stack.
+            navigationController?.popViewController(animated: true)
+        }
+    }
+
+    /// If you override this and return YES, then your CoreVC implementation will not
+    /// create and set up a cancel and instead just use the default
+    /// UIViewController back button behavior.
+    /// You won't receive calls to `handleCancelTapped` if this is YES.
+    /// Defaults to NO.
+    func useSystemBackButton() -> Bool {
+        return false
+    }
+}

+ 419 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPCustomerContext.swift

@@ -0,0 +1,419 @@
+//
+//  STPCustomerContext.swift
+//  StripeiOS
+//
+//  Created by Ben Guo on 5/2/17.
+//  Copyright © 2017 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripeCore
+@_spi(STP) import StripePaymentsUI
+
+/// An `STPCustomerContext` retrieves and updates a Stripe customer and their attached
+/// payment methods using an ephemeral key, a short-lived API key scoped to a specific
+/// customer object. If your current user logs out of your app and a new user logs in,
+/// be sure to either create a new instance of `STPCustomerContext` or clear the current
+/// instance's cache. On your backend, be sure to create and return a
+/// new ephemeral key for the Customer object associated with the new user.
+open class STPCustomerContext: NSObject, STPBackendAPIAdapter {
+    /// Initializes a new `STPCustomerContext` with the specified key provider.
+    /// Upon initialization, a CustomerContext will fetch a new ephemeral key from
+    /// your backend and use it to prefetch the customer object specified in the key.
+    /// Subsequent customer and payment method retrievals (e.g. by `STPPaymentContext`)
+    /// will return the prefetched customer / attached payment methods immediately if
+    /// its age does not exceed 60 seconds.
+    /// - Parameter keyProvider:   The key provider the customer context will use.
+    /// - Returns: the newly-instantiated customer context.
+    @objc(initWithKeyProvider:)
+    public convenience init(
+        keyProvider: STPCustomerEphemeralKeyProvider
+    ) {
+        self.init(keyProvider: keyProvider, apiClient: STPAPIClient.shared)
+    }
+
+    /// Initializes a new `STPCustomerContext` with the specified key provider.
+    /// Upon initialization, a CustomerContext will fetch a new ephemeral key from
+    /// your backend and use it to prefetch the customer object specified in the key.
+    /// Subsequent customer and payment method retrievals (e.g. by `STPPaymentContext`)
+    /// will return the prefetched customer / attached payment methods immediately if
+    /// its age does not exceed 60 seconds.
+    /// - Parameters:
+    ///   - keyProvider:   The key provider the customer context will use.
+    ///   - apiClient:       The API Client to use to make requests.
+    /// - Returns: the newly-instantiated customer context.
+    @objc(initWithKeyProvider:apiClient:)
+    public convenience init(
+        keyProvider: STPCustomerEphemeralKeyProvider?,
+        apiClient: STPAPIClient
+    ) {
+        let keyManager = STPEphemeralKeyManager(
+            keyProvider: keyProvider,
+            apiVersion: STPAPIClient.apiVersion,
+            performsEagerFetching: true
+        )
+        self.init(keyManager: keyManager, apiClient: apiClient)
+    }
+
+    /// `STPCustomerContext` will cache its customer object and associated payment methods
+    /// for up to 60 seconds. If your current user logs out of your app and a new user logs
+    /// in, be sure to either call this method or create a new instance of `STPCustomerContext`.
+    /// On your backend, be sure to create and return a new ephemeral key for the
+    /// customer object associated with the new user.
+    @objc
+    public func clearCache() {
+        clearCachedCustomer()
+        clearCachedPaymentMethods()
+    }
+
+    private var _includeApplePayPaymentMethods = false
+    /// By default, `STPCustomerContext` will filter Apple Pay when it retrieves
+    /// Payment Methods. Apple Pay payment methods should generally not be re-used and
+    /// shouldn't be offered to customers as a new payment method (Apple Pay payment
+    /// methods may only be re-used for subscriptions).
+    /// If you are using `STPCustomerContext` to back your own UI and would like to
+    /// disable Apple Pay filtering, set this property to YES.
+    /// Note: If you are using `STPPaymentContext`, you should not change this property.
+    @objc public var includeApplePayPaymentMethods: Bool {
+        get {
+            _includeApplePayPaymentMethods
+        }
+        set(includeApplePayMethods) {
+            _includeApplePayPaymentMethods = includeApplePayMethods
+            customer?.updateSources(filteringApplePay: !includeApplePayMethods)
+        }
+    }
+
+    private var _customer: STPCustomer?
+    private var customer: STPCustomer? {
+        get {
+            _customer
+        }
+        set(customer) {
+            _customer = customer
+            customerRetrievedDate = (customer) != nil ? Date() : nil
+        }
+    }
+    @objc internal var customerRetrievedDate: Date?
+
+    private var _paymentMethods: [STPPaymentMethod]?
+    private var paymentMethods: [STPPaymentMethod]? {
+        get {
+            if !includeApplePayPaymentMethods {
+                var paymentMethodsExcludingApplePay: [STPPaymentMethod]? = []
+                for paymentMethod in _paymentMethods ?? [] {
+                    let isApplePay =
+                        paymentMethod.type == .card && paymentMethod.card?.wallet?.type == .applePay
+                    if !isApplePay {
+                        paymentMethodsExcludingApplePay?.append(paymentMethod)
+                    }
+                }
+                return paymentMethodsExcludingApplePay ?? []
+            } else {
+                return _paymentMethods ?? []
+            }
+        }
+        set(paymentMethods) {
+            _paymentMethods = paymentMethods
+            paymentMethodsRetrievedDate = paymentMethods != nil ? Date() : nil
+        }
+    }
+    @objc internal var paymentMethodsRetrievedDate: Date?
+    private var keyManager: STPEphemeralKeyManagerProtocol
+    private var apiClient: STPAPIClient
+
+    init(
+        keyManager: STPEphemeralKeyManagerProtocol,
+        apiClient: STPAPIClient
+    ) {
+        STPAnalyticsClient.sharedClient.addClass(toProductUsageIfNecessary: STPCustomerContext.self)
+        self.keyManager = keyManager
+        self.apiClient = apiClient
+        _includeApplePayPaymentMethods = false
+        super.init()
+        retrieveCustomer(nil)
+        listPaymentMethodsForCustomer(completion: nil)
+    }
+
+    func clearCachedCustomer() {
+        customer = nil
+    }
+
+    func clearCachedPaymentMethods() {
+        paymentMethods = nil
+    }
+
+    func shouldUseCachedCustomer() -> Bool {
+        if customer == nil || customerRetrievedDate == nil {
+            return false
+        }
+        let now = Date()
+        if let customerRetrievedDate = customerRetrievedDate {
+            return now.timeIntervalSince(customerRetrievedDate) < CachedCustomerMaxAge
+        }
+        return false
+    }
+
+    func shouldUseCachedPaymentMethods() -> Bool {
+        if paymentMethods == nil || paymentMethodsRetrievedDate == nil {
+            return false
+        }
+        let now = Date()
+        if let paymentMethodsRetrievedDate = paymentMethodsRetrievedDate {
+            return now.timeIntervalSince(paymentMethodsRetrievedDate) < CachedCustomerMaxAge
+        }
+        return false
+    }
+
+    // MARK: - STPBackendAPIAdapter
+    @objc
+    public func retrieveCustomer(_ completion: STPCustomerCompletionBlock? = nil) {
+        if shouldUseCachedCustomer() {
+            if let completion = completion {
+                stpDispatchToMainThreadIfNecessary({
+                    completion(self.customer, nil)
+                })
+            }
+            return
+        }
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(nil, retrieveKeyError)
+                    })
+                }
+                return
+            }
+            self.apiClient.retrieveCustomer(using: ephemeralKey) { customer, error in
+                if let customer = customer {
+                    customer.updateSources(filteringApplePay: !self.includeApplePayPaymentMethods)
+                    self.customer = customer
+                }
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(self.customer, error)
+                    })
+                }
+            }
+        })
+    }
+
+    @objc
+    public func updateCustomer(
+        withShippingAddress shipping: STPAddress,
+        completion: STPErrorBlock?
+    ) {
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(retrieveKeyError)
+                    })
+                }
+                return
+            }
+            var params: [String: Any] = [:]
+            params["shipping"] = STPAddress.shippingInfoForCharge(
+                with: shipping,
+                shippingMethod: nil
+            )
+            self.apiClient.updateCustomer(
+                withParameters: params,
+                using: ephemeralKey
+            ) { customer, error in
+                if let customer = customer {
+                    customer.updateSources(filteringApplePay: !self.includeApplePayPaymentMethods)
+                    self.customer = customer
+                }
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(error)
+                    })
+                }
+            }
+        })
+    }
+
+    /// A convenience method for attaching the PaymentMethod to the current Customer
+    @objc
+    public func attachPaymentMethodToCustomer(
+        paymentMethodId: String,
+        completion: STPErrorBlock?
+    ) {
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(retrieveKeyError)
+                    })
+                }
+                return
+            }
+
+            self.apiClient.attachPaymentMethod(
+                paymentMethodId,
+                toCustomerUsing: ephemeralKey
+            ) { error in
+                self.clearCachedPaymentMethods()
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(error)
+                    })
+                }
+            }
+        })
+    }
+
+    @objc
+    public func attachPaymentMethod(
+        toCustomer paymentMethod: STPPaymentMethod,
+        completion: STPErrorBlock?
+    ) {
+        attachPaymentMethodToCustomer(
+            paymentMethodId: paymentMethod.stripeId,
+            completion: completion
+        )
+    }
+
+    /// A convenience method for detaching the PaymentMethod to the current Customer
+    @objc
+    public func detachPaymentMethodFromCustomer(
+        paymentMethodId: String,
+        completion: STPErrorBlock?
+    ) {
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(retrieveKeyError)
+                    })
+                }
+                return
+            }
+
+            self.apiClient.detachPaymentMethod(
+                paymentMethodId,
+                fromCustomerUsing: ephemeralKey
+            ) { error in
+                self.clearCachedPaymentMethods()
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(error)
+                    })
+                }
+            }
+        })
+
+    }
+
+    @objc
+    public func detachPaymentMethod(
+        fromCustomer paymentMethod: STPPaymentMethod,
+        completion: STPErrorBlock?
+    ) {
+        detachPaymentMethodFromCustomer(
+            paymentMethodId: paymentMethod.stripeId,
+            completion: completion
+        )
+    }
+
+    @objc
+    public func listPaymentMethodsForCustomer(completion: STPPaymentMethodsCompletionBlock? = nil) {
+        if shouldUseCachedPaymentMethods() {
+            if let completion = completion {
+                stpDispatchToMainThreadIfNecessary({
+                    completion(self.paymentMethods, nil)
+                })
+            }
+            return
+        }
+
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(nil, retrieveKeyError)
+                    })
+                }
+                return
+            }
+
+            self.apiClient.listPaymentMethodsForCustomer(using: ephemeralKey) {
+                paymentMethods,
+                error in
+                if paymentMethods != nil {
+                    self.paymentMethods = paymentMethods
+                }
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(self.paymentMethods, error)
+                    })
+                }
+            }
+        })
+    }
+
+    func saveLastSelectedPaymentMethodID(
+        forCustomer paymentMethodID: String?,
+        completion: STPErrorBlock?
+    ) {
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                if let completion = completion {
+                    stpDispatchToMainThreadIfNecessary({
+                        completion(retrieveKeyError)
+                    })
+                }
+                return
+            }
+
+            var customerToDefaultPaymentMethodID =
+                (UserDefaults.standard.dictionary(forKey: kLastSelectedPaymentMethodDefaultsKey))
+                as? [String: String] ?? [:]
+            if let customerID = ephemeralKey.customerID {
+                customerToDefaultPaymentMethodID[customerID] = paymentMethodID
+                UserDefaults.standard.set(
+                    customerToDefaultPaymentMethodID,
+                    forKey: kLastSelectedPaymentMethodDefaultsKey
+                )
+            }
+
+            if let completion = completion {
+                stpDispatchToMainThreadIfNecessary({
+                    completion(nil)
+                })
+            }
+        })
+    }
+
+    func retrieveLastSelectedPaymentMethodIDForCustomer(
+        completion: @escaping (String?, Error?) -> Void
+    ) {
+        keyManager.getOrCreateKey({ ephemeralKey, retrieveKeyError in
+            guard let ephemeralKey = ephemeralKey, retrieveKeyError == nil else {
+                stpDispatchToMainThreadIfNecessary({
+                    completion(nil, retrieveKeyError)
+                })
+                return
+            }
+
+            let customerToDefaultPaymentMethodID =
+                (UserDefaults.standard.dictionary(forKey: kLastSelectedPaymentMethodDefaultsKey))
+                as? [String: String] ?? [:]
+            stpDispatchToMainThreadIfNecessary({
+                completion(customerToDefaultPaymentMethodID[ephemeralKey.customerID ?? ""], nil)
+            })
+        })
+    }
+}
+
+/// Stores the key we use in NSUserDefaults to save a dictionary of Customer id to their last selected payment method ID
+private let kLastSelectedPaymentMethodDefaultsKey =
+    UserDefaults.StripeKeys.customerToLastSelectedPaymentMethod.rawValue
+private let CachedCustomerMaxAge: TimeInterval = 60
+
+/// :nodoc:
+@_spi(STP) extension STPCustomerContext: STPAnalyticsProtocol {
+    @_spi(STP) public static var stp_analyticsIdentifier = "STPCustomerContext"
+}

+ 104 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPEphemeralKey.swift

@@ -0,0 +1,104 @@
+//
+//  STPEphemeralKey.swift
+//  StripeiOS
+//
+//  Created by Ben Guo on 5/4/17.
+//  Copyright © 2017 Stripe, Inc. All rights reserved.
+//
+
+import Foundation
+@_spi(STP) import StripePayments
+
+class STPEphemeralKey: NSObject, STPAPIResponseDecodable {
+    private(set) var stripeID: String
+    private(set) var created: Date
+    private(set) var livemode = false
+    private(set) var secret: String
+    private(set) var expires: Date
+    private(set) var customerID: String?
+    private(set) var issuingCardID: String?
+
+    /// You cannot directly instantiate an `STPEphemeralKey`. You should instead use
+    /// `decodedObjectFromAPIResponse:` to create a key from a JSON response.
+    required init(
+        stripeID: String,
+        created: Date,
+        secret: String,
+        expires: Date
+    ) {
+        self.stripeID = stripeID
+        self.created = created
+        self.secret = secret
+        self.expires = expires
+        super.init()
+    }
+
+    private(set) var allResponseFields: [AnyHashable: Any] = [:]
+
+    class func decodedObject(fromAPIResponse response: [AnyHashable: Any]?) -> Self? {
+        guard let response = response else {
+            return nil
+        }
+        let dict = response.stp_dictionaryByRemovingNulls()
+
+        // required fields
+        guard
+            let stripeId = dict.stp_string(forKey: "id"),
+            let created = dict.stp_date(forKey: "created"),
+            let secret = dict.stp_string(forKey: "secret"),
+            let expires = dict.stp_date(forKey: "expires"),
+            let associatedObjects = dict.stp_array(forKey: "associated_objects"),
+            dict["livemode"] != nil
+        else {
+            return nil
+        }
+
+        var customerID: String?
+        var issuingCardID: String?
+        for obj in associatedObjects {
+            if let obj = obj as? [AnyHashable: Any] {
+                let type = obj.stp_string(forKey: "type")
+                if type == "customer" {
+                    customerID = obj.stp_string(forKey: "id")
+                }
+                if type == "issuing.card" {
+                    issuingCardID = obj.stp_string(forKey: "id")
+                }
+            }
+        }
+        if customerID == nil && issuingCardID == nil {
+            return nil
+        }
+        let key = self.init(stripeID: stripeId, created: created, secret: secret, expires: expires)
+        key.customerID = customerID
+        key.issuingCardID = issuingCardID
+        key.stripeID = stripeId
+        key.livemode = dict.stp_bool(forKey: "livemode", or: true)
+        key.created = created
+        key.secret = secret
+        key.expires = expires
+        key.allResponseFields = response
+        return key
+    }
+
+    override var hash: Int {
+        return stripeID.hash
+    }
+
+    override func isEqual(_ object: Any?) -> Bool {
+        if self === (object as? STPEphemeralKey) {
+            return true
+        }
+        if object == nil || !(object is STPEphemeralKey) {
+            return false
+        }
+        if let object = object as? STPEphemeralKey {
+            return isEqual(to: object)
+        }
+        return false
+    }
+
+    func isEqual(to other: STPEphemeralKey) -> Bool {
+        return stripeID == other.stripeID
+    }
+}

+ 0 - 0
Pods/Stripe/Stripe/StripeiOS/Source/STPEphemeralKeyManager.swift


Some files were not shown because too many files changed in this diff