掌握React Native中的Stripe PaymentSheet与Expo

862 阅读10分钟

如今,在大流行期间,大多数人更喜欢在线支付,不仅是因为它的无接触安全防范措施,而且还因为它快速、方便和可靠。很多移动应用程序都纳入了在线支付,如亚马逊、Uber等。

说到在线支付,很多应用开发者都在他们的React Native Android和iOS项目中使用Stripe,因为Stripe提供了一种将支付系统集成到这个框架中的简单方法。我们有两个选项可以将Stripe集成到React Native中。PaymentSheet,或者Elements。

PaymentSheet是一个由Stripe预先建立的结账面板,用户可以在其中输入卡的详细信息并进行支付。Elements则要复杂一些;你要更多地控制这个过程,所以你要放置卡片元素并负责进行支付。

在这篇文章中,我将讨论我对PaymentSheet的偏好,然后再深入到一个用React Native、Expo和PaymentSheet构建的捐赠应用实例的教程。

PaymentSheet与传统的Elements方法

以下是我认为PaymentSheet比Elements好的一些原因。

首先,Elements需要大量的配置。因为你负责支付,你需要管理支付表单。这可能很复杂,因为不同的国家有不同的卡配置;例如,印度的卡不需要邮政编码,而在美国则需要。您需要考虑所有这些条件,并在Elements中对其进行相应的管理。

然而,在PaymentSheet中,Stripe已经为你配置了这些东西中的大部分。因此,如果一个卡号需要一个邮政编码,就会自动提示用户添加一个邮政编码。另外,如果启用,PaymentSheet支持苹果支付和谷歌支付。

第二,Elements需要进行大量的错误处理。另一方面,PaymentSheet为你处理错误,并会提醒用户他们的支付方式出了什么问题,这意味着你对收款的担心减少了。

例如,如果卡的资金不足,Elements需要你读取错误信息并显示给用户。但当你使用PaymentSheet时,用户会被Stripe自动警告。你只需要处理付款后的流程。

最后,在使用Elements时,你可能会错过一些与支付处理有关的隐私或法律要求。例如,您可能在不知情的情况下存储了一些不应该存储的数据,如用户的付款细节。

不同的国家在数据隐私方面有不同的法律,要了解并遵守每一项法律可能会让人感到沮丧。PaymentSheet确保你不需要将任何数据保存在你的服务器上,因为支付所需的所有通信都是直接与Stripe进行的。这可以保护你不会意外地违反数据隐私法。

构建一个捐赠应用程序

今天我们将使用Expo和React Native与PaymentSheet建立一个捐赠应用程序,用于支付处理。我们还需要一个Node.js服务器来处理Stripe操作,例如启动支付,并在服务器上检测任何支付。

以下是你应该具备的条件,以便跟上我们今天要做的项目。

  • 在你的机器上安装Node
  • 一个代码编辑器--我更喜欢Visual Studio Code
  • 一个Stripe账户
  • 安装在您机器上的Stripe CLI
  • 用于Visual Studio Code的Stripe扩展
  • 对Node和React Native的工作知识

这里有GitHub仓库的链接,以防您被卡住并想参考代码。

让我们开始玩吧!创建一个你喜欢的文件夹(在我的例子中,我把它命名为expo-stripe ),作为我们的项目文件夹。

第一步是建立一个Node服务器,它将帮助我们启动支付。

创建一个Node.js服务器

在我们刚刚创建的expo-stripe 文件夹中,创建一个名为backend 的新文件夹。这就是我们的Node后端项目所在的地方。

使用下面的命令初始化一个Node项目,你将有一个package.json 文件准备好。

npm init -y

backend 文件夹的终端中运行以下命令,以安装一些有助于我们的服务器的依赖性。

npm install express cors stripe dotenv

Express是用来用Node轻松制作REST API的。它支持使用中间件,有很多不同的功能;另外,它很容易使用。

cors 包与Express一起使用,以使与我们的服务器的通信更容易,并确保在向我们的服务器发送请求时不会抛出CORS错误。

stripe 包帮助我们与Stripe服务进行通信,例如我们的Stripe账户,以初始化支付,或检查支付状态。

