@stripe/stripe-react-native 0.16.0 → 0.17.0

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 (145) hide show
  1. package/.husky/pre-commit +4 -0
  2. package/CHANGELOG.md +14 -0
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/java/com/reactnativestripesdk/CollectBankAccountLauncherFragment.kt +5 -1
  5. package/android/src/main/java/com/reactnativestripesdk/FinancialConnectionsSheetFragment.kt +284 -0
  6. package/android/src/main/java/com/reactnativestripesdk/GooglePayFragment.kt +4 -0
  7. package/android/src/main/java/com/reactnativestripesdk/GooglePayPaymentMethodLauncherFragment.kt +6 -2
  8. package/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt +9 -11
  9. package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +3 -1
  10. package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +46 -14
  11. package/android/src/main/java/com/reactnativestripesdk/utils/Errors.kt +4 -0
  12. package/android/src/main/java/com/reactnativestripesdk/utils/Extensions.kt +11 -0
  13. package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +5 -4
  14. package/ios/Errors.swift +11 -0
  15. package/ios/FinancialConnections.swift +258 -0
  16. package/ios/Mappers.swift +12 -9
  17. package/ios/StripeSdk.m +10 -1
  18. package/ios/StripeSdk.swift +28 -1
  19. package/lib/commonjs/NativeStripeSdk.js.map +1 -1
  20. package/lib/commonjs/components/AddToWalletButton.js +1 -1
  21. package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
  22. package/lib/commonjs/components/ApplePayButton.js +1 -1
  23. package/lib/commonjs/components/ApplePayButton.js.map +1 -1
  24. package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
  25. package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
  26. package/lib/commonjs/components/CardField.js +1 -1
  27. package/lib/commonjs/components/CardField.js.map +1 -1
  28. package/lib/commonjs/components/CardForm.js +1 -1
  29. package/lib/commonjs/components/CardForm.js.map +1 -1
  30. package/lib/commonjs/components/GooglePayButton.js +1 -1
  31. package/lib/commonjs/components/GooglePayButton.js.map +1 -1
  32. package/lib/commonjs/components/StripeContainer.js +1 -1
  33. package/lib/commonjs/components/StripeContainer.js.map +1 -1
  34. package/lib/commonjs/components/StripeProvider.js +1 -1
  35. package/lib/commonjs/components/StripeProvider.js.map +1 -1
  36. package/lib/commonjs/functions.js +1 -1
  37. package/lib/commonjs/functions.js.map +1 -1
  38. package/lib/commonjs/hooks/useApplePay.js +1 -1
  39. package/lib/commonjs/hooks/useApplePay.js.map +1 -1
  40. package/lib/commonjs/hooks/useConfirmPayment.js +1 -1
  41. package/lib/commonjs/hooks/useConfirmPayment.js.map +1 -1
  42. package/lib/commonjs/hooks/useConfirmSetupIntent.js +1 -1
  43. package/lib/commonjs/hooks/useConfirmSetupIntent.js.map +1 -1
  44. package/lib/commonjs/hooks/useFinancialConnectionsSheet.js +2 -0
  45. package/lib/commonjs/hooks/useFinancialConnectionsSheet.js.map +1 -0
  46. package/lib/commonjs/hooks/useGooglePay.js +1 -1
  47. package/lib/commonjs/hooks/useGooglePay.js.map +1 -1
  48. package/lib/commonjs/hooks/usePaymentSheet.js +1 -1
  49. package/lib/commonjs/hooks/usePaymentSheet.js.map +1 -1
  50. package/lib/commonjs/hooks/useStripe.js +1 -1
  51. package/lib/commonjs/hooks/useStripe.js.map +1 -1
  52. package/lib/commonjs/index.js +1 -1
  53. package/lib/commonjs/index.js.map +1 -1
  54. package/lib/commonjs/plugin/withStripe.js +1 -1
  55. package/lib/commonjs/plugin/withStripe.js.map +1 -1
  56. package/lib/commonjs/types/FinancialConnections.js +2 -0
  57. package/lib/commonjs/types/FinancialConnections.js.map +1 -0
  58. package/lib/commonjs/types/PaymentIntent.js.map +1 -1
  59. package/lib/commonjs/types/SetupIntent.js.map +1 -1
  60. package/lib/commonjs/types/index.js +1 -1
  61. package/lib/commonjs/types/index.js.map +1 -1
  62. package/lib/module/NativeStripeSdk.js.map +1 -1
  63. package/lib/module/components/AddToWalletButton.js +1 -1
  64. package/lib/module/components/AddToWalletButton.js.map +1 -1
  65. package/lib/module/components/ApplePayButton.js +1 -1
  66. package/lib/module/components/ApplePayButton.js.map +1 -1
  67. package/lib/module/components/AuBECSDebitForm.js +1 -1
  68. package/lib/module/components/AuBECSDebitForm.js.map +1 -1
  69. package/lib/module/components/CardField.js +1 -1
  70. package/lib/module/components/CardField.js.map +1 -1
  71. package/lib/module/components/CardForm.js +1 -1
  72. package/lib/module/components/CardForm.js.map +1 -1
  73. package/lib/module/components/GooglePayButton.js +1 -1
  74. package/lib/module/components/GooglePayButton.js.map +1 -1
  75. package/lib/module/components/StripeContainer.js +1 -1
  76. package/lib/module/components/StripeContainer.js.map +1 -1
  77. package/lib/module/components/StripeProvider.js +1 -1
  78. package/lib/module/components/StripeProvider.js.map +1 -1
  79. package/lib/module/functions.js +1 -1
  80. package/lib/module/functions.js.map +1 -1
  81. package/lib/module/hooks/useApplePay.js +1 -1
  82. package/lib/module/hooks/useApplePay.js.map +1 -1
  83. package/lib/module/hooks/useConfirmPayment.js +1 -1
  84. package/lib/module/hooks/useConfirmPayment.js.map +1 -1
  85. package/lib/module/hooks/useConfirmSetupIntent.js +1 -1
  86. package/lib/module/hooks/useConfirmSetupIntent.js.map +1 -1
  87. package/lib/module/hooks/useFinancialConnectionsSheet.js +2 -0
  88. package/lib/module/hooks/useFinancialConnectionsSheet.js.map +1 -0
  89. package/lib/module/hooks/useGooglePay.js +1 -1
  90. package/lib/module/hooks/useGooglePay.js.map +1 -1
  91. package/lib/module/hooks/usePaymentSheet.js +1 -1
  92. package/lib/module/hooks/usePaymentSheet.js.map +1 -1
  93. package/lib/module/hooks/useStripe.js +1 -1
  94. package/lib/module/hooks/useStripe.js.map +1 -1
  95. package/lib/module/index.js +1 -1
  96. package/lib/module/index.js.map +1 -1
  97. package/lib/module/plugin/withStripe.js +1 -1
  98. package/lib/module/plugin/withStripe.js.map +1 -1
  99. package/lib/module/types/FinancialConnections.js +2 -0
  100. package/lib/module/types/FinancialConnections.js.map +1 -0
  101. package/lib/module/types/PaymentIntent.js.map +1 -1
  102. package/lib/module/types/SetupIntent.js.map +1 -1
  103. package/lib/module/types/index.js +1 -1
  104. package/lib/module/types/index.js.map +1 -1
  105. package/lib/typescript/src/NativeStripeSdk.d.ts +3 -1
  106. package/lib/typescript/src/functions.d.ts +15 -1
  107. package/lib/typescript/src/hooks/useFinancialConnectionsSheet.d.ts +11 -0
  108. package/lib/typescript/src/hooks/useStripe.d.ts +3 -1
  109. package/lib/typescript/src/index.d.ts +1 -0
  110. package/lib/typescript/src/types/FinancialConnections.d.ts +100 -0
  111. package/lib/typescript/src/types/PaymentIntent.d.ts +1 -0
  112. package/lib/typescript/src/types/SetupIntent.d.ts +1 -0
  113. package/lib/typescript/src/types/Token.d.ts +18 -7
  114. package/lib/typescript/src/types/index.d.ts +2 -1
  115. package/package.json +10 -12
  116. package/src/NativeStripeSdk.tsx +7 -0
  117. package/src/functions.ts +58 -0
  118. package/src/hooks/useFinancialConnectionsSheet.tsx +34 -0
  119. package/src/hooks/useStripe.tsx +21 -0
  120. package/src/index.tsx +1 -0
  121. package/src/types/FinancialConnections.ts +126 -0
  122. package/src/types/PaymentIntent.ts +1 -0
  123. package/src/types/SetupIntent.ts +1 -0
  124. package/src/types/Token.ts +24 -7
  125. package/src/types/index.ts +2 -0
  126. package/android/.gradle/7.1/dependencies-accessors/dependencies-accessors.lock +0 -0
  127. package/android/.gradle/7.1/dependencies-accessors/gc.properties +0 -0
  128. package/android/.gradle/7.1/fileChanges/last-build.bin +0 -0
  129. package/android/.gradle/7.1/fileHashes/fileHashes.lock +0 -0
  130. package/android/.gradle/7.1/gc.properties +0 -0
  131. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  132. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  133. package/android/.gradle/checksums/checksums.lock +0 -0
  134. package/android/.gradle/vcs-1/gc.properties +0 -0
  135. package/android/.idea/gradle.xml +0 -13
  136. package/android/.idea/misc.xml +0 -9
  137. package/android/.idea/modules/android.iml +0 -18
  138. package/android/.idea/modules.xml +0 -8
  139. package/android/.idea/vcs.xml +0 -6
  140. package/android/local.properties +0 -8
  141. package/ios/StripeSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  142. package/ios/StripeSdk.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  143. package/ios/StripeSdk.xcodeproj/project.xcworkspace/xcuserdata/charliecruzan.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  144. package/ios/StripeSdk.xcodeproj/xcuserdata/charliecruzan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -22
  145. package/ios/StripeSdk.xcodeproj/xcuserdata/charliecruzan.xcuserdatad/xcschemes/xcschememanagement.plist +0 -27
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ yarn lint && yarn typescript
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.17.0 - 2022-08-11
6
+
7
+ ### Breaking changes
8
+
9
+ ### New features
10
+
11
+ - Added the [`collectBankAccountToken`](https://stripe.com/docs/financial-connections/connect-payouts?platform=react-native) & [`collectFinancialConnectionsAccounts`](https://stripe.com/docs/financial-connections/other-data-powered-products?platform=react-native) functions.
12
+
13
+ ### Fixes
14
+
15
+ - Fixed an issue where `collectBankAccountForPayment` and `collectBankAccountForSetup` would fail on Android when using React Native 0.65.x or under. [#1059](https://github.com/stripe/stripe-react-native/pull/1059)
16
+ - Fixed an issue where Android apps could crash with the error `IllegalStateException: Cannot remove Fragment attached to a different FragmentManager`. [#1054](https://github.com/stripe/stripe-react-native/pull/1054)
17
+ - Bumped Gradle from 4.2.2 to 7.1.1. [#1058](https://github.com/stripe/stripe-react-native/pull/1058)
18
+
5
19
  ## 0.16.0 - 2022-07-22
6
20
 
7
21
  ### Breaking changes
@@ -8,7 +8,7 @@ buildscript {
8
8
  }
9
9
 
10
10
  dependencies {
11
- classpath 'com.android.tools.build:gradle:4.2.2'
11
+ classpath 'com.android.tools.build:gradle:7.1.1'
12
12
  // noinspection DifferentKotlinGradleVersion
13
13
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
14
  }
@@ -83,7 +83,11 @@ class CollectBankAccountLauncherFragment(
83
83
  promise.resolve(createError(ErrorType.Failed.toString(), result.error))
84
84
  }
85
85
  }
86
- (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
86
+ removeFragment(context)
87
87
  }
88
88
  }
89
+
90
+ companion object {
91
+ const val TAG = "collect_bank_account_launcher_fragment"
92
+ }
89
93
  }
@@ -0,0 +1,284 @@
1
+ package com.reactnativestripesdk
2
+
3
+ import android.os.Bundle
4
+ import android.view.LayoutInflater
5
+ import android.view.View
6
+ import android.view.ViewGroup
7
+ import android.widget.FrameLayout
8
+ import androidx.appcompat.app.AppCompatActivity
9
+ import androidx.fragment.app.Fragment
10
+ import com.facebook.react.bridge.*
11
+ import com.reactnativestripesdk.utils.*
12
+ import com.reactnativestripesdk.utils.createError
13
+ import com.reactnativestripesdk.utils.createMissingActivityError
14
+ import com.reactnativestripesdk.utils.createResult
15
+ import com.reactnativestripesdk.utils.mapFromToken
16
+ import com.stripe.android.financialconnections.FinancialConnectionsSheet
17
+ import com.stripe.android.financialconnections.FinancialConnectionsSheetForTokenResult
18
+ import com.stripe.android.financialconnections.FinancialConnectionsSheetResult
19
+ import com.stripe.android.financialconnections.FinancialConnectionsSheetResultCallback
20
+ import com.stripe.android.financialconnections.model.*
21
+
22
+ class FinancialConnectionsSheetFragment : Fragment() {
23
+ enum class Mode {
24
+ ForToken, ForSession
25
+ }
26
+
27
+ private lateinit var promise: Promise
28
+ private lateinit var context: ReactApplicationContext
29
+ private lateinit var clientSecret: String
30
+ private lateinit var publishableKey: String
31
+ private lateinit var mode: Mode
32
+
33
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
34
+ savedInstanceState: Bundle?): View {
35
+ return FrameLayout(requireActivity()).also {
36
+ it.visibility = View.GONE
37
+ }
38
+ }
39
+
40
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
41
+ when (mode) {
42
+ Mode.ForToken -> {
43
+ FinancialConnectionsSheet.createForBankAccountToken(
44
+ this,
45
+ ::onFinancialConnectionsSheetForTokenResult
46
+ ).present(
47
+ configuration = FinancialConnectionsSheet.Configuration(
48
+ financialConnectionsSessionClientSecret = clientSecret,
49
+ publishableKey = publishableKey
50
+ )
51
+ )
52
+ }
53
+ Mode.ForSession -> {
54
+ FinancialConnectionsSheet.create(
55
+ this,
56
+ ::onFinancialConnectionsSheetForDataResult
57
+ ).present(
58
+ configuration = FinancialConnectionsSheet.Configuration(
59
+ financialConnectionsSessionClientSecret = clientSecret,
60
+ publishableKey = publishableKey
61
+ )
62
+ )
63
+ }
64
+ }
65
+ }
66
+
67
+ private fun onFinancialConnectionsSheetForTokenResult(result: FinancialConnectionsSheetForTokenResult) {
68
+ when(result) {
69
+ is FinancialConnectionsSheetForTokenResult.Canceled -> {
70
+ promise.resolve(
71
+ createError(ErrorType.Canceled.toString(), "The flow has been canceled")
72
+ )
73
+ }
74
+ is FinancialConnectionsSheetForTokenResult.Failed -> {
75
+ promise.resolve(
76
+ createError(ErrorType.Failed.toString(), result.error)
77
+ )
78
+ }
79
+ is FinancialConnectionsSheetForTokenResult.Completed -> {
80
+ promise.resolve(createTokenResult(result))
81
+ (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
82
+ }
83
+ }
84
+ }
85
+
86
+ private fun onFinancialConnectionsSheetForDataResult(result: FinancialConnectionsSheetResult) {
87
+ when(result) {
88
+ is FinancialConnectionsSheetResult.Canceled -> {
89
+ promise.resolve(
90
+ createError(ErrorType.Canceled.toString(), "The flow has been canceled")
91
+ )
92
+ }
93
+ is FinancialConnectionsSheetResult.Failed -> {
94
+ promise.resolve(
95
+ createError(ErrorType.Failed.toString(), result.error)
96
+ )
97
+ }
98
+ is FinancialConnectionsSheetResult.Completed -> {
99
+ promise.resolve(
100
+ WritableNativeMap().also {
101
+ it.putMap("session", mapFromSession(result.financialConnectionsSession))
102
+ }
103
+ )
104
+ (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
105
+ }
106
+ }
107
+ }
108
+
109
+ fun presentFinancialConnectionsSheet(clientSecret: String, mode: Mode, publishableKey: String, promise: Promise, context: ReactApplicationContext) {
110
+ this.promise = promise
111
+ this.context = context
112
+ this.clientSecret = clientSecret
113
+ this.publishableKey = publishableKey
114
+ this.mode = mode
115
+
116
+ (context.currentActivity as? AppCompatActivity)?.let {
117
+ attemptToCleanupPreviousFragment(it)
118
+ commitFragmentAndStartFlow(it)
119
+ } ?: run {
120
+ promise.resolve(createMissingActivityError())
121
+ return
122
+ }
123
+ }
124
+
125
+ private fun attemptToCleanupPreviousFragment(currentActivity: AppCompatActivity) {
126
+ currentActivity.supportFragmentManager.beginTransaction()
127
+ .remove(this)
128
+ .commitAllowingStateLoss()
129
+ }
130
+
131
+ private fun commitFragmentAndStartFlow(currentActivity: AppCompatActivity) {
132
+ try {
133
+ currentActivity.supportFragmentManager.beginTransaction()
134
+ .add(this, "financial_connections_sheet_launch_fragment")
135
+ .commit()
136
+ } catch (error: IllegalStateException) {
137
+ promise.resolve(createError(ErrorType.Failed.toString(), error.message))
138
+ }
139
+ }
140
+
141
+ companion object {
142
+ private fun createTokenResult(result: FinancialConnectionsSheetForTokenResult.Completed): WritableMap {
143
+ return WritableNativeMap().also {
144
+ it.putMap("session", mapFromSession(result.financialConnectionsSession))
145
+ it.putMap("token", mapFromToken(result.token))
146
+ }
147
+ }
148
+
149
+ private fun mapFromSession(financialConnectionsSession: FinancialConnectionsSession): WritableMap {
150
+ val session = WritableNativeMap()
151
+ session.putString("id", financialConnectionsSession.id)
152
+ session.putString("clientSecret", financialConnectionsSession.clientSecret)
153
+ session.putBoolean("livemode", financialConnectionsSession.livemode)
154
+ session.putArray("accounts", mapFromAccountsList(financialConnectionsSession.accounts))
155
+ return session
156
+ }
157
+
158
+ private fun mapFromAccountsList(accounts: FinancialConnectionsAccountList): ReadableArray {
159
+ val results: WritableArray = Arguments.createArray()
160
+ for (account in accounts.data) {
161
+ val map = WritableNativeMap()
162
+ map.putString("id", account.id)
163
+ map.putBoolean("livemode", account.livemode)
164
+ map.putString("displayName", account.displayName)
165
+ map.putString("status", mapFromStatus(account.status))
166
+ map.putString("institutionName", account.institutionName)
167
+ map.putString("last4", account.last4)
168
+ map.putDouble("created", account.created * 1000.0)
169
+ map.putMap("balance", mapFromAccountBalance(account.balance))
170
+ map.putMap("balanceRefresh", mapFromAccountBalanceRefresh(account.balanceRefresh))
171
+ map.putString("category", mapFromCategory(account.category))
172
+ map.putString("subcategory", mapFromSubcategory(account.subcategory))
173
+ map.putArray("permissions", (account.permissions?.map { permission -> mapFromPermission(permission) })?.toReadableArray())
174
+ map.putArray("supportedPaymentMethodTypes", (account.supportedPaymentMethodTypes.map { type -> mapFromSupportedPaymentMethodTypes(type) }).toReadableArray())
175
+ results.pushMap(map)
176
+ }
177
+ return results
178
+ }
179
+
180
+ private fun mapFromAccountBalance(balance: Balance?): WritableMap? {
181
+ if (balance == null) {
182
+ return null
183
+ }
184
+ val map = WritableNativeMap()
185
+ map.putDouble("asOf", balance.asOf * 1000.0)
186
+ map.putString("type", mapFromBalanceType(balance.type))
187
+ map.putMap("current", balance.current as ReadableMap)
188
+ WritableNativeMap().also {
189
+ it.putMap("available", balance.cash?.available as ReadableMap)
190
+ map.putMap("cash", it)
191
+ }
192
+ WritableNativeMap().also {
193
+ it.putMap("used", balance.credit?.used as ReadableMap)
194
+ map.putMap("credit", it)
195
+ }
196
+ return map
197
+ }
198
+
199
+ private fun mapFromAccountBalanceRefresh(balanceRefresh: BalanceRefresh?): WritableMap? {
200
+ if (balanceRefresh == null) {
201
+ return null
202
+ }
203
+ val map = WritableNativeMap()
204
+ map.putString("status", mapFromBalanceRefreshStatus(balanceRefresh.status))
205
+ map.putDouble("lastAttemptedAt", balanceRefresh.lastAttemptedAt * 1000.0)
206
+ return map
207
+ }
208
+
209
+ private fun mapFromStatus(status: FinancialConnectionsAccount.Status): String {
210
+ return when (status) {
211
+ FinancialConnectionsAccount.Status.ACTIVE -> "active"
212
+ FinancialConnectionsAccount.Status.DISCONNECTED -> "disconnected"
213
+ FinancialConnectionsAccount.Status.INACTIVE -> "inactive"
214
+ FinancialConnectionsAccount.Status.UNKNOWN -> "unparsable"
215
+ }
216
+ }
217
+
218
+ private fun mapFromCategory(category: FinancialConnectionsAccount.Category): String {
219
+ return when (category) {
220
+ FinancialConnectionsAccount.Category.CASH -> "cash"
221
+ FinancialConnectionsAccount.Category.CREDIT -> "credit"
222
+ FinancialConnectionsAccount.Category.INVESTMENT -> "investment"
223
+ FinancialConnectionsAccount.Category.OTHER -> "other"
224
+ FinancialConnectionsAccount.Category.UNKNOWN -> "unparsable"
225
+ }
226
+ }
227
+
228
+ private fun mapFromSubcategory(subcategory: FinancialConnectionsAccount.Subcategory): String {
229
+ return when (subcategory) {
230
+ FinancialConnectionsAccount.Subcategory.CHECKING -> "checking"
231
+ FinancialConnectionsAccount.Subcategory.CREDIT_CARD -> "creditCard"
232
+ FinancialConnectionsAccount.Subcategory.LINE_OF_CREDIT -> "lineOfCredit"
233
+ FinancialConnectionsAccount.Subcategory.MORTGAGE -> "mortgage"
234
+ FinancialConnectionsAccount.Subcategory.OTHER -> "other"
235
+ FinancialConnectionsAccount.Subcategory.SAVINGS -> "savings"
236
+ FinancialConnectionsAccount.Subcategory.UNKNOWN -> "unparsable"
237
+ }
238
+ }
239
+
240
+ private fun mapFromPermission(permission: FinancialConnectionsAccount.Permissions): String {
241
+ return when (permission) {
242
+ FinancialConnectionsAccount.Permissions.PAYMENT_METHOD -> "paymentMethod"
243
+ FinancialConnectionsAccount.Permissions.BALANCES -> "balances"
244
+ FinancialConnectionsAccount.Permissions.OWNERSHIP -> "ownership"
245
+ FinancialConnectionsAccount.Permissions.TRANSACTIONS -> "transactions"
246
+ FinancialConnectionsAccount.Permissions.UNKNOWN -> "unparsable"
247
+ }
248
+ }
249
+
250
+ private fun mapFromSupportedPaymentMethodTypes(type: FinancialConnectionsAccount.SupportedPaymentMethodTypes): String {
251
+ return when (type) {
252
+ FinancialConnectionsAccount.SupportedPaymentMethodTypes.US_BANK_ACCOUNT -> "usBankAccount"
253
+ FinancialConnectionsAccount.SupportedPaymentMethodTypes.LINK -> "link"
254
+ FinancialConnectionsAccount.SupportedPaymentMethodTypes.UNKNOWN -> "unparsable"
255
+ }
256
+ }
257
+
258
+ private fun mapFromBalanceType(type: Balance.Type): String {
259
+ return when (type) {
260
+ Balance.Type.CASH -> "cash"
261
+ Balance.Type.CREDIT -> "credit"
262
+ Balance.Type.UNKNOWN -> "unparsable"
263
+ }
264
+ }
265
+
266
+ private fun mapFromBalanceRefreshStatus(status: BalanceRefresh.BalanceRefreshStatus?): String {
267
+ return when (status) {
268
+ BalanceRefresh.BalanceRefreshStatus.SUCCEEDED -> "succeeded"
269
+ BalanceRefresh.BalanceRefreshStatus.FAILED -> "failed"
270
+ BalanceRefresh.BalanceRefreshStatus.PENDING -> "pending"
271
+ BalanceRefresh.BalanceRefreshStatus.UNKNOWN -> "unparsable"
272
+ null -> "null"
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ fun List<String>.toReadableArray(): ReadableArray {
279
+ val results: WritableArray = Arguments.createArray()
280
+ for (s in this) {
281
+ results.pushString(s)
282
+ }
283
+ return results
284
+ }
@@ -204,4 +204,8 @@ class GooglePayFragment(private val initPromise: Promise) : Fragment() {
204
204
  isPhoneNumberRequired = isPhoneNumberRequired
205
205
  )
206
206
  }
207
+
208
+ companion object {
209
+ const val TAG = "google_pay_launch_fragment"
210
+ }
207
211
  }
@@ -5,10 +5,10 @@ import android.view.LayoutInflater
5
5
  import android.view.View
6
6
  import android.view.ViewGroup
7
7
  import android.widget.FrameLayout
8
- import androidx.appcompat.app.AppCompatActivity
9
8
  import androidx.fragment.app.Fragment
10
9
  import com.facebook.react.bridge.Promise
11
10
  import com.facebook.react.bridge.ReactApplicationContext
11
+ import com.reactnativestripesdk.utils.removeFragment
12
12
  import com.stripe.android.googlepaylauncher.GooglePayEnvironment
13
13
  import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncher
14
14
 
@@ -37,9 +37,13 @@ class GooglePayPaymentMethodLauncherFragment(
37
37
  ),
38
38
  readyCallback = {
39
39
  promise.resolve(it)
40
- (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
40
+ removeFragment(context)
41
41
  },
42
42
  resultCallback = {}
43
43
  )
44
44
  }
45
+
46
+ companion object {
47
+ const val TAG = "google_pay_support_fragment"
48
+ }
45
49
  }
@@ -110,7 +110,7 @@ class PaymentLauncherFragment(
110
110
  (context.currentActivity as? AppCompatActivity)?.let {
111
111
  try {
112
112
  it.supportFragmentManager.beginTransaction()
113
- .add(fragment, "payment_launcher_fragment")
113
+ .add(fragment, TAG)
114
114
  .commit()
115
115
  } catch (error: IllegalStateException) {
116
116
  promise.resolve(createError(ErrorType.Failed.toString(), error.message))
@@ -119,6 +119,8 @@ class PaymentLauncherFragment(
119
119
  promise.resolve(createMissingActivityError())
120
120
  }
121
121
  }
122
+
123
+ const val TAG = "payment_launcher_fragment"
122
124
  }
123
125
 
124
126
  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@@ -154,25 +156,21 @@ class PaymentLauncherFragment(
154
156
  }
155
157
  is PaymentResult.Canceled -> {
156
158
  promise.resolve(createError(ConfirmPaymentErrorType.Canceled.toString(), message = null))
157
- cleanup()
159
+ removeFragment(context)
158
160
  }
159
161
  is PaymentResult.Failed -> {
160
162
  promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), paymentResult.throwable))
