stripe-react-native
stripe-react-native copied to clipboard
Different behavior for BLIK payment on Android
Describe the bug When confirming BLIK operation with passing a 6-digit code 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".
For more context: https://github.com/flutter-stripe/flutter_stripe/issues/1497
To Reproduce Servercode:
app.post(
'/create-payment-intent',
async (
req: express.Request,
res: express.Response
): Promise<express.Response<any>> => {
const payment_method_type: string = req.body['payment_method_type'];
const blik_code: string | undefined = req.body['blik_code'];
const bank: string | undefined = req.body['bank'];
const { secret_key } = getKeys();
const stripe = new Stripe(secret_key as string, {
apiVersion: '2023-08-16',
typescript: true,
});
if (payment_method_type == 'blik' && blik_code == undefined) {
return res.send({
error: 'blik_code is undefined',
});
} else if (payment_method_type == 'p24' && bank == undefined) {
return res.send({
error: 'bank is undefined',
});
}
var payment_method_data_type: Stripe.PaymentIntentCreateParams.PaymentMethodData.Type;
if (payment_method_type == 'blik') {
payment_method_data_type = 'blik';
} else if (payment_method_type == 'p24') {
payment_method_data_type = 'p24';
} else {
return res.send({
error: 'Pass payment_method_type blik or p24',
});
}
var bank_type: Stripe.PaymentIntentCreateParams.PaymentMethodData.P24.Bank | undefined;
if (bank == 'alior_bank') {
bank_type = 'alior_bank';
} else if (bank == 'bank_millennium') {
bank_type = 'bank_millennium';
} else if (bank == 'ing') {
bank_type = 'ing';
}
// Create a PaymentIntent with the order amount and currency.
const params: Stripe.PaymentIntentCreateParams = {
confirm: payment_method_type == 'blik' ? true : undefined,
amount: 1099,
currency: "pln",
payment_method_options: {
p24: payment_method_type == 'p24' ? {} : undefined,
blik: payment_method_type == 'blik' ? {
code: blik_code,
} : undefined,
},
payment_method_data: {
type: payment_method_data_type,
blik: payment_method_type == 'blik' ? {} : undefined,
p24: payment_method_type == 'p24' ? {
bank: bank_type,
} : undefined,
billing_details: payment_method_type == 'p24' ? {
email: '[email protected]',
} : undefined,
},
payment_method_types: [
payment_method_type,
],
};
console.log(`create-payment-intent params ${params}`)
try {
const paymentIntent: Stripe.PaymentIntent =
await stripe.paymentIntents.create(params);
// Send publishable key and PaymentIntent client_secret to client.
console.log(`create-payment-intent client_secret ${paymentIntent.client_secret}`)
return res.send({
clientSecret: paymentIntent.client_secret,
});
} catch (error: any) {
console.log(`create-payment-intent error ${error}`)
return res.send({
error: error.raw.message,
});
}
}
);
Create a payment intent and confirm it using this sdk.
Expected behavior PAyment should be successful on android like on iOS.
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
}
}
any updates?
hi, any updates?
hi, any updates?
Same problem here...