Laravel Cashier


1、简介

Laravel Cashier为Stripe的订购支付服务提供了一个优雅的、平滑的接口。它处理了几乎所有你恐惧编写的样板化的订购支付代码。除了基本的订购管理外,Cashier还支持处理优惠券、交换订购、订购“数量”、取消宽限期,甚至生成PDF发票。

1.1 配置

1.1.1 Composer

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

  1. "laravel/cashier": "~5.0" (For Stripe SDK ~2.0, and Stripe APIs on 2015-02-18 version and later)
  2. "laravel/cashier": "~4.0" (For Stripe APIs on 2015-02-18 version and later)
  3. "laravel/cashier": "~3.0" (For Stripe APIs up to and including 2015-02-16 version)
1.1.2 服务提供者

接下来,在config/app.php配置文件中注册服务提供者:Laravel\Cashier\CashierServiceProvider

1.1.3 迁移

实现Cashier之前,我们需要添加额外的字段到数据库。别担心,你可以使用Artisan命令cashier:table来创建迁移以添加必要的字段。例如,要添加该字段到用户表运行如下命令:

  1. php artisan cashier:table users

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

1.1.4 设置模型

接下来,添加Billable trait和相应的日期调整器到模型定义:

  1. use Laravel\Cashier\Billable;
  2. use Laravel\Cashier\Contracts\Billable as BillableContract;
  3. class User extends Model implements BillableContract{
  4. use Billable;
  5. protected $dates = ['trial_ends_at', 'subscription_ends_at'];
  6. }

添加字段到模型的$dates属性使Eloquent返回Carbon/Datetime实例格式字段而不是原生字符串。

1.1.5 Stripe键

最后,在配置文件config/services.php中设置Stripe键:

  1. 'stripe' => [
  2. 'model' => 'User',
  3. 'secret' => env('STRIPE_API_SECRET'),
  4. ],

2、订购

2.1 创建订购

要创建一个订购,首先获取一个账单模型的实例,通常是App\User的实例。获取到该模型实例之后,你可以使用subscription方法来管理模型的订购:

  1. $user = User::find(1);
  2. $user->subscription('monthly')->create($creditCardToken);

create方法将会自动创建Stripe订购,并使用Stripe客户ID和其他相关单据信息更新数据库。如果你在Stripe中的计划有一个试用配置,那么用户记录中的试用结束时间也会自动设置。

如果你想要实现试用期,但是完全在Laravel应用中管理试用而不是将其定义在Stripe中,你必须手动设置试用结束时间:

  1. $user->trial_ends_at = Carbon::now()->addDays(14);
  2. $user->save();
2.1.1 额外的用户详情

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

  1. $user->subscription('monthly')->create($creditCardToken, [
  2. 'email' => $email,
  3. 'description' => 'Our First Customer'
  4. ]);

要了解更多Stripe支持的字段,可以查看Stripe关于客户创建的文档。

2.1.2 优惠券

如果你想要在创建订购的时候使用优惠券,可以使用withCoupon方法:

  1. $user->subscription('monthly')
  2. ->withCoupon('code')
  3. ->create($creditCardToken);

2.2 检查订购状态

用户订购你的应用后,你可以使用各种便利的方法来简单检查订购状态。首先,如果用户有一个有效的订购,则subscribed方法返回true,即使订购现在出于试用期:

  1. if ($user->subscribed()) {
  2. //
  3. }

subscribed方法还可以用于路由中间件,基于用户订购状态允许你对路由和控制器的访问进行过滤:

  1. public function handle($request, Closure $next){
  2. if ($request->user() && ! $request->user()->subscribed()) {
  3. // This user is not a paying customer...
  4. return redirect('billing');
  5. }
  6. return $next($request);
  7. }

如果你想要判断一个用户是否还在试用期,可以使用onTrial方法,该方法在为还处于试用期的用户显示警告信息很有用:

  1. if ($user->onTrial()) {
  2. //
  3. }

onPlan方法可用于判断用户是否基于Stripe ID订购了给定的计划:

  1. if ($user->onPlan('monthly')) {
  2. //
  3. }
2.2.1 取消的订购状态

要判断用户是否曾经是有效的订购者,但现在取消了订购,可以使用cancelled方法:

  1. if ($user->cancelled()) {
  2. //
  3. }

你还可以判断用户是否曾经取消过订购,但现在仍然在“宽限期”直到订购完全失效。例如,如果一个用户在3月5号取消了一个实际有效期到3月10号的订购,该用户处于“宽限期”直到3月10号。注意subscribed方法在此期间仍然返回true

  1. if ($user->onGracePeriod()) {
  2. //
  3. }

everSubscribed方法可以用于判断用户是否订购过应用的计划:

  1. if ($user->everSubscribed()) {
  2. //
  3. }

2.3 改变计划

用户订购应用后,偶尔想要改变到新的订购计划,要将用户切换到新的订购,使用swap方法。例如,我们可以轻松切换用户到premium订购:

  1. $user = App\User::find(1);
  2. $user->subscription('premium')->swap();

如果用户在试用,试用期将会被维护。还有,如果订购存在数量,数量也可以被维护。当交换订购计划,还可以使用prorate方法来表明费用是按比例的,此外,你可以使用swapAndInvoice方法立即为用户计划改变开发票:

  1. $user->subscription('premium')
  2. ->prorate()
  3. ->swapAndInvoice();

