Skip to content

Laravel Cashier

介绍

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

配置

Composer

首先,将 Cashier 包添加到你的 composer.json 文件中并运行 composer update 命令:

php
"laravel/cashier": "~5.0" (适用于 Stripe SDK ~2.0 2015-02-18 及以后的 Stripe API)
"laravel/cashier": "~4.0" (适用于 2015-02-18 及以后的 Stripe API)
"laravel/cashier": "~3.0" (适用于 2015-02-16 及以前的 Stripe API)

服务提供者

接下来,在你的 app 配置文件中注册 Laravel\Cashier\CashierServiceProvider 服务提供者

迁移

在使用 Cashier 之前,我们需要向数据库添加几个列。别担心,你可以使用 cashier:table Artisan 命令创建一个迁移来添加必要的列。例如,要向用户表添加列,请运行命令:php artisan cashier:table users

创建迁移后,只需运行 migrate 命令。

模型设置

接下来,将 Billable trait 和适当的日期变换器添加到你的模型定义中:

php
use Laravel\Cashier\Billable;
use Laravel\Cashier\Contracts\Billable as BillableContract;

class User extends Model implements BillableContract
{
    use Billable;

    protected $dates = ['trial_ends_at', 'subscription_ends_at'];
}

将列添加到模型的 $dates 属性中将指示 Eloquent 返回这些列为 Carbon / DateTime 实例而不是原始字符串。

Stripe 密钥

最后,在你的 services.php 配置文件中设置你的 Stripe 密钥:

php
'stripe' => [
    'model'  => 'User',
    'secret' => env('STRIPE_API_SECRET'),
],

订阅

创建订阅

要创建订阅,首先检索你的可计费模型的实例,通常是 App\User 的实例。一旦检索到模型实例,你可以使用 subscription 方法来管理模型的订阅:

php
$user = User::find(1);

$user->subscription('monthly')->create($creditCardToken);

create 方法将自动创建 Stripe 订阅,并更新你的数据库中的 Stripe 客户 ID 和其他相关的计费信息。如果你的计划在 Stripe 中配置了试用期,试用期结束日期也将自动设置在用户记录上。

如果你想实现试用期,但完全在你的应用程序中管理试用期而不是在 Stripe 中定义它们,你必须手动设置试用期结束日期:

php
$user->trial_ends_at = Carbon::now()->addDays(14);

$user->save();

额外的用户详情

如果你想指定额外的客户详情,可以通过将它们作为 create 方法的第二个参数传递:

php
$user->subscription('monthly')->create($creditCardToken, [
    'email' => $email, 'description' => '我们的第一个客户'
]);

要了解 Stripe 支持的其他字段,请查看 Stripe 的客户创建文档

优惠券

如果你想在创建订阅时应用优惠券,可以使用 withCoupon 方法:

php
$user->subscription('monthly')
     ->withCoupon('code')
     ->create($creditCardToken);

检查订阅状态

一旦用户订阅了你的应用程序,你可以使用各种方便的方法轻松检查他们的订阅状态。首先,subscribed 方法返回 true 如果用户有一个活跃的订阅,即使订阅当前在其试用期内:

php
if ($user->subscribed()) {
    //
}

subscribed 方法也可以作为一个路由中间件的好候选者,允许你根据用户的订阅状态过滤对路由和控制器的访问:

php
public function handle($request, Closure $next)
{
    if ($request->user() && ! $request->user()->subscribed()) {
        // 这个用户不是付费客户...
        return redirect('billing');
    }

    return $next($request);
}

如果你想确定用户是否仍在他们的试用期内,可以使用 onTrial 方法。此方法可用于向用户显示警告,告知他们仍在试用期内:

php
if ($user->onTrial()) {
    //
}

onPlan 方法可用于确定用户是否订阅了给定计划,基于其 Stripe ID:

php
if ($user->onPlan('monthly')) {
    //
}

取消的订阅状态

要确定用户是否曾经是活跃的订阅者,但已取消他们的订阅,可以使用 cancelled 方法:

php
if ($user->cancelled()) {
    //
}

你还可以确定用户是否已取消他们的订阅,但仍在他们的“宽限期”内,直到订阅完全过期。例如,如果用户在 3 月 5 日取消订阅,而订阅原定于 3 月 10 日到期,则用户在 3 月 10 日之前处于“宽限期”。请注意,在此期间 subscribed 方法仍返回 true

php
if ($user->onGracePeriod()) {
    //
}

everSubscribed 方法可用于确定用户是否曾经在你的应用程序中订阅过计划:

php
if ($user->everSubscribed()) {
    //
}

更改计划

在用户订阅你的应用程序后,他们可能偶尔想要更改为新的订阅计划。要将用户切换到新的订阅,请使用 swap 方法。例如,我们可以轻松地将用户切换到 premium 订阅:

php
$user = App\User::find(1);

$user->subscription('premium')->swap();

如果用户在试用期内,试用期将保持不变。此外,如果订阅存在“数量”,该数量也将保持不变。在更换计划时,你还可以使用 prorate 方法指示费用应按比例分配。此外,你可以使用 swapAndInvoice 方法立即为用户更换计划开具发票:

php
$user->subscription('premium')
            ->prorate()
            ->swapAndInvoice();

订阅数量

