React Native如何在浏览器中打开链接并返回应用程序?

20

我正在使用React Native开发一个应用程序,该应用程序需要与付款网关进行通信,在完成支付过程(成功或失败)后,我需要向用户显示警报。为此,我在WebView中打开一个链接,然后使用onNavigationStateChange获取返回的URL,并显示成功或失败的消息。

但是,由于安全问题,此流程必须在默认设备浏览器中完成。

当前代码:

const BASEURL = 'https://gatewayURL/?ID=';
let Token = null;
let paymentAccepted = null;
let paymentFactorId = null;

class Gateway extends PureComponent {
  static propTypes = {
    dispatch: PropTypes.func,
    navigation: PropTypes.any,
  }

  componentWillMount() {
    this.props.dispatch(getPaymentStatus());
  }


  _onLoad(webViewState) {
    let url = webViewState.url.toString();
    let isResponseValid = url.includes('backFromGateway');
    if(isResponseValid){
      if(this.props.checkedPaymentStatus != 'checked' ){
        setTimeout(() => {
          this.props.dispatch(setPaymentStatus('checked'));

          let splitedURL = url.split("/");
          paymentFactorId = splitedURL[splitedURL.length -2];
          if(splitedURL[splitedURL.length - 1] === '0'){
            paymentAccepted = true;
            this.props.dispatch(setGatewayResponse('done', paymentFactorId));
          }
          else {
            paymentAccepted = false;
            this.props.dispatch(setGatewayResponse('rejected', paymentFactorId));
          }


          this.props.navigation.navigate('BackFromGateway', { title: '' })
        }, 1000);
      }
    }
  }


  render() {
    const { addNewOrderGatewayToken, checkedPaymentStatus } = this.props;
    token = addNewOrderGatewayToken;
    let view = null;
    if(checkedPaymentStatus !== 'checked'){
      view =  <WebView onNavigationStateChange={this._onLoad.bind(this)} style={styles.container} source={{ uri: `${BASEURL}${token}`  }}/>
    }
    else{
      view = <View></View>
    }

    return (
      <View style={styles.container}>
        {view}
      </View>      
    );
  }
}

有什么想法吗?
谢谢


我并不完全理解你在Webview安全问题方面的推理和意义是什么?是否有相关文章?Webview在移动端使用与Chrome相同的引擎,我不认为Webview在安全方面会受到影响。 - Jimi Pajala
@JimiPajala 我们本地商店只接受包含在线支付的应用程序使用这种方式。 - Mort
2个回答

23

如果您可以从网关网站进行回调,那么我建议使用深度链接来处理应用程序和浏览器之间的流程。基本上,您的应用程序将打开用于支付的网关网站,根据付款结果,网站将使用其深层链接向应用程序进行回调。然后,应用程序将侦听该链接,取出必要的信息并继续进行。

你需要做的是:

在您的应用程序中设置深度链接。您应该按照官方网站指南(这里)启用它。在此处为链接选择一个随机URL,例如gatewaylistener

设置来自网关到您的应用程序的必要回调。在您的情况下,由于您需要处理成功付款和失败付款,因此您可以添加2个回调,例如gatewaylistener://success?id={paymentId}gatewaylistener://error?id={paymentId}

最后,您需要从应用程序监听Web浏览器。一种方法是在打开网关的组件中添加侦听器。

// setup
componentDidMount() {
  Linking.getInitialURL().then((url) => {
    if (url) {
      this.handleOpenURL(url)
    }
  }).catch(err => {})
  Linking.addEventListener('url', this.handleOpenURL)
}

componentWillUnmount() {
  Linking.removeEventListener('url', this.handleOpenURL)
}

// open your gateway
async openGateWay = () => {
  const { addNewOrderGatewayToken } = this.props
  const url = `${BASEURL}${addNewOrderGatewayToken}`
  const canOpen = await Linking.canOpenURL(url)
  if (canOpen) {
    this.props.dispatch(setPaymentStatus('checked'))
    Linking.openURL(url)
  }
}

// handle gateway callbacks
handleOpenURL = (url) => {
  if (isSucceedPayment(url)) { // your condition
    // handle success payment
  } else {
    // handle failure
  }
}

1
应该是 <data android:scheme="gatewaylistener" />,因为你想要使用的方案不是http,而是 gatewaylistener。我建议你查看一些好的例子,比如 https://medium.com/react-native-training/deep-linking-your-react-native-app-d87c39a1ad5e,了解如何调用和设置深度链接。 - blaz
为什么我的方案不是http?我需要从浏览器监听一个URL,如果用户重定向到该URL,我的应用程序应该打开该屏幕并显示成功/失败消息。这样做对吗,还是我犯了一个错误?现在,我将用户发送到浏览器进行付款,但付款完成后,我的应用程序没有响应。 - Mort
1
Scheme value 是让 Android 系统知道当 Web URI intent 被调用时它可以做什么。你的应用程序不会监听来自浏览器的 URL,而是监听来自网关网站的回调。如果你做得正确,那么即使像 <a href="gatewaylistener://success">打开应用</a> 这样的链接标签也可以毫无问题地打开你的应用程序。 - blaz
当然,该方案可以是http或任何您想要的,但是您必须首先在应用程序中正确设置它,以确保系统知道在浏览器中调用URI时该怎么做。使用非HTTP方案是区分应用程序方案和普通Web方案的常见方法。 - blaz
我们如何使用钩子来获取这段代码? - undefined
显示剩余2条评论

2
为了进行身份验证,例如使用深度链接重定向,您可以使用嵌入式浏览器,在Android中使用Chrome Custom Tabs,而在iOS中使用SafariViewController。请检查 InAppBrowser组件,以便使用相同的代码支持两个平台(Linking已经在内部使用来检测深度链接重定向)。
从示例文件夹中可以看到,您可以使用自定义深度链接,该链接已经配置在您的应用程序中(对于Android来说是AndroidManifest,对于iOS来说是Info.plist)。
  getDeepLink (path = '') {
    const scheme = 'my-demo'
    const prefix = Platform.OS === 'android' ? `${scheme}://demo/` : `${scheme}://`
    return prefix + path
  }

  async tryDeepLinking () {
    const redirectToURL = `https://proyecto26.github.io/react-native-inappbrowser/`
    const redirectUrl = this.getDeepLink('home')
    const url = `${redirectToURL}?redirect_url=${encodeURIComponent(redirectUrl)}`
    try {
      if (await InAppBrowser.isAvailable()) {
        const result = await InAppBrowser.openAuth(url, redirectUrl)
        await this.sleep(800)
        Alert.alert('Response', JSON.stringify(result))
      } else {
        // You can use Linking directly for iOS < 9
      }
    } catch (error) {
      Alert.alert('Something’s wrong with the app :(')
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接