如何在React Native中整合PayPal支付

1,182 阅读7分钟

在React Native应用中原生添加支付界面的方法并不多。因此,许多移动开发者选择使用PayPal SDK建立自己的支付界面。

PayPal是世界上最早和最大的互联网支付服务之一。它在200个国家的可用性使其成为企业和个人的可靠支付界面。PayPal有一个庞大的库,可以与各种编程语言整合,包括Python、Java、JavaScript、PHP等。

在本教程中,我们将向你展示如何通过使用PayPal SDK for JavaScript将网络和本地技术相互联系起来,建立一个支付界面。

我们将从构建基本的React NativeReact应用开始。要继续学习,你应该已经熟悉了JavaScript、Node.js工具,如npxnpmyarn 、模块和基本的React组件。你还需要一个PayPal开发者账户来创建支付集成的凭证。

我们将使用PayPal的集成API,用于网页的JavaScript。我们将在React中建立支付界面,作为一个单页应用,并将其托管在Firebase主机上。最后,我们将使用React Native WebView来创建React Native应用和React Web应用之间的链接。

React Native PayPal集成。基本设置

首先,我们将创建一个基本的React Native应用。要创建该应用,只需输入以下命令。

$ npx react-native init myPayPalApp

我们还需要创建一个React应用。要创建网络应用,请输入以下命令。

npx create-react-app my-paypal-web

当这些应用程序正在初始化时,我们将使用我们的PayPal开发者账户来创建新的应用程序和整合所需的证书。

进入developer.paypal.com并登录你的开发者账户。登录后,你将被重定向到你的仪表板,它将看起来像这样。

Paypal Developers Dashboard

你会看到列出了一个默认的应用程序。在本教程中,我们将采取以下步骤来创建一个新的应用程序。

单击 "创建应用程序"按钮。我们使用PayPal沙盒进行测试,所以可以根据自己的喜好自由填充。然后点击,创建应用

Create New App Page

应用程序创建后,你将被重定向到设置和证书页面。你可以看到你的应用程序的沙盒帐户的电子邮件,客户ID,和其他设置。客户端ID是我们在本教程中所需要的。

My Payments Page

为了测试,我们还需要沙盒客户账户。要创建客户账户,在左边的导航菜单中点击沙盒下的账户

Sandbox Test Accounts Page

现在点击创建账户,然后选择美国并点击创建

Create New Sandbox Account

帐户创建后,要查看帐户凭证(即电子邮件和密码),以便在测试支付网关上登录,请将鼠标停留在新创建的帐户旁边的菜单按钮上,点击查看/编辑帐户

View and Edit Account Page

你会看到像这样的东西。

Account Info Modal

你会看到电子邮件ID以及系统生成的密码。你可以通过点击 "更改密码"链接来改变密码,或者你可以在付款时使用系统生成的密码来登录。

现在我们已经完成了基本要求,我们可以继续构建支付界面。

构建支付界面

对于支付界面,我们将对上面创建的React项目进行修改(my-paypal-web)。我们将在我们的网页上添加PayPal按钮,并将结果作为我们的回调。

从你刚才在PayPal上创建的新应用页面中复制客户ID(如上图所示),并将其粘贴到项目中的public/index.html 文件的<head>

<script src="https://www.paypal.com/sdk/js?client-id=[replace-this-with-your-client-id]&currency=USD"></script>

你的代码应该看起来像这样。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <script src="https://www.paypal.com/sdk/js?client-id=AXEWcCDcoTu8Wt1Ud0ifqLZM2A4_MbgJhNaTByCizxG0yi8V4o6sccW5RgXtXNesMh7n38Rp0Cv2KN63&currency=USD"></script>

    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

用你自己的客户ID添加这个脚本标签后,现在是时候为你的应用程序创建PayPal按钮了。

编辑App.js 文件。首先,创建一个对PayPal按钮的引用,作为一个React组件。

const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });

你可以在你的App.js 组件中使用它作为一个组件。只要删除父组件的内容,并添加PayPalButton 。你的App.js 应该看起来像这样。

import React from "react";
import ReactDOM from "react-dom";
import "./App.css";
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
function App() {
  function _createOrder(data, actions) {
    return actions.order.create({
      purchase_units: [
        {
          amount: {
            value: "1",
          },
        },
      ],
    });
  }
  return (
    <div className="App">
      <PayPalButton
        createOrder={(data, actions) => _createOrder(data, actions)}
      />
    </div>
  );
}
export default App;

