Adyen / adyen-react-native

Adyen React Native
https://docs.adyen.com/checkout
MIT License
43 stars 32 forks source link

When adyen refuses a payment for paypal, onError event is not fired and inapp web view closes. #359

Closed daviddamilola closed 5 months ago

daviddamilola commented 5 months ago

Describe the bug I am using the AdyenCheckout component, when paypal payment method is used, and adyen flags a payment as fraud based on the ip, the onError call back is never reached. The payment sheet also closes.

FYI for klarna and card payment methods, a refusalReason is sent but it is not the case for paypal

To Reproduce Steps to reproduce the behavior:

  1. pass onError to adyen checkout and other required config.
  2. pay with paypal on a device with blacklisted IP
  3. See that onError is never called. Expected behavior onError callback should be called.

Screenshots

https://github.com/Adyen/adyen-react-native/assets/44063913/1bc6ab74-3961-442d-8493-ac2086d10ee0

Desktop (please complete the following information):

Smartphone (please complete the following information):

Additional context the only response gotten from adyen is with a resultCode of RedirectShopper, if the connection is refused, as it indicates on adyen dashboard, the result code should at least be different

Screenshot 2024-02-04 at 21 20 07
{"action": {"method": "GET", "paymentMethodType": "paypal", "type": "redirect", "url": "https://test.adyen.com/hpp/checkout.shtml?u=redirectPayPal&p=eJztW993m7gS-mvsFx9xQAgQDzwkttNkmzZp7Cb1vuQIScQ0GCjISb1--R3Axr9jsufu9l5OkxwSa0aj*UZCM58CLIqSV3Mwwp7K5rLL0jQKOVNhEl-FQaJFoZ*xbPGZzaTHxELGKE6ERCwN0VJ0rMu9zHJo8gxTMzSzC22x6ENfL2WLlEVdPs4YlynLZKw8XUcGsaW0He5jVxiBK4jgjmP5riMMSkzCUGC7gUGprXMLOxaXSDe6fCr5czJXmvpvGEvmscoWpZODYZfPMzDHq8-Dr3fdKIzllZKz3OtY5x3nvINxKODSMc-gSil1LOIIxAT1EaE2QdSUFFm6E5jMIfDNC2XchytPoiRbd3W3BQOZ8yxMi4jWOucR48*1WgzzUYvu5NM8Ylmvz7KnpHfLYtUrtXtZGOdS1J3SLBFzrq7WPhsAP3AEQa7LbfCZB8iV3EC*ZZo2*AW-3N3uH*Wi7n*lGxagfqTuo453FUfR-KnWzCofES98RCn4iPzCR1T5iKixHujHHMShWg5jVI05n*8Na5hG3SkP-1pHBNOOBY0XJqnlYTxSSRVB86xY6lWzShSLbrOQrzqX07o59bVRWALraYJ1djYr1svSScPV9Y4z2DX6EKopLM9BmPO18nKMgyYq628MvxojnLEnmS*lq*U4z6Jaf6pUmpfCIhLVNTQ0JsJce82Xn6tfMClTlin1*BqmVcvWxKLRGG6PjnnRwWQ0hksdBhapejw1nc-8mIVR3iuUlhqvoVDTSsfUV-imMnyaqt1WzjZWfDyPoiXWfxoZbiWyASBzKmSDJsgG-1fIrNYis1uLjLQWmdlKZDcXFaybiwawCqV9WMQy6D4sjC38K2HVyayFyHBrkZmtRNYfV7D6TfaO-sG9wzi0dRi-dueod-t-e7qswSa*DXrmc8oMDvTMD3SBiLCB6uicINsOdN0SeuALs-Zih55NtgWH6Nm1ZGoqs1rxFEFb6p*maLrDJNZdgRwsMCKB6yNfxxQFnLuMU0fqhrHb-SBFm-wNihZVXq5JGq0tNCdp5DdJ*4dJ2qS1hUiNjLYWWetI2qS1VGbSWvo5ae0xz6S1xzyTdpK0SWvPCyatpZ*T1pK0yS-mM-8CstYdGUx*HbE*Sj8p0EwcBAQZOqWIOAFFlJscUWYTTEhgu*QY-SQUb0sO8c*zfNq7XHLKKhy9D1lFA-f5KPBQdD3-2btMgLuI3h*MP0vVO2KiV9vYJ6eSci58SZHtBhYivqDIlUIvPuoutSTzK270BjmlOiGPAPERiqq32CnMN4rmP9G09Bl9L31GLJ*i6ZKkPoGzyLXWbLMJRS2Hd9ZDbzHUT3Xzb2L6zjtxY1rbVlduQmsZGdidtRbVzLuz1qLqchdai06BNqH9j2X0gFqcBtxEIjAgo2NfR9QWLmx12CE2lZTQdSrazui6M9yWHMrog3muFr3L3nmWvMa1*lYeH7EojBnvjaZhpoo03tvq1HtNMrhAjnzzhJlSx7UCU0BVYuACiA9AsI2w6wpBTWaZ*MQJM3YNy3gEVI8Pg13NrSSeVw6jvHC4yOFIFA6jKfILh1HhMKochkLJsWtjDVJ55YRx5LD5um7*ncrfdw9uTm7LUvketPbkuy1orfkn4xaqdnHXPWjtOSTag9aes5Q9aL-oAKyqUqxBdyYzPoVcecbLPOM9JNlzGEMuBnac58P*zada5SpW8ikrn9vW1CKVXv9y2P9483X8*GH4eXh31T*o*bJ8rNvRa-FNJmR2JwNZ5Crp2bZhu7iWrgUmMW34YgjbjouI6TrI1YVEwqKWwUBIfXOjm5pn8YAp5n35Ru7d2wf73P3ycXxpW7XOKHzysJ26Z8-DP6d35D6-*mHHVqay-LPv3F*PLgI2*aCiH1Fy48w6*PxLxywipJgAsxpXt1nyXZbFjFdNLhQlQs4SRM1ukso4jF8SyN2ldvHwt6HtPRN*WE2s6znvxNPZRyyESs6qosAr64E31O7ZsnzwTmjdQtig1oBy4ahmDItTZjdB9Zi7cUTrhak*gyWRZAvvcxLLg2q4WbDwqWBtPylxxEajcOFG4cKNw4UbhQs3C5fZLFzmVrgaHOStTvGOWGsUOLNR4MzGgTMbBc5sFjjSLHBkK3DvYE5HrDUKHGkUONI4cKRR4MjpwK3MXIN67pFuyhazmoZ4xLEBUCaLwIbxE2yTHJIH-PUJIhwVq07cxBdhJEHnXZt7JlNYlyiJkQpnEviQF7AoL8zkX**uvToZQyaGfLt8tyefJmkqM6RkrrTyHSSNJ7N9BWjJZfYCEIENXdxWiK5i0AXP76QIARBk04sXoIgXUcKBmVU6nyRwMwGJeyd5QrbYT58dbGdlagKHQQGsa6vCAEFhoLE07WCrwGAVKODCkzgIsxmrsra9*d4TGPi7LytlYf5cTqXPcrjpyyUEdUs5gbCOivUBi*iwVnnm4FH3mHh5P5X30mGVJbkfhyqSJzPcYRMrSu8dxoIbYcErLJNj4lNY8EksOwnosJETaMxGaMwlGkLxMfkpOOY2nPckiMPmTgAjjYCRJTAolI-JTwEj28Des4EfNrcBLC92tyS*B5uiaME6JsAt4WdsmAXPpQWHp392lzuNBvdzrsp3Iq8WLJ4n0fyV9UbJK4uFrJUidlJnOAMi4A3YS3GueG51iJ4WW2QstLXO1a1nuI6GDV2jGtZp3Q5FOewkZTobwjZUbF51p3Lciu3Uvq7Pyvb8qXnIyucTus5gNdC4IA64m8MuWb3cOfe1F6w9XOC*9Yf9rX85oB8dirUxyS1T3I6N9OvL-Dq9N78t*j9*freZuB9djqJ0OBimo3h4markP0jk9Nw"}, "resultCode": "RedirectShopper"}
descorp commented 5 months ago

