@exodus/react-native-wallet 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +268 -0
  3. package/android/build.gradle +103 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifestNew.xml +2 -0
  6. package/android/src/main/java/com/expensify/wallet/Utils.kt +148 -0
  7. package/android/src/main/java/com/expensify/wallet/WalletModule.kt +380 -0
  8. package/android/src/main/java/com/expensify/wallet/WalletPackage.kt +48 -0
  9. package/android/src/main/java/com/expensify/wallet/components/RNAddToWalletButtonManager.kt +19 -0
  10. package/android/src/main/java/com/expensify/wallet/components/RNAddToWalletButtonView.kt +42 -0
  11. package/android/src/main/java/com/expensify/wallet/error/InvalidNetworkError.kt +6 -0
  12. package/android/src/main/java/com/expensify/wallet/event/OnCardActivatedEvent.kt +18 -0
  13. package/android/src/main/java/com/expensify/wallet/model/CardData.kt +12 -0
  14. package/android/src/main/java/com/expensify/wallet/model/CardStatus.kt +15 -0
  15. package/android/src/main/java/com/expensify/wallet/model/TokenizationStatus.kt +7 -0
  16. package/android/src/main/java/com/expensify/wallet/model/WalletData.kt +7 -0
  17. package/android/src/main/res/drawable/add_to_googlewallet_button_content.xml +7 -0
  18. package/android/src/main/res/drawable/badge_add_to_googlewallet_button_content.xml +7 -0
  19. package/android/src/main/res/drawable/googlewallet_button_background.xml +5 -0
  20. package/android/src/main/res/drawable/googlewallet_button_overlay.xml +11 -0
  21. package/android/src/main/res/drawable-af/add_to_googlewallet_button_content.xml +31 -0
  22. package/android/src/main/res/drawable-af/badge_add_to_googlewallet_button_content.xml +31 -0
  23. package/android/src/main/res/drawable-am/add_to_googlewallet_button_content.xml +34 -0
  24. package/android/src/main/res/drawable-am/badge_add_to_googlewallet_button_content.xml +34 -0
  25. package/android/src/main/res/drawable-ar/add_to_googlewallet_button_content.xml +7 -0
  26. package/android/src/main/res/drawable-ar/badge_add_to_googlewallet_button_content.xml +7 -0
  27. package/android/src/main/res/drawable-az/add_to_googlewallet_button_content.xml +7 -0
  28. package/android/src/main/res/drawable-az/badge_add_to_googlewallet_button_content.xml +7 -0
  29. package/android/src/main/res/drawable-bg/add_to_googlewallet_button_content.xml +7 -0
  30. package/android/src/main/res/drawable-bg/badge_add_to_googlewallet_button_content.xml +7 -0
  31. package/android/src/main/res/drawable-bn/add_to_googlewallet_button_content.xml +31 -0
  32. package/android/src/main/res/drawable-bn/badge_add_to_googlewallet_button_content.xml +31 -0
  33. package/android/src/main/res/drawable-br/add_to_googlewallet_button_content.xml +7 -0
  34. package/android/src/main/res/drawable-br/badge_add_to_googlewallet_button_content.xml +7 -0
  35. package/android/src/main/res/drawable-bs/add_to_googlewallet_button_content.xml +7 -0
  36. package/android/src/main/res/drawable-bs/badge_add_to_googlewallet_button_content.xml +7 -0
  37. package/android/src/main/res/drawable-by/add_to_googlewallet_button_content.xml +7 -0
  38. package/android/src/main/res/drawable-by/badge_add_to_googlewallet_button_content.xml +7 -0
  39. package/android/src/main/res/drawable-ca/add_to_googlewallet_button_content.xml +7 -0
  40. package/android/src/main/res/drawable-ca/badge_add_to_googlewallet_button_content.xml +7 -0
  41. package/android/src/main/res/drawable-cz/add_to_googlewallet_button_content.xml +7 -0
  42. package/android/src/main/res/drawable-cz/badge_add_to_googlewallet_button_content.xml +7 -0
  43. package/android/src/main/res/drawable-de/add_to_googlewallet_button_content.xml +7 -0
  44. package/android/src/main/res/drawable-de/badge_add_to_googlewallet_button_content.xml +7 -0
  45. package/android/src/main/res/drawable-dk/add_to_googlewallet_button_content.xml +7 -0
  46. package/android/src/main/res/drawable-dk/badge_add_to_googlewallet_button_content.xml +7 -0
  47. package/android/src/main/res/drawable-en-rau/add_to_googlewallet_button_content.xml +7 -0
  48. package/android/src/main/res/drawable-en-rau/badge_add_to_googlewallet_button_content.xml +7 -0
  49. package/android/src/main/res/drawable-en-rca/add_to_googlewallet_button_content.xml +7 -0
  50. package/android/src/main/res/drawable-en-rca/badge_add_to_googlewallet_button_content.xml +7 -0
  51. package/android/src/main/res/drawable-en-rgb/add_to_googlewallet_button_content.xml +7 -0
  52. package/android/src/main/res/drawable-en-rgb/badge_add_to_googlewallet_button_content.xml +7 -0
  53. package/android/src/main/res/drawable-en-rin/add_to_googlewallet_button_content.xml +7 -0
  54. package/android/src/main/res/drawable-en-rin/badge_add_to_googlewallet_button_content.xml +7 -0
  55. package/android/src/main/res/drawable-en-rsg/add_to_googlewallet_button_content.xml +7 -0
  56. package/android/src/main/res/drawable-en-rsg/badge_add_to_googlewallet_button_content.xml +7 -0
  57. package/android/src/main/res/drawable-en-rus/add_to_googlewallet_button_content.xml +7 -0
  58. package/android/src/main/res/drawable-en-rus/badge_add_to_googlewallet_button_content.xml +7 -0
  59. package/android/src/main/res/drawable-en-rza/add_to_googlewallet_button_content.xml +7 -0
  60. package/android/src/main/res/drawable-en-rza/badge_add_to_googlewallet_button_content.xml +7 -0
  61. package/android/src/main/res/drawable-es-res/add_to_googlewallet_button_content.xml +7 -0
  62. package/android/src/main/res/drawable-es-res/badge_add_to_googlewallet_button_content.xml +7 -0
  63. package/android/src/main/res/drawable-es-rus/add_to_googlewallet_button_content.xml +7 -0
  64. package/android/src/main/res/drawable-es-rus/badge_add_to_googlewallet_button_content.xml +7 -0
  65. package/android/src/main/res/drawable-et/add_to_googlewallet_button_content.xml +7 -0
  66. package/android/src/main/res/drawable-et/badge_add_to_googlewallet_button_content.xml +7 -0
  67. package/android/src/main/res/drawable-fa/add_to_googlewallet_button_content.xml +31 -0
  68. package/android/src/main/res/drawable-fa/badge_add_to_googlewallet_button_content.xml +34 -0
  69. package/android/src/main/res/drawable-fl/add_to_googlewallet_button_content.xml +7 -0
  70. package/android/src/main/res/drawable-fl/badge_add_to_googlewallet_button_content.xml +7 -0
  71. package/android/src/main/res/drawable-fp/add_to_googlewallet_button_content.xml +7 -0
  72. package/android/src/main/res/drawable-fp/badge_add_to_googlewallet_button_content.xml +7 -0
  73. package/android/src/main/res/drawable-fr-rca/add_to_googlewallet_button_content.xml +7 -0
  74. package/android/src/main/res/drawable-fr-rca/badge_add_to_googlewallet_button_content.xml +7 -0
  75. package/android/src/main/res/drawable-fr-rfr/add_to_googlewallet_button_content.xml +7 -0
  76. package/android/src/main/res/drawable-fr-rfr/badge_add_to_googlewallet_button_content.xml +7 -0
  77. package/android/src/main/res/drawable-gr/add_to_googlewallet_button_content.xml +7 -0
  78. package/android/src/main/res/drawable-gr/badge_add_to_googlewallet_button_content.xml +7 -0
  79. package/android/src/main/res/drawable-hdpi/googlepay_button_background_image.9.png +0 -0
  80. package/android/src/main/res/drawable-he/add_to_googlewallet_button_content.xml +7 -0
  81. package/android/src/main/res/drawable-he/badge_add_to_googlewallet_button_content.xml +7 -0
  82. package/android/src/main/res/drawable-hr/add_to_googlewallet_button_content.xml +7 -0
  83. package/android/src/main/res/drawable-hr/badge_add_to_googlewallet_button_content.xml +7 -0
  84. package/android/src/main/res/drawable-hu/add_to_googlewallet_button_content.xml +7 -0
  85. package/android/src/main/res/drawable-hu/badge_add_to_googlewallet_button_content.xml +7 -0
  86. package/android/src/main/res/drawable-hy/add_to_googlewallet_button_content.xml +7 -0
  87. package/android/src/main/res/drawable-hy/badge_add_to_googlewallet_button_content.xml +7 -0
  88. package/android/src/main/res/drawable-id/add_to_googlewallet_button_content.xml +7 -0
  89. package/android/src/main/res/drawable-id/badge_add_to_googlewallet_button_content.xml +7 -0
  90. package/android/src/main/res/drawable-is/add_to_googlewallet_button_content.xml +7 -0
  91. package/android/src/main/res/drawable-is/badge_add_to_googlewallet_button_content.xml +7 -0
  92. package/android/src/main/res/drawable-it/add_to_googlewallet_button_content.xml +7 -0
  93. package/android/src/main/res/drawable-it/badge_add_to_googlewallet_button_content.xml +7 -0
  94. package/android/src/main/res/drawable-jp/add_to_googlewallet_button_content.xml +7 -0
  95. package/android/src/main/res/drawable-jp/badge_add_to_googlewallet_button_content.xml +7 -0
  96. package/android/src/main/res/drawable-ka/add_to_googlewallet_button_content.xml +7 -0
  97. package/android/src/main/res/drawable-ka/badge_add_to_googlewallet_button_content.xml +7 -0
  98. package/android/src/main/res/drawable-kh/add_to_googlewallet_button_content.xml +33 -0
  99. package/android/src/main/res/drawable-kh/badge_add_to_googlewallet_button_content.xml +33 -0
  100. package/android/src/main/res/drawable-kk/add_to_googlewallet_button_content.xml +7 -0
  101. package/android/src/main/res/drawable-kk/badge_add_to_googlewallet_button_content.xml +7 -0
  102. package/android/src/main/res/drawable-ky/add_to_googlewallet_button_content.xml +7 -0
  103. package/android/src/main/res/drawable-ky/badge_add_to_googlewallet_button_content.xml +7 -0
  104. package/android/src/main/res/drawable-lo/add_to_googlewallet_button_content.xml +31 -0
  105. package/android/src/main/res/drawable-lo/badge_add_to_googlewallet_button_content.xml +31 -0
  106. package/android/src/main/res/drawable-lt/add_to_googlewallet_button_content.xml +7 -0
  107. package/android/src/main/res/drawable-lt/badge_add_to_googlewallet_button_content.xml +7 -0
  108. package/android/src/main/res/drawable-lv/add_to_googlewallet_button_content.xml +7 -0
  109. package/android/src/main/res/drawable-lv/badge_add_to_googlewallet_button_content.xml +7 -0
  110. package/android/src/main/res/drawable-mdpi/googlepay_button_background_image.9.png +0 -0
  111. package/android/src/main/res/drawable-mk/add_to_googlewallet_button_content.xml +7 -0
  112. package/android/src/main/res/drawable-mk/badge_add_to_googlewallet_button_content.xml +7 -0
  113. package/android/src/main/res/drawable-mn/add_to_googlewallet_button_content.xml +31 -0
  114. package/android/src/main/res/drawable-mn/badge_add_to_googlewallet_button_content.xml +31 -0
  115. package/android/src/main/res/drawable-my/add_to_googlewallet_button_content.xml +31 -0
  116. package/android/src/main/res/drawable-my/badge_add_to_googlewallet_button_content.xml +31 -0
  117. package/android/src/main/res/drawable-ne/add_to_googlewallet_button_content.xml +28 -0
  118. package/android/src/main/res/drawable-ne/badge_add_to_googlewallet_button_content.xml +31 -0
  119. package/android/src/main/res/drawable-nl/add_to_googlewallet_button_content.xml +7 -0
  120. package/android/src/main/res/drawable-nl/badge_add_to_googlewallet_button_content.xml +7 -0
  121. package/android/src/main/res/drawable-no/add_to_googlewallet_button_content.xml +7 -0
  122. package/android/src/main/res/drawable-no/badge_add_to_googlewallet_button_content.xml +7 -0
  123. package/android/src/main/res/drawable-pl/add_to_googlewallet_button_content.xml +7 -0
  124. package/android/src/main/res/drawable-pl/badge_add_to_googlewallet_button_content.xml +7 -0
  125. package/android/src/main/res/drawable-pt/add_to_googlewallet_button_content.xml +7 -0
  126. package/android/src/main/res/drawable-pt/badge_add_to_googlewallet_button_content.xml +7 -0
  127. package/android/src/main/res/drawable-ro/add_to_googlewallet_button_content.xml +7 -0
  128. package/android/src/main/res/drawable-ro/badge_add_to_googlewallet_button_content.xml +7 -0
  129. package/android/src/main/res/drawable-ru/add_to_googlewallet_button_content.xml +7 -0
  130. package/android/src/main/res/drawable-ru/badge_add_to_googlewallet_button_content.xml +7 -0
  131. package/android/src/main/res/drawable-se/add_to_googlewallet_button_content.xml +7 -0
  132. package/android/src/main/res/drawable-se/badge_add_to_googlewallet_button_content.xml +7 -0
  133. package/android/src/main/res/drawable-si/add_to_googlewallet_button_content.xml +31 -0
  134. package/android/src/main/res/drawable-si/badge_add_to_googlewallet_button_content.xml +31 -0
  135. package/android/src/main/res/drawable-sk/add_to_googlewallet_button_content.xml +7 -0
  136. package/android/src/main/res/drawable-sk/badge_add_to_googlewallet_button_content.xml +7 -0
  137. package/android/src/main/res/drawable-sl/add_to_googlewallet_button_content.xml +7 -0
  138. package/android/src/main/res/drawable-sl/badge_add_to_googlewallet_button_content.xml +7 -0
  139. package/android/src/main/res/drawable-sq/add_to_googlewallet_button_content.xml +7 -0
  140. package/android/src/main/res/drawable-sq/badge_add_to_googlewallet_button_content.xml +7 -0
  141. package/android/src/main/res/drawable-sr/add_to_googlewallet_button_content.xml +7 -0
  142. package/android/src/main/res/drawable-sr/badge_add_to_googlewallet_button_content.xml +7 -0
  143. package/android/src/main/res/drawable-sw/add_to_googlewallet_button_content.xml +31 -0
  144. package/android/src/main/res/drawable-sw/badge_add_to_googlewallet_button_content.xml +31 -0
  145. package/android/src/main/res/drawable-th/add_to_googlewallet_button_content.xml +7 -0
  146. package/android/src/main/res/drawable-th/badge_add_to_googlewallet_button_content.xml +7 -0
  147. package/android/src/main/res/drawable-tr/add_to_googlewallet_button_content.xml +7 -0
  148. package/android/src/main/res/drawable-tr/badge_add_to_googlewallet_button_content.xml +7 -0
  149. package/android/src/main/res/drawable-uk/add_to_googlewallet_button_content.xml +7 -0
  150. package/android/src/main/res/drawable-uk/badge_add_to_googlewallet_button_content.xml +7 -0
  151. package/android/src/main/res/drawable-ur/add_to_googlewallet_button_content.xml +34 -0
  152. package/android/src/main/res/drawable-ur/badge_add_to_googlewallet_button_content.xml +34 -0
  153. package/android/src/main/res/drawable-uz/add_to_googlewallet_button_content.xml +7 -0
  154. package/android/src/main/res/drawable-uz/badge_add_to_googlewallet_button_content.xml +7 -0
  155. package/android/src/main/res/drawable-vi/add_to_googlewallet_button_content.xml +7 -0
  156. package/android/src/main/res/drawable-vi/badge_add_to_googlewallet_button_content.xml +7 -0
  157. package/android/src/main/res/drawable-xhdpi/googlepay_button_background_image.9.png +0 -0
  158. package/android/src/main/res/drawable-xxhdpi/googlepay_button_background_image.9.png +0 -0
  159. package/android/src/main/res/drawable-xxxhdpi/googlepay_button_background_image.9.png +0 -0
  160. package/android/src/main/res/drawable-zh-rhk/add_to_googlewallet_button_content.xml +7 -0
  161. package/android/src/main/res/drawable-zh-rhk/badge_add_to_googlewallet_button_content.xml +7 -0
  162. package/android/src/main/res/drawable-zh-rtw/add_to_googlewallet_button_content.xml +7 -0
  163. package/android/src/main/res/drawable-zh-rtw/badge_add_to_googlewallet_button_content.xml +7 -0
  164. package/android/src/main/res/layout/add_to_googlewallet_badge.xml +35 -0
  165. package/android/src/main/res/layout/add_to_googlewallet_button.xml +34 -0
  166. package/android/src/main/res/values/googlewallet_strings.xml +7 -0
  167. package/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +83 -0
  168. package/assets/buttons/android_button.svg +16 -0
  169. package/assets/buttons/ios_button.svg +259 -0
  170. package/assets/hero-image.png +0 -0
  171. package/assets/signature-dark.png +0 -0
  172. package/assets/signature-light.png +0 -0
  173. package/ios/RNAddToWalletButtonBridge.m +8 -0
  174. package/ios/RNWallet.h +13 -0
  175. package/ios/RNWallet.mm +153 -0
  176. package/ios/WalletManager.swift +297 -0
  177. package/ios/components/RNAddToWalletButtonManager.swift +13 -0
  178. package/ios/components/RNAddToWalletButtonView.swift +67 -0
  179. package/ios/stucts/AddPassResponse.swift +36 -0
  180. package/ios/stucts/CardInfo.swift +37 -0
  181. package/ios/stucts/WalletEncryptedPayload.swift +27 -0
  182. package/lib/commonjs/AddToWalletButton.js +83 -0
  183. package/lib/commonjs/AddToWalletButton.js.map +1 -0
  184. package/lib/commonjs/NativeWallet.js +22 -0
  185. package/lib/commonjs/NativeWallet.js.map +1 -0
  186. package/lib/commonjs/index.js +156 -0
  187. package/lib/commonjs/index.js.map +1 -0
  188. package/lib/commonjs/utils.js +38 -0
  189. package/lib/commonjs/utils.js.map +1 -0
  190. package/lib/module/AddToWalletButton.js +76 -0
  191. package/lib/module/AddToWalletButton.js.map +1 -0
  192. package/lib/module/NativeWallet.js +17 -0
  193. package/lib/module/NativeWallet.js.map +1 -0
  194. package/lib/module/index.js +132 -0
  195. package/lib/module/index.js.map +1 -0
  196. package/lib/module/utils.js +32 -0
  197. package/lib/module/utils.js.map +1 -0
  198. package/lib/typescript/src/AddToWalletButton.d.ts +16 -0
  199. package/lib/typescript/src/AddToWalletButton.d.ts.map +1 -0
  200. package/lib/typescript/src/NativeWallet.d.ts +77 -0
  201. package/lib/typescript/src/NativeWallet.d.ts.map +1 -0
  202. package/lib/typescript/src/index.d.ts +22 -0
  203. package/lib/typescript/src/index.d.ts.map +1 -0
  204. package/lib/typescript/src/utils.d.ts +5 -0
  205. package/lib/typescript/src/utils.d.ts.map +1 -0
  206. package/package.json +157 -0
  207. package/react-native-wallet.podspec +36 -0
  208. package/src/AddToWalletButton.tsx +78 -0
  209. package/src/NativeWallet.ts +115 -0
  210. package/src/index.tsx +184 -0
  211. package/src/utils.ts +35 -0