最后,dotenv 包在我们的项目中设置了环境变量,这样我们就不必在代码中存储敏感数据,如Stripe的密匙。我们可以把它保存在一个单独的.env 文件中。

现在您可以在终端使用以下命令启动服务器。

nodemon index

如果您没有安装nodemon ,请使用下面的命令安装它。

npm install -g nodemon

获取我们的Stripe密匙

在我们实际处理Stripe操作之前,让我们从Stripe获得我们的秘钥。

打开您的Stripe仪表盘,并确保在侧边栏中查看测试数据被选中。然后,点击 "开发人员",再点击 "API密钥",就会看到您的API密钥。

记住要保持秘密密钥,不要与其他人分享,因为这将使他们能够访问您的Stripe账户。我们现在不需要可发布的密钥,但我们以后会在我们的React Native应用程序中使用它。

复制秘钥,回到你的backend 文件夹,创建一个新文件,命名为.env 。提到你的秘钥,格式如下所示。

STRIPE_SECRET_KEY=(secret key here)

初始化一个Express服务器并处理Stripe操作

backend 文件夹中创建一个新文件,命名为index.js 。这将是存储服务器主要逻辑的文件。

首先,让我们创建一个基本的服务器模板。

require("dotenv").config();
const express = require("express");
const app = express();
const Stripe = require("stripe");
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
const cors = require("cors");
const PORT = process.env.PORT || 5000;

app.use(express.json());
app.use(cors());

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

在上面的代码中,我们初始化了dotenv ,它为我们初始化了环境变量。在我们的例子中,它是Stripe的密匙。

然后我们初始化了一个Express应用程序,这样我们就可以创建一个REST API,并初始化了stripe ,以便从环境变量中传入秘密密钥。我们还导入了 CORS,以便我们可以在 Express 中使用它。

接下来,我们创建了一个名为PORT 的变量。这将检查环境变量中是否提供了一个端口,如果没有,就在5000端口上运行。这通常与Heroku一起使用,因为Heroku使用它自己的端口。

我们还实现了app.use(express.json()); ,以便我们的服务器可以接受JSON数据作为请求的有效载荷。

创建一个PaymentIntent

让我们创建一个POST路由(/donate),因为我们将需要用户的一些数据,我们正在创建一个PaymentIntent。

app.post("/donate", async (req, res) => {
  try {
    // Getting data from client
    let { amount, name } = req.body;
    // Simple validation
    if (!amount || !name)
      return res.status(400).json({ message: "All fields are required" });
    amount = parseInt(amount);
    // Initiate payment
    const paymentIntent = await stripe.paymentIntents.create({
      amount: Math.round(amount * 100),
      currency: "INR",
      payment_method_types: ["card"],
      metadata: { name },
    });
    // Extracting the client secret 
    const clientSecret = paymentIntent.client_secret;
    // Sending the client secret as response
    res.json({ message: "Payment initiated", clientSecret });
  } catch (err) {
    // Catch any error and send error 500 to client
    console.error(err);
    res.status(500).json({ message: "Internal Server Error" });
  }
});

在上面的代码中,我们正在从我们的应用程序中获取amountname ,并对其进行验证。如果数据没有被发送,我们将返回一个400错误。

然后,我们将金额转换为整数,以防止向服务器发送字符串形式的数字。这是因为我们只能向Stripe传递整数值。

接下来,我们创建一个PaymentIntent并传入要支付的金额(最低面额)、货币(我住在印度,所以对我来说是印度卢比;使用您在Stripe账户中设置的货币)、支付方式和包含用户姓名的元数据。

当我们在Stripe仪表板上检查交易时,你可以在元数据部分看到用户的名字。如果你想在付款中包含一些信息,这真的很有帮助,以便你以后可以找到它。

然后,我们从PaymentIntent中提取客户秘密,这有助于我们React Native应用中的Stripe实例识别付款并进行确认。

最后,我们将这个客户秘密发回给我们的应用程序,它现在可以确认付款。

创建webhook

为了在本地测试Stripe webhooks,我们需要在Visual Studio代码中使用Stripe扩展来使事情变得更简单。记住,在进入本节之前,你应该已经安装了Stripe CLI!

