@stripe/stripe-react-native 0.10.0 → 0.13.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.
- package/CHANGELOG.md +42 -0
- package/README.md +3 -3
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt +46 -10
- package/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt +5 -0
- package/android/src/main/java/com/reactnativestripesdk/CardFormView.kt +8 -0
- package/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt +6 -1
- package/android/src/main/java/com/reactnativestripesdk/Errors.kt +14 -1
- package/android/src/main/java/com/reactnativestripesdk/GooglePayFragment.kt +68 -52
- package/android/src/main/java/com/reactnativestripesdk/Mappers.kt +5 -7
- package/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt +127 -37
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetAppearance.kt +173 -0
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +60 -36
- package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +67 -177
- package/ios/CardFieldManager.m +1 -0
- package/ios/CardFieldView.swift +6 -0
- package/ios/Mappers.swift +8 -9
- package/ios/PaymentSheetAppearance.swift +209 -0
- package/ios/StripeSdk.swift +141 -116
- package/lib/commonjs/components/AddToWalletButton.js +1 -1
- package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
- package/lib/commonjs/components/ApplePayButton.js +1 -1
- package/lib/commonjs/components/ApplePayButton.js.map +1 -1
- package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
- package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
- package/lib/commonjs/components/CardField.js +1 -1
- package/lib/commonjs/components/CardField.js.map +1 -1
- package/lib/commonjs/components/CardForm.js +1 -1
- package/lib/commonjs/components/CardForm.js.map +1 -1
- package/lib/commonjs/components/GooglePayButton.js +1 -1
- package/lib/commonjs/components/GooglePayButton.js.map +1 -1
- package/lib/commonjs/components/StripeContainer.js +1 -1
- package/lib/commonjs/components/StripeContainer.js.map +1 -1
- package/lib/commonjs/functions.js.map +1 -1
- package/lib/commonjs/types/components/CardFieldInput.js.map +1 -1
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/module/components/AddToWalletButton.js +1 -1
- package/lib/module/components/AddToWalletButton.js.map +1 -1
- package/lib/module/components/ApplePayButton.js +1 -1
- package/lib/module/components/ApplePayButton.js.map +1 -1
- package/lib/module/components/AuBECSDebitForm.js +1 -1
- package/lib/module/components/AuBECSDebitForm.js.map +1 -1
- package/lib/module/components/CardField.js +1 -1
- package/lib/module/components/CardField.js.map +1 -1
- package/lib/module/components/CardForm.js +1 -1
- package/lib/module/components/CardForm.js.map +1 -1
- package/lib/module/components/GooglePayButton.js +1 -1
- package/lib/module/components/GooglePayButton.js.map +1 -1
- package/lib/module/components/StripeContainer.js +1 -1
- package/lib/module/components/StripeContainer.js.map +1 -1
- package/lib/module/functions.js.map +1 -1
- package/lib/module/types/components/CardFieldInput.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/typescript/example/src/screens/PaymentSheetAppearance.d.ts +3 -0
- package/lib/typescript/src/components/CardField.d.ts +3 -0
- package/lib/typescript/src/components/CardForm.d.ts +2 -0
- package/lib/typescript/src/types/PaymentSheet.d.ts +154 -1
- package/lib/typescript/src/types/Token.d.ts +9 -1
- package/lib/typescript/src/types/components/CardFieldInput.d.ts +1 -0
- package/lib/typescript/src/types/components/CardFormView.d.ts +6 -0
- package/lib/typescript/src/types/index.d.ts +1 -4
- package/package.json +1 -1
- package/src/components/CardField.tsx +5 -0
- package/src/components/CardForm.tsx +6 -0
- package/src/functions.ts +1 -1
- package/src/types/PaymentSheet.ts +159 -2
- package/src/types/Token.ts +13 -1
- package/src/types/components/CardFieldInput.ts +1 -0
- package/src/types/components/CardFormView.ts +7 -0
- package/src/types/index.ts +1 -5
- package/stripe-react-native.podspec +1 -1
- package/android/src/main/java/com/reactnativestripesdk/Constants.kt +0 -10
|
@@ -5,83 +5,171 @@ 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
|
|
8
9
|
import androidx.fragment.app.Fragment
|
|
9
10
|
import com.facebook.react.bridge.Promise
|
|
11
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
12
|
import com.stripe.android.ApiResultCallback
|
|
11
13
|
import com.stripe.android.Stripe
|
|
12
14
|
import com.stripe.android.model.*
|
|
13
15
|
import com.stripe.android.payments.paymentlauncher.PaymentLauncher
|
|
14
16
|
import com.stripe.android.payments.paymentlauncher.PaymentResult
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Instances of this class should only be initialized with the companion's helper methods.
|
|
20
|
+
*/
|
|
16
21
|
class PaymentLauncherFragment(
|
|
22
|
+
private val context: ReactApplicationContext,
|
|
17
23
|
private val stripe: Stripe,
|
|
18
24
|
private val publishableKey: String,
|
|
19
25
|
private val stripeAccountId: String?,
|
|
26
|
+
private val promise: Promise,
|
|
27
|
+
// Used when confirming a payment intent
|
|
28
|
+
private val paymentIntentClientSecret: String? = null,
|
|
29
|
+
private val confirmPaymentParams: ConfirmPaymentIntentParams? = null,
|
|
30
|
+
// Used when confirming a setup intent
|
|
31
|
+
private val setupIntentClientSecret: String? = null,
|
|
32
|
+
private val confirmSetupParams: ConfirmSetupIntentParams? = null,
|
|
33
|
+
// Used when handling the next action on a payment intent
|
|
34
|
+
private val handleNextActionClientSecret: String? = null,
|
|
20
35
|
) : Fragment() {
|
|
21
36
|
private lateinit var paymentLauncher: PaymentLauncher
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
|
|
38
|
+
companion object {
|
|
39
|
+
/**
|
|
40
|
+
* Helper-constructor used for confirming payment intents
|
|
41
|
+
*/
|
|
42
|
+
fun forPayment(context: ReactApplicationContext,
|
|
43
|
+
stripe: Stripe,
|
|
44
|
+
publishableKey: String,
|
|
45
|
+
stripeAccountId: String?,
|
|
46
|
+
promise: Promise,
|
|
47
|
+
paymentIntentClientSecret: String,
|
|
48
|
+
confirmPaymentParams: ConfirmPaymentIntentParams): PaymentLauncherFragment {
|
|
49
|
+
val paymentLauncherFragment = PaymentLauncherFragment(
|
|
50
|
+
context,
|
|
51
|
+
stripe,
|
|
52
|
+
publishableKey,
|
|
53
|
+
stripeAccountId,
|
|
54
|
+
promise,
|
|
55
|
+
paymentIntentClientSecret = paymentIntentClientSecret,
|
|
56
|
+
confirmPaymentParams = confirmPaymentParams
|
|
57
|
+
)
|
|
58
|
+
addFragment(paymentLauncherFragment, context, promise)
|
|
59
|
+
return paymentLauncherFragment
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Helper-constructor used for confirming setup intents
|
|
64
|
+
*/
|
|
65
|
+
fun forSetup(context: ReactApplicationContext,
|
|
66
|
+
stripe: Stripe,
|
|
67
|
+
publishableKey: String,
|
|
68
|
+
stripeAccountId: String?,
|
|
69
|
+
promise: Promise,
|
|
70
|
+
setupIntentClientSecret: String,
|
|
71
|
+
confirmSetupParams: ConfirmSetupIntentParams): PaymentLauncherFragment {
|
|
72
|
+
val paymentLauncherFragment = PaymentLauncherFragment(
|
|
73
|
+
context,
|
|
74
|
+
stripe,
|
|
75
|
+
publishableKey,
|
|
76
|
+
stripeAccountId,
|
|
77
|
+
promise,
|
|
78
|
+
setupIntentClientSecret = setupIntentClientSecret,
|
|
79
|
+
confirmSetupParams = confirmSetupParams
|
|
80
|
+
)
|
|
81
|
+
addFragment(paymentLauncherFragment, context, promise)
|
|
82
|
+
return paymentLauncherFragment
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Helper-constructor used for handling the next action on a payment intent
|
|
87
|
+
*/
|
|
88
|
+
fun forNextAction(context: ReactApplicationContext,
|
|
89
|
+
stripe: Stripe,
|
|
90
|
+
publishableKey: String,
|
|
91
|
+
stripeAccountId: String?,
|
|
92
|
+
promise: Promise,
|
|
93
|
+
handleNextActionClientSecret: String): PaymentLauncherFragment {
|
|
94
|
+
val paymentLauncherFragment = PaymentLauncherFragment(
|
|
95
|
+
context,
|
|
96
|
+
stripe,
|
|
97
|
+
publishableKey,
|
|
98
|
+
stripeAccountId,
|
|
99
|
+
promise,
|
|
100
|
+
handleNextActionClientSecret = handleNextActionClientSecret,
|
|
101
|
+
)
|
|
102
|
+
addFragment(paymentLauncherFragment, context, promise)
|
|
103
|
+
return paymentLauncherFragment
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private fun addFragment(fragment: PaymentLauncherFragment, context: ReactApplicationContext, promise: Promise) {
|
|
107
|
+
(context.currentActivity as? AppCompatActivity)?.let {
|
|
108
|
+
try {
|
|
109
|
+
it.supportFragmentManager.beginTransaction()
|
|
110
|
+
.add(fragment, "payment_launcher_fragment")
|
|
111
|
+
.commit()
|
|
112
|
+
} catch (error: IllegalStateException) {
|
|
113
|
+
promise.resolve(createError(ErrorType.Failed.toString(), error.message))
|
|
114
|
+
}
|
|
115
|
+
} ?: run {
|
|
116
|
+
promise.resolve(createMissingActivityError())
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
25
120
|
|
|
26
121
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
|
27
122
|
savedInstanceState: Bundle?): View {
|
|
28
123
|
paymentLauncher = createPaymentLauncher()
|
|
124
|
+
if (paymentIntentClientSecret != null && confirmPaymentParams != null) {
|
|
125
|
+
paymentLauncher.confirm(confirmPaymentParams)
|
|
126
|
+
} else if (setupIntentClientSecret != null && confirmSetupParams != null) {
|
|
127
|
+
paymentLauncher.confirm(confirmSetupParams)
|
|
128
|
+
} else if (handleNextActionClientSecret != null) {
|
|
129
|
+
paymentLauncher.handleNextActionForPaymentIntent(handleNextActionClientSecret)
|
|
130
|
+
} else {
|
|
131
|
+
throw Exception("Invalid parameters provided to PaymentLauncher. Ensure that you are providing the correct client secret and setup params (if necessary).")
|
|
132
|
+
}
|
|
29
133
|
return FrameLayout(requireActivity()).also {
|
|
30
134
|
it.visibility = View.GONE
|
|
31
135
|
}
|
|
32
136
|
}
|
|
33
137
|
|
|
34
|
-
fun handleNextActionForPaymentIntent(clientSecret: String, promise: Promise) {
|
|
35
|
-
this.clientSecret = clientSecret
|
|
36
|
-
this.promise = promise
|
|
37
|
-
paymentLauncher.handleNextActionForPaymentIntent(clientSecret)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
fun confirm(params: ConfirmPaymentIntentParams, clientSecret: String, promise: Promise) {
|
|
41
|
-
this.clientSecret = clientSecret
|
|
42
|
-
this.promise = promise
|
|
43
|
-
this.isPaymentIntent = true
|
|
44
|
-
paymentLauncher.confirm(params)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
fun confirm(params: ConfirmSetupIntentParams, clientSecret: String, promise: Promise) {
|
|
48
|
-
this.clientSecret = clientSecret
|
|
49
|
-
this.promise = promise
|
|
50
|
-
this.isPaymentIntent = false
|
|
51
|
-
paymentLauncher.confirm(params)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
138
|
private fun createPaymentLauncher(): PaymentLauncher {
|
|
55
139
|
return PaymentLauncher.create(this, publishableKey, stripeAccountId) { paymentResult ->
|
|
56
140
|
when (paymentResult) {
|
|
57
141
|
is PaymentResult.Completed -> {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
142
|
+
if (paymentIntentClientSecret != null) {
|
|
143
|
+
retrievePaymentIntent(paymentIntentClientSecret, stripeAccountId)
|
|
144
|
+
} else if (handleNextActionClientSecret != null) {
|
|
145
|
+
retrievePaymentIntent(handleNextActionClientSecret, stripeAccountId)
|
|
146
|
+
} else if (setupIntentClientSecret != null) {
|
|
147
|
+
retrieveSetupIntent(setupIntentClientSecret, stripeAccountId)
|
|
148
|
+
} else {
|
|
149
|
+
throw Exception("Failed to create Payment Launcher. No client secret provided.")
|
|
66
150
|
}
|
|
67
151
|
}
|
|
68
152
|
is PaymentResult.Canceled -> {
|
|
69
|
-
promise
|
|
70
|
-
|
|
153
|
+
promise.resolve(createError(ConfirmPaymentErrorType.Canceled.toString(), message = null))
|
|
154
|
+
cleanup()
|
|
71
155
|
}
|
|
72
156
|
is PaymentResult.Failed -> {
|
|
73
|
-
promise
|
|
74
|
-
|
|
157
|
+
promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), paymentResult.throwable))
|
|
158
|
+
cleanup()
|
|
75
159
|
}
|
|
76
160
|
}
|
|
77
161
|
}
|
|
78
162
|
}
|
|
79
163
|
|
|
164
|
+
private fun cleanup() {
|
|
165
|
+
(context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
|
|
166
|
+
}
|
|
167
|
+
|
|
80
168
|
private fun retrieveSetupIntent(clientSecret: String, stripeAccountId: String?) {
|
|
81
|
-
val promise = promise ?: throw Exception("No promise is set to handle payment results.")
|
|
82
169
|
stripe.retrieveSetupIntent(clientSecret, stripeAccountId, object : ApiResultCallback<SetupIntent> {
|
|
83
170
|
override fun onError(e: Exception) {
|
|
84
171
|
promise.resolve(createError(ConfirmSetupIntentErrorType.Failed.toString(), e))
|
|
172
|
+
cleanup()
|
|
85
173
|
}
|
|
86
174
|
|
|
87
175
|
override fun onSuccess(result: SetupIntent) {
|
|
@@ -113,15 +201,16 @@ class PaymentLauncherFragment(
|
|
|
113
201
|
promise.resolve(createError(ConfirmSetupIntentErrorType.Unknown.toString(), "unhandled error: ${result.status}"))
|
|
114
202
|
}
|
|
115
203
|
}
|
|
204
|
+
cleanup()
|
|
116
205
|
}
|
|
117
206
|
})
|
|
118
207
|
}
|
|
119
208
|
|
|
120
209
|
private fun retrievePaymentIntent(clientSecret: String, stripeAccountId: String?) {
|
|
121
|
-
val promise = promise ?: throw Exception("No promise is set to handle payment results.")
|
|
122
210
|
stripe.retrievePaymentIntent(clientSecret, stripeAccountId, object : ApiResultCallback<PaymentIntent> {
|
|
123
211
|
override fun onError(e: Exception) {
|
|
124
212
|
promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), e))
|
|
213
|
+
cleanup()
|
|
125
214
|
}
|
|
126
215
|
|
|
127
216
|
override fun onSuccess(result: PaymentIntent) {
|
|
@@ -153,6 +242,7 @@ class PaymentLauncherFragment(
|
|
|
153
242
|
promise.resolve(createError(ConfirmPaymentErrorType.Unknown.toString(), "unhandled error: ${result.status}"))
|
|
154
243
|
}
|
|
155
244
|
}
|
|
245
|
+
cleanup()
|
|
156
246
|
}
|
|
157
247
|
})
|
|
158
248
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
package com.reactnativestripesdk
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import android.os.Bundle
|
|
5
|
+
import com.stripe.android.paymentsheet.PaymentSheet
|
|
6
|
+
|
|
7
|
+
fun PaymentSheetFragment.buildPaymentSheetAppearance(userParams: Bundle?): PaymentSheet.Appearance {
|
|
8
|
+
val colorParams = userParams?.getBundle(PaymentSheetAppearanceKeys.COLORS)
|
|
9
|
+
val lightColorParams = colorParams?.getBundle(PaymentSheetAppearanceKeys.LIGHT) ?: colorParams
|
|
10
|
+
val darkColorParams = colorParams?.getBundle(PaymentSheetAppearanceKeys.DARK) ?: colorParams
|
|
11
|
+
|
|
12
|
+
return PaymentSheet.Appearance(
|
|
13
|
+
typography = buildTypography(userParams?.getBundle(PaymentSheetAppearanceKeys.FONT)),
|
|
14
|
+
colorsLight = buildColors(lightColorParams, PaymentSheet.Colors.defaultLight),
|
|
15
|
+
colorsDark = buildColors(darkColorParams, PaymentSheet.Colors.defaultDark),
|
|
16
|
+
shapes = buildShapes(userParams?.getBundle(PaymentSheetAppearanceKeys.SHAPES)),
|
|
17
|
+
primaryButton = buildPrimaryButton(userParams?.getBundle(PaymentSheetAppearanceKeys.PRIMARY_BUTTON))
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private fun PaymentSheetFragment.buildTypography(fontParams: Bundle?): PaymentSheet.Typography {
|
|
22
|
+
return PaymentSheet.Typography.default.copy(
|
|
23
|
+
sizeScaleFactor = getFloatOr(fontParams, PaymentSheetAppearanceKeys.SCALE, PaymentSheet.Typography.default.sizeScaleFactor),
|
|
24
|
+
fontResId = getFontResId(fontParams, PaymentSheetAppearanceKeys.FAMILY, PaymentSheet.Typography.default.fontResId)
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Throws(PaymentSheetAppearanceException::class)
|
|
29
|
+
private fun colorFromHexOrDefault(hexString: String?, default: Int): Int {
|
|
30
|
+
return hexString?.trim()?.replace("#","")?.let {
|
|
31
|
+
if (it.length == 6 || it.length == 8) {
|
|
32
|
+
return Color.parseColor("#$it")
|
|
33
|
+
} else throw PaymentSheetAppearanceException("Failed to set Payment Sheet appearance. Expected hex string of length 6 or 8, but received: $it")
|
|
34
|
+
} ?: run {
|
|
35
|
+
return default
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private fun buildColors(colorParams: Bundle?, default: PaymentSheet.Colors): PaymentSheet.Colors {
|
|
40
|
+
if (colorParams == null) {
|
|
41
|
+
return default
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return default.copy(
|
|
45
|
+
primary = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.PRIMARY), default.primary),
|
|
46
|
+
surface = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.BACKGROUND), default.surface),
|
|
47
|
+
component = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.COMPONENT_BACKGROUND), default.component),
|
|
48
|
+
componentBorder = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.COMPONENT_BORDER), default.componentBorder),
|
|
49
|
+
componentDivider = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.COMPONENT_DIVIDER), default.componentDivider),
|
|
50
|
+
onComponent = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.COMPONENT_TEXT), default.onComponent),
|
|
51
|
+
onSurface = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.PRIMARY_TEXT), default.onSurface),
|
|
52
|
+
subtitle = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.SECONDARY_TEXT), default.subtitle),
|
|
53
|
+
placeholderText = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.PLACEHOLDER_TEXT), default.placeholderText),
|
|
54
|
+
appBarIcon = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.ICON), default.appBarIcon),
|
|
55
|
+
error = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.ERROR), default.error),
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private fun buildShapes(shapeParams: Bundle?): PaymentSheet.Shapes {
|
|
60
|
+
return PaymentSheet.Shapes.default.copy(
|
|
61
|
+
cornerRadiusDp = getFloatOr(shapeParams, PaymentSheetAppearanceKeys.BORDER_RADIUS, PaymentSheet.Shapes.default.cornerRadiusDp),
|
|
62
|
+
borderStrokeWidthDp = getFloatOr(shapeParams, PaymentSheetAppearanceKeys.BORDER_WIDTH, PaymentSheet.Shapes.default.borderStrokeWidthDp)
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private fun PaymentSheetFragment.buildPrimaryButton(params: Bundle?): PaymentSheet.PrimaryButton {
|
|
67
|
+
if (params == null) {
|
|
68
|
+
return PaymentSheet.PrimaryButton()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
val fontParams = params.getBundle(PaymentSheetAppearanceKeys.FONT) ?: Bundle.EMPTY
|
|
72
|
+
val shapeParams = params.getBundle(PaymentSheetAppearanceKeys.SHAPES) ?: Bundle.EMPTY
|
|
73
|
+
val colorParams = params.getBundle(PaymentSheetAppearanceKeys.COLORS) ?: Bundle.EMPTY
|
|
74
|
+
val lightColorParams = colorParams.getBundle(PaymentSheetAppearanceKeys.LIGHT) ?: colorParams
|
|
75
|
+
val darkColorParams = colorParams.getBundle(PaymentSheetAppearanceKeys.DARK) ?: colorParams
|
|
76
|
+
|
|
77
|
+
return PaymentSheet.PrimaryButton(
|
|
78
|
+
colorsLight = buildPrimaryButtonColors(lightColorParams, PaymentSheet.PrimaryButtonColors.defaultLight),
|
|
79
|
+
colorsDark = buildPrimaryButtonColors(darkColorParams, PaymentSheet.PrimaryButtonColors.defaultDark),
|
|
80
|
+
shape = PaymentSheet.PrimaryButtonShape(
|
|
81
|
+
cornerRadiusDp = getFloatOrNull(shapeParams, PaymentSheetAppearanceKeys.BORDER_RADIUS),
|
|
82
|
+
borderStrokeWidthDp = getFloatOrNull(shapeParams, PaymentSheetAppearanceKeys.BORDER_WIDTH),
|
|
83
|
+
),
|
|
84
|
+
typography = PaymentSheet.PrimaryButtonTypography(
|
|
85
|
+
fontResId = getFontResId(fontParams, PaymentSheetAppearanceKeys.FAMILY, null)
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Throws(PaymentSheetAppearanceException::class)
|
|
91
|
+
private fun buildPrimaryButtonColors(colorParams: Bundle, default: PaymentSheet.PrimaryButtonColors): PaymentSheet.PrimaryButtonColors {
|
|
92
|
+
return PaymentSheet.PrimaryButtonColors(
|
|
93
|
+
background = colorParams.getString(PaymentSheetAppearanceKeys.BACKGROUND)?.trim()?.replace("#", "")?.let {
|
|
94
|
+
if (it.length == 6 || it.length == 8) {
|
|
95
|
+
Color.parseColor("#$it")
|
|
96
|
+
} else throw PaymentSheetAppearanceException("Failed to set Payment Sheet appearance. Expected hex string of length 6 or 8, but received: $it")
|
|
97
|
+
} ?: run {
|
|
98
|
+
null
|
|
99
|
+
},
|
|
100
|
+
onBackground = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.TEXT), default.onBackground),
|
|
101
|
+
border = colorFromHexOrDefault(colorParams.getString(PaymentSheetAppearanceKeys.BORDER), default.border),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun getFloatOr(bundle: Bundle?, key: String, defaultValue: Float): Float {
|
|
106
|
+
return if (bundle?.containsKey(key) == true) {
|
|
107
|
+
bundle.getFloat(key, bundle.getInt(key).toFloat())
|
|
108
|
+
} else {
|
|
109
|
+
defaultValue
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private fun getFloatOrNull(bundle: Bundle?, key: String): Float? {
|
|
114
|
+
return if (bundle?.containsKey(key) == true) {
|
|
115
|
+
bundle.getFloat(key, bundle.getInt(key).toFloat())
|
|
116
|
+
} else {
|
|
117
|
+
null
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Throws(PaymentSheetAppearanceException::class)
|
|
122
|
+
private fun PaymentSheetFragment.getFontResId(bundle: Bundle?, key: String, defaultValue: Int?): Int? {
|
|
123
|
+
val fontErrorPrefix = "Encountered an error when setting a custom font:"
|
|
124
|
+
if (bundle?.containsKey(key) != true) {
|
|
125
|
+
return defaultValue
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
val fontFileName = bundle.getString(key)
|
|
129
|
+
?: throw PaymentSheetAppearanceException("$fontErrorPrefix expected String for font.$key, but received null.")
|
|
130
|
+
if (Regex("[^a-z0-9]").containsMatchIn(fontFileName)) {
|
|
131
|
+
throw PaymentSheetAppearanceException(
|
|
132
|
+
"$fontErrorPrefix appearance.font.$key should only contain lowercase alphanumeric characters on Android, but received '$fontFileName'. This value must match the filename in android/app/src/main/res/font"
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
val id = resources.getIdentifier(fontFileName, "font", context?.packageName)
|
|
137
|
+
if (id == 0) {
|
|
138
|
+
throw PaymentSheetAppearanceException("$fontErrorPrefix Failed to find font: $fontFileName")
|
|
139
|
+
} else {
|
|
140
|
+
return id
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private class PaymentSheetAppearanceKeys {
|
|
145
|
+
companion object {
|
|
146
|
+
const val COLORS = "colors"
|
|
147
|
+
const val LIGHT = "light"
|
|
148
|
+
const val DARK = "dark"
|
|
149
|
+
const val PRIMARY = "primary"
|
|
150
|
+
const val BACKGROUND = "background"
|
|
151
|
+
const val COMPONENT_BACKGROUND = "componentBackground"
|
|
152
|
+
const val COMPONENT_BORDER = "componentBorder"
|
|
153
|
+
const val COMPONENT_DIVIDER = "componentDivider"
|
|
154
|
+
const val COMPONENT_TEXT = "componentText"
|
|
155
|
+
const val PRIMARY_TEXT = "primaryText"
|
|
156
|
+
const val SECONDARY_TEXT = "secondaryText"
|
|
157
|
+
const val PLACEHOLDER_TEXT = "placeholderText"
|
|
158
|
+
const val ICON = "icon"
|
|
159
|
+
const val ERROR = "error"
|
|
160
|
+
|
|
161
|
+
const val FONT = "font"
|
|
162
|
+
const val FAMILY = "family"
|
|
163
|
+
const val SCALE = "scale"
|
|
164
|
+
|
|
165
|
+
const val SHAPES = "shapes"
|
|
166
|
+
const val BORDER_RADIUS = "borderRadius"
|
|
167
|
+
const val BORDER_WIDTH = "borderWidth"
|
|
168
|
+
|
|
169
|
+
const val PRIMARY_BUTTON = "primaryButton"
|
|
170
|
+
const val TEXT = "text"
|
|
171
|
+
const val BORDER = "border"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
package com.reactnativestripesdk
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.content.Intent
|
|
5
|
-
import android.content.res.ColorStateList
|
|
6
4
|
import android.graphics.Bitmap
|
|
7
5
|
import android.graphics.Canvas
|
|
8
6
|
import android.graphics.Color
|
|
@@ -12,29 +10,37 @@ import android.view.LayoutInflater
|
|
|
12
10
|
import android.view.View
|
|
13
11
|
import android.view.ViewGroup
|
|
14
12
|
import android.widget.FrameLayout
|
|
13
|
+
import androidx.appcompat.app.AppCompatActivity
|
|
15
14
|
import androidx.appcompat.content.res.AppCompatResources
|
|
16
15
|
import androidx.core.graphics.drawable.DrawableCompat
|
|
17
16
|
import androidx.fragment.app.Fragment
|
|
18
|
-
import
|
|
17
|
+
import com.facebook.react.bridge.Promise
|
|
18
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
19
|
+
import com.facebook.react.bridge.WritableMap
|
|
20
|
+
import com.facebook.react.bridge.WritableNativeMap
|
|
19
21
|
import com.stripe.android.paymentsheet.PaymentOptionCallback
|
|
20
22
|
import com.stripe.android.paymentsheet.PaymentSheet
|
|
23
|
+
import com.stripe.android.paymentsheet.PaymentSheetResult
|
|
21
24
|
import com.stripe.android.paymentsheet.PaymentSheetResultCallback
|
|
22
25
|
import java.io.ByteArrayOutputStream
|
|
23
26
|
|
|
24
|
-
class PaymentSheetFragment
|
|
27
|
+
class PaymentSheetFragment(
|
|
28
|
+
private val context: ReactApplicationContext,
|
|
29
|
+
private val initPromise: Promise
|
|
30
|
+
) : Fragment() {
|
|
25
31
|
private var paymentSheet: PaymentSheet? = null
|
|
26
32
|
private var flowController: PaymentSheet.FlowController? = null
|
|
27
33
|
private var paymentIntentClientSecret: String? = null
|
|
28
34
|
private var setupIntentClientSecret: String? = null
|
|
29
35
|
private lateinit var paymentSheetConfiguration: PaymentSheet.Configuration
|
|
30
|
-
private
|
|
36
|
+
private var confirmPromise: Promise? = null
|
|
37
|
+
private var presentPromise: Promise? = null
|
|
31
38
|
|
|
32
39
|
override fun onCreateView(
|
|
33
40
|
inflater: LayoutInflater,
|
|
34
41
|
container: ViewGroup?,
|
|
35
42
|
savedInstanceState: Bundle?
|
|
36
43
|
): View {
|
|
37
|
-
localBroadcastManager = LocalBroadcastManager.getInstance(requireContext())
|
|
38
44
|
return FrameLayout(requireActivity()).also {
|
|
39
45
|
it.visibility = View.GONE
|
|
40
46
|
}
|
|
@@ -50,34 +56,51 @@ class PaymentSheetFragment : Fragment() {
|
|
|
50
56
|
val googlePayEnabled = arguments?.getBoolean("googlePay")
|
|
51
57
|
val testEnv = arguments?.getBoolean("testEnv")
|
|
52
58
|
val allowsDelayedPaymentMethods = arguments?.getBoolean("allowsDelayedPaymentMethods")
|
|
53
|
-
val primaryButtonColorHexStr = arguments?.getString("primaryButtonColor").orEmpty()
|
|
54
59
|
val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails")
|
|
55
60
|
paymentIntentClientSecret = arguments?.getString("paymentIntentClientSecret").orEmpty()
|
|
56
61
|
setupIntentClientSecret = arguments?.getString("setupIntentClientSecret").orEmpty()
|
|
62
|
+
val appearance = try {
|
|
63
|
+
buildPaymentSheetAppearance(arguments?.getBundle("appearance"))
|
|
64
|
+
} catch (error: PaymentSheetAppearanceException) {
|
|
65
|
+
initPromise.resolve(createError(ErrorType.Failed.toString(), error))
|
|
66
|
+
return
|
|
67
|
+
}
|
|
57
68
|
|
|
58
69
|
val paymentOptionCallback = PaymentOptionCallback { paymentOption ->
|
|
59
|
-
val intent = Intent(ON_PAYMENT_OPTION_ACTION)
|
|
60
|
-
|
|
61
70
|
if (paymentOption != null) {
|
|
62
71
|
val bitmap = getBitmapFromVectorDrawable(context, paymentOption.drawableResourceId)
|
|
63
72
|
val imageString = getBase64FromBitmap(bitmap)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
val option: WritableMap = WritableNativeMap()
|
|
74
|
+
option.putString("label", paymentOption.label)
|
|
75
|
+
option.putString("image", imageString)
|
|
76
|
+
presentPromise?.resolve(createResult("paymentOption", option))
|
|
77
|
+
} else {
|
|
78
|
+
presentPromise?.resolve(createError(PaymentSheetErrorType.Canceled.toString(), "The payment option selection flow has been canceled"))
|
|
67
79
|
}
|
|
68
|
-
localBroadcastManager.sendBroadcast(intent)
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
val paymentResultCallback = PaymentSheetResultCallback { paymentResult ->
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
when (paymentResult) {
|
|
84
|
+
is PaymentSheetResult.Canceled -> {
|
|
85
|
+
val message = "The payment flow has been canceled"
|
|
86
|
+
confirmPromise?.resolve(createError(PaymentSheetErrorType.Canceled.toString(), message))
|
|
87
|
+
?: run {
|
|
88
|
+
presentPromise?.resolve(createError(PaymentSheetErrorType.Canceled.toString(), message))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
is PaymentSheetResult.Failed -> {
|
|
92
|
+
confirmPromise?.resolve(createError(PaymentSheetErrorType.Failed.toString(), paymentResult.error))
|
|
93
|
+
?: run {
|
|
94
|
+
presentPromise?.resolve(createError(PaymentSheetErrorType.Failed.toString(), paymentResult.error))
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
is PaymentSheetResult.Completed -> {
|
|
98
|
+
confirmPromise?.resolve(WritableNativeMap()) ?: run {
|
|
99
|
+
presentPromise?.resolve(WritableNativeMap())
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
(context.currentActivity as? AppCompatActivity)?.supportFragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()
|
|
81
104
|
}
|
|
82
105
|
|
|
83
106
|
var defaultBillingDetails: PaymentSheet.BillingDetails? = null
|
|
@@ -100,7 +123,6 @@ class PaymentSheetFragment : Fragment() {
|
|
|
100
123
|
paymentSheetConfiguration = PaymentSheet.Configuration(
|
|
101
124
|
merchantDisplayName = merchantDisplayName,
|
|
102
125
|
allowsDelayedPaymentMethods = allowsDelayedPaymentMethods ?: false,
|
|
103
|
-
primaryButtonColor = primaryButtonColor,
|
|
104
126
|
defaultBillingDetails=defaultBillingDetails,
|
|
105
127
|
customer = if (customerId.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) PaymentSheet.CustomerConfiguration(
|
|
106
128
|
id = customerId,
|
|
@@ -110,7 +132,8 @@ class PaymentSheetFragment : Fragment() {
|
|
|
110
132
|
environment = if (testEnv == true) PaymentSheet.GooglePayConfiguration.Environment.Test else PaymentSheet.GooglePayConfiguration.Environment.Production,
|
|
111
133
|
countryCode = countryCode,
|
|
112
134
|
currencyCode = currencyCode
|
|
113
|
-
) else null
|
|
135
|
+
) else null,
|
|
136
|
+
appearance = appearance
|
|
114
137
|
)
|
|
115
138
|
|
|
116
139
|
if (arguments?.getBoolean("customFlow") == true) {
|
|
@@ -118,12 +141,12 @@ class PaymentSheetFragment : Fragment() {
|
|
|
118
141
|
configureFlowController()
|
|
119
142
|
} else {
|
|
120
143
|
paymentSheet = PaymentSheet(this, paymentResultCallback)
|
|
121
|
-
|
|
122
|
-
localBroadcastManager.sendBroadcast(intent)
|
|
144
|
+
initPromise.resolve(WritableNativeMap())
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
147
|
|
|
126
|
-
fun present() {
|
|
148
|
+
fun present(promise: Promise) {
|
|
149
|
+
this.presentPromise = promise
|
|
127
150
|
if(paymentSheet != null) {
|
|
128
151
|
if (!paymentIntentClientSecret.isNullOrEmpty()) {
|
|
129
152
|
paymentSheet?.presentWithPaymentIntent(paymentIntentClientSecret!!, paymentSheetConfiguration)
|
|
@@ -135,23 +158,24 @@ class PaymentSheetFragment : Fragment() {
|
|
|
135
158
|
}
|
|
136
159
|
}
|
|
137
160
|
|
|
138
|
-
fun confirmPayment() {
|
|
161
|
+
fun confirmPayment(promise: Promise) {
|
|
162
|
+
this.confirmPromise = promise
|
|
139
163
|
flowController?.confirm()
|
|
140
164
|
}
|
|
141
165
|
|
|
142
166
|
private fun configureFlowController() {
|
|
143
167
|
val onFlowControllerConfigure = PaymentSheet.FlowController.ConfigCallback { _, _ ->
|
|
144
|
-
val
|
|
145
|
-
val intent = Intent(ON_CONFIGURE_FLOW_CONTROLLER)
|
|
146
|
-
|
|
147
|
-
paymentOption?.let {
|
|
168
|
+
val result = flowController?.getPaymentOption()?.let {
|
|
148
169
|
val bitmap = getBitmapFromVectorDrawable(context, it.drawableResourceId)
|
|
149
170
|
val imageString = getBase64FromBitmap(bitmap)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
val option: WritableMap = WritableNativeMap()
|
|
172
|
+
option.putString("label", it.label)
|
|
173
|
+
option.putString("image", imageString)
|
|
174
|
+
createResult("paymentOption", option)
|
|
175
|
+
} ?: run {
|
|
176
|
+
WritableNativeMap()
|
|
153
177
|
}
|
|
154
|
-
|
|
178
|
+
initPromise.resolve(result)
|
|
155
179
|
}
|
|
156
180
|
|
|
157
181
|
if (!paymentIntentClientSecret.isNullOrEmpty()) {
|