Hey @daviddamilola

The payment sheet also closes.

On iOS payment sheet or SFSafariViewController does not (usually) dismiss itself 🤔

If I understand it correctly, what we see on this video is following: 1) You send /payments request 2) You receive action from Adyen API and pass it to SDK to handle 3) PayPal (or any other PM) appear, detects fraud (or whatever reason for denial) and call returnURL with "refused" status 4) You receive onAdditionalDetails callback and call component.hide(false) that dismiss

Is above correct?

daviddamilola commented 5 months ago

Hey @daviddamilola

The payment sheet also closes.

On iOS payment sheet or SFSafariViewController does not (usually) dismiss itself 🤔

If I understand it correctly, what we see on this video is following:

  1. You send /payments request
  2. You receive action from Adyen API and pass it to SDK to handle
  3. PayPal (or any other PM) appear, detects fraud (or whatever reason for denial) and call returnURL with "refused" status
  4. You receive onAdditionalDetails callback and call component.hide(false) that dismiss

Is above correct?

Hi @descorp , thanks for your response, step 3 is incorrect, there is no refused status in the response

{"action": {"method": "GET", "paymentMethodType": "paypal", "type": "redirect", "url": "https://test.adyen.com/hpp/checkout.shtml?u=redirectPayPal&p=eJztW993m7gS-mvsFx9xQAgQDzwkttNkmzZp7Cb1vuQIScQ0GCjISb1--R3Axr9jsufu9l5OkxwSa0aj*UZCM58CLIqSV3Mwwp7K5rLL0jQKOVNhEl-FQaJFoZ*xbPGZzaTHxELGKE6ERCwN0VJ0rMu9zHJo8gxTMzSzC22x6ENfL2WLlEVdPs4YlynLZKw8XUcGsaW0He5jVxiBK4jgjmP5riMMSkzCUGC7gUGprXMLOxaXSDe6fCr5czJXmvpvGEvmscoWpZODYZfPMzDHq8-Dr3fdKIzllZKz3OtY5x3nvINxKODSMc-gSil1LOIIxAT1EaE2QdSUFFm6E5jMIfDNC2XchytPoiRbd3W3BQOZ8yxMi4jWOucR48*1WgzzUYvu5NM8Ylmvz7KnpHfLYtUrtXtZGOdS1J3SLBFzrq7WPhsAP3AEQa7LbfCZB8iV3EC*ZZo2*AW-3N3uH*Wi7n*lGxagfqTuo453FUfR-KnWzCofES98RCn4iPzCR1T5iKixHujHHMShWg5jVI05n*8Na5hG3SkP-1pHBNOOBY0XJqnlYTxSSRVB86xY6lWzShSLbrOQrzqX07o59bVRWALraYJ1djYr1svSScPV9Y4z2DX6EKopLM9BmPO18nKMgyYq628MvxojnLEnmS*lq*U4z6Jaf6pUmpfCIhLVNTQ0JsJce82Xn6tfMClTlin1*BqmVcvWxKLRGG6PjnnRwWQ0hksdBhapejw1nc-8mIVR3iuUlhqvoVDTSsfUV-imMnyaqt1WzjZWfDyPoiXWfxoZbiWyASBzKmSDJsgG-1fIrNYis1uLjLQWmdlKZDcXFaybiwawCqV9WMQy6D4sjC38K2HVyayFyHBrkZmtRNYfV7D6TfaO-sG9wzi0dRi-dueod-t-e7qswSa*DXrmc8oMDvTMD3SBiLCB6uicINsOdN0SeuALs-Zih55NtgWH6Nm1ZGoqs1rxFEFb6p*maLrDJNZdgRwsMCKB6yNfxxQFnLuMU0fqhrHb-SBFm-wNihZVXq5JGq0tNCdp5DdJ*4dJ2qS1hUiNjLYWWetI2qS1VGbSWvo5ae0xz6S1xzyTdpK0SWvPCyatpZ*T1pK0yS-mM-8CstYdGUx*HbE*Sj8p0EwcBAQZOqWIOAFFlJscUWYTTEhgu*QY-SQUb0sO8c*zfNq7XHLKKhy9D1lFA-f5KPBQdD3-2btMgLuI3h*MP0vVO2KiV9vYJ6eSci58SZHtBhYivqDIlUIvPuoutSTzK270BjmlOiGPAPERiqq32CnMN4rmP9G09Bl9L31GLJ*i6ZKkPoGzyLXWbLMJRS2Hd9ZDbzHUT3Xzb2L6zjtxY1rbVlduQmsZGdidtRbVzLuz1qLqchdai06BNqH9j2X0gFqcBtxEIjAgo2NfR9QWLmx12CE2lZTQdSrazui6M9yWHMrog3muFr3L3nmWvMa1*lYeH7EojBnvjaZhpoo03tvq1HtNMrhAjnzzhJlSx7UCU0BVYuACiA9AsI2w6wpBTWaZ*MQJM3YNy3gEVI8Pg13NrSSeVw6jvHC4yOFIFA6jKfILh1HhMKochkLJsWtjDVJ55YRx5LD5um7*ncrfdw9uTm7LUvketPbkuy1orfkn4xaqdnHXPWjtOSTag9aes5Q9aL-oAKyqUqxBdyYzPoVcecbLPOM9JNlzGEMuBnac58P*zada5SpW8ikrn9vW1CKVXv9y2P9483X8*GH4eXh31T*o*bJ8rNvRa-FNJmR2JwNZ5Crp2bZhu7iWrgUmMW34YgjbjouI6TrI1YVEwqKWwUBIfXOjm5pn8YAp5n35Ru7d2wf73P3ycXxpW7XOKHzysJ26Z8-DP6d35D6-*mHHVqay-LPv3F*PLgI2*aCiH1Fy48w6*PxLxywipJgAsxpXt1nyXZbFjFdNLhQlQs4SRM1ukso4jF8SyN2ldvHwt6HtPRN*WE2s6znvxNPZRyyESs6qosAr64E31O7ZsnzwTmjdQtig1oBy4ahmDItTZjdB9Zi7cUTrhak*gyWRZAvvcxLLg2q4WbDwqWBtPylxxEajcOFG4cKNw4UbhQs3C5fZLFzmVrgaHOStTvGOWGsUOLNR4MzGgTMbBc5sFjjSLHBkK3DvYE5HrDUKHGkUONI4cKRR4MjpwK3MXIN67pFuyhazmoZ4xLEBUCaLwIbxE2yTHJIH-PUJIhwVq07cxBdhJEHnXZt7JlNYlyiJkQpnEviQF7AoL8zkX**uvToZQyaGfLt8tyefJmkqM6RkrrTyHSSNJ7N9BWjJZfYCEIENXdxWiK5i0AXP76QIARBk04sXoIgXUcKBmVU6nyRwMwGJeyd5QrbYT58dbGdlagKHQQGsa6vCAEFhoLE07WCrwGAVKODCkzgIsxmrsra9*d4TGPi7LytlYf5cTqXPcrjpyyUEdUs5gbCOivUBi*iwVnnm4FH3mHh5P5X30mGVJbkfhyqSJzPcYRMrSu8dxoIbYcErLJNj4lNY8EksOwnosJETaMxGaMwlGkLxMfkpOOY2nPckiMPmTgAjjYCRJTAolI-JTwEj28Des4EfNrcBLC92tyS*B5uiaME6JsAt4WdsmAXPpQWHp392lzuNBvdzrsp3Iq8WLJ4n0fyV9UbJK4uFrJUidlJnOAMi4A3YS3GueG51iJ4WW2QstLXO1a1nuI6GDV2jGtZp3Q5FOewkZTobwjZUbF51p3Lciu3Uvq7Pyvb8qXnIyucTus5gNdC4IA64m8MuWb3cOfe1F6w9XOC*9Yf9rX85oB8dirUxyS1T3I6N9OvL-Dq9N78t*j9*freZuB9djqJ0OBimo3h4markP0jk9Nw"}, "resultCode": "RedirectShopper"}

