我们生活在一个与许多在线服务互动的世界里,并通过在线支付网关向这些服务付款。而我们,作为开发者,有责任将这些支付网关整合在一起,使其对用户和收款方都是安全的。
在这篇文章中,我们将介绍如何在使用Stripe接受在线支付时实施3D安全保护。
什么是3D安全?
3D安全是Stripe在实际处理付款之前对用户进行认证的一种方式。当用户输入他的银行卡信息时,会有一个弹出窗口或重定向提示他,以验证付款。
这通常是通过OTP验证身份,但这可能取决于发卡的银行。在一些国家,3D安全是没有必要的,但在印度等国家,3D安全是必须的。
你可以在你的Stripe账户中设置雷达规则,要求3D安全认证,但如果你的支付表单中没有代码,使3D安全弹出窗口工作,那就没有用。
在这篇文章中,我们将使用NodeJS、React,当然还有Stripe,创建一个简单的捐赠网络应用。我们将涵盖以下主题。
- 设置Stripe并获得API密钥
- 设置NodeJS后端和React前端
- 在前端创建一个结账表单
- 以常规方式处理付款
- 在需要认证的情况下使用3D安全作为后备方案
- 确认付款
- 添加经常性付款(订阅)。
- 测试我们的集成
你需要什么?
让我们开始吧!
首先,我们将在后端工作。我更喜欢 "API优先 "的方法,这意味着你首先创建一个API,然后再进行前端的其他工作。
我们将使用NodeJS、Express和一个Stripe包来创建我们的后端,以获取与支付有关的内容。
启动后端
让我们来创建我们的后端。要做到这一点,打开终端/命令提示符,并键入以下命令,在你想要的文件夹中启动一个NodeJS项目。
npm init -y
运行此命令将在该文件夹中生成一个package.json
文件。
现在使用以下命令打开文件夹中的VSCode,以便我们可以开始编辑。
code .
现在VSCode已经打开,你可以使用集成的终端,这将使你的生活更容易。只要在Windows上按Ctrl + J或在Mac上按Command + J就可以在VSCode中打开终端。
让我们安装几个软件包,它们将帮助我们进一步完成项目。在终端输入以下命令,我们将看到这些包的作用。
npm install express cors stripe dotenv
这些是正在安装的软件包。
Express
是用来轻松创建HTTP服务器的CORS
帮助我们摆脱客户端应用程序中的跨源错误。Stripe
是与Stripe的实际连接。我们可以使用这个包来获取付款细节并创建付款。Dotenv
帮助我们启用环境变量来存储敏感数据
在环境变量中添加Stripe密匙
在进一步使用这个支付系统之前,让我们在环境变量中设置Stripe的秘密密钥。
所有秘密的API密钥和凭证都必须存储在环境变量中,这样如果实际代码被盗,数据就不会被盗。
要获得您的Stripe秘钥,请打开您的Stripe仪表盘,您会看到一个类似于下图的侧面菜单。
现在,点击 "开发人员",然后点击API密钥。在那里你应该看到你的Stripe可发布和秘密密钥。
现在,我们需要的是秘密密钥。请注意,您不应该与任何人分享您的秘密密钥。分享您的秘密密钥将使其他人能够访问您的Stripe账户。
另一方面,可发布的密钥是我们在前端使用的密钥,如果有人获得了它,也没有关系,因为它是要公开的。
现在,复制您的Stripe密匙并进入VSCode,创建一个名为.env
的新文件,并按以下格式粘贴密钥。
STRIPE_SECRET_KEY=(secret key here)
.env
文件是用来存储环境变量的。dotenv
包将搜索这个文件以加载环境变量。现在,.env
文件已经完成,我们不需要在本教程中再碰环境变量。
安装Nodemon
在遵循本教程时,你可能需要多次重启服务器。为了避免这种情况,我们可以安装一个名为nodemon
的全局包,每当我们保存一个文件时,它就会自动重启我们的服务器。你可以在这里阅读更多关于Nodemon的信息。
在终端键入以下命令。
npm install -g nodemon
如果需要的话,使用sudo
,因为Nodemon应该是全局安装的,所以它将需要root权限。
设置一个Express服务器
让我们来创建将运行我们的服务器的文件。我们可以把它命名为index.js
,因为它在package.json
文件中被默认指定为main
。如果你愿意,你可以改变这个名字,但在本教程中我们将坚持使用index.js
。
让我们从创建一个Express服务器和一个简单的路由开始。
const express = require("express");
const app = express();
const PORT = process.env.PORT || 5000;
const cors = require("cors");
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/", (req, res) => res.json({ status: 200, message: "API Works" }));
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
这将创建一个简单的Express服务器和一个主页路由,它简单地返回一个JSON,说明API的工作。
在这里,我们将端口设置为process.env.PORT || 5000
,因为如果你决定将这个服务器部署到像Heroku这样的服务中,他们会将其托管在自己的端口上,这些端口存储在他们的环境变量中,所以我们让他们决定端口。如果process.env.PORT
是未定义的,该应用程序在本地运行,将使用5000端口。
我们使用cors
包作为一个Express中间件,这样客户端应用程序就可以与我们的服务器正常互动,而不会出现任何跨源错误。你可以根据你的需要来配置cors
包,但在本教程中,我们将只允许任何流量。
在中间件部分,我们也允许通过请求体的JSON和url编码的数据,Express会自动为我们解析。
现在,如果你转到Postman或任何其他HTTP客户端,对以下内容执行GET请求 [http://localhost:5000](http://localhost:5000)
的GET请求,你会得到以下JSON响应。
{
"status": 200,
"message": "API Works"
}
如果你看到这条信息,你的Express服务器已经设置好了。现在让我们进入下一个步骤。
设置dotenv
现在让我们来配置一下dotenv
包,以便它能正确识别我们来自.env
文件的环境变量。在顶部写下以下代码。
require("dotenv").config();
初始化Stripe
现在让我们来设置我们与Stripe的连接。在之前的教程中,我们已经安装了一个名为stripe
的包,它将帮助我们与Stripe通信。但首先,我们需要向它提供我们的Stripe密匙,以便它能与我们的Stripe账户进行交互。
在我们之前创建的文件上面包含这个片段。
const Stripe = require("stripe");
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
前面我们处理了环境变量,这里我们使用了我们存储的STRIPE_SECRET_KEY
。现在Stripe识别了您的账户,我们可以与Stripe进一步互动。
整个代码直到现在应该显示如下。
require("dotenv").config();
const express = require("express");
const app = express();
const PORT = process.env.PORT || 5000;
const cors = require("cors");
const Stripe = require("stripe");
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/", (req, res) => res.json({ status: 200, message: "API Works" }));
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
收集数据用于支付
让我们考虑一下我们需要从用户那里收集哪些数据来启动支付。为了本教程,我们将保持简单。
- 电子邮件地址
- 支付金额
paymentMethod
,一个由Stripe在前端生成的ID,将代表一个特定的卡- 订阅,
onetime
或monthly
。如果订阅设置为,我们将设置经常性的付款。monthly
由于我们正在 "创建 "一个付款,我们将使用一个POST请求。使用POST请求的另一个原因是,我们发送给服务器的数据不会显示在URL本身,这与GET请求不同。另外,GET请求可以通过浏览器直接访问,这不是我们想要的。
因此,让我们创建一个POST请求监听器并收集数据。
app.post("/donate", async (req, res) => {
try {
let { email, amount, paymentMethod, subscription } = req.body;
if (!email || !amount || !paymentMethod || !subscription)
return res.status(400).json({ status: 400, message: "All fields are required!" });
amount = parseInt(amount);
if (subscription === "onetime") {
// One time payment code here
}
if (subscription === "monthly") {
// Recurring payment code here
}
res.status(400).json({ status: 400, message: "Invalid type" });
} catch(err) {
console.error(err);
res.status(500).json({ status: 200, message: "Internal server error" });
}
});
在上面的代码中,我们正在做以下工作。
- 当然是在
/donate
路由上设置一个POST监听器 - 从用户那里收集
email
,amount
, 和paymentMethod
- 验证字段,因此,如果任何字段丢失,将发送一条错误信息
- 有时,客户端应用程序可能以字符串形式发送
amount
,在这种情况下,我们要使用parseInt()
函数将金额转换为整数值
首先,我们要处理的是一次性支付。
尝试简单的HTTP支付
我们只有在需要时才使用3D安全,或者按照我们在Stripe仪表盘中的雷达规则。在我们进入3D安全之前,我们必须尝试HTTP支付,因为有些卡不支持3D安全。
现在是时候联系Stripe了。
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100),
currency: "INR",
receipt_email: email,
description: "Payment for donation",
payment_method: paymentMethod,
confirm: true
});
这样就可以立即启动支付了。confirm
字段告诉Stripe在收到付款后立即确认。如果你不指定confirm
,它就不会向用户收费,你需要手动确认订单,然后再向Stripe提出请求。
在amount
字段中,您指定二级货币单位(例如,美元是美分,印度卢比是派萨)。Math.round()
,这里用于删除任何小数,因为Stripe不喜欢小数。
根据您的Stripe账户位置指定货币。对我来说是,印度,所以我使用INR
的货币。
一旦支付完成,收据将被发送到指定的电子邮件。在这种情况下,我们提到了我们从用户那里收集的电子邮件。
现在让我们检查一下这个简单的HTTP支付是否成功。要做到这一点,我们可以检查paymentIntent
的状态属性。
if (paymentIntent.status === "succeeded") {
// Payment successful!
return res.json({
status: 200,
message: "Payment Successful!",
id: paymentIntent.id
});
}
>
这就是一个简单的HTTP支付的全部内容。在这里,paymentIntent.id
可以作为一个支付ID。而我们用return
来立即停止进一步的执行,这样就不会出现意外的错误。
然而,如果状态不是succeeded
,而是requires_action
,这意味着需要3D安全。因此,我们将在这里处理3D安全问题。
- 我们将获得一个
client_secret
,用于支付意向。 - 我们将把
client_secret
发送给前端 - 前端将使用
client_secret
,使用3D安全进行验证。 - 我们将在后端制定一个路线,再次检查支付状态
获取client_secret
并将其发送到前端
让我们检查一下我们创建的支付意图是否需要3D安全,然后发送客户秘密。
if (paymentIntent.status === "requires_action") {
return res.json({
status: 200,
message: "3D secure required",
actionRequired: true,
clientSecret: paymentIntent.client_secret
});
}
这样一来,我们就把客户秘密发送到前端。一旦我们完成了后端部分,我们将在本文后面处理前端的问题。
最后,如果状态既不是succeeded
,也不是requires_action
,我们将通知用户支付失败。我们在前面的案例中使用了return
,所以我们不需要使用else
。
return res.status(400).json({
status: 400,
message: "Payment failed!"
});
处理经常性支付
在经常性支付中,我们不直接使用支付意图。创建经常性付款的过程有点不同。
- 首先,我们创建一个价格,这将是我们的捐款金额
- 接下来我们用用户的电子邮件创建一个Stripe客户
- 然后,我们创建一个订阅,并向客户收取价格。如果需要认证,Stripe将每月向客户发送一封关于付款的电子邮件。
- 最后,我们让用户在我们的网站上自己支付第一张发票
之前我们为monthly
订阅类型创建了一个if
报表。所有的经常性付款代码都在那里。
创建一个价格
让我们继续进行第一步,创建一个价格。
const price = await stripe.prices.create({
unit_amount: Math.round(amount * 100),
recurring: { interval: "month" },
currency: "INR",
product_data: {
name: "Recurring donation"
}
});
这里的unit_amount
是实际的金额--我们已经讨论了如何将其发送到Stripe。
我们还为recurring
,提供一个interval
。在这种情况下,我们把它设置为month
。product_data
对象包含一些关于产品本身的信息。在这种情况下,它只是一个捐款,所以我们只是指定它。
创建一个客户
现在,让我们来创建客户。
const customer = await stripe.customers.create({
email,
description: "Donation customer",
payment_method: paymentMethod,
invoice_settings: {
default_payment_method: paymentMethod
}
});
在这里我们指定paymentMethod
,这样我们就可以在需要的时候立即向客户收费,而不会有任何麻烦。
创建一个订阅
这是向客户实际收费的地方。当启动订阅时,会产生一张发票,可以由用户支付,但我们将让用户立即支付发票以开始订阅。
我们可以从订阅中获得paymentIntent
,然后我们可以像以前一样做检查。
const subscribe = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: price.id }],
expand: ["latest_invoice.payment_intent"]
});
我们要传入客户的ID和价格,把所有东西联系在一起。另外,为了获得最新发票的paymentIntent
,我们使用expand
属性。
当我们试图创建一个订阅时,Stripe已经尝试了一个基于HTTP的支付。现在,我们需要像以前一样处理3D安全支付的问题。
if (
subscribe.latest_invoice.payment_intent.status === "requires_action"
) {
// proceed to 3ds
return res.status(200).json({
status: 200,
message: "3D Secure required",
actionRequired: true,
clientSecret: subscribe.latest_invoice.payment_intent.client_secret,
id: subscribe.latest_invoice.payment_intent.id,
});
}
if (subscribe.latest_invoice.payment_intent.status === "succeeded") {
return res.json({
status: 200,
message: "Payment successful!",
});
}
return res.status(400).json({ status: 400, message: "Payment failed!" });
这与我们用于一次性付款的方法相同。我们已经完成了后台的支付路线。
还有一条路线需要覆盖--检查路线。在前端认证后,我们需要一个路由来检查和验证后端的状态。
app.get("/check/:id", async (req, res) => {
try {
const id = req.params.id;
const paymentIntent = await stripe.paymentIntents.retrieve(id);
if (paymentIntent?.status === "succeeded") {
return res.json({
status: 200,
message: "Payment successful!",
id,
});
}
res
.status(400)
.json({
status: 200,
message: "Payment failed! Please try again later.",
});
} catch (err) {
console.error(err);
res.status(500).json({ status: 500, message: "Internal server error" });
}
});
这次我们使用一个GET请求,并检查付款是否真正完成。如果你不想使用webhook,想马上为用户提供一个虚拟服务,可以这样做。
这将是你的应用程序知道支付成功和用户可以使用的地方。但在这种情况下,这是一个捐赠网站,我们不需要在这里做任何特别的事情。
完成index.js
代码
你的index.js
文件现在应该是这样的。
require("dotenv").config();
const express = require("express");
const app = express();
const PORT = process.env.PORT || 5000;
const cors = require("cors");
const Stripe = require("stripe");
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/", (req, res) => res.json({ status: 200, message: "API Works" }));
app.post("/donate", async (req, res) => {
try {
let { email, amount, paymentMethod, subscription } = req.body;
if (!email || !amount || !paymentMethod || !subscription)
return res.status(400).json({ status: 400, message: "All fields are required!" });
amount = parseInt(amount);
if (subscription === "onetime") {
// One time payment code here
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100),
currency: "INR",
receipt_email: email,
description: "Payment for donation",
payment_method: paymentMethod,
confirm: true
});
if (paymentIntent.status === "succeeded") {
// Payment successful!
return res.json({
status: 200,
message: "Payment Successful!",
id: paymentIntent.id
});
}
if (paymentIntent.status === "requires_action") {
return res.json({
status: 200,
message: "3D secure required",
actionRequired: true,
clientSecret: paymentIntent.client_secret
});
}
return res.status(400).json({
status: 400,
message: "Payment failed!"
});
}
if (subscription === "monthly") {
// Recurring payment code here
const price = await stripe.prices.create({
unit_amount: Math.round(amount * 100),
recurring: { interval: "month" },
currency: "INR",
product_data: {
name: "Recurring donation"
}
});
const customer = await stripe.customers.create({
email,
description: "Donation customer",
payment_method: paymentMethod,
invoice_settings: {
default_payment_method: paymentMethod
}
});
const subscribe = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: price.id }],
expand: ["latest_invoice.payment_intent"]
});
if (
subscribe.latest_invoice.payment_intent.status === "requires_action"
) {
// proceed to 3ds
return res.status(200).json({
status: 200,
message: "3D Secure required",
actionRequired: true,
clientSecret: subscribe.latest_invoice.payment_intent.client_secret,
id: subscribe.latest_invoice.payment_intent.id,
});
}
if (subscribe.latest_invoice.payment_intent.status === "succeeded") {
return res.json({
status: 200,
message: "Payment successful!",
});
}
return res.status(400).json({ status: 400, message: "Payment failed!" });
}
res.status(400).json({ status: 400, message: "Invalid type" });
} catch(err) {
console.error(err);
res.status(500).json({ status: 200, message: "Internal server error" });
}
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
创建前端
现在让我们转向前端,学习如何触发3D安全认证以及如何启动支付。
我们不会在前端做任何花哨的造型。让我们保持简单,并专注于支付方面的事情。
我们将在前端使用React。创建一个名为frontend
的新文件夹,在该文件夹中打开终端,并输入以下命令。
npx create-react-app .
.
指定我们要在当前文件夹中创建一个React应用程序。
现在让我们安装一些我们在制作这个应用时需要的包。
npm install axios @stripe/react-stripe-js @stripe/stripe-js
axios
是一个库,可以轻松地进行HTTP请求,而不需要搞乱fetch
API。- 这两个Stripe包对于创建Stripe元素和与Stripe通信都很有用。
现在使用以下命令在React应用程序中打开VSCode。
code .
一旦进入集成终端,键入以下命令来启动React应用程序。
npm start
应该打开一个新的浏览器标签,你应该看到以下屏幕。
如果你看到这个屏幕,你已经成功启动了一个React应用程序。现在让我们做一些清理工作。
在src
,删除以下我们不需要的文件。
App.test.js
setupTests.js
logo.svg
一旦你删除这些文件,你会看到一个错误弹出。这是因为我们破坏了一些东西。
进入App.js
,删除顶部的标识导入和第一个div
下的内容。删除App.css
中的所有内容。
你的App.js
应该看起来像这样。
import "./App.css";
function App() {
return <div className="app"></div>;
}
export default App;
接下来,让我们创建一个新的组件,名为Checkout
。在src
中创建两个文件:Checkout.js
和Checkout.css
。
由于我们在本教程中不关注样式,我提供了一个CSS文件的内容,但我们不会去看Checkout.css
中实际发生的情况。
.checkout {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100%;
}
.checkout__container {
background-color: #f5f5f5;
padding: 20px;
width: 25%;
display: flex;
flex-direction: column;
}
.checkout__textBox {
padding: 10px;
font-size: 18px;
margin-bottom: 10px;
}
.checkout__radio {
margin-bottom: 10px;
}
.checkout__btn {
margin-top: 10px;
padding: 10px;
font-size: 18px;
border: none;
background-color: #0984e3;
color: white;
}
现在,打开Checkout.js
,创建一个React功能组件。
import React from "react";
function Checkout() {
return <div className="checkout"></div>;
}
export default Checkout;
现在让我们导入这个组件并在App.js
中使用它。
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import "./App.css";
import Checkout from "./Checkout";
const stripePromise = loadStripe("(publishable key here)");
function App() {
return (
<div className="app">
<Elements stripe={stripePromise}>
<Checkout />
</Elements>
</div>
);
}
export default App;
我们将我们的Checkout
组件包裹在Stripe提供给我们的Elements
中。这个组件充当了我们需要的所有Stripe元素和服务的包装器。
我们使用loadStripe()
函数并传入可发布的密钥,然后在Elements
组件中传入stripePromise
作为stripe
作为道具。
现在让我们去Checkout.js
,并做出我们表单的基本布局。
import { CardElement } from "@stripe/react-stripe-js";
import React, { useState } from "react";
function Checkout() {
const [email, setEmail] = useState("");
const [amount, setAmount] = useState("");
const [subscription, setSubscription] = useState("onetime");
const handleSubmit = async (e) => {
try {
e.preventDefault();
} catch (error) {
console.error(error);
alert("Payment failed!");
}
};
return (
<div className="checkout">
<form className="checkout__container" onSubmit={handleSubmit}>
<input
type="email"
value={email}
className="checkout__textBox"
onChange={(e) => setEmail(e.target.value)}
placeholder="E-mail Address"
/>
<input
type="number"
value={amount}
className="checkout__textBox"
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount"
/>
<div className="checkout__radio">
<input
type="radio"
onChange={(e) => setSubscription("onetime")}
checked={subscription === "onetime"}
/>
Onetime
</div>
<div className="checkout__radio">
<input
type="radio"
onChange={(e) => setSubscription("monthly")}
checked={subscription === "monthly"}
/>
Monthly
</div>
<CardElement
options={{
style: {
base: {
fontSize: "16px",
color: "#424770",
"::placeholder": {
color: "#aab7c4",
},
},
invalid: {
color: "#9e2146",
},
},
}}
/>
<button className="checkout__btn" type="submit">
Donate
</button>
</form>
</div>
);
}
export default Checkout;
我们创建了一个基本的表单,要求输入电子邮件和所需的金额。CardElement
组件被用来显示一个小元素,让用户输入卡的详细信息。
现在让我们来处理用户提交表单时的事件。
const handleSubmit = async (e) => {
try {
e.preventDefault();
if (!elements || !stripe) return;
const cardElement = elements.getElement(CardElement);
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: cardElement,
});
} catch (error) {
console.error(error);
alert("Payment failed!");
}
};
首先,我们将检查Stripe和Elements是否已经加载。如果没有,那么表单将不做任何事情。在没有加载Stripe的情况下,你怎么能处理付款?
然后我们进入到cardElement
。之所以太容易找到它,是因为整个表单中只能有一个CardElement
。
接下来,我们从cardElement
中输入的细节创建一个paymentMethod
,这又会返回一个包含支付方式ID的对象,这是我们在后端需要的。
现在让我们进入后端并处理付款。
首先,让我们导入axios
。
import axios from "axios"
然后,让我们向后端发出请求,提供关于付款的信息。
const res = await axios.post("http://localhost:5000/donate", {
amount,
email,
subscription,
stripeToken: paymentMethod.id,
});
如果请求中出现错误或响应代码指向错误,代码将停止执行并转到catch
块来处理错误。
现在后端将尝试执行简单的HTTP支付,我们将得到一个响应。如果我们需要3D安全,actionRequired
将是true
。
if (res.data.actionRequired) {
// We perform 3D Secure authentication
const { paymentIntent, error } = await stripe.confirmCardPayment(
res.data.clientSecret
);
if (error) return alert("Error in payment, please try again later");
if (paymentIntent.status === "succeeded")
return alert(`Payment successful, payment ID - ${res.data.id}`);
const res2 = await axios.get(`http://localhost:5000/check/${res.data.id}`);
alert(`Payment successful, payment ID - ${res.data.id}`);
} else {
// Simple HTTP Payment was successful
alert(`Payment successful, payment ID - ${res.data.id}`);
}
在这里,我们检查actionRequired
是否是true
。如果是,我们需要触发一个3D安全认证弹出窗口。我们通过将我们从服务器得到的clientSecret
传递给confirmCardPayment()
函数,从stripe
。
然后,我们得到paymentIntent
,并通过向我们的Express服务器的/check
路由发送付款意图ID,从我们的服务器检查付款情况。如果支付成功,路由会返回一个200状态代码,否则我们的代码将通过catch
块,如前所述。
因此,这就是你如何触发3D安全。这里是Checkout.js
的完整代码。
import { CardElement } from "@stripe/react-stripe-js";
import React, { useState } from "react";
import axios from "axios";
function Checkout() {
const [email, setEmail] = useState("");
const [amount, setAmount] = useState("");
const [subscription, setSubscription] = useState("onetime");
const handleSubmit = async (e) => {
try {
e.preventDefault();
if (!elements || !stripe) return;
const cardElement = elements.getElement(CardElement);
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: cardElement,
});
const res = await axios.post("http://localhost:5000/donate", {
amount,
email,
subscription,
stripeToken: paymentMethod.id,
});
if (res.data.actionRequired) {
// We perform 3D Secure authentication
const { paymentIntent, error } = await stripe.confirmCardPayment(
res.data.clientSecret
);
if (error) return alert("Error in payment, please try again later");
if (paymentIntent.status === "succeeded")
return alert(`Payment successful, payment ID - ${res.data.id}`);
const res2 = await axios.get(`http://localhost:5000/check/${res.data.id}`);
alert(`Payment successful, payment ID - ${res.data.id}`);
} else {
// Simple HTTP Payment was successful
alert(`Payment successful, payment ID - ${res.data.id}`);
}
} catch (error) {
console.error(error);
alert("Payment failed!");
}
};
return (
<div className="checkout">
<form className="checkout__container" onSubmit={handleSubmit}>
<input
type="email"
value={email}
className="checkout__textBox"
onChange={(e) => setEmail(e.target.value)}
placeholder="E-mail Address"
/>
<input
type="number"
value={amount}
className="checkout__textBox"
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount"
/>
<div className="checkout__radio">
<input
type="radio"
onChange={(e) => setSubscription("onetime")}
checked={subscription === "onetime"}
/>
Onetime
</div>
<div className="checkout__radio">
<input
type="radio"
onChange={(e) => setSubscription("monthly")}
checked={subscription === "monthly"}
/>
Monthly
</div>
<CardElement
options={{
style: {
base: {
fontSize: "16px",
color: "#424770",
"::placeholder": {
color: "#aab7c4",
},
},
invalid: {
color: "#9e2146",
},
},
}}
/>
<button className="checkout__btn" type="submit">
Donate
</button>
</form>
</div>
);
}
export default Checkout;
为了测试你的Stripe集成,这里有Stripe提供的一些卡的细节来测试。你需要在测试模式下使用这些卡,而且你不会被收费。
当你用3D安全技术输入卡片时,将打开一个弹出窗口。在生产环境中,用户将被发送一条短信到他们的手机号码,以验证付款。
你可以设置你的雷达规则,对支持的卡强制使用3D安全,只是要注意,雷达规则并不适用所有国家。
下一步是什么?
我建议你查看Stripe的更多内容,比如Apple Pay、Google Pay、保存的卡片、非会话支付以及提供的其他多种支付方式。
你也可以查看Stripe Checkout,在那里你只需要传入产品,付款将由Stripe处理。
The postImplementing 3D Secure in Stripeappeared first onLogRocket Blog.