Laravel Laravel

以下是翻译后的版本:

  • 序章

    • 发布说明
    • 升级指南
    • 贡献指南
  • 快速开始

    • 安装
    • 配置
    • 目录结构
    • 前端
    • 入门套件
    • 部署
  • 架构概念

    • 请求生命周期
    • 服务容器
    • 服务提供者
    • 门面
  • 基础

    • 路由
    • 中间件
    • CSRF 保护
    • 控制器
    • 请求
    • 响应
    • 视图
    • Blade 模板
    • 资源打包
    • URL 生成
    • 会话
    • 验证
    • 错误处理
    • 日志记录
  • 深入探讨

    • Artisan 控制台
    • 广播
    • 缓存
    • 集合
    • 并发
    • 上下文
    • 契约
    • 事件
    • 文件存储
    • 辅助函数
    • HTTP 客户端
    • 本地化
    • 邮件
    • 通知
    • 包开发
    • 进程
    • 队列
    • 速率限制
    • 字符串
    • 任务调度
  • 安全

    • 认证
    • 授权
    • 邮件验证
    • 加密
    • 哈希
    • 密码重置
  • 数据库

    • 快速开始
    • 查询构建器
    • 分页
    • 迁移
    • 数据填充
    • Redis
    • MongoDB
  • Eloquent ORM

    • 快速开始
    • 关系
    • 集合
    • 变换器/类型转换
    • API 资源
    • 序列化
    • 工厂
  • 测试

    • 快速开始
    • HTTP 测试
    • 控制台测试
    • 浏览器测试
    • 数据库测试
    • Mock 测试
  • 软件包

    • Breeze
    • Cashier(Stripe)
    • Cashier(Paddle)
    • Dusk
    • Envoy
    • Fortify
    • Folio
    • Homestead
    • Horizon
    • Jetstream
    • Mix
    • Octane
    • Passport
    • Pennant
    • Pint
    • Precognition
    • Prompts
    • Pulse
    • Reverb
    • Sail
    • Sanctum
    • Scout
    • Socialite
    • Telescope
    • Valet
  • API 文档
Icon

提示 您正在浏览旧版本的 Laravel 的文档. 请考虑将你的项目升级到 Laravel 11.x.

交易工具包 (Stripe)
10.x
10.x 9.x 8.5 8.x 7.x 6.x 5.8 5.7 5.6 5.5 5.4 5.3 5.2 5.1

Laravel 10 中文文档 /

未匹配的标注

Laravel Cashier (Stripe)

  • 简介
  • 升级 Cashier
  • 安装
    • 数据库迁移
  • 配置信息
    • 计费模型
    • API 密钥
    • 货币配置
    • 税务配置
    • 日志
    • 使用自定义模型
  • 消费者
    • 获取消费者
    • 创建消费者
    • 更新消费者
    • 余额
    • 税号
    • 使用 Stripe 同步客户数据
    • 计费门户
  • 支付方式
    • 存储支付方式
    • 检索支付方式
    • 判断用户是否有支付方式
    • 更新默认支付方式
    • 添加支付方式
    • 删除支付方式
  • 订阅内容
    • 创建订阅
    • 检查订阅状态
    • 修改价格
    • 订阅数量
    • 多个产品的订阅
    • 多方案订阅计划
    • 计量计费
    • 订阅税
    • 订阅锚定日期
    • 取消订阅
    • 恢复订阅
  • 订阅试用
    • 预先使用付款方式
    • 没有预先付款方式
    • 延长试用期
  • 处理 Stripe Webhooks
    • 定义 Webhook 事件处理器
    • 验证 Webhook 签名
  • 单次收费
    • 基本使用
    • 带发票的支付
    • 创建支付意向
    • 退款
  • 结账
    • 产品结账
    • 单次支付结账
    • 订阅结账
    • 收集税号
    • 访客结账
  • 发票
    • 获取发票
    • 即将发布的发票
    • 预览订阅发票
    • 生成发票 PDF
  • 处理支付失败
  • 强大的客户身份验证 (SCA)
    • 需要额外确认的支付
    • 非会话支付通知
  • Stripe SDK
  • 测试

简介

Laravel Cashier Stripe 为 Stripe 的订阅计费服务提供了一个富有表现力、流畅的接口。它处理了几乎所有你害怕编写的订阅计费样板代码。除了基本的订阅管理,Cashier 还可以处理优惠券、交换订阅、订阅 「数量」、取消宽限期,甚至生成发票 PDF。