the result code is "resultCode": "RedirectShopper"

our onAdditionalDetails is

  const onAdditionalDetails = async (
    data: PaymentMethodData,
    nativeComponent: AdyenActionComponent,
  ) => {
    getPaymentDetails(data, {
      onSuccess: result => {
        if (result.action) {
          nativeComponent.handle(result.action);
        } else {
          processResult(result, nativeComponent, {
            onSuccess: successHandler,
          });
        }
      },
      onError: () => {
        nativeComponent.hide(false);
        setFieldValue('error', 'Your payment method failed');
      },
    });
  };
descorp commented 5 months ago

Hey @daviddamilola

But step 4 does occur, right? Meaning you receive onAdditionalDetails callback ?

daviddamilola commented 5 months ago

Hey @daviddamilola

But step 4 does occur, right? Meaning you receive onAdditionalDetails callback ?

yes @descorp

descorp commented 5 months ago

Good :)

step 3 is incorrect, there is no refused status in the response

This is why you do not get onError - the IP check happens on PayPal side. Adyen can only find about the outcome/reason after you submit PaymentMethodData to \payment\details API.

In your processResult function you should be able to check what does result contains: resultCode, refusalReason etc

daviddamilola commented 5 months ago

Good :)

step 3 is incorrect, there is no refused status in the response

This is why you do not get onError - the IP check happens on PayPal side. Adyen can only find about the outcome/reason after you submit PaymentMethodData to \payment\details API.

In processResult you can check what does result contains: resultCode, refusalReason etc

Ah, this makes sense. Thank you very much, i can see the result in the \payment\details call : ).