adyen-react-native icon indicating copy to clipboard operation
adyen-react-native copied to clipboard

Make `hide(true|false)` async to resolve Apple Pay dismiss

Open faizantariq2 opened this issue 11 months ago • 11 comments

Describe the bug I have done a transaction via Apple Pay on a real device. After a successful transaction, when I go for the next transaction again and try to open Apple Pay again, it does not open. start('applepay')

To Reproduce Steps to reproduce the behavior:

  1. Go for transaction via apple pay
  2. even it's fail or successful. then again for for another transaction
  3. apple pay is not opening

Expected behavior it should open the apple pay even for second payment

Screenshots

Smartphone (please complete the following information):

  • Device: iphone
  • Adyen sdk version: 2.4.0

Additional context Error on xcode: [Presentation] Attempt to present <PKPaymentAuthorizationViewController: 0x107d23f00> on <RNSScreen: 0x106e9edf0> (from <RNSScreen: 0x106e9edf0>) whose view is not in the window hierarchy.

i'm suspecting an issue on this block.

 internal func present(_ component: PresentableComponent) {
        guard let presenter = BaseModule.currentPresenter ?? UIViewController.topPresenter else { return sendEvent(error: NativeModuleError.notKeyWindow) }
        defer {
            BaseModule.currentPresenter = presenter
        }
 
        guard component.requiresModalPresentation else {
            presenter.present(component.viewController, animated: true)
            return
        }
 
        let navigation = UINavigationController(rootViewController: component.viewController)
        component.viewController.navigationItem.rightBarButtonItem = .init(barButtonSystemItem: .cancel,
                                                                           target: self,
                                                                           action: #selector(cancelDidPress))
        presenter.present(navigation, animated: true)
    }

it's going to else condition in guard component.requiresModalPresentation else { presenter.present(component.viewController, animated: true) return }

faizantariq2 avatar Mar 10 '25 11:03 faizantariq2

Hey @faizantariq2

it's going to else condition in guard component.requiresModalPresentation else { ... }

That is expected behaviour. ApplePay Sheet do not require UINavigationController

[Presentation] Attempt to present <PKPaymentAuthorizationViewController: 0x107d23f00> on <RNSScreen: 0x106e9edf0> (from <RNSScreen: 0x106e9edf0>) whose view is not in the window hierarchy.

This means presenter is not in the window hierarchy. I only see two explanations

  1. previous BaseModule.currentPresenter was not cleaned (do you call nativeComponent.hide(success) ?)
  2. window hierarchy is somehow messed up (make a breakpoint and check "Debug View Hierarchy" in both cases).

descorp avatar Mar 17 '25 14:03 descorp

  1. yes, we call nativeComponent.hide(success) this.

This problem only occurs when we use Apple Pay. Everything works fine with other payment methods like card , Blik etc. Regardless of what I do, other payment methods function correctly.

The issue arises when I use Apple Pay to complete a transaction. Upon returning for a second attempt to use Apple Pay or even the drop-in feature, it fails to open

faizantariq2 avatar Mar 18 '25 07:03 faizantariq2

Could you compare the view hierarchy of the first and second Apple Pay attempts?

In a video I received from the Support team, I noticed that the Apple Pay sheet was dismissed very quickly. My theory is that there may not be enough time to call BaseModule.currentPresenter = nil (see https://github.com/Adyen/adyen-react-native/blob/develop/ios/Components/BaseModule.swift#L149-L151) upon dismissal. If this is the case, it could lead to unexpected results on Apple's side.

Could you try adding a half-second delay before calling nativeComponent.hide(success)?

descorp avatar Mar 18 '25 12:03 descorp

Hi @descorp

We tried adding delay as suggested, but still same scenario, Apple pay is not opening second time.

Here I am attaching Xcode logs, might be these logs can help you to reach the root cause.

2025-03-19 12:01:07.392214+0000 storybookMobileSIT[534:28008] Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service

2025-03-19 12:01:09.358691+0000 storybookMobileSIT[534:28226] [javascript] EventEmitter.removeListener('didUpdateDimensions', ...): Method has been deprecated. Please instead use `remove()` on the subscription returned by `EventEmitter.addListener`.

2025-03-19 12:01:10.876976+0000 storybookMobileSIT[534:28008] [Presentation] Attempt to present <PKPaymentAuthorizationViewController: 0x109057a60> on <RNSScreen: 0x1090565a0> (from <RNSScreen: 0x1090565a0>) whose view is not in the window hierarchy.

2025-03-19 12:01:30.581251+0000 storybookMobileSIT[534:28008] Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service`

ahsan-lebara avatar Mar 19 '25 12:03 ahsan-lebara

EventEmitter.removeListener('didUpdateDimensions', ...): Method has been deprecated. Please instead use remove() on the subscription returned by EventEmitter.addListener.

Thanks for reporting, I wasn't aware this one is deprecated. However it is not part of the problem.

[Presentation] Attempt to present <PKPaymentAuthorizationViewController: 0x109057a60> on <RNSScreen: 0x1090565a0> (from <RNSScreen: 0x1090565a0>) whose view is not in the window hierarchy.

This is the problem. Could you make a breakpoint in this line and see if it is called?

descorp avatar Mar 19 '25 12:03 descorp

Hi @descorp

I tried putting breakpoint, but this line is not being called anytime.

Image

ahsan-lebara avatar Mar 19 '25 13:03 ahsan-lebara

And on a like 141 ? This problem happens because dismiss is async and there is no way to tell when it is finished.

We tried adding delay as suggested, but still same scenario, Apple pay is not opening second time.

Probably my first instruction was incorrect. Could you try adding a half-second delay after calling nativeComponent.hide(success), but before any navigation logic?

descorp avatar Mar 19 '25 15:03 descorp

Hi @descorp

After adding delay of 1 second, this line is being executed every-time and Apple pay is opening all the time as expected.: BaseModule.currentPresenter = nil

Can we avoid adding delay? Can you push this fix in newer version, so we don't have to use delays.

Image

ahsan-lebara avatar Mar 21 '25 11:03 ahsan-lebara

Hey @ahsan-lebara

Thanks for confirming! We were aware of this problem, but holistic fix would require a breaking change that we can only address in a major version.

Because this is asynchronous call, there is nothing we currently do from SDK side, the fix can only be applied on app level.

I'll keep you posted on any future developments.

descorp avatar Mar 21 '25 14:03 descorp

Could this also be causing the app-freezes I see in #445, since both are caused by timing issues in the closing of the ApplePay sheet?

ChielBruin avatar Mar 27 '25 09:03 ChielBruin

Hey @ChielBruin

This could be, actually - a race condition prevents Apple Pay VC to be dismissed and messing with VC stack and further navigation. Could you check if this is the case?

Something simple like:

const onSubmit = useCallback( (data, nativeComponent ) => {
    // Make /paymens call

    nativeComponent.hide(isSuccess);
    if (data.paymentMethod.type === 'applepay') {
       await sleep(1000);
    } 
    // navigate further
  }, [some, dependency]);

descorp avatar Mar 27 '25 12:03 descorp