在 Node.js 中实现支付网关集成

在 Node.js 中实现支付网关集成

引言:支付网关是什么?

大家好,欢迎来到今天的讲座!今天我们要聊的是如何在 Node.js 中实现支付网关的集成。如果你是第一次接触这个话题,别担心,我会尽量用轻松诙谐的语言,带你一步步了解支付网关的工作原理,并教你如何在 Node.js 项目中实现它。

首先,什么是支付网关?简单来说,支付网关就像是你和银行之间的“桥梁”。当你在网上购物时,点击“支付”按钮后,你的信用卡信息并不会直接发送到商家的服务器,而是通过支付网关进行处理。支付网关会验证你的支付信息,确保它是安全的,并将结果返回给商家。这样做的好处是,商家不需要自己处理敏感的支付信息,从而减少了安全风险。

支付网关通常由第三方公司提供,比如 PayPal、Stripe、Square 等。这些公司提供了 API(应用程序编程接口),开发者可以通过这些 API 将支付功能集成到自己的应用中。今天我们主要讨论的是如何使用 Stripe 的 API 来实现支付网关集成。

为什么选择 Stripe?

在众多支付网关中,为什么我们选择了 Stripe?其实原因很简单:

  1. 易于集成:Stripe 提供了非常详细的文档和丰富的 SDK,几乎支持所有的主流编程语言,包括 Node.js。
  2. 全球支持:Stripe 支持全球 135+ 个国家/地区的支付,涵盖了多种货币和支付方式。
  3. 安全性:Stripe 符合 PCI DSS 标准,这意味着它已经通过了严格的安全审查,开发者无需担心支付信息的安全问题。
  4. 灵活性:除了基本的支付功能,Stripe 还提供了订阅、发票、退款等高级功能,适合各种业务需求。

当然,其他支付网关也有各自的优点,但考虑到我们的主题是 Node.js,Stripe 的 Node.js SDK 无疑是最好的选择之一。

准备工作

在开始编写代码之前,我们需要做一些准备工作。首先是安装必要的工具和库。

1. 安装 Node.js 和 npm

如果你还没有安装 Node.js 和 npm(Node Package Manager),请先去官网下载并安装最新版本。安装完成后,你可以通过以下命令检查是否安装成功:

node -v
npm -v

如果看到版本号,说明安装成功!

2. 创建一个新的 Node.js 项目

接下来,创建一个新的 Node.js 项目。打开终端,进入你想要存放项目的文件夹,然后运行以下命令:

mkdir payment-gateway-integration
cd payment-gateway-integration
npm init -y

这将会创建一个名为 payment-gateway-integration 的文件夹,并在其中生成一个 package.json 文件。-y 参数会自动为你生成默认的配置,省去了手动输入的麻烦。

3. 安装 Stripe SDK

现在我们来安装 Stripe 的官方 Node.js SDK。运行以下命令:

npm install stripe

安装完成后,你就可以在项目中使用 Stripe 的 API 了。

4. 获取 Stripe API 密钥

要使用 Stripe 的 API,你需要一个 API 密钥。登录 Stripe Dashboard,进入开发者设置页面,找到“API keys”部分。你会看到两个密钥:Publishable keySecret key

  • Publishable key:用于前端,允许你在浏览器中与 Stripe 交互,例如收集支付信息。
  • Secret key:用于后端,允许你在服务器上处理支付请求。注意:永远不要将 Secret key 暴露在前端代码中!

将这两个密钥保存在一个安全的地方,稍后我们会用到它们。

实现支付网关的基本流程

好了,准备工作完成了,接下来我们来看看如何实现支付网关的基本流程。整个流程可以分为以下几个步骤:

  1. 前端:收集支付信息
  2. 后端:创建支付意图
  3. 前端:确认支付
  4. 后端:处理支付结果

1. 前端:收集支付信息

在前端,我们需要收集用户的支付信息。为了确保支付信息的安全性,我们不会直接将信用卡信息发送到服务器,而是通过 Stripe 的 JavaScript SDK 来处理。Stripe 提供了一个叫做 Elements 的组件,可以帮助我们轻松地构建安全的支付表单。

首先,在 public 文件夹中创建一个 HTML 文件,命名为 index.html,并在其中引入 Stripe 的 JavaScript SDK:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Payment Gateway Integration</title>
  <script src="https://js.stripe.com/v3/"></script>
