-
Notifications
You must be signed in to change notification settings - Fork 43
Description
Describe the bug
I have moved away from the "Advanced flow: three API requests" and to the session protocol as this is the suggested method for making 3D secure checks work with the app (according to Adyen - Build your integration ) and this appear to works for iOS. But for Android I've experienced a weird interaction between the Adyen library and the App "MitID" that most payment providers in Denmark use to perform the 3D secure check.
On Android the MitID app appears to be opened inside my own app rather than redirected to, and it is not closed after the payment has been accepted, effectively hijacking my app.
Below is a screenshot to illustrate the issue, I've had to gray out the content for privacy purposes but I hope it still makes sense. The body of "My App" is the exact same as that of the "MitID App" and there is no way to navigating back to the real "My App" so I end up with two instances of the MitID App running, one as it's own app and one inside my app. The only way to "fix" it is to close "My App" but this breaks the payment flow.
The implementation
In package.json:
"@adyen/react-native": "^2.7.2"
The Adyen payment components looks like this:
export const AdyenPayment = (props: {
adyenCheckoutData?: AdyenCheckoutData
orderId: string
onCancel: () => void
onComplete: () => void
status: OrderStatus | undefined
countryCode: string | undefined
paymentAmount: {
value: number
currency: string
}
showStorePaymentField: boolean
}) => {
return (
<AdyenCheckout
config={{
clientKey: props.clientId,
environment: props.live ? 'live-eu' : 'test',
returnUrl: adyenReturnUrl,
countryCode: props.countryCode,
amount: props.paymentAmount,
applepay: {
merchantID: props.appleMerchantId,
merchantName: props.appDisplayName,
},
card: {
showStorePaymentField: props.showStorePaymentField,
holderNameRequired: true,
},
locale: globalLocale,
}}
session={props.session}
onError={(payload: AdyenError, nativeComponent: AdyenComponent) => {
// ...
}}
onComplete={(result: string, nativeComponent: AdyenComponent) => {
// ...
}}
>
<Inner />
</AdyenCheckout>
)
}
const Inner = () => {
const [started, setStarted] = useState<boolean>(false)
const { start, paymentMethods } = useAdyenCheckout()
useEffect(() => {
if (paymentMethods?.paymentMethods == undefined) return
setStarted(true)
if (start && !started) {
start('dropIn')
}
}, [start, paymentMethods?.paymentMethods])
return <ActivityIndicator />
}
MainActivity.kt looks like this:
import android.os.Bundle
class MainActivity : ReactActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
AdyenCheckout.setLauncherActivity(this)
}
}
It is not clear to me how the returnUrl should be filled out in this case, and maybe this is causing the issue? The documentation for the returnURL says:
"Custom URL scheme of your iOS app. This value is overridden for Android by AdyenCheckout. You can also send this property from your backend.".
According to the documentation then filling out <data android:scheme="myapp" android:path="/payment" /> is only necessary "For standalone components", so I've not done that.
Summary
I realise that this might be hard to reproduce, but I hope that the nature of the issue is clear. I don't know if it's common for apps to open each other in this way, and maybe this protocol is used by other 3D secure apps other than MitID. It might be a result of how the Android implementation handles 3D verification. According to this post Stack Overflow embedded web views are no longer supported for MitID on Android and the login flow must use Chrome Custom Tabs (or an external browser) - I'm not sure what the Android implementation does in this case.
I'll look forward to your reply, thank you in advance :)