升级 Cashier

升级到新版本的 Cashier 时,请务必仔细阅读升级指南。

注意
为了防止破坏性变更,Cashier 使用固定的 Stripe API 版本。 Cashier 14 使用 Stripe API 版本 2022-11-15 。Stripe API 版本将在次要版本上更新,以利用新的 Stripe 功能和改进。

安装

首先,使用 Composer 为 Stripe 安装 Cashier 扩展包:

composer require laravel/cashier

注意
为确保 Cashier 正确处理所有 Stripe 事件,请记得设置 Cashier 的 webhook。

数据库迁移

Cashier 的服务提供器注册了自己的数据库迁移目录,因此请记住在安装此包后迁移数据库。Cashier 迁移将向 users 表中添加多个列,并创建一个新的 subscriptions 表来保存客户的所有订阅:

php artisan migrate

如果需要覆盖 Cashier 附带的迁移,可以使用 vendor:publish Artisan 命令发布它们:

php artisan vendor:publish --tag="cashier-migrations"

如果你想阻止 Cashier 的迁移完全运行,可以使用 Cashier 提供的ignoreMigrations 方法。通常应在 AppServiceProvider 类的 register 方法中调用此方法:

use Laravel\Cashier\Cashier;

/**
 * 注册任何应用程序服务。
 *
 */
public function register(): void
{
    Cashier::ignoreMigrations();
}

注意
Stripe 建议用于存储 Stripe 标识符的任何列都应区分大小写。因此,在使用 MySQL 时,应该确保将 stripe_id 列排序规则设置为 utf8_bin 。更多关于这方面的信息可以在 Stripe 文档中找到。

配置

订单模型

在使用 Cashier 之前,需要将 Billable trait 添加到可订单模型定义中。通常会放在 App\Models\User 模型中。这个特性提供了多个方法以便执行常用支付任务,如创建订阅、应用优惠券和更新支付方法信息:

use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}

Cashier 默认假设你的 Billable 模型是 Laravel 自带的 App\Models\User 类。如果需要修改可以在 useCustomerModel 方法定义一个不同的模型。通常此方法在 AppServiceProvider 类的boot方法中被调用:

use App\Models\Cashier\User;
use Laravel\Cashier\Cashier;

/**
 * 引导任何应用程序服务。
 */
public function boot(): void
{
    Cashier::useCustomerModel(User::class);
}

注意
如果你使用的不是 Laravel 自带的 App\Models\User 模型,需要发布并修改默认的 Cashier 迁移文件以匹配你使用模型对应的表名。

API 秘钥

接下来需要在 .env 文件中配置 Stripe 秘钥,可以在 Stripe 后台控制面板中获取Stripe API 秘钥:

STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret

注意
你应该确保在应用程序的.env文件中定义了STRIPE_WEBHOOK_SECRET环境变量,因为该变量用于确保传入的 Webhook 确实来自 Stripe。

货币配置

Cashier 默认货币是美元 (USD),可以在 .env 中设置 CASHIER_CURRENCY 环境变量来修改默认的货币配置:

CASHIER_CURRENCY=eur

除了配置 Cashier 的货币之外,还可以在格式化用于显示在发票上的金额时指定本地化配置。在底层,Cashier 使用了 PHP 的 NumberFormatter 类来设置本地货币:

CASHIER_CURRENCY_LOCALE=nl_BE

注意
为了使用本地化配置而不是 en,需要确保安装了 PHP ext-intl PHP 扩展并在服务器上启用配置。

税务配置

感谢Stripe 税务,可以自动计算 Stripe 生成的所有发票的税费。 可以通过应用程序的 App\Providers\AppServiceProvider类的 boot 方法中调用 calculateTaxes 来启用自动税务计算:

use Laravel\Cashier\Cashier;

/**
 * 引导任何应用程序服务。
 */
public function boot(): void
{
    Cashier::calculateTaxes();
}

启动税务计算后,任何新订阅和生成的一次性发票都会进行自动税务计算。

为了使这个功能正常使用,客户的账单明细中例如客户姓名、住址、发票 ID 需要同步到 Stripe。你可以使用 Cashier 提供的客户数据同步和 Tax ID 方法来完成此操作。

注意
对于单次收费或单次支付结账,不支持计算税费。