从侧边栏打开Stripe扩展,你应该在顶部看到以下选项。

Screenshot of Stripe extension events folder

单击 "将事件转发到本地机器"。输入webhook URL为 [http://localhost:5000/stripe](http://localhost:5000/stripe);我们将在稍后处理该路由。如果提示您登录到Stripe并授权CLI,请这样做并重复这一过程。

如果你看一下终端,你会得到一个测试网络挂钩的密匙。在.env 文件中以下列格式复制并粘贴。

STRIPE_WEBHOOK_SECRET=(stripe webhook secret)

现在让我们创建一个路由来处理Stripe的webhook请求。首先,在我们之前做的JSON解析器之前添加以下一行(Stripe需要使用原始体)。

app.use("/stripe", express.raw({ type: "*/*" }));

现在我们可以着手处理路由了。

app.post("/stripe", async (req, res) => {
  // Get the signature from the headers
  const sig = req.headers["stripe-signature"];
  let event;
  try {
    // Check if the event is sent from Stripe or a third party
    // And parse the event
    event = await stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    // Handle what happens if the event is not from Stripe
    console.log(err);
    return res.status(400).json({ message: err.message });
  }
  // Event when a payment is initiated
  if (event.type === "payment_intent.created") {
    console.log(`${event.data.object.metadata.name} initated payment!`);
  }
  // Event when a payment is succeeded
  if (event.type === "payment_intent.succeeded") {
    console.log(`${event.data.object.metadata.name} succeeded payment!`);
    // fulfilment
  }
  res.json({ ok: true });
});

在这里,我们要从头文件中获取Stripe的签名,因为我们需要首先验证该请求是由Stripe还是一些假装是Stripe的第三方提出的。

我们将使用constructEvent ,根据我们存储在环境变量中的webhook秘密检查签名。如果请求来自第三方,则会抛出一个状态代码为400的错误。

现在,如果请求是来自Stripe本身,事件将被存储在eventmetadata 而现在我们可以使用之前制作的PaymentIntent的event.type 和用户的付款状态。如果你有任何付款后的动作,你可以在这里执行。

最后,我们添加一个响应,让Stripe知道我们已经收到了他们的请求。

现在我们的服务器已经设置好了,我们终于可以进入我们的Expo和React Native应用。

用Expo和React Native创建一个应用程序

回到你的项目文件夹(对我来说是expo-stripe )。如果你还没有安装Expo CLI,用下面的命令安装它。

npm install -g expo-cli

现在你可以用Expo创建React Native应用了。在终端中输入以下命令。

expo init rn-app

当提示有选项时,选择第一个选项,即一个带有管理的工作流的空白Expo应用。这将创建一个名为rn-app 的新文件夹,包含我们的React Native文件。

进入rn-app 文件夹,输入以下命令,在你的网络浏览器中启动开发工具。

npm start

这应该会自动启动你的浏览器,你应该看到这样的屏幕。

Screenshot of metro bundler startup

使用左侧边栏上的任何选项运行该应用程序。在我的例子中,我将使用一个iPhone模拟器。如果你在Windows机器上,你可能需要使用一个安卓模拟器或一个真正的物理设备。

运行后,你的模拟器应该启动,你应该看到该应用程序。

Blank React Native Expo app on iphone

现在,让我们来安装Expo的Stripe软件包。Expo已经很容易将Stripe集成到React Native中,而不需要触及本地项目文件。

在终端键入以下命令来安装Stripe React Native包。

expo install @stripe/stripe-react-native

记住我们使用的是expo install ,而不是npm install ,因为Expo为我们处理了安装。

在React Native中配置Stripe

在配置Stripe之前,我强烈建议安装Visual Studio Code的ES7 Snippets扩展,因为它可以生成空白组件模板。

创建一个名为components 的新文件夹和一个名为Checkout.js 的新文件。开始输入rnfe ,选择片段,你将有一个组件准备好。保存该文件;我们将在稍后使用它。

转到App.js ,导入Checkout 组件和StripeProvider

import { StripeProvider } from "@stripe/stripe-react-native";
import Checkout from "./components/Checkout";

现在,你的JSX应该看起来像这样。

<View style={styles.container}>
  <StatusBar style="dark" />
  <StripeProvider publishableKey="(stripe publishable key here)">
    <Checkout />
  </StripeProvider>
</View>

记住要把您的Stripe可发布密钥放在Stripe仪表板上。我们将Checkout 组件包装在StripeProvider 内,这样我们就可以访问Stripe服务。

进入Checkout.js ,为nameamount 创建状态。我们还将使用Stripe钩子来与Stripe通信。

const [name, setName] = useState("");
const [amount, setAmount] = useState("1");
const stripe = useStripe();

默认的名字将是空的,默认的金额将是INR。

现在,让我们创建一个基本的布局,并将这些状态与文本框进行映射。你的JSX应该看起来像这样。

<View>
  <TextInput
    placeholder="Name"
    style={{ padding: 10, borderColor: "black", borderWidth: 1 }}
    value={name}
    onChangeText={(e) => setName(e)}
  />
  <TextInput
    placeholder="Amount"
    keyboardType="numeric"
    style={{ padding: 10, borderColor: "black", borderWidth: 1 }}
    value={amount}
    onChangeText={(e) => setAmount(e)}
  />
  <Button title="Donate" onPress={donate} />
</View>

创建一个名为donate() 的函数,我们将在稍后进行处理。

const donate = async () => {};

生成的布局应该如下所示。

Simple donate app on an iphone with space for name, donation amount, and a blue donate button.

接下来,我们将处理donate 这个函数。

const donate = async () => {
  try {
    const finalAmount = parseInt(amount);
    if (finalAmount < 1) return Alert.alert("You cannot donate below 1 INR");
    const response = await fetch("http://localhost:5000/donate", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ amount: finalAmount, name }),
    });
    const data = await response.json();
    if (!response.ok) {
      return Alert.alert(data.message);
    }
    const initSheet = await stripe.initPaymentSheet({
      paymentIntentClientSecret: data.clientSecret,
    });
    if (initSheet.error) {
      console.error(initSheet.error);
      return Alert.alert(initSheet.error.message);
    }
    const presentSheet = await stripe.presentPaymentSheet({
      clientSecret: data.clientSecret,
    });
    if (presentSheet.error) {
      console.error(presentSheet.error);
      return Alert.alert(presentSheet.error.message);
    }
    Alert.alert("Donated successfully! Thank you for the donation.");
  } catch (err) {
    console.error(err);
    Alert.alert("Payment failed!");
  }
};

