Stripe 插件
通过这个插件,你可以轻松将 Stripe 集成到 Payload 中。只需提供你的 Stripe 凭证,该插件就会在两个平台之间建立双向通信通道。这使你能够轻松地在两者之间同步数据,并通过 Payload 的 访问控制 代理 Stripe REST API。使用此插件可以完全将计费工作交给 Stripe,同时保留对应用程序数据的完全控制。
例如,你可能正在构建一个电子商务或 SaaS 应用程序,其中有一个 products
或 plans
集合,需要一次性付款或订阅。你可以将这些产品与 Stripe 关联,然后轻松订阅与计费相关的事件来执行业务逻辑,例如处理有效购买或订阅取消。
要在前端构建结账流程,你可以使用 Stripe Checkout,也可以使用 Stripe Web Elements 从头开始构建完全自定义的结账体验。要构建完全自定义的安全客户仪表板,你可以利用 Payload 的访问控制来限制对 Stripe 资源的访问,这样用户就无需离开你的网站来管理他们的账户。
这个插件的优势在于,你的应用程序的全部内容和业务逻辑都可以在 Payload 中处理,而 Stripe 只负责计费和支付处理。你可以构建一个完全专有的应用程序,它可以在你拥有的 API 和数据库上无限定制和扩展。像 Shopify 或 BigCommerce 这样的托管服务可能会分割你的应用程序内容,然后向你收取访问费用。
该插件是完全开源的,源代码可以在这里找到。 如果需要帮助,请查看我们的 社区帮助。如果发现错误, 请 提交新问题 并提供尽可能多的细节。
核心功能
- 在交付 SaaS 应用时隐藏你的 Stripe 凭证
- 通过 Payload 访问控制 允许受限密钥
- 在 Stripe 和 Payload 之间建立双向通信通道
- 代理 Stripe REST API
- 代理 Stripe webhooks
- 自动同步两个平台之间的数据
安装
使用任意 JavaScript 包管理器(如 pnpm、npm 或 Yarn)安装插件:
pnpm add @payloadcms/plugin-stripe
基本用法
在你的 Payload 配置 的 plugins
数组中,调用插件并传入 选项:
import { buildConfig } from 'payload'
import { stripePlugin } from '@payloadcms/plugin-stripe'
const config = buildConfig({
plugins: [
stripePlugin({
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
}),
],
})
export default config
选项
选项 | 类型 | 默认值 | 描述 |
---|---|---|---|
stripeSecretKey * | string | undefined | 你的 Stripe 密钥 |
stripeWebhooksEndpointSecret | string | undefined | 你的 Stripe webhook 端点密钥 |
rest | boolean | false | 当设为 true 时,开启 /api/stripe/rest 端点 |
webhooks | object or function | undefined | 可以是一个处理所有 webhook 事件的函数,也可以是一个按事件名称组织的 Stripe webhook 处理器对象 |
sync | array | undefined | 同步配置数组 |
logs | boolean | false | 当设为 true 时,在控制台实时输出同步事件日志 |
* 星号表示该属性为必填项。
端点
以下自定义端点会自动为你开启:
端点 | 方法 | 描述 |
---|---|---|
/api/stripe/rest | POST | 在 Payload 访问控制 后代理 Stripe REST API 并返回结果。详情请参阅 REST 代理 部分。 |
/api/stripe/webhooks | POST | 处理所有 Stripe webhook 事件 |
Stripe REST 代理
如果 rest
设置为 true,会将 Stripe REST API 代理到 Payload 访问控制 之后并返回结果。此标志仅应用于本地开发环境,更多安全注意事项请参阅下方的安全提示。
const res = await fetch(`/api/stripe/rest`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
// Authorization: `JWT ${token}` // 注意:如果不在浏览器环境中(如使用 curl 或 Postman)需要添加此项
},
body: JSON.stringify({
stripeMethod: 'stripe.subscriptions.list',
stripeArgs: [
{
customer: 'abc',
},
],
}),
})
如需在服务端代理 API,请使用 stripeProxy 函数。
注意:
这些路由中的 /api
部分可能会根据你的 Payload 配置设置而有所不同。
警告:
在生产环境中开放 REST 代理端点存在潜在安全风险。认证用户将拥有对 Stripe REST API 的开放访问权限。在生产环境中,请创建自己的端点并使用 stripeProxy 函数在服务端代理 Stripe API。
Webhooks
Stripe webhooks 用于从 Stripe 同步数据到 Payload。Webhooks 会监听你的 Stripe 账户事件,以便你可以触发相应的操作。按照以下步骤启用 webhooks。
开发环境:
- 使用 Stripe CLI 登录
stripe login
- 将事件转发到本地
stripe listen --forward-to localhost:3000/api/stripe/webhooks
- 将生成的密钥粘贴到
.env
文件中,命名为STRIPE_WEBHOOKS_ENDPOINT_SECRET
生产环境:
- 登录 Stripe 仪表盘并创建新的 webhook
- 将
YOUR_DOMAIN_NAME/api/stripe/webhooks
粘贴为 "Webhook Endpoint URL" - 选择要广播的事件类型
- 将生成的密钥粘贴到
.env
文件中,命名为STRIPE_WEBHOOKS_ENDPOINT_SECRET
- 然后,使用插件配置中的
webhooks
部分来处理这些事件:
import { buildConfig } from 'payload'
import stripePlugin from '@payloadcms/plugin-stripe'
const config = buildConfig({
plugins: [
stripePlugin({
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
stripeWebhooksEndpointSecret: process.env.STRIPE_WEBHOOKS_ENDPOINT_SECRET,
webhooks: {
'customer.subscription.updated': ({ event, stripe, stripeConfig }) => {
// 执行操作...
},
},
// 注意:你也可以捕获所有 Stripe webhook 事件并自行处理事件类型
// webhooks: (event, stripe, stripeConfig) => {
// switch (event.type): {
// case 'customer.subscription.updated': {
// // 执行操作...
// break;
// }
// default: {
// break;
// }
// }
// }
}),
],
})
export default config
完整的可用 webhooks 列表,请参见此处。
Node 节点
在服务器端,你应该直接使用 stripe npm 模块与 Stripe 交互。代码示例如下:
import Stripe from 'stripe'
const stripeSecretKey = process.env.STRIPE_SECRET_KEY
const stripe = new Stripe(stripeSecretKey, {
apiVersion: '2022-08-01',
})
export const MyFunction = async () => {
try {
const customer = await stripe.customers.create({
email: data.email,
})
// 执行操作...
} catch (error) {
console.error(error.message)
}
}
或者,你也可以使用 stripeProxy
与 Stripe 交互,这正是 /api/stripe/rest
端点内部实现的方式。以下是相同的示例,但通过代理实现:
import { stripeProxy } from '@payloadcms/plugin-stripe'
export const MyFunction = async () => {
try {
const customer = await stripeProxy({
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
stripeMethod: 'customers.create',
stripeArgs: [
{
email: data.email,
},
],
})
if (customer.status === 200) {
// 执行操作...
}
if (customer.status >= 400) {
throw new Error(customer.message)
}
} catch (error) {
console.error(error.message)
}
}
同步功能
此选项会自动在 Payload 集合和 Stripe 资源之间建立基本同步。它会创建所有必要的钩子和 webhook 处理器,你唯一需要做的就是将 Payload 字段映射到对应的 Stripe 属性。当文档在 Stripe 或 Payload 中被创建、更新或删除时,变更会在两端同步反映。
注意:
如需启用双向同步,请确保设置 webhooks
并在配置中传入
stripeWebhooksEndpointSecret
。
import { buildConfig } from 'payload'
import stripePlugin from '@payloadcms/plugin-stripe'
const config = buildConfig({
plugins: [
stripePlugin({
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
stripeWebhooksEndpointSecret: process.env.STRIPE_WEBHOOKS_ENDPOINT_SECRET,
sync: [
{
collection: 'customers',
stripeResourceType: 'customers',
stripeResourceTypeSingular: 'customer',
fields: [
{
fieldPath: 'name', // 这是你 Payload 配置中的字段
stripeProperty: 'name', // 如需访问嵌套属性,可使用点表示法
},
],
},
],
}),
],
})
export default config
注意:
由于 Stripe API 的限制,目前仅支持顶级字段的同步。这是因为每个 Stripe 对象都是独立实体, 难以抽象成简单的可重用库。未来我们可能会找到解决方案,但目前这类情况需要硬编码处理。
使用 sync
功能将实现以下效果:
- 在每个集合上添加并维护一个
stripeID
只读字段,该字段由 Stripe 生成并用作交叉引用 - 添加直接跳转到 Stripe.com 资源页面的链接
- 在每个集合上添加并维护一个
skipSync
只读标志,防止钩子触发 webhook 时产生无限循环同步 - 为每个集合添加以下钩子:
beforeValidate
:createNewInStripe
beforeChange
:syncExistingWithStripe
afterDelete
:deleteFromStripe
- 处理以下 Stripe webhook 事件:
STRIPE_TYPE.created
:handleCreatedOrUpdated
STRIPE_TYPE.updated
:handleCreatedOrUpdated
STRIPE_TYPE.deleted
:handleDeleted
TypeScript
所有类型都可以直接导入:
import {
StripeConfig,
StripeWebhookHandler,
StripeProxy,
...
} from '@payloadcms/plugin-stripe/types';