日志

Cashier 允许你指定日志通道来记录所有与 Stripe 相关的异常。可以通过在 .env 中配置 CASHIER_LOGGER 来指定:

CASHIER_LOGGER=stack

对 Stripe 的 API 调用生成的异常将通过应用程序的默认日志通道记录。

使用自定义模型

你可以通过定义自己的模型并扩展相应的 Cashier 模型来自由扩展 Cashier 内部的模型,增加一些方法:

use Laravel\Cashier\Subscription as CashierSubscription;

class Subscription extends CashierSubscription
{
    // ...
}

定义模型后,可以通过 Laravel\Cashier\Cashier 类配置 Cashier 使用自定义的模型。通常还需要在 App\Providers\AppServiceProvider 类的 boot 中注册一下:

use App\Models\Cashier\Subscription;
use App\Models\Cashier\SubscriptionItem;

/**
 * 引导任何应用程序服务。
 */
public function boot(): void
{
    Cashier::useSubscriptionModel(Subscription::class);
    Cashier::useSubscriptionItemModel(SubscriptionItem::class);
}

消费者

获取消费者

你可以使用 Cashier::findBillable 方法通过 Stripe ID 查询消费者信息。该方法返回的是一个 billable 模型实例:

use Laravel\Cashier\Cashier;

$user = Cashier::findBillable($stripeId);

创建客户

有时,你可能希望在不开始订阅的情况下创建一个 Stripe 客户。 你可以使用 createAsStripeCustomer 方法完成此操作:

$stripeCustomer = $user->createAsStripeCustomer();

在 Stripe 中创建客户后,你可以在以后开始订阅。 你可以提供一个可选的 $options 数组来传递任何额外的 Stripe API 支持的客户创建参数:

$stripeCustomer = $user->createAsStripeCustomer($options);

如果你想为计费模型返回 Stripe 客户对象,你可以使用 asStripeCustomer 方法:

$stripeCustomer = $user->asStripeCustomer();

如果你想为给定的计费模型检索 Stripe 客户对象,但不确定该计费模型是否已经是 Stripe 中的客户,则可以使用 createOrGetStripeCustomer 方法。 如果尚不存在,此方法将在 Stripe 中创建一个新客户:

$stripeCustomer = $user->createOrGetStripeCustomer();

更新客户

有时,你可能希望直接向 Stripe 客户更新其他信息。 你可以使用 updateStripeCustomer 方法完成此操作。 此方法接受一组 Stripe API 支持的客户更新选项:

$stripeCustomer = $user->updateStripeCustomer($options);

余额

Stripe 允许你贷记或借记客户的「余额」。 稍后,此余额将在新发票上贷记或借记。 要检查客户的总余额,你可以使用计费模型上提供的「余额」方法。 balance 方法将返回以客户货币表示的余额的格式化字符串表示形式:

$balance = $user->balance();

要记入客户的余额,可以为该 creditBalance 方法提供一个值。如果你愿意,还可以提供描述:

$user->creditBalance(500, 'Premium customer top-up.');

为该方法提供一个值 debitBalance 将从客户的余额中扣除:

$user->debitBalance(300, 'Bad usage penalty.');

applyBalance 方法会创建一条客户余额流水记录。可以通过调用 balanceTransactions 方法获取余额交易记录,这有助于提供借记或贷记记录给客户查看:

// 检索所有交易...
$transactions = $user->balanceTransactions();

foreach ($transactions as $transaction) {
    // 交易量...
    $amount = $transaction->amount(); // $2.31

    // 在可用的情况下检索相关发票...
    $invoice = $transaction->invoice();
}

税号

Cashier 提供了一种管理客户税号的简便方法。taxIds 例如,taxIds 方法可用于检索作为集合分配给客户的所有税号:

$taxIds = $user->taxIds();

你还可以通过标识符检索客户的特定税号:

$taxId = $user->findTaxId('txi_belgium');

你可以通过向 createTaxId 方法提供有效的 type 和值来创建新的税号:

$taxId = $user->createTaxId('eu_vat', 'BE0123456789');

createTaxId 方法将立即将增值税 ID 添加到客户的帐户中。 增值税 ID 的验证也由 Stripe 完成; 然而,这是一个异步的过程。 你可以通过订阅 customer.tax_id.updated webhook 事件并检查 增值税 ID verification 参数。 有关处理 webhook 的更多信息,请参阅有关定义 webhook 处理程序的文档。