161
- cleanup()
163
+ removeFragment(context)
162
164
  }
163
165
  }
164
166
  }
165
167
  }
166
168
 
167
- private fun cleanup() {
168
- (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
169
- }
170
-
171
169
  private fun retrieveSetupIntent(clientSecret: String, stripeAccountId: String?) {
172
170
  stripe.retrieveSetupIntent(clientSecret, stripeAccountId, object : ApiResultCallback<SetupIntent> {
173
171
  override fun onError(e: Exception) {
174
172
  promise.resolve(createError(ConfirmSetupIntentErrorType.Failed.toString(), e))
175
- cleanup()
173
+ removeFragment(context)
176
174
  }
177
175
 
178
176
  override fun onSuccess(result: SetupIntent) {
@@ -204,7 +202,7 @@ class PaymentLauncherFragment(
204
202
  promise.resolve(createError(ConfirmSetupIntentErrorType.Unknown.toString(), "unhandled error: ${result.status}"))
205
203
  }
206
204
  }
207
- cleanup()
205
+ removeFragment(context)
208
206
  }
209
207
  })
210
208
  }
@@ -213,7 +211,7 @@ class PaymentLauncherFragment(
213
211
  stripe.retrievePaymentIntent(clientSecret, stripeAccountId, object : ApiResultCallback<PaymentIntent> {
214
212
  override fun onError(e: Exception) {
215
213
  promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), e))
216
- cleanup()
214
+ removeFragment(context)
217
215
  }