进入项目目录,根据你的偏好,输入yarn startnpm start 。React服务器在localhost上启动和运行后,会在浏览器窗口自动打开localhost:3000 。如果没有,请访问 [http://localhost:3000](http://localhost:3000)从你喜欢的浏览器访问。你会看到一个像这样的输出。

My Paypal App Web Page

你可以随心所欲地设计页面的样式,我们在此不做赘述。你可以根据你的喜好进行修改。

如果你看一下代码,你可以看到,我们已经为PayPalButton 定义了一个道具,名为 [createOrder](https://developer.paypal.com/docs/business/javascript-sdk/javascript-sdk-reference/#createorder),这使你能够指定请求的内容。在这个例子中主要是金额,但你也可以指定货币等。

对于回调,我们将创建函数并将它们作为道具添加到PayPalButton 。首先,我们将添加onApprove 。这个函数在订单被PayPal批准时被调用。

要创建一个异步的_onApprove 函数。

async function _onApprove(data, actions) {
    let order = await actions.order.capture();
    console.log(order);
    return order;
  }

这里,await关键字被用来获取订单的详细信息。然后,我们将console.log 订单的详细信息。

同时将这个函数添加到PayPalButton 道具中。

<PayPalButton
    createOrder={(data, actions) => _createOrder(data, actions)}
    onApprove={(data, actions) => _onApprove(data, actions)}
/>

此外,我们将添加onCancelonError props,以便在用户取消付款或PayPal端出现一些错误时获得回调。我们将只创建一个调用函数,并在这两个props上使用它。

function _onError(err) {
  console.log(err);
}

你的PayPal按钮代码应该是这样的。

<PayPalButton
    createOrder={(data, actions) => _createOrder(data, actions)}
    onApprove={(data, actions) => _onApprove(data, actions)}
    onCancel={() => _onError("Canceled")}
    onError={(err) => _onError(err)}
/>

添加这些内容后,你的App.js 文件应该是这样的。

import React from "react";
import ReactDOM from "react-dom";
import "./App.css";
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
function App() {
  function _createOrder(data, actions) {
    return actions.order.create({
      purchase_units: [
        {
          amount: {
            value: "1",
          },
        },
      ],
    });
  }
  async function _onApprove(data, actions) {
    let order = await actions.order.capture();
    console.log(order);
    return order;
  }
  function _onError(err) {
    console.log(err);
  }
  return (
    <div className="App">
      <PayPalButton
        createOrder={(data, actions) => _createOrder(data, actions)}
        onApprove={(data, actions) => _onApprove(data, actions)}
        onCancel={() => _onError("Canceled")}
        onError={(err) => _onError(err)}
      />
    </div>
  );
}
export default App;

我们所做的是创建一个来自用户的1美元的支付请求,并记录它以查看结果。

要测试你的支付网关,只需点击PayPal按钮,输入用户凭证(即第一步的电子邮件和密码),然后付款。

Enter Sandbox Account Credentials

Payment Page

付款完成后,你会被重新定向到你的应用程序页面。通过右击打开开发者控制台,检查元素,然后转到控制台标签。你会看到你刚刚进行的虚拟支付的结果。

Callback From Payment Page

设置Firebase主机

由于我们使用Firebase来托管我们的React Web应用,首先要注册一个Firebase账户

一旦你有了一个账户,点击添加项目并设置一个项目名称。

Set Project Name

你可以选择是否启用或禁用分析功能。

Google Analytics

现在你已经创建了你的Firebase项目,点击继续

Project Ready

要设置主机,去侧边栏的主机标签,然后点击开始按钮。

Firebase Hosting Get Started

打开一个终端窗口,在你的系统上安装firebase-tools

$ npm install -g firebase-tools

用你的Firebase账户登录,这样你就可以很容易地从终端窗口连接到项目。

$ firebase login

用你的账户授权登录。在你成功登录后,进入my-paypal-web 项目目录,输入以下命令。

$firebase init

使用键盘上的方向键,导航到Hosting 。按空格键选择,按回车/回车键继续。

Firebase Init Hosting

因为我们已经创建了一个项目,我们将选择Use and existing project

Use and Existing Project

接下来,从列表中选择我们创建的项目,然后点击return/enter。

Project List

在下一步,输入以下配置。

=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? build
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
? Set up automatic builds and deploys with GitHub? No

完成这些后,你会在底部看到一条成功信息。

✔  Wrote build/index.html

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!

要推送你的网络应用,请输入以下命令。

$ yarn build

构建完成后,输入。

$ firebase deploy

你会看到一个带有托管URL的结果。

=== Deploying to 'my-pay-web'...

i  deploying hosting
i  hosting[my-pay-web]: beginning deploy...
i  hosting[my-pay-web]: found 18 files in buildhosting[my-pay-web]: file upload complete
i  hosting[my-pay-web]: finalizing version...
✔  hosting[my-pay-web]: version finalized
i  hosting[my-pay-web]: releasing new version...
✔  hosting[my-pay-web]: release completeDeploy complete!

Project Console: https://console.firebase.google.com/project/my-pay-web/overview
Hosting URL: https://my-pay-web.web.app

你可以直接复制托管URL,并将其粘贴到你喜欢的浏览器中。下面是我的看起来像什么。

https://my-pay-web.web.app

My Paypal Web Running

在React Native中构建基本的WebView

如果你之前有使用React Native的经验,那就更棒了。如果没有,官方文档提供了一个设置环境和安装应用的指南。如果你还没有这样做,只要进入项目目录,使用下面的命令来安装React Native的WebView模块。

yarn add react-native-webview

接下来,用USB连接你的设备,并输入以下命令。

npx react-native run-android

在你的设备或模拟器上成功安装了该应用后,打开App.js 文件。删除默认的额外代码并导入WebView模块。

import { WebView } from 'react-native-webview';

为了从React Native初始化支付网关,我们将创建一个按钮,在模式中显示网络视图,并从WebView中获得响应。我们还将创建一个useState() 钩子来显示和隐藏WebView。

const [showGateway, setShowGateway] = useState(false);

按钮。

<View style={styles.btnCon}>
  <TouchableOpacity
    style={styles.btn}
    onPress={() => setShowGateway(true)}>
      <Text style={styles.btnTxt}>Pay Using PayPal</Text>
  </TouchableOpacity>
</View>

按钮的样式。

  btnCon: {
    height: 45,
    width: '70%',
    elevation: 1,
    backgroundColor: '#00457C',
    borderRadius: 3,
  },
  btn: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnTxt: {
    color: '#fff',
    fontSize: 18,
  },

按钮的输出。

Paypal Button Output

现在,从react-native 中导入Modal 组件,并创建带有基本Web视图的模态,显示google.com

Modal和WebView。

{showGateway ? (
        <Modal
          visible={showGateway}
          onDismiss={() => setShowGateway(false)}
          onRequestClose={() => setShowGateway(false)}
          animationType={"fade"}
          transparent>
          <View style={styles.webViewCon}>
            <View style={styles.wbHead}>
              <TouchableOpacity
                style={{padding: 13}}
                onPress={() => setShowGateway(false)}>
                <Feather name={'x'} size={24} />
              </TouchableOpacity>
              <Text
                style={{
                  flex: 1,
                  textAlign: 'center',
                  fontSize: 16,
                  fontWeight: 'bold',
                  color: '#00457C',
                }}>
                PayPal GateWay
              </Text>
              <View style={{padding: 13}}>
                <ActivityIndicator size={24} color={'#00457C'} />
              </View>
            </View>
            <WebView
              source={{uri: 'https://www.google.com'}}
              style={{flex: 1}}
            />
          </View>
        </Modal>
      ) : null}

我们正在使用<Feather /> ,从 [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons)ActivityIndicator ,从react-native

模态和WebView的输出。

Modal Webview Output

现在,我们将在页面加载后显示/隐藏ActivityIndicator ,以获得WebView的回调。如果页面已经加载,我们将添加以下钩子和道具。

const [prog, setProg] = useState(false);
const [progClr, setProgClr] = useState('#000');

WebView的道具。

onLoadStart={() => {
  setProg(true);
  setProgClr('#000');
}}
onLoadProgress={() => {
  setProg(true);
  setProgClr('#00457C');
}}
onLoadEnd={() => {
  setProg(false);
}}
onLoad={() => {
  setProg(false);
}}

为了增加用户体验,这段代码会根据页面的进度改变ActivityIndicator 的颜色。

你的App.js 文件现在应该是这样的。

import React, {useState} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  Modal,
  ActivityIndicator,
} from 'react-native';
import {WebView} from 'react-native-webview';
import Feather from 'react-native-vector-icons/Feather';


const App = () => {
  const [showGateway, setShowGateway] = useState(false);
  const [prog, setProg] = useState(false);
  const [progClr, setProgClr] = useState('#000');
  return (
    <SafeAreaView style={{flex: 1}}>
      <View style={styles.container}>
        <View style={styles.btnCon}>
          <TouchableOpacity
            style={styles.btn}
            onPress={() => setShowGateway(true)}>
            <Text style={styles.btnTxt}>Pay Using PayPal</Text>
          </TouchableOpacity>
        </View>
      </View>
      {showGateway ? (
        <Modal
          visible={showGateway}
          onDismiss={() => setShowGateway(false)}
          onRequestClose={() => setShowGateway(false)}
          animationType={"fade"}
          transparent>
          <View style={styles.webViewCon}>
            <View style={styles.wbHead}>
              <TouchableOpacity
                style={{padding: 13}}
                onPress={() => setShowGateway(false)}>
                <Feather name={'x'} size={24} />
              </TouchableOpacity>
              <Text
                style={{
                  flex: 1,
                  textAlign: 'center',
                  fontSize: 16,
                  fontWeight: 'bold',
                  color: '#00457C',
                }}>
                PayPal GateWay
              </Text>
              <View style={{padding: 13, opacity: prog ? 1 : 0}}>
                <ActivityIndicator size={24} color={progClr} />
              </View>
            </View>
            <WebView
              source={{uri: 'https://www.google.com'}}
              style={{flex: 1}}
              onLoadStart={() => {
                setProg(true);
                setProgClr('#000');
              }}
              onLoadProgress={() => {
                setProg(true);
                setProgClr('#00457C');
              }}
              onLoadEnd={() => {
                setProg(false);
              }}
              onLoad={() => {
                setProg(false);
              }}
            />
          </View>
        </Modal>
      ) : null}
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#fff',
  },
  btnCon: {
    height: 45,
    width: '70%',
    elevation: 1,
    backgroundColor: '#00457C',
    borderRadius: 3,
  },
  btn: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnTxt: {
    color: '#fff',
    fontSize: 18,
  },
  webViewCon: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  wbHead: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#f9f9f9',
    zIndex: 25,
    elevation: 2,
  },
});
export default App;