有时订阅会受到“数量”的影响。例如,你的应用程序可能会按每个帐户每月每用户收费 $10。要轻松增加或减少你的订阅数量,请使用 incrementdecrement 方法:

php
$user = User::find(1);

$user->subscription()->increment();

// 在订阅的当前数量上增加五个...
$user->subscription()->increment(5);

$user->subscription()->decrement();

// 在订阅的当前数量上减少五个...
$user->subscription()->decrement(5);

或者,你可以使用 updateQuantity 方法设置特定数量:

php
$user->subscription()->updateQuantity(10);

有关订阅数量的更多信息,请查阅 Stripe 文档

订阅税

使用 Cashier,可以轻松提供发送到 Stripe 的 tax_percent 值。要指定用户在订阅上支付的税率,请在你的可计费模型上实现 getTaxPercent 方法,并返回一个介于 0 和 100 之间的数值,最多保留两位小数。

php
public function getTaxPercent() {
    return 20;
}

这使你能够在模型基础上应用税率,这对于跨多个国家的用户群可能很有帮助。

取消订阅

要取消订阅,只需在用户的订阅上调用 cancel 方法:

php
$user->subscription()->cancel();

当订阅被取消时,Cashier 将自动在你的数据库中设置 subscription_ends_at 列。此列用于知道何时 subscribed 方法应开始返回 false。例如,如果客户在 3 月 1 日取消订阅,但订阅原定于 3 月 5 日结束,则 subscribed 方法将继续返回 true 直到 3 月 5 日。

你可以使用 onGracePeriod 方法确定用户是否已取消他们的订阅但仍在他们的“宽限期”内:

php
if ($user->onGracePeriod()) {
    //
}

恢复订阅

如果用户已取消他们的订阅并且你希望恢复它,请使用 resume 方法:

php
$user->subscription('monthly')->resume($creditCardToken);

如果用户取消订阅,然后在订阅完全过期之前恢复该订阅,他们将不会立即被计费。相反,他们的订阅将被重新激活,并且他们将在原始计费周期中被计费。

处理 Stripe Webhooks

失败的订阅

如果客户的信用卡过期怎么办?不用担心 - Cashier 包含一个 Webhook 控制器,可以轻松地为你取消客户的订阅。只需将一个路由指向控制器:

php
Route::post('stripe/webhook', '\Laravel\Cashier\WebhookController@handleWebhook');

就是这样!失败的付款将由控制器捕获和处理。当 Stripe 确定订阅失败时(通常在三次付款失败后),控制器将取消客户的订阅。别忘了:你需要在 Stripe 控制面板设置中配置 webhook URI。

由于 Stripe webhooks 需要绕过 Laravel 的 CSRF 验证,请确保在你的 VerifyCsrfToken 中间件中将 URI 列为例外:

php
protected $except = [
    'stripe/*',
];

其他 Webhooks

如果你有其他想要处理的 Stripe webhook 事件,只需扩展 Webhook 控制器。你的方法名称应符合 Cashier 的预期约定,具体来说,方法应以 handle 和你想要处理的 Stripe webhook 的“驼峰式”名称为前缀。例如,如果你想处理 invoice.payment_succeeded webhook,你应该在控制器中添加一个 handleInvoicePaymentSucceeded 方法。

php
<?php

namespace App\Http\Controllers;

use Laravel\Cashier\WebhookController as BaseController;

class WebhookController extends BaseController
{
    /**
     * 处理一个 stripe webhook。
     *
     * @param  array  $payload
     * @return Response
     */
    public function handleInvoicePaymentSucceeded($payload)
    {
        // 处理事件
    }
}

单次收费

如果你想对订阅客户的信用卡进行“一次性”收费,可以在可计费模型实例上使用 charge 方法。charge 方法接受你想要收费的金额,以你的应用程序使用的货币的最低单位表示。因此,下面的示例将对用户的信用卡收费 100 美分,即 $1.00:

php
$user->charge(100);

charge 方法接受一个数组作为其第二个参数,允许你将任何选项传递给底层的 Stripe 收费创建:

php
$user->charge(100, [
    'source' => $token,
    'receipt_email' => $user->email,
]);

如果收费失败,charge 方法将返回 false。这通常表示收费被拒绝:

php
if ( ! $user->charge(100)) {
    // 收费被拒绝...
}

如果收费成功,完整的 Stripe 响应将从方法中返回。

发票

你可以使用 invoices 方法轻松检索可计费模型的发票数组:

php
$invoices = $user->invoices();

在为客户列出发票时,你可以使用发票的辅助方法来显示相关的发票信息。例如,你可能希望在表格中列出每张发票,允许用户轻松下载其中的任何一张:

php
<table>
    @foreach ($invoices as $invoice)
        <tr>
            <td>{{ $invoice->dateString() }}</td>
            <td>{{ $invoice->dollars() }}</td>
            <td><a href="/user/invoice/{{ $invoice->id }}">下载</a></td>
        </tr>
    @endforeach
</table>

生成发票 PDF

在路由或控制器中,使用 downloadInvoice 方法生成发票的 PDF 下载。此方法将自动生成适当的 HTTP 响应以将下载发送到浏览器:

php
Route::get('user/invoice/{invoice}', function ($invoiceId) {
    return Auth::user()->downloadInvoice($invoiceId, [
        'vendor'  => '你的公司',
        'product' => '你的产品',
    ]);
});