你可以使用 deleteTaxId 方法删除税号:

$user->deleteTaxId('txi_belgium');

使用 Stripe 同步客户数据

通常,当你的应用程序的用户更新他们的姓名、电子邮件地址或其他也由 Stripe 存储的信息时,你应该通知 Stripe 更新。 这样一来,Stripe 的信息副本将与你的应用程序同步。

要自动执行此操作,你可以在计费模型上定义一个事件侦听器,以响应模型的updated 事件。然后,在你的事件监听器中,你可以在模型上调用 syncStripeCustomerDetails 方法:

use App\Models\User;
use function Illuminate\Events\queueable;

/**
 * 模型的「引导」方法。
 */
protected static function booted(): void
{
    static::updated(queueable(function (User $customer) {
        if ($customer->hasStripeId()) {
            $customer->syncStripeCustomerDetails();
        }
    }));
}

现在,每次更新你的客户模型时,其信息都会与 Stripe 同步。 为方便起见,Cashier 会在初始创建客户时自动将你客户的信息与 Stripe 同步。

你可以通过覆盖 Cashier 提供的各种方法来自定义用于将客户信息同步到 Stripe 的列。 例如,当 Cashier 将客户信息同步到 Stripe 时,你可以重写 stripeName 方法来自定义应该被视为客户「姓名」的属性:

/**
 * 获取应同步到 Stripe 的客户名称。
 */
public function stripeName(): string|null
{
    return $this->company_name;
}

同样,你可以复写 stripeEmail、stripePhone 和 stripeAddress 方法。 当更新 Stripe 客户对象时,这些方法会将信息同步到其相应的客户参数。 如果你希望完全控制客户信息同步过程,你可以复写 syncStripeCustomerDetails 方法。

订单入口

Stripe 提供了一个简单的方式来设置订单入口以便用户可以管理订阅、支付方法、以及查看历史账单。你可以在控制器或路由中使用 redirectToBillingPortal 方法将用户重定向到账单入口:

use Illuminate\Http\Request;

Route::get('/billing-portal', function (Request $request) {
    return $request->user()->redirectToBillingPortal();
});

默认情况下,当用户完成对订阅的管理后,会将能够通过 Stripe 计费门户中的链接返回到应用的 home 路由,你可以通过传递 URL 作为 redirectToBillingPortal 方法的参数来自定义用户返回的 URL:

use Illuminate\Http\Request;

Route::get('/billing-portal', function (Request $request) {
    return $request->user()->redirectToBillingPortal(route('billing'));
});

如果你只想要生成订单入口的 URL,可以使用 billingPortalUrl 方法:

$url = $request->user()->billingPortalUrl(route('billing'));

支付方式

存储支付方式

为了使用 Stripe 创建订阅或者进行「一次性」支付,你需要存储支付方法并从 Stripe 中获取对应的标识符。这种方式可用于实现你是否计划使用这个支付方法进行订阅还是单次收费,下面我们分别来介绍这两种方法。

订阅付款方式

当存储客户的信用卡信息以备将来订阅使用时,必须使用 Stripe「Setup Intents」API 来安全地收集客户的支付方式详细信息。 「Setup Intent」向 Stripe 指示向客户的付款方式收费的目的。 Cashier 的 Billable 特性包括 createSetupIntent 方法,可轻松创建新的设置目的。 你应该从将呈现收集客户付款方式详细信息的表单的路由或控制器调用此方法:

return view('update-payment-method', [
    'intent' => $user->createSetupIntent()
]);

创建设置目的并将其传递给视图后,你应该将其秘密附加到将收集付款方式的元素。 例如,考虑这个「更新付款方式」表单:

<input id="card-holder-name" type="text">

<!-- Stripe 元素占位符 -->
<div id="card-element"></div>

<button id="card-button" data-secret="{{ $intent->client_secret }}">
    更新付款方式
</button>

接下来,可以使用 Stripe.js 库将 Stripe 元素 附加到表单并安全地收集客户的付款详细信息:

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

接下来,可以验证卡并使用 Stripe 的 confirmCardSetup 方法从 Stripe 检索安全的「支付方式标识符」:

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;

