Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Alert user if attempting to use methods before initializing Stripe #853

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Renamed `type` field to `paymentMethodType` on `PaymentMethod.Result`, `PaymentIntent.Result`, and `SetupIntent.Result` (result of `createPaymentMethod`, `retrieveSetupIntent`, `confirmSetupIntent`, `confirmPayment`, `collectBankAccountForPayment`, `collectBankAccountForSetup`, `verifyMicrodepositsForPayment`, and `verifyMicrodepositsForSetup`).
- [#849](https://github.com/stripe/stripe-react-native/pull/849) BREAKING CHANGE: Renamed `placeholder` prop on `<CardField />` and `<CardForm />` to `placeholders`.
- [#849](https://github.com/stripe/stripe-react-native/pull/849) Feat: Added customized styling options to `<CardForm />` on Android.
- [#853](https://github.com/stripe/stripe-react-native/pull/853) Fix: resolve with useful error if attempting to use methods before initializing Stripe

## 0.7.0

Expand Down
98 changes: 71 additions & 27 deletions android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
override fun getName(): String {
return "StripeSdk"
}
private lateinit var stripe: Stripe
private var stripe: Stripe? = null

private lateinit var paymentLauncherFragment: PaymentLauncherFragment

Expand All @@ -50,14 +50,19 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
private var initGooglePayPromise: Promise? = null
private var presentGooglePayPromise: Promise? = null

private val MISSING_INIT_ERROR = createError(
"Failed",
"Stripe has not been initialized. Make sure you have initialized Stripe in your app with the StripeProvider component or the initStripe method."
)

private val mActivityEventListener = object : BaseActivityEventListener() {
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
if (::stripe.isInitialized) {
if (stripe != null) {
paymentSheetFragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
googlePayFragment?.activity?.activityResultRegistry?.dispatchResult(requestCode, resultCode, data)
try {
val result = AddPaymentMethodActivityStarter.Result.fromIntent(data)
if (data?.getParcelableExtra<Parcelable>("extra_activity_result") != null) {
data?.getParcelableExtra<Parcelable>("extra_activity_result")?.let {
onFpxPaymentMethodResult(result)
}
} catch (e: java.lang.Exception) {
Expand Down Expand Up @@ -214,7 +219,7 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React

PaymentConfiguration.init(reactApplicationContext, publishableKey, stripeAccountId)

paymentLauncherFragment = PaymentLauncherFragment(stripe, publishableKey, stripeAccountId)
paymentLauncherFragment = PaymentLauncherFragment(stripe!!, publishableKey, stripeAccountId)
getCurrentActivityOrResolveWithError(promise)?.let {
it.supportFragmentManager.beginTransaction()
.add(paymentLauncherFragment, "payment_launcher_fragment")
Expand All @@ -236,6 +241,11 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React

@ReactMethod
fun initPaymentSheet(params: ReadableMap, promise: Promise) {
if (stripe == null) {
promise.resolve(MISSING_INIT_ERROR)
return
}

getCurrentActivityOrResolveWithError(promise)?.let { activity ->
this.initPaymentSheetPromise = promise

Expand All @@ -251,14 +261,24 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React

@ReactMethod
fun presentPaymentSheet(promise: Promise) {
this.presentPaymentSheetPromise = promise
paymentSheetFragment?.present()
paymentSheetFragment?.let {
this.presentPaymentSheetPromise = promise
it.present()
} ?: run {
promise.resolve(
createError("Failed", "Payment sheet has not been initialized."))
}
}

@ReactMethod
fun confirmPaymentSheetPayment(promise: Promise) {
this.confirmPaymentSheetPaymentPromise = promise
paymentSheetFragment?.confirmPayment()
paymentSheetFragment?.let {
this.confirmPaymentSheetPaymentPromise = promise
it.confirmPayment()
} ?: run {
promise.resolve(
createError("Failed", "Payment sheet has not been initialized."))
}
}

private fun payWithFpx() {
Expand Down Expand Up @@ -310,7 +330,7 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
val billingDetailsParams = mapToBillingDetails(getMapOrNull(paymentMethodData, "billingDetails"), cardAddress)

val paymentMethodCreateParams = PaymentMethodCreateParams.create(cardParams, billingDetailsParams)
stripe.createPaymentMethod(
stripe?.createPaymentMethod(
paymentMethodCreateParams,
callback = object : ApiResultCallback<PaymentMethod> {
override fun onError(e: Exception) {
Expand All @@ -321,7 +341,9 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
val paymentMethodMap: WritableMap = mapFromPaymentMethod(result)
promise.resolve(createResult("paymentMethod", paymentMethodMap))
}
})
}) ?: run {
promise.resolve(MISSING_INIT_ERROR)
}
}

@ReactMethod
Expand Down Expand Up @@ -363,8 +385,12 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
)
CoroutineScope(Dispatchers.IO).launch {
runCatching {
val token = stripe.createBankAccountToken(bankAccountParams, null, stripeAccountId)
promise.resolve(createResult("token", mapFromToken(token)))
stripe?.let {
val token = it.createBankAccountToken(bankAccountParams, null, stripeAccountId)
promise.resolve(createResult("token", mapFromToken(token)))
} ?: run {
promise.resolve(MISSING_INIT_ERROR)
}
}.onFailure {
promise.resolve(createError(CreateTokenErrorType.Failed.toString(), it.message))
}
Expand Down Expand Up @@ -393,11 +419,15 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React

CoroutineScope(Dispatchers.IO).launch {
try {
val token = stripe.createCardToken(
cardParams = cardParams,
stripeAccountId = stripeAccountId
)
promise.resolve(createResult("token", mapFromToken(token)))
stripe?.let {
val token = it.createCardToken(
cardParams = cardParams,
stripeAccountId = stripeAccountId
)
promise.resolve(createResult("token", mapFromToken(token)))
} ?: run {
promise.resolve(MISSING_INIT_ERROR)
}
} catch (e: Exception) {
promise.resolve(createError(CreateTokenErrorType.Failed.toString(), e.message))
}
Expand All @@ -406,7 +436,7 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React

@ReactMethod
fun createTokenForCVCUpdate(cvc: String, promise: Promise) {
stripe.createCvcUpdateToken(
stripe?.createCvcUpdateToken(
cvc,
callback = object : ApiResultCallback<Token> {
override fun onSuccess(result: Token) {
Expand All @@ -420,7 +450,9 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
promise.resolve(createError("Failed", e))
}
}
)
) ?: run {
promise.resolve(MISSING_INIT_ERROR)
}
}

@ReactMethod
Expand Down Expand Up @@ -498,23 +530,31 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
@ReactMethod
fun retrievePaymentIntent(clientSecret: String, promise: Promise) {
CoroutineScope(Dispatchers.IO).launch {
val paymentIntent = stripe.retrievePaymentIntentSynchronous(clientSecret)
paymentIntent?.let {
promise.resolve(createResult("paymentIntent", mapFromPaymentIntentResult(it)))
stripe?.let { stripe ->
val paymentIntent = stripe.retrievePaymentIntentSynchronous(clientSecret)
paymentIntent?.let {
promise.resolve(createResult("paymentIntent", mapFromPaymentIntentResult(it)))
} ?: run {
promise.resolve(createError(RetrievePaymentIntentErrorType.Unknown.toString(), "Failed to retrieve the PaymentIntent"))
}
} ?: run {
promise.resolve(createError(RetrievePaymentIntentErrorType.Unknown.toString(), "Failed to retrieve the PaymentIntent"))
promise.resolve(MISSING_INIT_ERROR)
}
}
}

@ReactMethod
fun retrieveSetupIntent(clientSecret: String, promise: Promise) {
CoroutineScope(Dispatchers.IO).launch {
val setupIntent = stripe.retrieveSetupIntentSynchronous(clientSecret)
setupIntent?.let {
promise.resolve(createResult("setupIntent", mapFromSetupIntentResult(it)))
stripe?.let { stripe ->
val setupIntent = stripe.retrieveSetupIntentSynchronous(clientSecret)
setupIntent?.let {
promise.resolve(createResult("setupIntent", mapFromSetupIntentResult(it)))
} ?: run {
promise.resolve(createError(RetrieveSetupIntentErrorType.Unknown.toString(), "Failed to retrieve the SetupIntent"))
}
} ?: run {
promise.resolve(createError(RetrieveSetupIntentErrorType.Unknown.toString(), "Failed to retrieve the SetupIntent"))
promise.resolve(MISSING_INIT_ERROR)
}
}
}
Expand Down Expand Up @@ -646,6 +686,10 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React

@ReactMethod
fun verifyMicrodeposits(isPaymentIntent: Boolean, clientSecret: String, params: ReadableMap, promise: Promise) {
val stripe = stripe ?: run {
promise.resolve(MISSING_INIT_ERROR)
return
}
val amounts = params.getArray("amounts")
val descriptorCode = params.getString("descriptorCode")

Expand Down