</head>
<body>
  <h1>Checkout</h1>
  <form id="payment-form">
    <div id="card-element"><!-- Stripe Elements will be inserted here --></div>
    <button id="submit">Pay</button>
  </form>

  <script>
    // Initialize Stripe with your publishable key
    const stripe = Stripe('your-publishable-key');

    // Create an instance of Elements
    const elements = stripe.elements();

    // Create a card element and mount it to the DOM
    const cardElement = elements.create('card');
    cardElement.mount('#card-element');

    // Handle form submission
    document.getElementById('payment-form').addEventListener('submit', async (event) => {
      event.preventDefault();

      // Create a PaymentMethod object
      const { error, paymentMethod } = await stripe.createPaymentMethod({
        type: 'card',
        card: cardElement,
      });

      if (error) {
        console.error(error);
      } else {
        // Send the payment method ID to the server
        fetch('/create-payment-intent', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ paymentMethodId: paymentMethod.id }),
        })
        .then(response => response.json())
        .then(data => {
          if (data.clientSecret) {
            confirmPayment(data.clientSecret);
          }
        });
      }
    });

    // Confirm the payment
    async function confirmPayment(clientSecret) {
      const { error } = await stripe.confirmCardPayment(clientSecret, {
        payment_method: { card: cardElement },
      });

      if (error) {
        console.error(error);
      } else {
        alert('Payment successful!');
      }
    }
  </script>
</body>
</html>

在这段代码中,我们做了几件重要的事情:

  • 使用 Stripe('your-publishable-key') 初始化 Stripe。
  • 使用 elements.create('card') 创建一个卡元素,并将其挂载到页面上的 #card-element 元素中。
  • 当用户提交表单时,调用 stripe.createPaymentMethod 创建一个支付方法对象,并将其发送到服务器。
  • 最后,使用 stripe.confirmCardPayment 确认支付。

2. 后端:创建支付意图

在前端收集完支付信息后,我们需要在后端创建一个支付意图(Payment Intent)。支付意图是 Stripe 的核心概念之一,它表示一次支付的意图,并包含了支付的状态和相关信息。

server.js 文件中,编写以下代码:

const express = require('express');
const stripe = require('stripe')('your-secret-key');

const app = express();
app.use(express.json());