使用WebView将PayPal界面连接到React Native应用中

为了从WebView页面接收数据到我们的React Native应用,我们将使用onMessage 道具。我们还需要在我们的Web应用中添加一些代码来发送所需的数据。window.ReactNativeWebView.postMessage 方法是用来从WebView发送数据到我们的React Native应用。

在对你的_onApprove_onError 函数进行必要的修改后,你的代码应该是这样的。

async function _onApprove(data, actions) {
    let order = await actions.order.capture();
    console.log(order);
    window.ReactNativeWebView &&
      window.ReactNativeWebView.postMessage(JSON.stringify(order));
    return order;
}
function _onError(err) {
    console.log(err);
    let errObj = {
      err: err,
      status: "FAILED",
    };
    window.ReactNativeWebView &&
      window.ReactNativeWebView.postMessage(JSON.stringify(errObj));
}

我们使用JSON.stringify ,因为postMessage只能接受字符串参数。

记住要建立网络应用,并将其部署到Firebase。

$ yarn build
$ firebase deploy

为了获得React Native方面的数据,我们将使用onMessage 。创建以下函数并将其添加到onMessage 道具中。

function onMessage(e) {
    let data = e.nativeEvent.data;
    setShowGateway(false);
    console.log(data);
  }

添加onMessage 道具,并将源uri 设置为你的web应用的URI。