218
216
 
219
217
  override fun onSuccess(result: PaymentIntent) {
@@ -245,7 +243,7 @@ class PaymentLauncherFragment(
245
243
  promise.resolve(createError(ConfirmPaymentErrorType.Unknown.toString(), "unhandled error: ${result.status}"))
246
244
  }
247
245
  }
248
- cleanup()
246
+ removeFragment(context)
249
247
  }
250
248
  })
251
249
  }
@@ -95,7 +95,7 @@ class PaymentSheetFragment(
95
95
  is PaymentSheetResult.Completed -> {
96
96
  resolvePaymentResult(WritableNativeMap())
97
97
  // Remove the fragment now, we can be sure it won't be needed again if an intent is successful
98
- (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
98
+ removeFragment(context)
99
99
  }
100
100
  }
101
101
  }
@@ -196,6 +196,8 @@ class PaymentSheetFragment(
196
196
  }
197
197
 
198
198
  companion object {
199
+ const val TAG = "payment_sheet_launch_fragment"
200
+
199
201
  internal fun buildGooglePayConfig(params: Bundle?): PaymentSheet.GooglePayConfiguration? {
200
202
  if (params == null) {
201
203
  return null
@@ -5,6 +5,7 @@ import android.content.Intent
5
5
  import android.os.Parcelable
6
6
  import android.util.Log
7
7
  import androidx.appcompat.app.AppCompatActivity
8
+ import androidx.fragment.app.Fragment
8
9
  import com.facebook.react.bridge.*
9
10
  import com.facebook.react.module.annotations.ReactModule
10
11
  import com.reactnativestripesdk.pushprovisioning.PushProvisioningProxy
@@ -39,6 +40,15 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
39
40
  private var paymentSheetFragment: PaymentSheetFragment? = null
40
41
  private var googlePayFragment: GooglePayFragment? = null
41
42
  private var paymentLauncherFragment: PaymentLauncherFragment? = null
43
+ private var collectBankAccountLauncherFragment: CollectBankAccountLauncherFragment? = null
44
+ private var financialConnectionsSheetFragment: FinancialConnectionsSheetFragment? = null
45
+ private var allFragments : Array<Fragment?> = arrayOf(
46
+ paymentSheetFragment,
47
+ googlePayFragment,
48
+ paymentLauncherFragment,
49
+ collectBankAccountLauncherFragment,
50
+ financialConnectionsSheetFragment
51
+ )
42
52
 
43
53
  private var confirmPromise: Promise? = null
44
54
  private var confirmPaymentClientSecret: String? = null
@@ -46,11 +56,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
46
56
  private val mActivityEventListener = object : BaseActivityEventListener() {
47
57
  override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
48
58
  if (::stripe.isInitialized) {
49
- // BEGIN - Necessary on older versions of React Native (~0.64 and below)
50
- paymentSheetFragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
51
- googlePayFragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
52
- paymentLauncherFragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
53
- // END
59
+ dispatchActivityResultsToFragments(requestCode, resultCode, data)
54
60
  try {
55
61
  val result = AddPaymentMethodActivityStarter.Result.fromIntent(data)
56
62
  if (data?.getParcelableExtra<Parcelable>("extra_activity_result") != null) {
@@ -67,6 +73,13 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
67
73
  reactContext.addActivityEventListener(mActivityEventListener)
68
74
  }
69
75
 
76
+ // Necessary on older versions of React Native (~0.65 and below)
77
+ private fun dispatchActivityResultsToFragments(requestCode: Int, resultCode: Int, data: Intent?) {
78
+ for (fragment in allFragments) {
79
+ fragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
80
+ }
81
+ }
82
+
70
83
  private fun configure3dSecure(params: ReadableMap) {
71
84
  val stripe3dsConfigBuilder = PaymentAuthConfig.Stripe3ds2Config.Builder()
72
85
  if (params.hasKey("timeout")) stripe3dsConfigBuilder.setTimeout(params.getInt("timeout"))
@@ -121,17 +134,14 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
121
134
  @ReactMethod
122
135
  fun initPaymentSheet(params: ReadableMap, promise: Promise) {
123
136
  getCurrentActivityOrResolveWithError(promise)?.let { activity ->
124
- paymentSheetFragment?.let {
125
- // If a payment sheet was already initialized, we want to remove its fragment first
126
- activity.supportFragmentManager.beginTransaction().remove(it).commitAllowingStateLoss()
127
- }
137
+ paymentSheetFragment?.removeFragment(reactApplicationContext)
128
138
  paymentSheetFragment = PaymentSheetFragment(reactApplicationContext, promise).also {
129
139
  val bundle = toBundleObject(params)
130
140
  it.arguments = bundle
131
141
  }
132
142
  try {
133
143
  activity.supportFragmentManager.beginTransaction()
134
- .add(paymentSheetFragment!!, "payment_sheet_launch_fragment")
144
+ .add(paymentSheetFragment!!, PaymentSheetFragment.TAG)
135
145
  .commit()
136
146
  } catch (error: IllegalStateException) {
137
147
  promise.resolve(createError(ErrorType.Failed.toString(), error.message))
@@ -480,7 +490,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
480
490
  getCurrentActivityOrResolveWithError(promise)?.let {
481
491
  try {
482
492
  it.supportFragmentManager.beginTransaction()
483
- .add(fragment, "google_pay_support_fragment")
493
+ .add(fragment, GooglePayPaymentMethodLauncherFragment.TAG)
484
494
  .commit()
485
495
  } catch (error: IllegalStateException) {
486
496
  promise.resolve(createError(ErrorType.Failed.toString(), error.message))
@@ -498,7 +508,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
498
508
  getCurrentActivityOrResolveWithError(promise)?.let {
499
509
  try {
500
510
  it.supportFragmentManager.beginTransaction()
501
- .add(googlePayFragment!!, "google_pay_launch_fragment")
511
+ .add(googlePayFragment!!, GooglePayFragment.TAG)
502
512
  .commit()
503
513
  } catch (error: IllegalStateException) {
504
514
  promise.resolve(createError(ErrorType.Failed.toString(), error.message))
@@ -603,7 +613,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
603
613
  billingDetails.getString("email")
604
614
  )
605
615
 
606
- val fragment = CollectBankAccountLauncherFragment(
616
+ collectBankAccountLauncherFragment = CollectBankAccountLauncherFragment(
607
617
  reactApplicationContext,
608
618
  publishableKey,
609
619
  clientSecret,
@@ -614,7 +624,7 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
614
624
  getCurrentActivityOrResolveWithError(promise)?.let {
615
625
  try {
616
626
  it.supportFragmentManager.beginTransaction()
617
- .add(fragment, "collect_bank_account_launcher_fragment")
627
+ .add(collectBankAccountLauncherFragment!!, "collect_bank_account_launcher_fragment")
618
628
  .commit()
619
629
  } catch (error: IllegalStateException) {
620
630
  promise.resolve(createError(ErrorType.Failed.toString(), error.message))
@@ -690,6 +700,28 @@ class StripeSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
690
700
  }
691
701
  }
692
702
 
703
+ @ReactMethod
704
+ fun collectBankAccountToken(clientSecret: String, promise: Promise) {
705
+ if (!::stripe.isInitialized) {
706
+ promise.resolve(createMissingInitError())
707
+ return
708
+ }
709
+ financialConnectionsSheetFragment = FinancialConnectionsSheetFragment().also {
710
+ it.presentFinancialConnectionsSheet(clientSecret, FinancialConnectionsSheetFragment.Mode.ForToken, publishableKey, promise, reactApplicationContext)
711
+ }
712
+ }
713
+
714
+ @ReactMethod
715
+ fun collectFinancialConnectionsAccounts(clientSecret: String, promise: Promise) {
716
+ if (!::stripe.isInitialized) {
717
+ promise.resolve(createMissingInitError())
718
+ return
719
+ }
720
+ financialConnectionsSheetFragment = FinancialConnectionsSheetFragment().also {
721
+ it.presentFinancialConnectionsSheet(clientSecret, FinancialConnectionsSheetFragment.Mode.ForSession, publishableKey, promise, reactApplicationContext)
722
+ }
723
+ }
724
+
693
725
  /**
694
726
  * Safely get and cast the current activity as an AppCompatActivity. If that fails, the promise
695
727
  * provided will be resolved with an error message instructing the user to retry the method.
@@ -111,3 +111,7 @@ internal fun createError(code: String, error: Throwable): WritableMap {
111
111
  null,
112
112
  null)
113
113
  }
114
+
115
+ internal fun createMissingInitError(): WritableMap {
116
+ return createError(ErrorType.Failed.toString(), "Stripe has not been initialized. Initialize Stripe in your app with the StripeProvider component or the initStripe method.")
117
+ }
@@ -3,6 +3,9 @@ package com.reactnativestripesdk.utils
3
3
  import android.content.Context
4
4
  import android.view.View
5
5
  import android.view.inputmethod.InputMethodManager
6
+ import androidx.appcompat.app.AppCompatActivity
7
+ import androidx.fragment.app.Fragment
8
+ import com.facebook.react.bridge.ReactApplicationContext
6
9
 
7
10
  fun View.showSoftKeyboard() {
8
11
  post {
@@ -19,3 +22,11 @@ fun View.hideSoftKeyboard() {
19
22
  imm?.hideSoftInputFromWindow(windowToken, 0)
20
23
  }
21
24
  }
25
+
26
+ fun Fragment.removeFragment(context: ReactApplicationContext) {
27
+ (context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.let {
28
+ if (it.findFragmentByTag(this.tag) != null) {
29
+ it.beginTransaction().remove(this).commitAllowingStateLoss()
30
+ }
31
+ }
32
+ }
@@ -217,12 +217,11 @@ internal fun mapFromBankAccountStatus(status: BankAccount.Status?): String {
217
217
  }
218
218
 
219
219
  internal fun mapFromBankAccount(bankAccount: BankAccount?): WritableMap? {
220
- val bankAccountMap: WritableMap = WritableNativeMap()
221
-
222
220
  if (bankAccount == null) {
223
221
  return null
224
222
  }
225
223
 
224
+ val bankAccountMap: WritableMap = WritableNativeMap()
226
225
  bankAccountMap.putString("id", bankAccount.id)
227
226
  bankAccountMap.putString("bankName", bankAccount.bankName)
228
227
  bankAccountMap.putString("accountHolderName", bankAccount.accountHolderName)
@@ -231,6 +230,8 @@ internal fun mapFromBankAccount(bankAccount: BankAccount?): WritableMap? {
231
230
  bankAccountMap.putString("country", bankAccount.countryCode)
232
231
  bankAccountMap.putString("routingNumber", bankAccount.routingNumber)
233
232
  bankAccountMap.putString("status", mapFromBankAccountStatus(bankAccount.status))
233
+ bankAccountMap.putString("fingerprint", bankAccount.fingerprint)
234
+ bankAccountMap.putString("last4", bankAccount.last4)
234
235
 
235
236
  return bankAccountMap
236
237
  }
@@ -312,13 +313,13 @@ internal fun mapFromCard(card: Card?): WritableMap? {
312
313
 
313
314
  internal fun mapFromToken(token: Token): WritableMap {
314
315
  val tokenMap: WritableMap = WritableNativeMap()
315
-
316
316
  tokenMap.putString("id", token.id)
317
- tokenMap.putString("created", token.created.time.toString())
317
+ tokenMap.putDouble("created", token.created.time.toDouble())
318
318
  tokenMap.putString("type", mapTokenType(token.type))
319
319
  tokenMap.putBoolean("livemode", token.livemode)
320
320
  tokenMap.putMap("bankAccount", mapFromBankAccount(token.bankAccount))
321
321
  tokenMap.putMap("card", mapFromCard(token.card))
322
+ tokenMap.putBoolean("used", token.used)
322
323
 
323
324
  return tokenMap
324
325
  }