@stripe/stripe-react-native 0.30.0 → 0.31.1
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.
- package/CHANGELOG.md +11 -0
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +41 -10
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +10 -3
- package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +129 -82
- package/android/src/main/java/com/reactnativestripesdk/customersheet/CustomerSheetFragment.kt +329 -0
- package/android/src/main/java/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt +138 -0
- package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +8 -2
- package/ios/CustomerSheet/CustomerSheetUtils.swift +181 -0
- package/ios/CustomerSheet/ReactNativeCustomerAdapter.swift +173 -0
- package/ios/Mappers.swift +1 -1
- package/ios/StripeSdk+CustomerSheet.swift +166 -0
- package/ios/StripeSdk+PaymentSheet.swift +2 -2
- package/ios/StripeSdk.m +53 -0
- package/ios/StripeSdk.swift +14 -1
- package/jest/mock.js +0 -37
- package/lib/commonjs/NativeStripeSdk.js.map +1 -1
- package/lib/commonjs/components/CustomerSheet.js +2 -0
- package/lib/commonjs/components/CustomerSheet.js.map +1 -0
- package/lib/commonjs/functions.js +1 -1
- package/lib/commonjs/functions.js.map +1 -1
- package/lib/commonjs/index.js +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types/CustomerSheet.js +2 -0
- package/lib/commonjs/types/CustomerSheet.js.map +1 -0
- package/lib/commonjs/types/Errors.js +1 -1
- package/lib/commonjs/types/Errors.js.map +1 -1
- package/lib/commonjs/types/index.js +1 -1
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/module/NativeStripeSdk.js.map +1 -1
- package/lib/module/components/CustomerSheet.js +2 -0
- package/lib/module/components/CustomerSheet.js.map +1 -0
- package/lib/module/functions.js +1 -1
- package/lib/module/functions.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/CustomerSheet.js +2 -0
- package/lib/module/types/CustomerSheet.js.map +1 -0
- package/lib/module/types/Errors.js +1 -1
- package/lib/module/types/Errors.js.map +1 -1
- package/lib/module/types/index.js +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/typescript/src/NativeStripeSdk.d.ts +14 -1
- package/lib/typescript/src/components/CustomerSheet.d.ts +59 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/types/CustomerSheet.d.ts +94 -0
- package/lib/typescript/src/types/Errors.d.ts +4 -0
- package/lib/typescript/src/types/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/NativeStripeSdk.tsx +31 -0
- package/src/components/CustomerSheet.tsx +327 -0
- package/src/functions.ts +5 -0
- package/src/index.tsx +3 -0
- package/src/types/CustomerSheet.ts +111 -0
- package/src/types/Errors.ts +5 -0
- package/src/types/index.ts +1 -0
- package/stripe-react-native.podspec +1 -1
- package/android/src/main/java/com/reactnativestripesdk/GooglePayFragment.kt +0 -211
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
package com.reactnativestripesdk
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.app.Application
|
|
5
|
+
import android.graphics.drawable.Drawable
|
|
6
|
+
import android.os.Bundle
|
|
7
|
+
import android.os.Handler
|
|
8
|
+
import android.os.Looper
|
|
9
|
+
import android.util.Log
|
|
10
|
+
import android.view.LayoutInflater
|
|
11
|
+
import android.view.View
|
|
12
|
+
import android.view.ViewGroup
|
|
13
|
+
import android.widget.FrameLayout
|
|
14
|
+
import androidx.fragment.app.Fragment
|
|
15
|
+
import com.facebook.react.bridge.*
|
|
16
|
+
import com.reactnativestripesdk.customersheet.ReactNativeCustomerAdapter
|
|
17
|
+
import com.reactnativestripesdk.utils.*
|
|
18
|
+
import com.stripe.android.customersheet.CustomerAdapter
|
|
19
|
+
import com.stripe.android.customersheet.CustomerEphemeralKey
|
|
20
|
+
import com.stripe.android.customersheet.CustomerSheet
|
|
21
|
+
import com.stripe.android.customersheet.CustomerSheetResult
|
|
22
|
+
import com.stripe.android.customersheet.ExperimentalCustomerSheetApi
|
|
23
|
+
import com.stripe.android.customersheet.PaymentOptionSelection
|
|
24
|
+
import com.stripe.android.model.PaymentMethod
|
|
25
|
+
import com.stripe.android.paymentsheet.*
|
|
26
|
+
import kotlinx.coroutines.CoroutineScope
|
|
27
|
+
import kotlinx.coroutines.Dispatchers
|
|
28
|
+
import kotlinx.coroutines.launch
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@OptIn(ExperimentalCustomerSheetApi::class)
|
|
32
|
+
class CustomerSheetFragment : Fragment() {
|
|
33
|
+
private var customerSheet: CustomerSheet? = null
|
|
34
|
+
internal var customerAdapter: ReactNativeCustomerAdapter? = null
|
|
35
|
+
internal var context: ReactApplicationContext? = null
|
|
36
|
+
internal var initPromise: Promise? = null
|
|
37
|
+
private var presentPromise: Promise? = null
|
|
38
|
+
|
|
39
|
+
override fun onCreateView(
|
|
40
|
+
inflater: LayoutInflater,
|
|
41
|
+
container: ViewGroup?,
|
|
42
|
+
savedInstanceState: Bundle?
|
|
43
|
+
): View {
|
|
44
|
+
return FrameLayout(requireActivity()).also {
|
|
45
|
+
it.visibility = View.GONE
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
50
|
+
super.onViewCreated(view, savedInstanceState)
|
|
51
|
+
|
|
52
|
+
val context = context ?: run {
|
|
53
|
+
Log.e("StripeReactNative", "No context found during CustomerSheet.initialize. Please file an issue: https://github.com/stripe/stripe-react-native/issues")
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
val initPromise = initPromise ?: run {
|
|
57
|
+
Log.e("StripeReactNative", "No promise found for CustomerSheet.initialize. Please file an issue: https://github.com/stripe/stripe-react-native/issues")
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
val headerTextForSelectionScreen = arguments?.getString("headerTextForSelectionScreen")
|
|
62
|
+
val merchantDisplayName = arguments?.getString("merchantDisplayName")
|
|
63
|
+
val googlePayEnabled = arguments?.getBoolean("googlePayEnabled") ?: false
|
|
64
|
+
val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails")
|
|
65
|
+
val billingConfigParams = arguments?.getBundle("billingDetailsCollectionConfiguration")
|
|
66
|
+
val setupIntentClientSecret = arguments?.getString("setupIntentClientSecret")
|
|
67
|
+
val customerId = arguments?.getString("customerId")
|
|
68
|
+
val customerEphemeralKeySecret = arguments?.getString("customerEphemeralKeySecret")
|
|
69
|
+
val customerAdapterOverrideParams = arguments?.getBundle("customerAdapter")
|
|
70
|
+
|
|
71
|
+
if (customerId == null) {
|
|
72
|
+
initPromise.resolve(createError(ErrorType.Failed.toString(), "You must provide a value for `customerId`"))
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if (customerEphemeralKeySecret == null) {
|
|
76
|
+
initPromise.resolve(createError(ErrorType.Failed.toString(), "You must provide a value for `customerEphemeralKeySecret`"))
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
val appearance = try {
|
|
81
|
+
buildPaymentSheetAppearance(arguments?.getBundle("appearance"), context)
|
|
82
|
+
} catch (error: PaymentSheetAppearanceException) {
|
|
83
|
+
initPromise.resolve(createError(ErrorType.Failed.toString(), error))
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
val configuration = CustomerSheet.Configuration.builder()
|
|
88
|
+
.appearance(appearance)
|
|
89
|
+
.googlePayEnabled(googlePayEnabled)
|
|
90
|
+
.merchantDisplayName(merchantDisplayName)
|
|
91
|
+
.headerTextForSelectionScreen(headerTextForSelectionScreen)
|
|
92
|
+
|
|
93
|
+
billingDetailsBundle?.let {
|
|
94
|
+
configuration.defaultBillingDetails(createDefaultBillingDetails(billingDetailsBundle))
|
|
95
|
+
}
|
|
96
|
+
billingConfigParams?.let {
|
|
97
|
+
configuration.billingDetailsCollectionConfiguration(createBillingDetailsCollectionConfiguration(billingConfigParams))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
val customerAdapter = createCustomerAdapter(
|
|
101
|
+
context, customerId, customerEphemeralKeySecret, setupIntentClientSecret, customerAdapterOverrideParams
|
|
102
|
+
).also {
|
|
103
|
+
this.customerAdapter = it
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
customerSheet = CustomerSheet.create(
|
|
107
|
+
fragment = this,
|
|
108
|
+
configuration = configuration.build(),
|
|
109
|
+
customerAdapter = customerAdapter,
|
|
110
|
+
callback = ::handleResult
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
initPromise.resolve(WritableNativeMap())
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private fun handleResult(result: CustomerSheetResult) {
|
|
117
|
+
val presentPromise = presentPromise ?: run {
|
|
118
|
+
Log.e("StripeReactNative", "No promise found for CustomerSheet.present")
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var promiseResult = Arguments.createMap()
|
|
123
|
+
when (result) {
|
|
124
|
+
is CustomerSheetResult.Failed -> {
|
|
125
|
+
presentPromise.resolve(createError(ErrorType.Failed.toString(), result.exception))
|
|
126
|
+
}
|
|
127
|
+
is CustomerSheetResult.Selected -> {
|
|
128
|
+
promiseResult = createPaymentOptionResult(result.selection)
|
|
129
|
+
}
|
|
130
|
+
is CustomerSheetResult.Canceled -> {
|
|
131
|
+
promiseResult = createPaymentOptionResult(result.selection)
|
|
132
|
+
promiseResult.putMap("error", Arguments.createMap().also { it.putString("code", ErrorType.Canceled.toString()) })
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
presentPromise.resolve(promiseResult)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fun present(timeout: Long?, promise: Promise) {
|
|
139
|
+
presentPromise = promise
|
|
140
|
+
if (timeout != null) {
|
|
141
|
+
presentWithTimeout(timeout, promise)
|
|
142
|
+
}
|
|
143
|
+
customerSheet?.present() ?: run {
|
|
144
|
+
promise.resolve(createMissingInitError())
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private fun presentWithTimeout(timeout: Long, promise: Promise) {
|
|
149
|
+
var customerSheetActivity: Activity? = null
|
|
150
|
+
var activities: MutableList<Activity> = mutableListOf()
|
|
151
|
+
val activityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
|
|
152
|
+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
|
153
|
+
customerSheetActivity = activity
|
|
154
|
+
activities.add(activity)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
override fun onActivityStarted(activity: Activity) {}
|
|
158
|
+
|
|
159
|
+
override fun onActivityResumed(activity: Activity) {}
|
|
160
|
+
|
|
161
|
+
override fun onActivityPaused(activity: Activity) {}
|
|
162
|
+
|
|
163
|
+
override fun onActivityStopped(activity: Activity) {}
|
|
164
|
+
|
|
165
|
+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
|
166
|
+
|
|
167
|
+
override fun onActivityDestroyed(activity: Activity) {
|
|
168
|
+
customerSheetActivity = null
|
|
169
|
+
activities = mutableListOf()
|
|
170
|
+
context?.currentActivity?.application?.unregisterActivityLifecycleCallbacks(this)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Handler(Looper.getMainLooper()).postDelayed({
|
|
175
|
+
//customerSheetActivity?.finish()
|
|
176
|
+
for (a in activities) {
|
|
177
|
+
a.finish()
|
|
178
|
+
}
|
|
179
|
+
}, timeout)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
context?.currentActivity?.application?.registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
|
|
183
|
+
|
|
184
|
+
customerSheet?.present() ?: run {
|
|
185
|
+
promise.resolve(createMissingInitError())
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
internal fun retrievePaymentOptionSelection(promise: Promise) {
|
|
190
|
+
CoroutineScope(Dispatchers.IO).launch {
|
|
191
|
+
runCatching {
|
|
192
|
+
val result = customerSheet?.retrievePaymentOptionSelection() ?: run {
|
|
193
|
+
promise.resolve(createMissingInitError())
|
|
194
|
+
return@launch
|
|
195
|
+
}
|
|
196
|
+
var promiseResult = Arguments.createMap()
|
|
197
|
+
when (result) {
|
|
198
|
+
is CustomerSheetResult.Failed -> {
|
|
199
|
+
promise.resolve(createError(ErrorType.Failed.toString(), result.exception))
|
|
200
|
+
}
|
|
201
|
+
is CustomerSheetResult.Selected -> {
|
|
202
|
+
promiseResult = createPaymentOptionResult(result.selection)
|
|
203
|
+
}
|
|
204
|
+
is CustomerSheetResult.Canceled -> {
|
|
205
|
+
promiseResult = createPaymentOptionResult(result.selection)
|
|
206
|
+
promiseResult.putMap("error", Arguments.createMap().also { it.putString("code", ErrorType.Canceled.toString()) })
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
promise.resolve(promiseResult)
|
|
210
|
+
}.onFailure {
|
|
211
|
+
promise.resolve(createError(CreateTokenErrorType.Failed.toString(), it.message))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
companion object {
|
|
217
|
+
internal const val TAG = "customer_sheet_launch_fragment"
|
|
218
|
+
|
|
219
|
+
internal fun createMissingInitError(): WritableMap {
|
|
220
|
+
return createError(ErrorType.Failed.toString(), "No customer sheet has been initialized yet.")
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
internal fun createDefaultBillingDetails(bundle: Bundle): PaymentSheet.BillingDetails {
|
|
224
|
+
val addressBundle = bundle.getBundle("address")
|
|
225
|
+
val address = PaymentSheet.Address(
|
|
226
|
+
addressBundle?.getString("city"),
|
|
227
|
+
addressBundle?.getString("country"),
|
|
228
|
+
addressBundle?.getString("line1"),
|
|
229
|
+
addressBundle?.getString("line2"),
|
|
230
|
+
addressBundle?.getString("postalCode"),
|
|
231
|
+
addressBundle?.getString("state"))
|
|
232
|
+
return PaymentSheet.BillingDetails(
|
|
233
|
+
address,
|
|
234
|
+
bundle.getString("email"),
|
|
235
|
+
bundle.getString("name"),
|
|
236
|
+
bundle.getString("phone"))
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
internal fun createBillingDetailsCollectionConfiguration(bundle: Bundle): PaymentSheet.BillingDetailsCollectionConfiguration {
|
|
240
|
+
return PaymentSheet.BillingDetailsCollectionConfiguration(
|
|
241
|
+
name = mapToCollectionMode(bundle.getString("name")),
|
|
242
|
+
phone = mapToCollectionMode(bundle.getString("phone")),
|
|
243
|
+
email = mapToCollectionMode(bundle.getString("email")),
|
|
244
|
+
address = mapToAddressCollectionMode(bundle.getString("address")),
|
|
245
|
+
attachDefaultsToPaymentMethod = bundle.getBoolean("attachDefaultsToPaymentMethod")
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
internal fun createCustomerAdapter(
|
|
250
|
+
context: ReactApplicationContext,
|
|
251
|
+
customerId: String,
|
|
252
|
+
customerEphemeralKeySecret: String,
|
|
253
|
+
setupIntentClientSecret: String?,
|
|
254
|
+
customerAdapterOverrideParams: Bundle?,
|
|
255
|
+
): ReactNativeCustomerAdapter {
|
|
256
|
+
val ephemeralKeyProvider = {
|
|
257
|
+
CustomerAdapter.Result.success(
|
|
258
|
+
CustomerEphemeralKey.create(
|
|
259
|
+
customerId = customerId,
|
|
260
|
+
ephemeralKey = customerEphemeralKeySecret,
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
val customerAdapter = if (setupIntentClientSecret != null) {
|
|
265
|
+
CustomerAdapter.create(
|
|
266
|
+
context,
|
|
267
|
+
customerEphemeralKeyProvider = ephemeralKeyProvider,
|
|
268
|
+
setupIntentClientSecretProvider = {
|
|
269
|
+
CustomerAdapter.Result.success(
|
|
270
|
+
setupIntentClientSecret,
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
} else {
|
|
275
|
+
CustomerAdapter.create(
|
|
276
|
+
context,
|
|
277
|
+
customerEphemeralKeyProvider = ephemeralKeyProvider,
|
|
278
|
+
setupIntentClientSecretProvider = null
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return ReactNativeCustomerAdapter(
|
|
283
|
+
context = context,
|
|
284
|
+
adapter = customerAdapter,
|
|
285
|
+
overridesFetchPaymentMethods = customerAdapterOverrideParams?.getBoolean("fetchPaymentMethods") ?: false,
|
|
286
|
+
overridesAttachPaymentMethod = customerAdapterOverrideParams?.getBoolean("attachPaymentMethod") ?: false,
|
|
287
|
+
overridesDetachPaymentMethod = customerAdapterOverrideParams?.getBoolean("detachPaymentMethod") ?: false,
|
|
288
|
+
overridesSetSelectedPaymentOption = customerAdapterOverrideParams?.getBoolean("setSelectedPaymentOption") ?: false,
|
|
289
|
+
overridesFetchSelectedPaymentOption = customerAdapterOverrideParams?.getBoolean("fetchSelectedPaymentOption") ?: false,
|
|
290
|
+
overridesSetupIntentClientSecretForCustomerAttach = customerAdapterOverrideParams?.getBoolean("setupIntentClientSecretForCustomerAttach") ?: false
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
internal fun createPaymentOptionResult(selection: PaymentOptionSelection?): WritableMap {
|
|
295
|
+
var paymentOptionResult = Arguments.createMap()
|
|
296
|
+
|
|
297
|
+
when (selection) {
|
|
298
|
+
is PaymentOptionSelection.GooglePay -> {
|
|
299
|
+
paymentOptionResult = buildResult(
|
|
300
|
+
selection.paymentOption.label,
|
|
301
|
+
selection.paymentOption.icon(),
|
|
302
|
+
null)
|
|
303
|
+
}
|
|
304
|
+
is PaymentOptionSelection.PaymentMethod -> {
|
|
305
|
+
paymentOptionResult = buildResult(
|
|
306
|
+
selection.paymentOption.label,
|
|
307
|
+
selection.paymentOption.icon(),
|
|
308
|
+
selection.paymentMethod)
|
|
309
|
+
}
|
|
310
|
+
null -> {}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return paymentOptionResult
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private fun buildResult(label: String, drawable: Drawable, paymentMethod: PaymentMethod?): WritableMap {
|
|
317
|
+
val result = Arguments.createMap()
|
|
318
|
+
val paymentOption = Arguments.createMap().also {
|
|
319
|
+
it.putString("label", label)
|
|
320
|
+
it.putString("image", getBase64FromBitmap(getBitmapFromDrawable(drawable)))
|
|
321
|
+
}
|
|
322
|
+
result.putMap("paymentOption", paymentOption)
|
|
323
|
+
if (paymentMethod != null) {
|
|
324
|
+
result.putMap("paymentMethod", mapFromPaymentMethod(paymentMethod))
|
|
325
|
+
}
|
|
326
|
+
return result
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
package/android/src/main/java/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
package com.reactnativestripesdk.customersheet
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import com.facebook.react.bridge.WritableMap
|
|
8
|
+
import com.reactnativestripesdk.StripeSdkModule
|
|
9
|
+
import com.stripe.android.customersheet.CustomerAdapter
|
|
10
|
+
import com.stripe.android.customersheet.ExperimentalCustomerSheetApi
|
|
11
|
+
import com.stripe.android.model.PaymentMethod
|
|
12
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
13
|
+
|
|
14
|
+
@OptIn(ExperimentalCustomerSheetApi::class)
|
|
15
|
+
class ReactNativeCustomerAdapter (
|
|
16
|
+
private val context: ReactApplicationContext,
|
|
17
|
+
private val adapter: CustomerAdapter,
|
|
18
|
+
private val overridesFetchPaymentMethods: Boolean,
|
|
19
|
+
private val overridesAttachPaymentMethod: Boolean,
|
|
20
|
+
private val overridesDetachPaymentMethod: Boolean,
|
|
21
|
+
private val overridesSetSelectedPaymentOption: Boolean,
|
|
22
|
+
private val overridesFetchSelectedPaymentOption: Boolean,
|
|
23
|
+
private val overridesSetupIntentClientSecretForCustomerAttach: Boolean
|
|
24
|
+
) : CustomerAdapter by adapter {
|
|
25
|
+
internal var fetchPaymentMethodsCallback: CompletableDeferred<List<PaymentMethod>>? = null
|
|
26
|
+
internal var attachPaymentMethodCallback: CompletableDeferred<PaymentMethod>? = null
|
|
27
|
+
internal var detachPaymentMethodCallback: CompletableDeferred<PaymentMethod>? = null
|
|
28
|
+
internal var setSelectedPaymentOptionCallback: CompletableDeferred<Unit>? = null
|
|
29
|
+
internal var fetchSelectedPaymentOptionCallback: CompletableDeferred<String?>? = null
|
|
30
|
+
internal var setupIntentClientSecretForCustomerAttachCallback: CompletableDeferred<String>? = null
|
|
31
|
+
|
|
32
|
+
override suspend fun retrievePaymentMethods(): CustomerAdapter.Result<List<PaymentMethod>> {
|
|
33
|
+
if (overridesFetchPaymentMethods) {
|
|
34
|
+
CompletableDeferred<List<PaymentMethod>>().also {
|
|
35
|
+
fetchPaymentMethodsCallback = it
|
|
36
|
+
emitEvent("onCustomerAdapterFetchPaymentMethodsCallback", Arguments.createMap())
|
|
37
|
+
val resultFromJavascript = it.await()
|
|
38
|
+
return CustomerAdapter.Result.success(resultFromJavascript)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return adapter.retrievePaymentMethods()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override suspend fun attachPaymentMethod(paymentMethodId: String): CustomerAdapter.Result<PaymentMethod> {
|
|
46
|
+
if (overridesAttachPaymentMethod) {
|
|
47
|
+
CompletableDeferred<PaymentMethod>().also {
|
|
48
|
+
attachPaymentMethodCallback = it
|
|
49
|
+
val params = Arguments.createMap().also {
|
|
50
|
+
it.putString("paymentMethodId", paymentMethodId)
|
|
51
|
+
}
|
|
52
|
+
emitEvent("onCustomerAdapterAttachPaymentMethodCallback", params)
|
|
53
|
+
val resultFromJavascript = it.await()
|
|
54
|
+
return CustomerAdapter.Result.success(resultFromJavascript)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return adapter.attachPaymentMethod(paymentMethodId)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override suspend fun detachPaymentMethod(paymentMethodId: String): CustomerAdapter.Result<PaymentMethod> {
|
|
62
|
+
if (overridesDetachPaymentMethod) {
|
|
63
|
+
CompletableDeferred<PaymentMethod>().also {
|
|
64
|
+
detachPaymentMethodCallback = it
|
|
65
|
+
val params = Arguments.createMap().also {
|
|
66
|
+
it.putString("paymentMethodId", paymentMethodId)
|
|
67
|
+
}
|
|
68
|
+
emitEvent("onCustomerAdapterDetachPaymentMethodCallback", params)
|
|
69
|
+
val resultFromJavascript = it.await()
|
|
70
|
+
return CustomerAdapter.Result.success(resultFromJavascript)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return adapter.detachPaymentMethod(paymentMethodId)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override suspend fun setSelectedPaymentOption(paymentOption: CustomerAdapter.PaymentOption?): CustomerAdapter.Result<Unit> {
|
|
78
|
+
if (overridesSetSelectedPaymentOption) {
|
|
79
|
+
CompletableDeferred<Unit>().also {
|
|
80
|
+
setSelectedPaymentOptionCallback = it
|
|
81
|
+
val params = Arguments.createMap().also {
|
|
82
|
+
it.putString("paymentOption", paymentOption?.id)
|
|
83
|
+
}
|
|
84
|
+
emitEvent("onCustomerAdapterSetSelectedPaymentOptionCallback", params)
|
|
85
|
+
val resultFromJavascript = it.await()
|
|
86
|
+
return CustomerAdapter.Result.success(resultFromJavascript)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return adapter.setSelectedPaymentOption(paymentOption)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override suspend fun retrieveSelectedPaymentOption(): CustomerAdapter.Result<CustomerAdapter.PaymentOption?> {
|
|
94
|
+
if (overridesFetchSelectedPaymentOption) {
|
|
95
|
+
CompletableDeferred<String?>().also {
|
|
96
|
+
fetchSelectedPaymentOptionCallback = it
|
|
97
|
+
emitEvent("onCustomerAdapterFetchSelectedPaymentOptionCallback", Arguments.createMap())
|
|
98
|
+
val resultFromJavascript = it.await()
|
|
99
|
+
return CustomerAdapter.Result.success(
|
|
100
|
+
if (resultFromJavascript != null) {
|
|
101
|
+
CustomerAdapter.PaymentOption.fromId(resultFromJavascript)
|
|
102
|
+
} else {
|
|
103
|
+
null
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return adapter.retrieveSelectedPaymentOption()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override suspend fun setupIntentClientSecretForCustomerAttach(): CustomerAdapter.Result<String> {
|
|
113
|
+
if (overridesSetupIntentClientSecretForCustomerAttach) {
|
|
114
|
+
CompletableDeferred<String>().also {
|
|
115
|
+
setupIntentClientSecretForCustomerAttachCallback = it
|
|
116
|
+
emitEvent("onCustomerAdapterSetupIntentClientSecretForCustomerAttachCallback", Arguments.createMap())
|
|
117
|
+
val resultFromJavascript = it.await()
|
|
118
|
+
return CustomerAdapter.Result.success(resultFromJavascript)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return adapter.setupIntentClientSecretForCustomerAttach()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private fun emitEvent(eventName: String, params: WritableMap) {
|
|
126
|
+
val stripeSdkModule: StripeSdkModule? = context.getNativeModule(StripeSdkModule::class.java)
|
|
127
|
+
if (stripeSdkModule == null || stripeSdkModule.eventListenerCount == 0) {
|
|
128
|
+
Log.e(
|
|
129
|
+
"StripeReactNative",
|
|
130
|
+
"Tried to call $eventName, but no callback was found. Please file an issue: https://github.com/stripe/stripe-react-native/issues"
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
stripeSdkModule?.sendEvent(context, eventName, params)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
@@ -850,9 +850,15 @@ fun toBundleObject(readableMap: ReadableMap?): Bundle {
|
|
|
850
850
|
ReadableType.Null -> result.putString(key, null)
|
|
851
851
|
ReadableType.Boolean -> result.putBoolean(key, readableMap.getBoolean(key))
|
|
852
852
|
ReadableType.Number -> try {
|
|
853
|
-
|
|
853
|
+
val numAsInt = readableMap.getInt(key)
|
|
854
|
+
val numAsDouble = readableMap.getDouble(key)
|
|
855
|
+
if (numAsDouble - numAsInt != 0.0) {
|
|
856
|
+
result.putDouble(key, numAsDouble)
|
|
857
|
+
} else {
|
|
858
|
+
result.putInt(key, numAsInt)
|
|
859
|
+
}
|
|
854
860
|
} catch (e: Exception) {
|
|
855
|
-
|
|
861
|
+
Log.e("toBundleException", "Failed to add number to bundle. Failed on: $key.")
|
|
856
862
|
}
|
|
857
863
|
ReadableType.String -> result.putString(key, readableMap.getString(key))
|
|
858
864
|
ReadableType.Map -> result.putBundle(key, toBundleObject(readableMap.getMap(key)))
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
//
|
|
2
|
+
// CustomerSheetUtils.swift
|
|
3
|
+
// stripe-react-native
|
|
4
|
+
//
|
|
5
|
+
// Created by Charles Cruzan on 08/28/23.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
@_spi(PrivateBetaCustomerSheet) import StripePaymentSheet
|
|
10
|
+
|
|
11
|
+
class CustomerSheetUtils {
|
|
12
|
+
internal class func buildCustomerSheetConfiguration(
|
|
13
|
+
appearance: PaymentSheet.Appearance,
|
|
14
|
+
style: PaymentSheet.UserInterfaceStyle,
|
|
15
|
+
removeSavedPaymentMethodMessage: String?,
|
|
16
|
+
returnURL: String?,
|
|
17
|
+
headerTextForSelectionScreen: String?,
|
|
18
|
+
applePayEnabled: Bool?,
|
|
19
|
+
merchantDisplayName: String?,
|
|
20
|
+
billingDetailsCollectionConfiguration: NSDictionary?,
|
|
21
|
+
defaultBillingDetails: NSDictionary?
|
|
22
|
+
) -> CustomerSheet.Configuration {
|
|
23
|
+
var config = CustomerSheet.Configuration()
|
|
24
|
+
config.appearance = appearance
|
|
25
|
+
config.style = style
|
|
26
|
+
config.removeSavedPaymentMethodMessage = removeSavedPaymentMethodMessage
|
|
27
|
+
config.returnURL = returnURL
|
|
28
|
+
config.headerTextForSelectionScreen = headerTextForSelectionScreen
|
|
29
|
+
config.applePayEnabled = applePayEnabled ?? false
|
|
30
|
+
if let merchantDisplayName = merchantDisplayName {
|
|
31
|
+
config.merchantDisplayName = merchantDisplayName
|
|
32
|
+
}
|
|
33
|
+
if let billingConfigParams = billingDetailsCollectionConfiguration {
|
|
34
|
+
config.billingDetailsCollectionConfiguration.name = StripeSdk.mapToCollectionMode(str: billingConfigParams["name"] as? String)
|
|
35
|
+
config.billingDetailsCollectionConfiguration.phone = StripeSdk.mapToCollectionMode(str: billingConfigParams["phone"] as? String)
|
|
36
|
+
config.billingDetailsCollectionConfiguration.email = StripeSdk.mapToCollectionMode(str: billingConfigParams["email"] as? String)
|
|
37
|
+
config.billingDetailsCollectionConfiguration.address = StripeSdk.mapToAddressCollectionMode(str: billingConfigParams["address"] as? String)
|
|
38
|
+
config.billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod = billingConfigParams["attachDefaultsToPaymentMethod"] as? Bool == true
|
|
39
|
+
}
|
|
40
|
+
if let defaultBillingDetails = defaultBillingDetails {
|
|
41
|
+
config.defaultBillingDetails.name = defaultBillingDetails["name"] as? String
|
|
42
|
+
config.defaultBillingDetails.email = defaultBillingDetails["email"] as? String
|
|
43
|
+
config.defaultBillingDetails.phone = defaultBillingDetails["phone"] as? String
|
|
44
|
+
if let address = defaultBillingDetails["address"] as? [String: String] {
|
|
45
|
+
config.defaultBillingDetails.address = .init(city: address["city"],
|
|
46
|
+
country: address["country"],
|
|
47
|
+
line1: address["line1"],
|
|
48
|
+
line2: address["line2"],
|
|
49
|
+
postalCode: address["postalCode"],
|
|
50
|
+
state: address["state"])
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return config
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
internal class func buildStripeCustomerAdapter(
|
|
57
|
+
customerId: String,
|
|
58
|
+
ephemeralKeySecret: String,
|
|
59
|
+
setupIntentClientSecret: String?,
|
|
60
|
+
customerAdapter: NSDictionary,
|
|
61
|
+
stripeSdk: StripeSdk
|
|
62
|
+
) -> StripeCustomerAdapter {
|
|
63
|
+
if (customerAdapter.count > 0) {
|
|
64
|
+
return buildCustomerAdapterOverride(
|
|
65
|
+
customerAdapter: customerAdapter,
|
|
66
|
+
customerId: customerId,
|
|
67
|
+
ephemeralKeySecret: ephemeralKeySecret,
|
|
68
|
+
setupIntentClientSecret: setupIntentClientSecret,
|
|
69
|
+
stripeSdk: stripeSdk
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if let setupIntentClientSecret = setupIntentClientSecret {
|
|
74
|
+
return StripeCustomerAdapter(
|
|
75
|
+
customerEphemeralKeyProvider: {
|
|
76
|
+
return CustomerEphemeralKey(customerId: customerId, ephemeralKeySecret: ephemeralKeySecret)
|
|
77
|
+
},
|
|
78
|
+
setupIntentClientSecretProvider: {
|
|
79
|
+
return setupIntentClientSecret
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return StripeCustomerAdapter(
|
|
85
|
+
customerEphemeralKeyProvider: {
|
|
86
|
+
return CustomerEphemeralKey(customerId: customerId, ephemeralKeySecret: ephemeralKeySecret)
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
internal class func buildCustomerAdapterOverride(
|
|
92
|
+
customerAdapter: NSDictionary,
|
|
93
|
+
customerId: String,
|
|
94
|
+
ephemeralKeySecret: String,
|
|
95
|
+
setupIntentClientSecret: String?,
|
|
96
|
+
stripeSdk: StripeSdk
|
|
97
|
+
) -> StripeCustomerAdapter {
|
|
98
|
+
return ReactNativeCustomerAdapter(
|
|
99
|
+
fetchPaymentMethods: customerAdapter["fetchPaymentMethods"] as? Bool ?? false,
|
|
100
|
+
attachPaymentMethod: customerAdapter["attachPaymentMethod"] as? Bool ?? false,
|
|
101
|
+
detachPaymentMethod: customerAdapter["detachPaymentMethod"] as? Bool ?? false,
|
|
102
|
+
setSelectedPaymentOption: customerAdapter["setSelectedPaymentOption"] as? Bool ?? false,
|
|
103
|
+
fetchSelectedPaymentOption: customerAdapter["fetchSelectedPaymentOption"] as? Bool ?? false,
|
|
104
|
+
setupIntentClientSecretForCustomerAttach: customerAdapter["setupIntentClientSecretForCustomerAttach"] as? Bool ?? false,
|
|
105
|
+
customerId: customerId,
|
|
106
|
+
ephemeralKeySecret: ephemeralKeySecret,
|
|
107
|
+
setupIntentClientSecret: setupIntentClientSecret,
|
|
108
|
+
stripeSdk: stripeSdk
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
internal class func getModalPresentationStyle(_ string: String?) -> UIModalPresentationStyle {
|
|
113
|
+
switch (string) {
|
|
114
|
+
case "fullscreen":
|
|
115
|
+
return .fullScreen
|
|
116
|
+
case "popover":
|
|
117
|
+
fallthrough
|
|
118
|
+
default:
|
|
119
|
+
return .popover
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
internal class func getModalTransitionStyle(_ string: String?) -> UIModalTransitionStyle {
|
|
124
|
+
switch (string) {
|
|
125
|
+
case "flip":
|
|
126
|
+
return .flipHorizontal
|
|
127
|
+
case "curl":
|
|
128
|
+
return .partialCurl
|
|
129
|
+
case "dissolve":
|
|
130
|
+
return .crossDissolve
|
|
131
|
+
case "slide":
|
|
132
|
+
fallthrough
|
|
133
|
+
default:
|
|
134
|
+
return .coverVertical
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
internal class func buildPaymentOptionResult(label: String, imageData: String?, paymentMethod: STPPaymentMethod?) -> NSMutableDictionary {
|
|
140
|
+
let result: NSMutableDictionary = [:]
|
|
141
|
+
let paymentOption: NSMutableDictionary = [:]
|
|
142
|
+
paymentOption.setValue(label, forKey: "label")
|
|
143
|
+
if (imageData != nil) {
|
|
144
|
+
paymentOption.setValue(imageData, forKey: "image")
|
|
145
|
+
}
|
|
146
|
+
result.setValue(paymentOption, forKey: "paymentOption")
|
|
147
|
+
if (paymentMethod != nil) {
|
|
148
|
+
result.setValue(Mappers.mapFromPaymentMethod(paymentMethod), forKey: "paymentMethod")
|
|
149
|
+
}
|
|
150
|
+
return result
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
internal class func interpretResult(result: CustomerSheet.CustomerSheetResult) -> NSDictionary {
|
|
154
|
+
var payload: NSMutableDictionary = [:]
|
|
155
|
+
switch result {
|
|
156
|
+
case .error(let error):
|
|
157
|
+
return Errors.createError(ErrorType.Failed, error as NSError)
|
|
158
|
+
case .selected(let paymentOption):
|
|
159
|
+
switch paymentOption {
|
|
160
|
+
case .applePay(let paymentOptionDisplayData):
|
|
161
|
+
payload = CustomerSheetUtils.buildPaymentOptionResult(label: paymentOptionDisplayData.label, imageData: paymentOptionDisplayData.image.pngData()?.base64EncodedString(), paymentMethod: nil)
|
|
162
|
+
case .paymentMethod(let paymentMethod, let paymentOptionDisplayData):
|
|
163
|
+
payload = CustomerSheetUtils.buildPaymentOptionResult(label: paymentOptionDisplayData.label, imageData: paymentOptionDisplayData.image.pngData()?.base64EncodedString(), paymentMethod: paymentMethod)
|
|
164
|
+
case .none:
|
|
165
|
+
break
|
|
166
|
+
}
|
|
167
|
+
case .canceled(let paymentOption):
|
|
168
|
+
switch paymentOption {
|
|
169
|
+
case .applePay(let paymentOptionDisplayData):
|
|
170
|
+
payload = CustomerSheetUtils.buildPaymentOptionResult(label: paymentOptionDisplayData.label, imageData: paymentOptionDisplayData.image.pngData()?.base64EncodedString(), paymentMethod: nil)
|
|
171
|
+
case .paymentMethod(let paymentMethod, let paymentOptionDisplayData):
|
|
172
|
+
payload = CustomerSheetUtils.buildPaymentOptionResult(label: paymentOptionDisplayData.label, imageData: paymentOptionDisplayData.image.pngData()?.base64EncodedString(), paymentMethod: paymentMethod)
|
|
173
|
+
case .none:
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
payload.setValue(["code": ErrorType.Canceled], forKey: "error")
|
|
177
|
+
}
|
|
178
|
+
return payload
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
}
|