Describe the bug
I want to confirm a BLIK operation with passing a 6-digit code to BE during creating payment intent.
Android returns error "The payment has been canceled" but I see that payment appears in Stripe dashboard as "Succeeded".
iOS with the same implementation return "Payment successfully completed" and I also see that payment appears in Stripe dashboard as "Succeeded".
To Reproduce
Steps to reproduce the behavior:
You can use example from this repository and my code:
final result = await _createPaymentIntent();
final clientSecret = await result['clientSecret'];
// 2. use the client secret to confirm the payment and handle the result.
try {
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: clientSecret,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Payment succesfully completed'),
),
);
} on Exception catch (e) {
if (e is StripeException) {
print("error: ${e.error.message}");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error from Stripe: ${e.error.localizedMessage}'),
),
);
} else {
print("error: ${e}");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Unforeseen error: ${e}'),
),
);
}
Expected behavior
Android returns no error and message "Payment successfully completed" as like on iOS.
Smartphone / tablet
Device: Android Emulator, Samsung s10+ and more
OS: Android 13
Package version: 9.5.0+1
Flutter version 3.13.9
Additional context
I found that isNextActionSuccessState(result.nextActionType) in retrievePaymentIntent function in PaymentLauncherFragment.kt in reactnativestripesdk package returns false and thats why it returns error with the message "The payment has been canceled"
private fun retrievePaymentIntent(clientSecret: String, stripeAccountId: String?) {
stripe.retrievePaymentIntent(clientSecret, stripeAccountId, expand = listOf("payment_method"), object : ApiResultCallback<PaymentIntent> {
override fun onError(e: Exception) {
promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), e))
removeFragment(context)
}
override fun onSuccess(result: PaymentIntent) {
when (result.status) {
StripeIntent.Status.Succeeded,
StripeIntent.Status.Processing,
StripeIntent.Status.RequiresConfirmation,
StripeIntent.Status.RequiresCapture -> {
promise.resolve(createResult("paymentIntent", mapFromPaymentIntentResult(result)))
}
StripeIntent.Status.RequiresAction -> {
// nextActionType is BlikAuthorize and this method will return false
if (isNextActionSuccessState(result.nextActionType)) {
promise.resolve(createResult("paymentIntent", mapFromPaymentIntentResult(result)))
} else {
(result.lastPaymentError)?.let {
promise.resolve(createError(ConfirmPaymentErrorType.Canceled.toString(), it))
} ?: run {
// Android will go here
promise.resolve(createError(ConfirmPaymentErrorType.Canceled.toString(), "The payment has been canceled"))
}
}
}
StripeIntent.Status.RequiresPaymentMethod -> {
promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), result.lastPaymentError))
}
StripeIntent.Status.Canceled -> {
promise.resolve(createError(ConfirmPaymentErrorType.Canceled.toString(), result.lastPaymentError))
}
else -> {
promise.resolve(createError(ConfirmPaymentErrorType.Unknown.toString(), "unhandled error: ${result.status}"))
}
}
removeFragment(context)
}
})
}
On iOS StripeSdk.swift has different implementation for this confirmPayment
func onCompleteConfirmPayment(status: STPPaymentHandlerActionStatus, paymentIntent: STPPaymentIntent?, error: NSError?) {
self.confirmPaymentClientSecret = nil
switch (status) {
case .failed:
confirmPaymentResolver?(Errors.createError(ErrorType.Failed, error))
break
case .canceled:
let statusCode: String
if (paymentIntent?.status == STPPaymentIntentStatus.requiresPaymentMethod) {
statusCode = ErrorType.Failed
} else {
statusCode = ErrorType.Canceled
}
if let lastPaymentError = paymentIntent?.lastPaymentError {
confirmPaymentResolver?(Errors.createError(statusCode, lastPaymentError))
} else {
confirmPaymentResolver?(Errors.createError(statusCode, "The payment has been canceled"))
}
break
case .succeeded:
// iOS will go here
if let paymentIntent = paymentIntent {
let intent = Mappers.mapFromPaymentIntent(paymentIntent: paymentIntent)
confirmPaymentResolver?(Mappers.createResult("paymentIntent", intent))
}
break
@unknown default:
confirmPaymentResolver?(Errors.createError(ErrorType.Unknown, "Cannot complete the payment"))
break
}
}
Describe the bug I want to confirm a BLIK operation with passing a 6-digit code to BE during creating payment intent. Android returns error "The payment has been canceled" but I see that payment appears in Stripe dashboard as "Succeeded". iOS with the same implementation return "Payment successfully completed" and I also see that payment appears in Stripe dashboard as "Succeeded".
To Reproduce Steps to reproduce the behavior:
Server code:
Mobile code:
Expected behavior Android returns no error and message "Payment successfully completed" as like on iOS.
Smartphone / tablet
Additional context I found that
isNextActionSuccessState(result.nextActionType)
inretrievePaymentIntent
function inPaymentLauncherFragment.kt
in reactnativestripesdk package returns false and thats why it returns error with the message "The payment has been canceled"On iOS StripeSdk.swift has different implementation for this confirmPayment