Adyen / Adyen/adyen-react-native

3D secure check gets stuck on Android when using sessions.

Open
#651 29 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
TypeScript
Stars
71
Forks
45
Avg merge
2d 5h
Merged PRs (30d)
1

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.

Image

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 :)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Android entry point in MainActivity.kt and the AdyenCheckout session configuration shown in the issue, including returnUrl and the Android launcher setup. Compare the session flow with the documented Android redirect behavior and the package.json version. Done means the MitID verification does not remain embedded in a second app instance and the payment flow returns correctly to the original app.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin, react-native, typescript
Domain
authentication, mobile, payments
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.