<WebView
  source={{uri: 'https://www.google.com'}}
  onMessage={onMessage}
  ...
/>

现在是测试支付网关的时候了。在这一点上,我们在React Native应用中记录来自postMessage 的结果。

点击使用PayPal支付,显示支付页面。输入你的凭证并进行支付。

Payment Page Paypal App
Credentials Page
Pay Now Page

付款成功(或不成功)后,你会看到控制台中打印的结果。

Payment Result in React Native

你可以通过在onMessage 函数中添加一个警报,提醒用户他们刚刚付款的状态。

function onMessage(e) {
    ...
    let payment = JSON.parse(data);
    if (payment.status === 'COMPLETED') {
      alert('PAYMENT MADE SUCCESSFULLY!');
    } else {
      alert('PAYMENT FAILED. PLEASE TRY AGAIN.');
    }
}

下面是输出结果。

Payment Success Alert

完整的代码如下。

App.js (Reactmy-paypal-web)。

import React from "react";
import ReactDOM from "react-dom";
import "./App.css";
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
function App() {
  function _createOrder(data, actions) {
    return actions.order.create({
      purchase_units: [
        {
          amount: {
            value: "1",
          },
        },
      ],
    });
  }
  async function _onApprove(data, actions) {
    let order = await actions.order.capture();
    console.log(order);
    window.ReactNativeWebView &&
      window.ReactNativeWebView.postMessage(JSON.stringify(order));
    return order;
  }
  function _onError(err) {
    console.log(err);
    let errObj = {
      err: err,
      status: "FAILED",
    };
    window.ReactNativeWebView &&
      window.ReactNativeWebView.postMessage(JSON.stringify(errObj));
  }
  return (
    <div className="App">
      <PayPalButton
        createOrder={(data, actions) => _createOrder(data, actions)}
        onApprove={(data, actions) => _onApprove(data, actions)}
        onCancel={() => _onError("CANCELED")}
        onError={(err) => _onError("ERROE")}
      />
    </div>
  );
}
export default App;