首先,我们要把数值从字符串转换为整数。然后,我们检查金额是否小于₹1 INR。如果是,应用程序就会抛出一个警报,交易就会停止。

接下来,我们向Node后端发出HTTP POST请求,以获得客户秘密。如果有任何错误,我们通过检查response.ok 来处理。

然后,我们使用initPaymentSheet() 函数初始化 PaymentSheet,并传入我们从后端得到的客户秘密。我们再次使用presentPaymentSheet() 来展示PaymentSheet,向它提供客户秘密,并检查是否有任何错误。如果我们发现任何错误,就会向用户发送一个警告。

如果没有错误,你应该得到一个 "捐赠成功 "的提示。当你点击捐赠按钮时,你会看到以下内容。

screenshot of a payment info screen for adding credit card info

这是PaymentSheet,由Stripe制作。从这里开始,一切都由Stripe处理,你不需要担心错误的处理。

一旦支付成功,你可以在运行Node服务器的控制台中清楚地看到它与用户的名字。在生产环境中,你将需要从Stripe仪表板上手动创建一个webhook,并使用实时密钥而不是测试密钥。

接下来是什么?

恭喜你!你已经成功地集成了PaymentSheet。你已经成功地将PaymentSheet集成到你的Expo应用中。我希望你能尽可能多地使用这个功能。也许你可以尝试在使用PaymentSheet的同时使用Apple Pay和Google Pay,我认为这将是一个很好的锻炼机会

如果你面临任何挑战,你可以随时参考本文前面链接的Github资源库。

The postMastering Stripe PaymentSheet in React Native with Expoappeared first onLogRocket Blog.