2.4 订购数量

有时候订购也会被数量影响,例如,你的应用每个账户每月需要付费$10,要简单增加或减少订购数量,使用incrementdecrement方法:

  1. $user = User::find(1);
  2. $user->subscription()->increment();
  3. // 当前订购数量+5...
  4. $user->subscription()->increment(5);
  5. $user->subscription()->decrement();
  6. // 当前订购数量-5...
  7. $user->subscription()->decrement(5);

你也可以使用updateQuantity方法指定数量:

  1. $user->subscription()->updateQuantity(10);

想要了解更多订购数量信息,查阅相关Stripe文档。

2.5 订购税金

在Cashier中,提供tax_percent值发送给Stripe很简单。要指定用户支付订购的税率,实现账单模型的getTaxPercent方法,并返回一个在0到100之间的数值,不要超过两位小数:

  1. public function getTaxPercent() {
  2. return 20;
  3. }

这将使你可以在模型基础上使用税率,对跨越不同国家的用户很有用。

2.6 取消订购

要取消订购,可以调用用户订购上的cancel 方法:

  1. $user->subscription()->cancel();

当订购被取消时,Cashier将会自动设置数据库中的subscription_ends_at字段。该字段用于了解subscribed方法什么时候开始返回false。例如,如果客户3月1号份取消订购,但订购直到3月5号才会结束,那么subscribed方法继续返回 true直到3月5号。

你可以使用onGracePeriod方法判断用户是否已经取消订购但仍然在“宽限期”:

  1. if ($user->onGracePeriod()) {
  2. //
  3. }

2.7 恢复订购

如果用户已经取消订购但你想恢复,使用resume方法:

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

如果用户取消订购然后在订购失效前恢复,将不会立即付款,相反,他们的订购会重新激活,他们将会在正常的付款周期进行支付。

3、处理Stripe

3.1 失败的订购

如果客户的信用卡失效怎么办?不用担心——Cashier包含了Webhook控制器,该控制器可以轻易的为你取消客户订购。只需要设置下到该控制器的路由:

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

就这样!失败的支付将会被该控制器捕获和处理。当Stripe判断订购失败(正常情况下尝试支付失败三次后)时该控制器将会取消客户的订购。不要忘了:你需要在Stripe控制面板设置中配置webhook URI。

由于Stripe webhooks需要通过Laravel的CSRF验证,确保将该URI置于VerifyCsrfToken中间件排除列表中:

  1. protected $except = [
  2. 'stripe/*',
  3. ];

3.2 其它Webhooks

如果你有额外想要处理的Stripe webhook事件,只需简单继承Webhook控制器, 你的方法名应该和Cashier期望的约定一致,尤其是方法应该以“handle”开头并以驼峰命名法命名。例如,如果你想要处理invoice.payment_succeeded webhook,你应该添加handleInvoicePaymentSucceeded方法到控制器:

  1. <?php
  2. namespace App\Http\Controller;
  3. use Laravel\Cashier\WebhookController as BaseController;
  4. class WebhookController extends BaseController{
  5. /**
  6. * 处理 stripe webhook.
  7. *
  8. * @param array $payload
  9. * @return Response
  10. */
  11. public function handleInvoicePaymentSucceeded($payload)
  12. {
  13. // 处理该事件
  14. }
  15. }

4、一次性付款

如果你想要使用订购客户的信用卡一次性结清账单,可以使用单据模型实例上的charge方法,该方法接收付款金额(应用使用的货币的最小单位对应的金额数值)作为参数,例如,下面的例子要使用信用卡支付100美分,或1美元:

  1. $user->charge(100);

charge方法接收一个数组作为第二个参数,允许你传递任何你想要传递的底层Stripe账单创建参数:

  1. $user->charge(100, [
  2. 'source' => $token,
  3. 'receipt_email' => $user->email,]
  4. );

如果支付失败charge方法将返回false,这通常表明付款被拒绝:

  1. if ( ! $user->charge(100)) {
  2. // The charge was denied...
  3. }

如果支付成功,该方法将会返回一个完整的Stripe响应。

5、发票

你可以使用invoices方法轻松获取账单模型的发票数组:

  1. $invoices = $user->invoices();

当列出客户发票时,你可以使用发票的帮助函数来显示相关的发票信息。例如,你可能想要在表格中列出每张发票,从而允许用户方便的下载它们:

  1. <table>
  2. @foreach ($invoices as $invoice)
  3. <tr>
  4. <td>{{ $invoice->dateString() }}</td>
  5. <td>{{ $invoice->dollars() }}</td>
  6. <td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
  7. </tr>
  8. @endforeach
  9. </table>

5.1 生成PDF发票

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

  1. Route::get('user/invoice/{invoice}', function ($invoiceId) {
  2. return Auth::user()->downloadInvoice($invoiceId, [
  3. 'vendor' => 'Your Company',
  4. 'product' => 'Your Product',
  5. ]);
  6. });

扩展阅读 1:实例教程 —— Laravel Cashier 安装配置

扩展阅读 2:实例教程 —— Laravel Cashier 实现付费会员和分期付款