craftcms / stripe
Craft CMS 的 Stripe 集成
Requires
- php: ^8.2
- craftcms/cms: ^5.1.0
- stripe/stripe-php: ^13|^14
Requires (Dev)
- craftcms/commerce: ^5.0.0-beta.3
- craftcms/ecs: dev-main
- craftcms/phpstan: dev-main
- craftcms/rector: dev-main
- vlucas/phpdotenv: ^5.4.1
This package is auto-updated.
Last update: 2024-09-19 16:20:40 UTC
README
将您的 Craft 内容连接到 Stripe 强大的计费工具,并构建一个流畅的店面。
要求
此插件需要 Craft CMS 5.1.0 或更高版本,以及具有访问开发者功能的 Stripe 账户。
提示
从 Craft Commerce 转换?请查看专门的迁移部分。
安装
插件商店
访问您的安装控制面板的 插件商店 屏幕,然后搜索 Stripe。
点击 安装 按钮,然后查看配置说明!
Composer
这些说明假设您正在使用 DDEV,但在其他环境中也可以运行类似命令。打开终端并运行...
# Navigate to the project directory: cd /path/to/my-project # Require the plugin with Composer: ddev composer require craftcms/stripe # Install the plugin with Craft: ddev craft plugin/install stripe
配置
Stripe 插件从三个来源构建其配置
- 项目配置 — 通过 Craft 控制面板中的“Stripe”→“设置”屏幕进行管理。
- 插件配置文件 — 将
config/stripe.php
文件添加到您的项目中,并返回一个以craft\stripe\models\Settings
类属性为键的选项映射。 - 环境变量 — 一些选项可以直接作为环境变量设置。
API 密钥
Stripe 使用一对“可发布”和“秘密”密钥与他们的 API 通信。在您的 Stripe 账户中,切换到 测试模式,然后访问 开发者 部分,并获取您的开发密钥。
注意
有关 Stripe API 密钥 的更多信息。
将这些密钥添加到您的项目的 .env
文件中
STRIPE_PUBLISHABLE_KEY="pk_test_************************" STRIPE_SECRET_KEY="sk_test_************************"
然后,在控制面板中,访问 Stripe → 设置,并在相应的字段中输入您选择的变量名称。Craft 将根据其在环境中的发现提供建议。
Webhooks
Webhooks 对于插件正确工作至关重要 — 它们允许将产品数据和非平台客户活动快速同步到您的站点。
提示
请确保执行初始的 同步 以导入现有的 Stripe 数据。
要在本地开发环境中测试 webhooks,我们建议使用 Stripe CLI 创建隧道并转发事件。按照您平台上的安装说明进行操作,然后运行
stripe listen --forward-to https://my-craft-project.ddev.site/stripe/webhooks/handle
注意
此处提供的主机名应与您访问项目的本地方式一致 — Stripe 不需要在公共互联网上解析它以测试 webhook 的投递!
CLI 工具将在准备就绪时通知您,并输出以 whsec_
开头的 webhook 签名密钥。将此值添加到您的 .env
文件中,然后返回控制面板中的 Stripe → 设置。
同步
Webhook 会在 Stripe 和 Craft 之间同步产品和客户数据,但它们只会报告 更改。
您有两种方式用于初始导入 Stripe 数据
- 小型产品目录通常可以使用控制面板实用工具:访问 实用工具 → Stripe 同步所有,然后点击 同步所有数据。
- 大型目录应通过命令行执行同步:如果您刚开始,请运行
ddev craft stripe/sync/all
,或者使用更细粒度的 CLI 工具 导入特定类型的记录。
内容 + 字段
Stripe 的 产品、价格 和 订阅 都存储在 Craft 中的 元素 中。这意味着它们可以访问您期望的完整内容建模工具套件!
每个元素类型的字段布局在插件的 设置 屏幕中管理。
产品 URL
除了字段布局外,产品元素还支持 URI 格式 和 模板 设置,它们在其他元素类型上的工作方式相同:当请求产品的 URL 时,Craft 会加载元素并将其传递到指定的模板,在 product
变量下。
注意
价格和订阅没有自己的 URL。您可以使用查询参数或 自定义路由 来根据特定的 URI 模式加载这些元素。
从 Craft Commerce 迁移
使用我们功能齐全的电子商务系统 Craft Commerce 的用户可以将现有订阅迁移到独立的 Stripe 插件,而不会丢失任何客户数据。
一旦您已完全升级到 Craft 5.1 和 Craft Commerce 5.0,请按照上述正常的 安装 和 配置 指示进行操作。然后,运行以下这对控制台命令
# Pre-populate plugin tables with existing Stripe data: ddev craft stripe/commerce/migrate # Perform a synchronization to bring in additional records: ddev craft stripe/sync/all
API 变更
由于它们更接近于 Stripe 的计费架构而不是传统的单一物品“计划”,因此您将不同于在 Craft Commerce 中与订阅交互
- 在 Craft 中不配置计划。相反,可以在 Stripe 中将产品(或更准确地说是 价格)设置为 周期性。您将在控制面板中单个产品元素的 价格 表中看到这一点,作为一个价格和间隔的组合(例如 $5.00/天)。
- 一些与网关无关的元素查询方法没有被转换到 Stripe 插件中
dateExpired()
:不作为本地属性跟踪。您可以通过subscription.data.ended_at
访问订阅结束的日期戳。isExpired()
:与上述类似,非过期订阅将有一个null
的subscription.data.ended_at
值。trialDays()
:使用subscription.data.trial_start
和trial_end
,或访问订阅的底层items
数组以获取每个周期性物品的价格和配置信息。status()
:状态可能不会以与 Craft Commerce 中的定义一致的方式表现。
店面
一旦您已通过(通过 同步 和/或 webhook)将 Stripe 的数据填充到 Craft 项目中,您就可以开始构建内容和店面。
提示
参考部分包含关于系统中的各种对象的详细信息。
列出产品
单个产品会自动根据其URI格式获取URL,但如何收集和显示由您决定。
要获取产品列表,请使用craft.stripeProducts
元素查询工厂元素查询
{% set products = craft.stripeProducts.all() %} <ul> {% for product in products %} {% set image = product.featureImage.eagerly().one() %} <li> <figure> {{ image.getImg() }} </figure> <strong>{{ product.getLink() }}</strong> </li> {% endfor %} </ul>
产品模板
在单个产品页面上,Craft通过product
变量提供当前产品
<h1>{{ product.title }}</h1>
您为产品配置的任何自定义字段都将作为属性提供,就像其他元素类型一样
{{ product.customDescriptionField|md }}
价格
与Craft Commerce类似,Stripe使用“产品”作为逻辑分组商品和服务的手段——客户实际购买的东西被称为“价格”。
Stripe插件使用嵌套元素来处理这种关系。每个产品元素将拥有一个或多个价格元素,并通过prices
属性或getPrices()
方法公开它们
<h1>{{ product.title }}</h1> <ul> {% for price in product.prices %} <li> {{ price.data|unitAmount }} {{ tag('a', { text: "Buy now", href: price.getCheckoutUrl( currentUser ?? false, 'shop/thank-you?session={CHECKOUT_SESSION_ID}', product.url, {} ), }) }} </li> {% endfor %} </ul>
结账链接
当客户准备好购买产品或开始订阅时,您将提供结账链接。结账链接是特殊的、安全的、参数化的URL,作为预配置项目列表的一部分存在于结账会话中。Stripe没有所谓的“购物车”;相反,产品是逐个购买的。
点击结账链接会将客户带到Stripe的托管结账页面,在那里他们可以使用账户中可用和启用的任何方法完成付款。
要输出结账链接,请使用stripeCheckoutUrl()
函数
{% set price = product.prices.one() %} {{ tag('a', { href: stripeCheckoutUrl( [ { price: price.stripeId, quantity: 1, }, ], currentUser ?? false, 'shop/thank-you?session={CHECKOUT_SESSION_ID}', product.url, {} ), text: 'Checkout', }) }}
提示
将false
作为stripeCheckoutUrl()
的第二个参数传递允许您创建匿名结账URL。
结账表单
作为生成静态结账链接的替代方案,您可以构建一个表单,该表单将项目列表和其他参数发送到Craft,Craft将动态创建结账会话,然后重定向客户到Stripe托管的结账页面
{% set prices = product.prices.all() %} <form method="post"> {{ csrfInput() }} {{ actionInput('stripe/checkout') }} {{ hiddenInput('successUrl', 'shop/thank-you?session={CHECKOUT_SESSION_ID}'|hash) }} {{ hiddenInput('cancelUrl', 'shop'|hash) }} {% if not currentUser %} {{ hiddenInput('customer', 'false') }} {% endif %} <select name="lineItems[0][price]"> {% for price in prices %} <option value="{{ price.stripeId }}">{{ price.data|unitAmount }}</option> {% endfor %} </select> <input type="text" name="lineItems[0][quantity]" value="1"> <button>Buy now</button> </form>
提示
默认情况下,将使用当前登录用户。
要允许匿名结账,您可以将{{ hiddenInput('customer', 'false') }}
添加到表单中。
账单门户
客户可以通过Stripe的托管账单门户管理他们的订阅和支付方式。您可以使用currentUser.getStripeBillingPortalSessionUrl()
方法生成指向客户账单门户的URL
{{ tag('a', { text: "Billing Portal", href: currentUser.getStripeBillingPortalSessionUrl('shop'), }) }}
该方法接受一个returnUrl
参数,指定客户完成管理订阅和支付方式后要重定向到的URL。
除了此方法外,还有currentUser.getStripeBillingPortalSessionPaymentMethodUpdateUrl()
,该方法为客户端生成一个更新默认支付方式的URL。
{{ tag('a', { text: "Update Payment Method", href: currentUser.getStripeBillingPortalSessionPaymentMethodUpdateUrl('shop'), }) }}
它使用Stripe的流程类型直接链接到支付方式更新屏幕。
元素API
我们的元素API插件与Stripe配合得很好!所有三个插件提供的元素类型(产品、价格和订阅)都可以在您的element-api.php
配置文件中使用
return [ 'endpoints' => [ 'api/products' => function() { return [ 'elementType' => craft\stripe\elements\Product::class, // ... ]; }, ], ];
其他功能
产品字段
创建一个 Stripe 产品 字段,并将其添加到字段布局中,以关联产品元素到系统中其他内容。
直接API访问
该插件公开其Stripe API客户端以供高级使用。在Twig中,您可以通过craft.stripe.api.client
访问它。
{% set client = craft.stripe.api.client %} {% set checkout = client .getService('checkout') .getService('sessions') .retrieve('cs_test_****************************************') %}
在PHP中,您可以像这样进行等效调用
$client = craft\stripe\Plugin::getInstance()->getApi()->getClient(); $checkout = $client ->checkout ->sessions ->retrieve('cs_test_****************************************');
警告
我们无法提供涉及直接使用Stripe API的自定义支持。如果在项目过程中需要访问特定API,请考虑发起讨论!
技巧、故障排除、常见问题解答
我在哪里更改产品的标题?
产品标题和价格与Stripe保持同步,以便在两个空间中轻松识别。
如果您想自定义Craft中的产品名称,请创建一个纯文本字段并将其添加到产品字段布局。Stripe将始终在结账或发票上显示规范标题,因此您需要一种方式让客户能够识别不同的产品——不要为其他商品重复使用产品定义。
我无法创建webhook。
如果Craft无法写入项目根目录下的.env
文件,您可能需要在Stripe仪表板中手动创建webhook,然后将其暴露给环境
STRIPE_WH_ID="we_************************" STRIPE_WH_KEY="whsec_**************************************************************"
警告
在这种情况下,环境变量名称是严格的!
参考
Twig过滤器
该插件提供了四个新的Twig过滤器
unitPrice
—— 接受一个价格元素的Stripedata
数组,并输出其成本和间隔的格式化表达式:每单位10.50英镑/月
pricePerUnit
—— 与上述类似,但仅输出成本组件,不包含间隔:每单位5.00美元 + 20.00美元起价
unitAmount
—— 与上述类似,但仅输出单位组件,例如:每10组13.00美元
。interval
—— 与上述类似,但仅输出间隔组件,例如:一次性
或每月一次
。
在大多数情况下,您将想要使用unitPrice
过滤器,因为它将提供有关价格的完整信息。所有过滤器都应该传递价格的data
属性,它是Stripe的原始价格对象。
{{ price.data|unitPrice }}
CLI
要查看所有可用的控制台命令,请运行ddev craft help
。Stripe插件添加了两个主要命令组
Craft Commerce迁移
将现有的Craft Commerce订阅迁移到与Stripe插件兼容的记录。
ddev craft stripe/commerce/migrate
原子同步
您可以一次性同步所有内容…
ddev craft stripe/sync/all
…或者只同步其中的一部分
- 客户:
ddev craft stripe/sync/customers
- 发票:
ddev craft stripe/sync/invoices
- 支付方式:
ddev craft stripe/sync/payment-methods
- 产品和价格:
ddev craft stripe/sync/products-and-prices
- 订阅:
ddev craft stripe/sync/subscriptions
扩展
同步事件
在同步过程中,Stripe插件会在更新每个产品、价格或订阅元素之前发出事件。同步可能通过CLI、控制面板实用程序或响应webhook来执行。
craft\base\Event::on( craft\stripe\services\Products::class, craft\stripe\services\Products::EVENT_BEFORE_SYNCHRONIZE_PRODUCT, function(craft\stripe\events\StripeProductSyncEvent $event) { // Set a custom field value when a product looks “shippable”: if ($event->source->package_dimensions !== null) { $event->element->setFieldValue('requiresShipping', true); } }, );
您可以将$event->isValid
设置为在同步过程中防止更新持久化。
结账事件
通过监听craft\stripe\services\Checkout::EVENT_BEFORE_START_CHECKOUT_SESSION
事件,在生成Checkout会话时自定义发送到Stripe的参数。craft\stripe\events\CheckoutSessionEvent
将包含一个params
属性,该属性包含即将通过Stripe API客户端发送的请求数据。您可以修改或扩展这些数据以适应您的应用程序——所有处理程序调用后属性的值将原封不动地传递给API客户端
craft\base\Event::on( craft\stripe\services\Checkout::class, craft\stripe\services\Checkout::EVENT_BEFORE_START_CHECKOUT_SESSION, function(craft\stripe\events\CheckoutSessionEvent $event) { // Add metadata if the customer is a logged-in “member”: $currentUser = Craft::$app->getUser()->getIdentity(); // Nothing to do: if (!$currentUser) { return; } if ($currentUser->isInGroup('members')) { // Memoize + assign values: $data = $event->params; $data['metadata']['is_member'] = true; // Set back onto the event: $event->params = $data; } }, );