// Endpoint to create a PaymentIntent
app.post('/create-payment-intent', async (req, res) => {
  try {
    const { paymentMethodId } = req.body;

    // Create a PaymentIntent with the received payment method ID
    const paymentIntent = await stripe.paymentIntents.create({
      amount: 1000, // Amount in cents (e.g., $10.00)
      currency: 'usd',
      payment_method: paymentMethodId,
      confirm: true, // Automatically confirm the payment
    });

    res.json({ clientSecret: paymentIntent.client_secret });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Start the server
app.listen(3000, () => {
  console.log('Server is running on port 3000 🚀');
});

在这段代码中,我们做了以下几件事:

  • 使用 stripe.paymentIntents.create 创建一个支付意图,并传入从前端接收到的支付方法 ID。
  • 设置 amountcurrency,分别表示支付金额和货币类型。注意,金额是以最小单位(如美分)表示的,因此 $10.00 应该写成 1000
  • 设置 confirm: true,表示我们希望 Stripe 自动确认这次支付。
  • 返回 client_secret,这是前端确认支付时需要的凭据。

3. 前端:确认支付

当后端返回 client_secret 后,前端会调用 stripe.confirmCardPayment 来确认支付。这个过程实际上是通过 Stripe 的 API 与支付网关进行通信,确保支付顺利完成。

在之前的前端代码中,我们已经实现了 confirmPayment 函数,它会接收 client_secret 并调用 stripe.confirmCardPayment。如果支付成功,用户会看到一个弹窗提示;如果失败,则会在控制台中输出错误信息。

4. 后端:处理支付结果

支付完成后,我们通常还需要在后端处理支付结果。例如,更新订单状态、发送确认邮件等。为了实现这一点,我们可以监听 Stripe 的 Webhook 事件。

配置 Webhook

Stripe 提供了一种称为 Webhook 的机制,允许我们在支付事件发生时接收通知。我们需要在 Stripe Dashboard 中配置 Webhook,告诉 Stripe 将事件发送到我们的服务器。

  1. 登录 Stripe Dashboard,进入开发者设置页面,找到“Webhooks”部分。
  2. 点击“Add endpoint”,输入你的服务器 URL(例如 http://localhost:3000/webhook)。
  3. 选择你感兴趣的事件类型,例如 payment_intent.succeededpayment_intent.payment_failed

处理 Webhook 事件

server.js 中添加一个路由来处理 Webhook 事件:

const bodyParser = require('body-parser');
const crypto = require('crypto');

// Middleware to parse raw request body for Webhook
app.use(
  '/webhook',
  bodyParser.raw({ type: 'application/json' })
);

// Webhook endpoint
app.post('/webhook', async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const endpointSecret = 'your-webhook-secret'; // Get this from Stripe Dashboard

  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle different types of events
  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      console.log('Payment succeeded:', paymentIntent);
      // Update order status, send confirmation email, etc.
      break;

    case 'payment_intent.payment_failed':
      const failedIntent = event.data.object;
      console.log('Payment failed:', failedIntent);
      // Notify user, log error, etc.
      break;

    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  res.json({ received: true });
});

在这段代码中,我们做了以下几件事:

  • 使用 bodyParser.raw 解析原始请求体,因为 Webhook 请求是以 JSON 格式发送的。
  • 使用 stripe.webhooks.constructEvent 验证 Webhook 签名,确保请求来自 Stripe。
  • 根据不同的事件类型(如 payment_intent.succeededpayment_intent.payment_failed),执行相应的逻辑。

高级功能:订阅和退款

除了基本的支付功能,Stripe 还提供了许多高级功能,例如订阅和退款。下面我们简要介绍一下如何实现这些功能。

订阅

订阅是一种常见的商业模式,适用于按月或按年收费的服务。Stripe 提供了强大的订阅管理功能,允许你轻松地创建和管理订阅计划。

创建订阅计划

首先,我们需要在 Stripe Dashboard 中创建一个订阅计划。进入“Billing” -> “Products & prices”,点击“Create product”,然后为产品添加价格计划。你可以选择按月或按年收费,并设置具体的金额和货币。

创建订阅

在后端,我们可以使用 stripe.subscriptions.create 来创建一个订阅。以下是一个简单的示例:

app.post('/create-subscription', async (req, res) => {
  try {
    const { customerId, priceId } = req.body;

    const subscription = await stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: priceId }],
      expand: ['latest_invoice.payment_intent'],
    });

    res.json({ subscription });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

在这个例子中,我们传入了 customerIdpriceId,分别表示客户 ID 和订阅计划的价格 ID。Stripe 会根据这些信息创建一个订阅,并返回订阅对象。

退款

有时,用户可能会要求退款。Stripe 也提供了简单的 API 来处理退款。以下是一个退款的示例:

app.post('/refund-payment', async (req, res) => {
  try {
    const { paymentIntentId } = req.body;

    const refund = await stripe.refunds.create({
      payment_intent: paymentIntentId,
    });

    res.json({ refund });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

在这个例子中,我们传入了 paymentIntentId,表示要退款的支付意图 ID。Stripe 会根据这个 ID 执行退款操作,并返回退款对象。

总结

通过今天的讲座,我们学习了如何在 Node.js 中实现支付网关集成。我们从基础知识开始,逐步介绍了如何使用 Stripe 的 API 创建支付意图、处理支付结果、配置 Webhook 以及实现订阅和退款等功能。希望这些内容对你有所帮助!

如果你有任何问题或建议,欢迎在评论区留言。感谢大家的参与,下次再见! 😊


附录:常用 API 参考表

API 方法 描述 示例
stripe.paymentIntents.create 创建支付意图 stripe.paymentIntents.create({ amount: 1000, currency: 'usd' })
stripe.paymentMethods.create 创建支付方法 stripe.paymentMethods.create({ type: 'card', card: cardElement })
stripe.confirmCardPayment 确认支付 stripe.confirmCardPayment(clientSecret)
stripe.subscriptions.create 创建订阅 stripe.subscriptions.create({ customer: 'cus_123', items: [{ price: 'price_456' }] })
stripe.refunds.create 创建退款 stripe.refunds.create({ payment_intent: 'pi_789' })

Q&A

问:我可以在生产环境中使用测试模式吗?

答:不建议在生产环境中使用测试模式。测试模式仅用于开发和调试,所有交易都是模拟的,不会真正扣款。在上线前,请务必切换到生产模式,并确保你已经准备好处理真实的支付请求。

问:如何处理支付失败的情况?

答:支付失败时,Stripe 会返回一个错误对象,包含详细的错误信息。你可以在前端捕获这些错误,并根据具体情况采取相应的措施,例如提示用户重新输入支付信息或联系客服。

问:Stripe 支持哪些支付方式?

答:Stripe 支持多种支付方式,包括信用卡、借记卡、Apple Pay、Google Pay、支付宝、微信支付等。具体支持的支付方式取决于你所在的国家和地区。你可以在 Stripe Dashboard 中查看完整的支付方式列表。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注