expo / expo

An open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.
https://docs.expo.dev
MIT License
33.46k stars 5.35k forks source link

Expo router tries to navigate to stripe's return url (safepay) #27179

Closed jongbelegen closed 6 months ago

jongbelegen commented 7 months ago

Minimal reproducible example

https://github.com/jongbelegen/expo-router-stripe-safepay

Which package manager are you using? (Yarn is recommended)

pnpm

If the issue is web-related, please select the bundler (web.bundler in the app.json)

metro

Summary

When making a payment with Stripe that requires leaving the app (e.g., using iDeal). Stripe uses a return URL to catch some details of the payment made (https://docs.stripe.com/payments/accept-a-payment?platform=react-native&ui=payment-sheet#react-native-set-up-return-url).

The Expo router tries to navigate to this route and shows a 404.

It is not desired that the expo router tries to navigate to this page.

Environment

  expo-env-info 1.2.0 environment info:
    System:
      OS: macOS 14.3
      Shell: 5.9 - /bin/zsh
    Binaries:
      Node: 20.9.0 - /usr/local/bin/node
      Yarn: 1.22.19 - ~/.yarn/bin/yarn
      npm: 10.1.0 - /usr/local/bin/npm
    Managers:
      CocoaPods: 1.14.3 - /usr/local/bin/pod
    SDKs:
      iOS SDK:
        Platforms: DriverKit 23.2, iOS 17.2, macOS 14.2, tvOS 17.2, visionOS 1.0, watchOS 10.2
    IDEs:
      Android Studio: Hedgehog 2023.1.1 Hedgehog 2023.1.1
      Xcode: 15.2/15C500b - /usr/bin/xcodebuild
    npmPackages:
      expo: ~50.0.7 => 50.0.7
      expo-router: ~3.4.7 => 3.4.7
      react: 18.2.0 => 18.2.0
      react-dom: 18.2.0 => 18.2.0
      react-native: 0.73.4 => 0.73.4
      react-native-web: ~0.19.6 => 0.19.10
    Expo Workflow: managed
marklawlor commented 6 months ago

Change your StripeHandleUrl to be a component that prevents rendering under the deep link URL is resolved..

export function StripeHandleUrl({ children }) {
  const { handleURLCallback } = useStripe();
  const [urlHandled, setUrlHandled] = useState(false)

  const handleDeepLink = useCallback(
    async (url: string | null) => {
      if (url) {
        const stripeHandled = await handleURLCallback(url);
        if (stripeHandled) {
          router.nativgate('/') // Navigate to where you need to
        } 
        setUrlHandled(true) // Otherwise just render the children
      }
    },
    [handleURLCallback]
  );

  useEffect(() => {
    const getUrlAsync = async () => {
      const initialUrl = await Linking.getInitialURL();
      handleDeepLink(initialUrl);
    };

    getUrlAsync();

    const deepLinkListener = Linking.addEventListener(
      'url',
      (event: { url: string }) => {
        handleDeepLink(event.url);
      }
    );

    return () => deepLinkListener.remove();
  }, [handleDeepLink]);

  return urlHandled ? <>{children}</> : null
}

Then in your root _layout, it now wraps your childre instead of being a sibling.


<StripeHandleUrl>
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
        <Stack>
            <Stack.Screen name="(tabs)" options={{headerShown: false}}/>
            <Stack.Screen name="modal" options={{presentation: 'modal'}}/>
        </Stack>
    </ThemeProvider>
</StripeHandleUrl>