@@ -0,0 +1,297 @@
1
+ import Foundation
2
+ import PassKit
3
+ import UIKit
4
+ import React
5
+
6
+ public typealias CompletionHandler = (OperationResult, NSDictionary?) -> Void
7
+
8
+ @objc public protocol WalletDelegate {
9
+ func sendEvent(name: String, result: NSDictionary)
10
+ }
11
+
12
+ @objc
13
+ open class WalletManager: UIViewController {
14
+
15
+ @objc public weak var delegate: WalletDelegate? = nil
16
+
17
+ private var addPassViewController: PKAddPaymentPassViewController?
18
+
19
+ private var presentAddPaymentPassCompletionHandler: (CompletionHandler)?
20
+
21
+ private var addPaymentPassCompletionHandler: (CompletionHandler)?
22
+
23
+ private var addPassHandler: ((PKAddPaymentPassRequest) -> Void)?
24
+
25
+ @objc public var packageName = "react-native-wallet"
26
+
27
+ let passLibrary = PKPassLibrary()
28
+
29
+ override init(nibName: String?, bundle: Bundle?) {
30
+ super.init(nibName: nibName, bundle: bundle)
31
+ addPassObserver()
32
+ }
33
+
34
+ required public init?(coder: NSCoder) {
35
+ super.init(coder: coder)
36
+ addPassObserver()
37
+ }
38
+
39
+ deinit {
40
+ NotificationCenter.default.removeObserver(self)
41
+ }
42
+
43
+ func addPassObserver() {
44
+ NotificationCenter.default.addObserver(
45
+ self,
46
+ selector: #selector(passLibraryDidChange),
47
+ name: NSNotification.Name(rawValue: PKPassLibraryNotificationName.PKPassLibraryDidChange.rawValue),
48
+ object: passLibrary
49
+ )
50
+ }
51
+
52
+ @objc func passLibraryDidChange(_ notification: Notification) {
53
+ guard let userInfo = notification.userInfo else {
54
+ return
55
+ }
56
+
57
+ // Check if passes were added or status changed
58
+ if let addedPasses = userInfo[PKPassLibraryNotificationKey.addedPassesUserInfoKey] as? [PKPass] {
59
+ checkPassActivationStatus(addedPasses)
60
+ }
61
+
62
+ // Check for updated passes
63
+ if let replacedPasses = userInfo[PKPassLibraryNotificationKey.replacementPassesUserInfoKey] as? [PKPass] {
64
+ checkPassActivationStatus(replacedPasses)
65
+ }
66
+ }
67
+
68
+ func checkPassActivationStatus(_ passes: [PKPass]) {
69
+ for pass in passes {
70
+ if pass.secureElementPass?.passActivationState == .activated {
71
+ delegate?.sendEvent(name: Event.onCardActivated.rawValue, result: [
72
+ "state": "activated",
73
+ "serialNumber": pass.serialNumber
74
+ ]);
75
+ }
76
+ }
77
+ }
78
+
79
+ @objc
80
+ public func checkWalletAvailability() -> Bool {
81
+ return isPassKitAvailable();
82
+ }
83
+
84
+ @objc
85
+ public func IOSPresentAddPaymentPassView(cardData: NSDictionary, completion: @escaping CompletionHandler) {
86
+ guard isPassKitAvailable() else {
87
+ completion(.error, [
88
+ "errorMessage": "InApp enrollment not available for this device"
89
+ ])
90
+ return
91
+ }
92
+
93
+ let card: CardInfo
94
+ do {
95
+ card = try CardInfo(cardData: cardData)
96
+ }
97
+ catch {
98
+ completion(.error, [
99
+ "errorMessage": "Invalid card data. Please check your card information and try again..."
100
+ ])
101
+ return
102
+ }
103
+
104
+ guard let configuration = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
105
+ completion(.error, [
106
+ "errorMessage": "InApp enrollment configuraton fails"
107
+ ])
108
+ return
109
+ }
110
+
111
+ configuration.cardholderName = card.cardHolderName
112
+ configuration.primaryAccountSuffix = card.lastDigits
113
+ configuration.localizedDescription = String(card.cardDescription)
114
+
115
+ guard let enrollViewController = PKAddPaymentPassViewController(requestConfiguration: configuration, delegate: self) else {
116
+ completion(.error, [
117
+ "errorMessage": "InApp enrollment controller configuration fails"
118
+ ])
119
+ return
120
+ }
121
+
122
+ presentAddPaymentPassCompletionHandler = completion
123
+ DispatchQueue.main.async {
124
+ if self.addPassViewController == nil {
125
+ self.addPassViewController = enrollViewController
126
+ RCTPresentedViewController()?.present(enrollViewController, animated: true, completion: nil)
127
+ } else {
128
+ self.logInfo(message: "EnrollViewController is already presented.")
129
+ self.presentAddPaymentPassCompletionHandler = nil
130
+ completion(.error, [
131
+ "errorMessage": "EnrollViewController is already presented."
132
+ ])
133
+ }
134
+ }
135
+ }
136
+
137
+ @objc
138
+ public func IOSHandleAddPaymentPassResponse(payload: NSDictionary, completion: @escaping CompletionHandler) {
139
+ guard addPassHandler != nil else {
140
+ hideModal()
141
+ completion(.error, [
142
+ "errorMessage": "addPassHandler unavailable"
143
+ ])
144
+ return
145
+ }
146
+
147
+ let walletData: WalletEncryptedPayload
148
+ do {
149
+ walletData = try WalletEncryptedPayload(data: payload)
150
+ } catch {
151
+ hideModal()
152
+ completion(.error, [
153
+ "errorMessage": "Invalid payload data"
154
+ ])
155
+ return
156
+ }
157
+
158
+ self.addPaymentPassCompletionHandler = completion
159
+
160
+ let addPaymentPassRequest = PKAddPaymentPassRequest()
161
+ addPaymentPassRequest.encryptedPassData = walletData.encryptedPassData
162
+ addPaymentPassRequest.activationData = walletData.activationData
163
+ addPaymentPassRequest.ephemeralPublicKey = walletData.ephemeralPublicKey
164
+ self.addPassHandler?(addPaymentPassRequest)
165
+ self.addPassHandler = nil
166
+ }
167
+
168
+ private func getPassActivationState(matching condition: (PKSecureElementPass) -> Bool) -> NSNumber {
169
+ let paymentPasses = passLibrary.passes(of: .payment)
170
+ if paymentPasses.isEmpty {
171
+ self.logInfo(message: "No passes found in Wallet.")
172
+ return NSNumber(value: -1)
173
+ }
174
+
175
+ for pass in paymentPasses {
176
+ guard let securePassElement = pass.secureElementPass else { continue }
177
+ if condition(securePassElement) {
178
+ return NSNumber(value: securePassElement.passActivationState.rawValue)
179
+ }
180
+ }
181
+ return NSNumber(value: -1)
182
+ }
183
+
184
+ @objc public func getCardStatusBySuffix(last4Digits: NSString) -> NSNumber {
185
+ return getPassActivationState { pass in
186
+ return pass.primaryAccountNumberSuffix.hasSuffix(last4Digits as String)
187
+ }
188
+ }
189
+
190
+ @objc public func getCardStatusByIdentifier(identifier: NSString) -> NSNumber {
191
+ return getPassActivationState { pass in
192
+ return pass.primaryAccountIdentifier == identifier as String
193
+ }
194
+ }
195
+
196
+ private func isPassKitAvailable() -> Bool {
197
+ return PKAddPaymentPassViewController.canAddPaymentPass()
198
+ }
199
+
200
+ private func hideModal() {
201
+ DispatchQueue.main.async {
202
+ if let enrollVC = self.addPassViewController, enrollVC.isBeingPresented || enrollVC.presentingViewController != nil {
203
+ enrollVC.dismiss(animated: true, completion: {
204
+ self.addPassViewController = nil
205
+ })
206
+ } else {
207
+ self.logInfo(message: "EnrollViewController is not presented currently.")
208
+ }
209
+ }
210
+ }
211
+
212
+ private func logInfo(message: String) {
213
+ print("[\(packageName)] \(message)")
214
+ }
215
+ }
216
+
217
+ extension WalletManager: PKAddPaymentPassViewControllerDelegate {
218
+ // Perform the bridge from Apple -> Issuer -> Apple
219
+ public func addPaymentPassViewController(
220
+ _ controller: PKAddPaymentPassViewController,
221
+ generateRequestWithCertificateChain certificates: [Data],
222
+ nonce: Data, nonceSignature: Data,
223
+ completionHandler handler: @escaping (PKAddPaymentPassRequest) -> Void) {
224
+ let stringNonce = nonce.base64EncodedString() as NSString
225
+ let stringNonceSignature = nonceSignature.base64EncodedString() as NSString
226
+ let stringCertificates = certificates.map {
227
+ $0.base64EncodedString() as NSString
228
+ }
229
+ let reqestCardData = AddPassResponse(status: .completed, nonce: stringNonce, nonceSignature: stringNonceSignature, certificates: stringCertificates)
230
+ self.addPassHandler = handler
231
+
232
+ // Retry the JS issuer callback if the user tries again to add a payment pass
233
+ if let addPaymentPassHandler = addPaymentPassCompletionHandler {
234
+ addPaymentPassHandler(.retry, reqestCardData.toNSDictionary())
235
+ addPaymentPassCompletionHandler = nil
236
+ return
237
+ }
238
+
239
+ // Finish IOSPresentAddPaymentPassView function
240
+ if let presentPassHandler = presentAddPaymentPassCompletionHandler {
241
+ presentPassHandler(.completed, reqestCardData.toNSDictionary())
242
+ presentAddPaymentPassCompletionHandler = nil
243
+ }
244
+ }
245
+
246
+ // This method will be called when enroll process ends (with success/error)
247
+ public func addPaymentPassViewController(
248
+ _ controller: PKAddPaymentPassViewController,
249
+ didFinishAdding pass: PKPaymentPass?,
250
+ error: Error?) {
251
+ if addPassViewController == nil {
252
+ return
253
+ }
254
+
255
+ let errorMessage = error?.localizedDescription ?? ""
256
+
257
+ if error != nil {
258
+ self.logInfo(message: "Error: \(errorMessage)")
259
+ delegate?.sendEvent(name: Event.onCardActivated.rawValue, result: [
260
+ "state": "canceled"
261
+ ]);
262
+ }
263
+
264
+ // Cancel the IOSPresentAddPaymentPassView function when the user cancelled the modal
265
+ if let handler = presentAddPaymentPassCompletionHandler {
266
+ let response = AddPassResponse(status: .canceled, nonce: nil, nonceSignature: nil, certificates: nil)
267
+ handler(.canceled, response.toNSDictionary())
268
+ }
269
+
270
+ // If the pass is returned complete the IOSHandleAddPaymentPassResponse function
271
+ if let addPaymentPassHandler = addPaymentPassCompletionHandler {
272
+ if pass != nil {
273
+ addPaymentPassHandler(.completed, nil)
274
+ } else {
275
+ addPaymentPassHandler(.error, [
276
+ "errorMessage": "Could not add card. \(errorMessage)."
277
+ ])
278
+ }
279
+ }
280
+
281
+ addPassHandler = nil
282
+ hideModal()
283
+ addPaymentPassCompletionHandler = nil
284
+ presentAddPaymentPassCompletionHandler = nil
285
+ }
286
+ }
287
+
288
+ extension WalletManager {
289
+ enum Event: String, CaseIterable {
290
+ case onCardActivated
291
+ }
292
+
293
+ @objc
294
+ public static var supportedEvents: [String] {
295
+ return Event.allCases.map(\.rawValue);
296
+ }
297
+ }
@@ -0,0 +1,13 @@
1
+ import Foundation
2
+ import React
3
+
4
+ @objc(RNAddToWalletButtonManager)
5
+ class RNAddToWalletButtonManager: RCTViewManager {
6
+ override func view() -> UIView! {
7
+ return RNAddToWalletButtonView()
8
+ }
9
+
10
+ override static func requiresMainQueueSetup() -> Bool {
11
+ return true
12
+ }
13
+ }
@@ -0,0 +1,67 @@
1
+ import Foundation
2
+ import PassKit
3
+ import UIKit
4
+
5
+ @objc(RNAddToWalletButtonView)
6
+ class RNAddToWalletButtonView: UIView {
7
+ private var addPassButton = PKAddPassButton(addPassButtonStyle: .blackOutline)
8
+ private let defaultWidth: CGFloat = 120
9
+ private let defaultHeight: CGFloat = 40
10
+
11
+ @objc var buttonStyle: NSString = "" {
12
+ didSet {
13
+ updateButtonStyle()
14
+ }
15
+ }
16
+
17
+ @objc var borderRadius: CGFloat = 4.0 {
18
+ didSet {
19
+ applyBorderRadius()
20
+ }
21
+ }
22
+
23
+ override init(frame: CGRect) {
24
+ super.init(frame: frame)
25
+ setupButton()
26
+ }
27
+
28
+ required init?(coder: NSCoder) {
29
+ super.init(coder: coder)
30
+ setupButton()
31
+ }
32
+
33
+ private func setupButton() {
34
+ addSubview(addPassButton)
35
+ updateButtonStyle()
36
+ }
37
+
38
+ private func applyBorderRadius() {
39
+ let maxRadius = addPassButton.bounds.height / 2
40
+ let radius = min(borderRadius, maxRadius)
41
+
42
+ addPassButton.layer.cornerRadius = radius
43
+ addPassButton.layer.masksToBounds = true
44
+ addPassButton.clipsToBounds = true
45
+ }
46
+
47
+ override func layoutSubviews() {
48
+ super.layoutSubviews()
49
+
50
+ let width = bounds.width > 0 ? bounds.width : defaultWidth
51
+ let height = bounds.height > 0 ? bounds.height : defaultHeight
52
+
53
+ addPassButton.frame = CGRect(x: 0, y: 0, width: width, height: height)
54
+
55
+ applyBorderRadius()
56
+ }
57
+
58
+ private func updateButtonStyle() {
59
+ let styleString = (buttonStyle as String).lowercased()
60
+ let style: PKAddPassButtonStyle = (styleString == "black") ? .black : .blackOutline
61
+ addPassButton.removeFromSuperview()
62
+ addPassButton = PKAddPassButton(addPassButtonStyle: style)
63
+
64
+ addSubview(addPassButton)
65
+ setNeedsLayout()
66
+ }
67
+ }
@@ -0,0 +1,36 @@
1
+ import PassKit
2
+
3
+ struct AddPassResponse {
4
+ let status: OperationResult
5
+ let nonce: NSString?
6
+ let nonceSignature: NSString?
7
+ let certificates: [NSString]?
8
+
9
+ func toNSDictionary() -> NSDictionary {
10
+ if status.rawValue > 0 {
11
+ return [
12
+ "status": status.rawValue,
13
+ ]
14
+ }
15
+
16
+ guard let nonce = nonce,
17
+ let nonceSignature = nonceSignature,
18
+ let certificates = certificates else {
19
+ return [:]
20
+ }
21
+
22
+ return [
23
+ "status": status.rawValue,
24
+ "nonce": nonce,
25
+ "nonceSignature": nonceSignature,
26
+ "certificates": certificates
27
+ ]
28
+ }
29
+ }
30
+
31
+ @objc public enum OperationResult: Int {
32
+ case completed = 0
33
+ case canceled
34
+ case retry
35
+ case error
36
+ }
@@ -0,0 +1,37 @@
1
+ import PassKit
2
+
3
+ struct CardInfo {
4
+ let network: PKPaymentNetwork
5
+ let cardHolderName: String
6
+ let lastDigits: String
7
+ let cardDescription: String
8
+
9
+ init(cardData: NSDictionary) throws {
10
+ guard let networkString = cardData["network"] as? String, !networkString.isEmpty,
11
+ let network = CardInfo.getNetwork(from: networkString),
12
+ let cardHolderName = cardData["cardHolderName"] as? String, !cardHolderName.isEmpty,
13
+ let lastDigits = cardData["lastDigits"] as? String, !lastDigits.isEmpty,
14
+ let cardDescription = cardData["cardDescription"] as? String, !cardDescription.isEmpty else {
15
+ throw CardInfoError.invalidData(description: "Required data fields are missing or invalid.")
16
+ }
17
+
18
+ self.network = network
19
+ self.cardHolderName = cardHolderName
20
+ self.lastDigits = lastDigits
21
+ self.cardDescription = cardDescription
22
+ }
23
+
24
+ private static func getNetwork(from identifier: String) -> PKPaymentNetwork? {
25
+ switch identifier.lowercased() {
26
+ case "visa": return .visa
27
+ case "mastercard": return .masterCard
28
+ case "amex": return .amex
29
+ case "discover": return .discover
30
+ default: return nil
31
+ }
32
+ }
33
+ }
34
+
35
+ enum CardInfoError: Error {
36
+ case invalidData(description: String)
37
+ }
@@ -0,0 +1,27 @@
1
+ struct WalletEncryptedPayload {
2
+ let activationData: Data?
3
+ let ephemeralPublicKey: Data?
4
+ let encryptedPassData: Data?
5
+
6
+ init(data: NSDictionary) throws {
7
+ guard let activationDataString = data["activationData"] as? String, !activationDataString.isEmpty,
8
+ let ephemeralPublicKeyString = data["ephemeralPublicKey"] as? String, !ephemeralPublicKeyString.isEmpty,
9
+ let encryptedPassDataString = data["encryptedPassData"] as? String, !encryptedPassDataString.isEmpty else {
10
+ throw WalletPayloadError.invalidData(description: "Required data fields are missing or invalid.")
11
+ }
12
+
13
+ guard let activationData = Data(base64Encoded: activationDataString, options: .ignoreUnknownCharacters),
14
+ let ephemeralPublicKey = Data(base64Encoded: ephemeralPublicKeyString, options: .ignoreUnknownCharacters),
15
+ let encryptedPassData = Data(base64Encoded: encryptedPassDataString, options: .ignoreUnknownCharacters) else {
16
+ throw WalletPayloadError.invalidData(description: "Data encoding failed or data is corrupted.")
17
+ }
18
+
19
+ self.activationData = activationData
20
+ self.ephemeralPublicKey = ephemeralPublicKey
21
+ self.encryptedPassData = encryptedPassData
22
+ }
23
+ }
24
+
25
+ enum WalletPayloadError: Error {
26
+ case invalidData(description: String)
27
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _reactNative = require("react-native");
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const NativeWalletButton = (0, _reactNative.requireNativeComponent)('RNAddToWalletButton');
11
+ const BUTTON_TYPE_BREAKPOINT = 236;
12
+ const BUTTON_DIMENSIONS = {
13
+ basic: {
14
+ ios: {
15
+ width: 300,
16
+ height: 40
17
+ },
18
+ android: {
19
+ width: 300,
20
+ height: 48
21
+ }
22
+ },
23
+ badge: {
24
+ ios: {
25
+ width: 120,
26
+ height: 40
27
+ },
28
+ android: {
29
+ width: 200,
30
+ height: 56
31
+ }
32
+ }
33
+ };
34
+ function AddToWalletButton({
35
+ style,
36
+ buttonStyle = 'black',
37
+ buttonType = 'basic',
38
+ borderRadius = 4,
39
+ onPress
40
+ }) {
41
+ const flattenedStyle = _reactNative.StyleSheet.flatten(style) || {};
42
+ const safeButtonType = buttonType === 'badge' ? 'badge' : 'basic';
43
+ const currentDimensions = BUTTON_DIMENSIONS[safeButtonType][_reactNative.Platform.OS];
44
+ const {
45
+ width = currentDimensions.width,
46
+ height = currentDimensions.height,
47
+ ...rest
48
+ } = flattenedStyle;
49
+ return /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
50
+ onPress: onPress,
51
+ activeOpacity: 0.8,
52
+ style: [rest,
53
+ // Android allows us to define the type of the button, however on iOS type depends on the width.
54
+ // Adding this limits to ensure consistent behavior across platforms.
55
+ buttonType === 'badge' ? {
56
+ maxWidth: BUTTON_TYPE_BREAKPOINT - 1
57
+ } : {
58
+ minWidth: BUTTON_TYPE_BREAKPOINT
59
+ }, {
60
+ width,
61
+ height
62
+ }, styles.touchable]
63
+ }, /*#__PURE__*/_react.default.createElement(NativeWalletButton, {
64
+ style: styles.fill,
65
+ buttonStyle: buttonStyle,
66
+ borderRadius: borderRadius,
67
+ buttonType: safeButtonType
68
+ }));
69
+ }
70
+ const styles = _reactNative.StyleSheet.create({
71
+ touchable: {
72
+ justifyContent: 'center',
73
+ alignItems: 'center',
74
+ margin: 10
75
+ },
76
+ fill: {
77
+ flex: 1,
78
+ width: '100%',
79
+ height: '100%'
80
+ }
81
+ });
82
+ var _default = exports.default = AddToWalletButton;
83
+ //# sourceMappingURL=AddToWalletButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","NativeWalletButton","requireNativeComponent","BUTTON_TYPE_BREAKPOINT","BUTTON_DIMENSIONS","basic","ios","width","height","android","badge","AddToWalletButton","style","buttonStyle","buttonType","borderRadius","onPress","flattenedStyle","StyleSheet","flatten","safeButtonType","currentDimensions","Platform","OS","rest","createElement","TouchableOpacity","activeOpacity","maxWidth","minWidth","styles","touchable","fill","create","justifyContent","alignItems","margin","flex","_default","exports"],"sourceRoot":"../../src","sources":["AddToWalletButton.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAEA,IAAAC,YAAA,GAAAD,OAAA;AAA4F,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAgB5F,MAAMG,kBAA0D,GAAG,IAAAC,mCAAsB,EAAC,qBAAqB,CAAC;AAEhH,MAAMC,sBAAsB,GAAG,GAAG;AAClC,MAAMC,iBAAiB,GAAG;EACxBC,KAAK,EAAE;IACLC,GAAG,EAAE;MAACC,KAAK,EAAE,GAAG;MAAEC,MAAM,EAAE;IAAE,CAAC;IAC7BC,OAAO,EAAE;MAACF,KAAK,EAAE,GAAG;MAAEC,MAAM,EAAE;IAAE;EAClC,CAAC;EACDE,KAAK,EAAE;IACLJ,GAAG,EAAE;MAACC,KAAK,EAAE,GAAG;MAAEC,MAAM,EAAE;IAAE,CAAC;IAC7BC,OAAO,EAAE;MAACF,KAAK,EAAE,GAAG;MAAEC,MAAM,EAAE;IAAE;EAClC;AACF,CAAC;AAED,SAASG,iBAAiBA,CAAC;EAACC,KAAK;EAAEC,WAAW,GAAG,OAAO;EAAEC,UAAU,GAAG,OAAO;EAAEC,YAAY,GAAG,CAAC;EAAEC;AAAc,CAAC,EAAE;EACjH,MAAMC,cAAc,GAAGC,uBAAU,CAACC,OAAO,CAACP,KAAK,CAAC,IAAI,CAAC,CAAC;EACtD,MAAMQ,cAA0B,GAAGN,UAAU,KAAK,OAAO,GAAG,OAAO,GAAG,OAAO;EAC7E,MAAMO,iBAAiB,GAAGjB,iBAAiB,CAACgB,cAAc,CAAC,CAACE,qBAAQ,CAACC,EAAE,CAAsB;EAC7F,MAAM;IAAChB,KAAK,GAAGc,iBAAiB,CAACd,KAAK;IAAEC,MAAM,GAAGa,iBAAiB,CAACb,MAAM;IAAE,GAAGgB;EAAI,CAAC,GAAGP,cAAc;EAEpG,oBACEvB,MAAA,CAAAM,OAAA,CAAAyB,aAAA,CAAC5B,YAAA,CAAA6B,gBAAgB;IACfV,OAAO,EAAEA,OAAQ;IACjBW,aAAa,EAAE,GAAI;IACnBf,KAAK,EAAE,CACLY,IAAI;IACJ;IACA;IACAV,UAAU,KAAK,OAAO,GAAG;MAACc,QAAQ,EAAEzB,sBAAsB,GAAG;IAAC,CAAC,GAAG;MAAC0B,QAAQ,EAAE1B;IAAsB,CAAC,EACpG;MACEI,KAAK;MACLC;IACF,CAAC,EACDsB,MAAM,CAACC,SAAS;EAChB,gBAEFrC,MAAA,CAAAM,OAAA,CAAAyB,aAAA,CAACxB,kBAAkB;IACjBW,KAAK,EAAEkB,MAAM,CAACE,IAAK;IACnBnB,WAAW,EAAEA,WAAY;IACzBE,YAAY,EAAEA,YAAa;IAC3BD,UAAU,EAAEM;EAAe,CAC5B,CACe,CAAC;AAEvB;AAEA,MAAMU,MAAM,GAAGZ,uBAAU,CAACe,MAAM,CAAC;EAC/BF,SAAS,EAAE;IACTG,cAAc,EAAE,QAAQ;IACxBC,UAAU,EAAE,QAAQ;IACpBC,MAAM,EAAE;EACV,CAAC;EACDJ,IAAI,EAAE;IACJK,IAAI,EAAE,CAAC;IACP9B,KAAK,EAAE,MAAM;IACbC,MAAM,EAAE;EACV;AACF,CAAC,CAAC;AAAC,IAAA8B,QAAA,GAAAC,OAAA,CAAAvC,OAAA,GAEYW,iBAAiB","ignoreList":[]}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.PACKAGE_NAME = void 0;
7
+ var _reactNative = require("react-native");
8
+ const PACKAGE_NAME = exports.PACKAGE_NAME = '@expensify/react-native-wallet';
9
+ // Try catch block to prevent crashing in case the module is not linked.
10
+ // Especialy useful for builds where Google SDK is not available
11
+ // eslint-disable-next-line import/no-mutable-exports
12
+ let Wallet;
13
+ try {
14
+ Wallet = _reactNative.TurboModuleRegistry.getEnforcing('RNWallet');
15
+ } catch (error) {
16
+ if (error instanceof Error) {
17
+ // eslint-disable-next-line no-console
18
+ console.warn(`[${PACKAGE_NAME}] Failed to load Wallet module, ${error.message}`);
19
+ }
20
+ }
21
+ var _default = exports.default = Wallet;
22
+ //# sourceMappingURL=NativeWallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","PACKAGE_NAME","exports","Wallet","TurboModuleRegistry","getEnforcing","error","Error","console","warn","message","_default","default"],"sourceRoot":"../../src","sources":["NativeWallet.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAqFA,MAAMC,YAAY,GAAAC,OAAA,CAAAD,YAAA,GAAG,gCAAgC;AACrD;AACA;AACA;AACA,IAAIE,MAAwB;AAC5B,IAAI;EACFA,MAAM,GAAGC,gCAAmB,CAACC,YAAY,CAAO,UAAU,CAAC;AAC7D,CAAC,CAAC,OAAOC,KAAK,EAAE;EACd,IAAIA,KAAK,YAAYC,KAAK,EAAE;IAC1B;IACAC,OAAO,CAACC,IAAI,CAAC,IAAIR,YAAY,mCAAmCK,KAAK,CAACI,OAAO,EAAE,CAAC;EAClF;AACF;AAAC,IAAAC,QAAA,GAAAT,OAAA,CAAAU,OAAA,GACcT,MAAM","ignoreList":[]}