Laravel Cashier
介绍
Laravel Cashier 提供了一个对 Stripe 订阅计费服务的表达性、流畅的接口。它处理了几乎所有你不想编写的订阅计费样板代码。除了基本的订阅管理,Cashier 还可以处理优惠券、交换订阅、订阅“数量”、取消宽限期,甚至生成发票 PDF。
配置
Composer
首先,将 Cashier 包添加到你的 composer.json
文件中并运行 composer update
命令:
"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 和适当的日期变换器添加到你的模型定义中:
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 密钥:
'stripe' => [
'model' => 'User',
'secret' => env('STRIPE_API_SECRET'),
],
订阅
创建订阅
要创建订阅,首先检索你的可计费模型的实例,通常是 App\User
的实例。一旦检索到模型实例,你可以使用 subscription
方法来管理模型的订阅:
$user = User::find(1);
$user->subscription('monthly')->create($creditCardToken);
create
方法将自动创建 Stripe 订阅,并更新你的数据库中的 Stripe 客户 ID 和其他相关的计费信息。如果你的计划在 Stripe 中配置了试用期,试用期结束日期也将自动设置在用户记录上。
如果你想实现试用期,但完全在你的应用程序中管理试用期而不是在 Stripe 中定义它们,你必须手动设置试用期结束日期:
$user->trial_ends_at = Carbon::now()->addDays(14);
$user->save();
额外的用户详情
如果你想指定额外的客户详情,可以通过将它们作为 create
方法的第二个参数传递:
$user->subscription('monthly')->create($creditCardToken, [
'email' => $email, 'description' => '我们的第一个客户'
]);
要了解 Stripe 支持的其他字段,请查看 Stripe 的客户创建文档。
优惠券
如果你想在创建订阅时应用优惠券,可以使用 withCoupon
方法:
$user->subscription('monthly')
->withCoupon('code')
->create($creditCardToken);
检查订阅状态
一旦用户订阅了你的应用程序,你可以使用各种方便的方法轻松检查他们的订阅状态。首先,subscribed
方法返回 true
如果用户有一个活跃的订阅,即使订阅当前在其试用期内:
if ($user->subscribed()) {
//
}
subscribed
方法也可以作为一个路由中间件的好候选者,允许你根据用户的订阅状态过滤对路由和控制器的访问:
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed()) {
// 这个用户不是付费客户...
return redirect('billing');
}
return $next($request);
}
如果你想确定用户是否仍在他们的试用期内,可以使用 onTrial
方法。此方法可用于向用户显示警告,告知他们仍在试用期内:
if ($user->onTrial()) {
//
}
onPlan
方法可用于确定用户是否订阅了给定计划,基于其 Stripe ID:
if ($user->onPlan('monthly')) {
//
}
取消的订阅状态
要确定用户是否曾经是活跃的订阅者,但已取消他们的订阅,可以使用 cancelled
方法:
if ($user->cancelled()) {
//
}
你还可以确定用户是否已取消他们的订阅,但仍在他们的“宽限期”内,直到订阅完全过期。例如,如果用户在 3 月 5 日取消订阅,而订阅原定于 3 月 10 日到期,则用户在 3 月 10 日之前处于“宽限期”。请注意,在此期间 subscribed
方法仍返回 true
。
if ($user->onGracePeriod()) {
//
}
everSubscribed
方法可用于确定用户是否曾经在你的应用程序中订阅过计划:
if ($user->everSubscribed()) {
//
}
更改计划
在用户订阅你的应用程序后,他们可能偶尔想要更改为新的订阅计划。要将用户切换到新的订阅,请使用 swap
方法。例如,我们可以轻松地将用户切换到 premium
订阅:
$user = App\User::find(1);
$user->subscription('premium')->swap();
如果用户在试用期内,试用期将保持不变。此外,如果订阅存在“数量”,该数量也将保持不变。在更换计划时,你还可以使用 prorate
方法指示费用应按比例分配。此外,你可以使用 swapAndInvoice
方法立即为用户更换计划开具发票:
$user->subscription('premium')
->prorate()
->swapAndInvoice();
订阅数量
有时订阅会受到“数量”的影响。例如,你的应用程序可能会按每个帐户每月每用户收费 $10。要轻松增加或减少你的订阅数量,请使用 increment
和 decrement
方法:
$user = User::find(1);
$user->subscription()->increment();
// 在订阅的当前数量上增加五个...
$user->subscription()->increment(5);
$user->subscription()->decrement();
// 在订阅的当前数量上减少五个...
$user->subscription()->decrement(5);
或者,你可以使用 updateQuantity
方法设置特定数量:
$user->subscription()->updateQuantity(10);
有关订阅数量的更多信息,请查阅 Stripe 文档。
订阅税
使用 Cashier,可以轻松提供发送到 Stripe 的 tax_percent
值。要指定用户在订阅上支付的税率,请在你的可计费模型上实现 getTaxPercent
方法,并返回一个介于 0 和 100 之间的数值,最多保留两位小数。
public function getTaxPercent() {
return 20;
}
这使你能够在模型基础上应用税率,这对于跨多个国家的用户群可能很有帮助。
取消订阅
要取消订阅,只需在用户的订阅上调用 cancel
方法:
$user->subscription()->cancel();
当订阅被取消时,Cashier 将自动在你的数据库中设置 subscription_ends_at
列。此列用于知道何时 subscribed
方法应开始返回 false
。例如,如果客户在 3 月 1 日取消订阅,但订阅原定于 3 月 5 日结束,则 subscribed
方法将继续返回 true
直到 3 月 5 日。
你可以使用 onGracePeriod
方法确定用户是否已取消他们的订阅但仍在他们的“宽限期”内:
if ($user->onGracePeriod()) {
//
}
恢复订阅
如果用户已取消他们的订阅并且你希望恢复它,请使用 resume
方法:
$user->subscription('monthly')->resume($creditCardToken);
如果用户取消订阅,然后在订阅完全过期之前恢复该订阅,他们将不会立即被计费。相反,他们的订阅将被重新激活,并且他们将在原始计费周期中被计费。
处理 Stripe Webhooks
失败的订阅
如果客户的信用卡过期怎么办?不用担心 - Cashier 包含一个 Webhook 控制器,可以轻松地为你取消客户的订阅。只需将一个路由指向控制器:
Route::post('stripe/webhook', '\Laravel\Cashier\WebhookController@handleWebhook');
就是这样!失败的付款将由控制器捕获和处理。当 Stripe 确定订阅失败时(通常在三次付款失败后),控制器将取消客户的订阅。别忘了:你需要在 Stripe 控制面板设置中配置 webhook URI。
由于 Stripe webhooks 需要绕过 Laravel 的 CSRF 验证,请确保在你的 VerifyCsrfToken
中间件中将 URI 列为例外:
protected $except = [
'stripe/*',
];
其他 Webhooks
如果你有其他想要处理的 Stripe webhook 事件,只需扩展 Webhook 控制器。你的方法名称应符合 Cashier 的预期约定,具体来说,方法应以 handle
和你想要处理的 Stripe webhook 的“驼峰式”名称为前缀。例如,如果你想处理 invoice.payment_succeeded
webhook,你应该在控制器中添加一个 handleInvoicePaymentSucceeded
方法。
<?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:
$user->charge(100);
charge
方法接受一个数组作为其第二个参数,允许你将任何选项传递给底层的 Stripe 收费创建:
$user->charge(100, [
'source' => $token,
'receipt_email' => $user->email,
]);
如果收费失败,charge
方法将返回 false
。这通常表示收费被拒绝:
if ( ! $user->charge(100)) {
// 收费被拒绝...
}
如果收费成功,完整的 Stripe 响应将从方法中返回。
发票
你可以使用 invoices
方法轻松检索可计费模型的发票数组:
$invoices = $user->invoices();
在为客户列出发票时,你可以使用发票的辅助方法来显示相关的发票信息。例如,你可能希望在表格中列出每张发票,允许用户轻松下载其中的任何一张:
<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 响应以将下载发送到浏览器:
Route::get('user/invoice/{invoice}', function ($invoiceId) {
return Auth::user()->downloadInvoice($invoiceId, [
'vendor' => '你的公司',
'product' => '你的产品',
]);
});