App.js (React NativemyPayPalApp)。

import React, {useState} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  Modal,
  ActivityIndicator,
} from 'react-native';
import {WebView} from 'react-native-webview';
import Feather from 'react-native-vector-icons/Feather';
const App = () => {
  const [showGateway, setShowGateway] = useState(false);
  const [prog, setProg] = useState(false);
  const [progClr, setProgClr] = useState('#000');
  function onMessage(e) {
    let data = e.nativeEvent.data;
    setShowGateway(false);
    console.log(data);
    let payment = JSON.parse(data);
    if (payment.status === 'COMPLETED') {
      alert('PAYMENT MADE SUCCESSFULLY!');
    } else {
      alert('PAYMENT FAILED. PLEASE TRY AGAIN.');
    }
  }
  return (
    <SafeAreaView style={{flex: 1}}>
      <View style={styles.container}>
        <View style={styles.btnCon}>
          <TouchableOpacity
            style={styles.btn}
            onPress={() => setShowGateway(true)}>
            <Text style={styles.btnTxt}>Pay Using PayPal</Text>
          </TouchableOpacity>
        </View>
      </View>
      {showGateway ? (
        <Modal
          visible={showGateway}
          onDismiss={() => setShowGateway(false)}
          onRequestClose={() => setShowGateway(false)}
          animationType={'fade'}
          transparent>
          <View style={styles.webViewCon}>
            <View style={styles.wbHead}>
              <TouchableOpacity
                style={{padding: 13}}
                onPress={() => setShowGateway(false)}>
                <Feather name={'x'} size={24} />
              </TouchableOpacity>
              <Text
                style={{
                  flex: 1,
                  textAlign: 'center',
                  fontSize: 16,
                  fontWeight: 'bold',
                  color: '#00457C',
                }}>
                PayPal GateWay
              </Text>
              <View style={{padding: 13, opacity: prog ? 1 : 0}}>
                <ActivityIndicator size={24} color={progClr} />
              </View>
            </View>
            <WebView
              source={{uri: 'https://my-pay-web.web.app/'}}
              style={{flex: 1}}
              onLoadStart={() => {
                setProg(true);
                setProgClr('#000');
              }}
              onLoadProgress={() => {
                setProg(true);
                setProgClr('#00457C');
              }}
              onLoadEnd={() => {
                setProg(false);
              }}
              onLoad={() => {
                setProg(false);
              }}
              onMessage={onMessage}
            />
          </View>
        </Modal>
      ) : null}
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#fff',
  },
  btnCon: {
    height: 45,
    width: '70%',
    elevation: 1,
    backgroundColor: '#00457C',
    borderRadius: 3,
  },
  btn: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnTxt: {
    color: '#fff',
    fontSize: 18,
  },
  webViewCon: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  wbHead: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#f9f9f9',
    zIndex: 25,
    elevation: 2,
  },
});
export default App;

结论

如果你已经走到了这一步,恭喜你--你已经成功地为React Native设置了使用PayPal的测试支付网关。虽然上面的代码应该足以满足基本的支付系统,但你可以根据自己的需要来改变它。你也可以参考官方的PayPal指南来进一步参考。

The postHow to integrate PayPal payments with React Nativeappeared first onLogRocket Blog.