cardButton.addEventListener('click', async (e) => {
    const { setupIntent, error } = await stripe.confirmCardSetup(
        clientSecret, {
            payment_method: {
                card: cardElement,
                billing_details: { name: cardHolderName.value }
            }
        }
    );

    if (error) {
        // 向用户显示「error.message」...
    } else {
        // 卡已验证成功...
    }
});

订阅付款方式

存储客户的银行卡信息以备将来订阅时使用,必须使用 Stripe「Setup Intents」API 来安全地收集客户的支付方式详细信息。 「设置意图」 向Stripe 指示向客户的付款方式收费的目的。 Cashier 的 Billable 特性包括 createSetupIntent 方法,可轻松创建新的设置意图。你应该从路由或控制器调用此方法,该路由或控制器将呈现收集客户付款方法详细信息的表单:

return view('update-payment-method', [
    'intent' => $user->createSetupIntent()
]);

创建设置意图并将其传递给视图后,你应该将其秘密附加到将收集付款方式的元素。 例如,考虑这个「更新付款方式」表单:

<input id="card-holder-name" type="text">

<!-- Stripe 元素占位符 -->
<div id="card-element"></div>

<button id="card-button" data-secret="{{ $intent->client_secret }}">
    更新付款方式
</button>

接下来,可以使用 Stripe.js 库将 Stripe 元素附加到表单并安全地收集客户的付款详细信息:

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

接下来,可以从 Stripe 搜索安全的「支付方式标识符」验证银行卡并使用 Stripe 的 confirmCardSetup 方法:

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;

cardButton.addEventListener('click', async (e) => {
    const { setupIntent, error } = await stripe.confirmCardSetup(
        clientSecret, {
            payment_method: {
                card: cardElement,
                billing_details: { name: cardHolderName.value }
            }
        }
    );

    if (error) {
        // 向用户显示「error.message」...
    } else {
        // 该银行卡已验证成功...
    }
});

银行卡通过 Stripe 验证后,你可以将生成的 setupIntent.payment_method 标识符传递给你的 Laravel 应用程序,在那里它可以附加到客户。 付款方式可以添加为新付款方式或用于更新默认付款方式。 你还可以立即使用付款方式标识符来创建新订阅。

技巧
如果你想了解有关设置目的和收集客户付款详细信息的更多信息,请查看 Stripe 提供的概述。

单笔费用的支付方式

当然,在针对客户的支付方式进行单笔收费时,我们只需要使用一次支付方式标识符。 由于 Stripe 的限制,你不能使用客户存储的默认付款方式进行单笔收费。 你必须允许客户使用 Stripe.js 库输入他们的付款方式详细信息。 例如,考虑以下形式:

<input id="card-holder-name" type="text">

<!-- Stripe 元素占位符 -->
<div id="card-element"></div>

<button id="card-button">
    处理付款
</button>

定义这样的表单后,可以使用 Stripe.js 库将Stripe 元素附加到表单并安全地收集客户的付款详细信息:

<script src="https://js.stripe.com/v3/"></script>

<script>
    const stripe = Stripe('stripe-public-key');

    const elements = stripe.elements();
    const cardElement = elements.create('card');

    cardElement.mount('#card-element');
</script>

接下来,可以验证卡并使用 Stripe 的 createPaymentMethod 方法 从 Stripe 检索安全的「支付方式标识符」-方法):

const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');

cardButton.addEventListener('click', async (e) => {
    const { paymentMethod, error } = await stripe.createPaymentMethod(
        'card', cardElement, {
            billing_details: { name: cardHolderName.value }
        }
    );

    if (error) {
        // 向用户显示「error.message」...
    } else {
        // 卡已验证成功...
    }
});

银行卡通过 Stripe 验证后,你可以将生成的 setupIntent.payment_method 标识符传递给你的 Laravel 应用程序,在那里它可以附加到客户。付款方式可以添加为新付款方式或用于更新默认付款方式。 你还可以立即使用付款方式标识符来创建新订阅。

笔记
如果你想了解有关设置目的和收集客户付款详细信息的更多信息,请查看 Stripe 提供的概述.

单笔费用的支付方式

当然,在针对客户的支付方式进行单笔收费时,我们只需要使用一次支付方式标识符。 由于 Stripe 的限制,你不能使用客户存储的默认付款方式进行单笔收费。 你必须允许客户使用 Stripe.js 库输入他们的付款方式详细信息。 例如,考虑以下形式:

<input id="card-holder-name" type="text