1、简介 laravelcashier 为通过 stripe实现订阅支付服务提供了一个优雅平滑的接口。它封装了几乎所有你恐惧编写的样板化的订阅支付代码。除了基本的订阅管理外,cashier还支持处理优惠券、订阅升级/替换、订阅“数量”、取消宽限期,甚至生成pdf 发票。
1.1 安装&配置 composer
首先,添加 cashier 包到 composer.json文件并运行 composer update命令:
laravel/cashier: ~6.0
服务提供者
接下来,在 config/app.php配置文件中注册服务提供者: laravel\cashier\cashierserviceprovider。
迁移
使用 cashier 之前,我们需要准备好数据库。我们需要添加一个字段到 users表,还要创建新的 subscriptions表来处理所有用户订阅:
schema::table('users', function ($table) { $table->string('stripe_id')->nullable(); $table->string('card_brand')->nullable(); $table->string('card_last_four')->nullable();});schema::create('subscriptions', function ($table) { $table->increments('id'); $table->integer('user_id'); $table->string('name'); $table->string('stripe_id'); $table->string('stripe_plan'); $table->integer('quantity'); $table->timestamp('trial_ends_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->timestamps();});
创建好迁移后,只需简单运行 migrate命令,相应修改就会更新到数据库。
设置模型
接下来,添加 billabletrait 到 user模型类:
use laravel\cashier\billable;class user extends authenticatable{ use billable;}
stripe键
最后,在配置文件 config/services.php中设置 stripe 键:
'stripe' => [ 'model' => 'user', 'secret' => env('stripe_api_secret'),],
2、订阅实现 2.1 创建订阅 要创建一个订阅,首先要获取一个账单模型的实例,通常是 app\user的实例。获取到该模型实例之后,你可以使用 newsubscription方法来创建该模型的订阅:
$user = user::find(1);$user->newsubscription('main', 'monthly')->create($creditcardtoken);
第一个传递给 newsubscription方法的参数是该订阅的名字,如果应用只有一个订阅,可以将其称作 main或 primary,第二个参数用于指定用户订阅的 stripe计划,该值对应 stripe 中相应计划的 id。
create方法会自动创建这个 stripe 订阅,同时更新数据库中 stripe 的客户 id(即 users表中的 stripe_id)和其它相关的账单信息。如果你的订阅计划有试用期,试用期结束时间也会自动被设置到数据库相应字段。
额外的用户信息
如果你想要指定额外的客户信息,你可以将其作为第二个参数传递给 create方法:
$user->newsubscription('main', 'monthly')->create($creditcardtoken, [ 'email' => $email, 'description' => 'our first customer']);
要了解更多 stripe 支持的字段,可以查看 stripe 关于 创建消费者的文档。
优惠券
如果你想要在创建订阅的时候使用优惠券,可以使用 withcoupon方法:
$user->newsubscription('main', 'monthly') ->withcoupon('code') ->create($creditcardtoken);
2.2 检查订阅状态 用户订阅你的应用后,你可以使用各种便利的方法来简单检查订阅状态。首先,如果用户有一个有效的订阅,则 subscribed方法返回 true,即使订阅现在出于试用期:
if ($user->subscribed('main')) { //}
subscribed方法还可以用于路由中间件,基于用户订阅状态允许你对路由和控制器的访问进行过滤:
public function handle($request, closure $next){ if ($request->user() && ! $request->user()->subscribed('main')) { // this user is not a paying customer... return redirect('billing'); } return $next($request);}
如果你想要判断一个用户是否还在试用期,可以使用 ontrial方法,该方法在为还处于试用期的用户显示警告信息很有用:
if ($user->->subscription('main')->ontrial()) { //}
onplan方法可用于判断用户是否基于 stripe id 订阅了给定的计划:
if ($user->onplan('monthly')) { //}
已取消的订阅状态
要判断用户是否曾经是有效的订阅者,但现在取消了订阅,可以使用 cancelled方法:
if ($user->subscription('main')->cancelled()) { //}
你还可以判断用户是否曾经取消过订阅,但现在仍然在“宽限期”直到完全失效。例如,如果一个用户在3月5号取消了一个实际有效期到3月10号的订阅,该用户处于“宽限期”直到3月10号。注意 subscribed方法在此期间仍然返回 true。
if ($user->subscription('main')->ongraceperiod()) { //}
2.3 修改订阅 用户订阅应用后,偶尔想要改变到新的订阅计划,要将用户切换到新的订阅,使用 swap方法。例如,我们可以轻松切换用户到 premium订阅:
$user = app\user::find(1);$user->subscription('main')->swap('stripe-plan-id');
如果用户在试用,试用期将会被维护。还有,如果订阅存在数量,数量也可以被维护。切换订阅计划后,
可以使用 invoice方法立即给用户开发票:
$user->subscription('main')->swap('stripe-plan-id');$user->invoice();
2.4 订阅数量 有时候订阅也会被数量影响,例如,应用中每个账户每月需要付费$10,要简单增加或减少订阅数量,使用 incrementquantity和 decrementquantity方法:
$user = user::find(1);$user->subscription('main')->incrementquantity();// add five to the subscription's current quantity...$user->subscription('main')->incrementquantity(5);$user->subscription('main')->decrementquantity();// subtract five to the subscription's current quantity...$user->subscription('main')->decrementquantity(5);
你也可以使用 updatequantity方法指定数量:
$user->subscription('main')->updatequantity(10);
想要了解更多订阅数量信息,查阅相关 stripe文档。
2.5 订阅税金 在 cashier 中,提供 tax_percent值发送给 stripe 很简单。要指定用户支付订阅的税率,实现账单模型的 gettaxpercent方法,并返回一个在0到100之间的数值,不要超过两位小数:
public function gettaxpercent() { return 20;}
这将使你可以在模型基础上使用税率,对跨越不同国家的用户很有用。
2.6 取消订阅 要取消订阅,可以调用用户订阅上的 cancel方法:
$user->subscription('main')->cancel();
当订阅被取消时,cashier 将会自动设置数据库中的 subscription_ends_at字段。该字段用于了解 subscribed方法什么时候开始返回 false。例如,如果客户3月1号份取消订阅,但订阅直到3月5号才会结束,那么 subscribed方法继续返回 true直到3月5号。
你可以使用 ongraceperiod方法判断用户是否已经取消订阅但仍然在“宽限期”:
if ($user->subscription('main')->ongraceperiod()) { //}
2.7 恢复订阅 如果用户已经取消订阅但想要恢复该订阅,可以使用 resume方法,前提是该用户必须在宽限期内:
$user->subscription('main')->resume();
如果该用户取消了一个订阅然后在订阅失效之前恢复了这个订阅,则不会立即支付该账单,取而代之的,他们的订阅只是被重新激活,并回到正常的支付周期。
3、处理 stripewebhook 3.1 订阅失败处理 如果客户的信用卡失效怎么办?不用担心—— cashier 自带了 webhook 控制器,该控制器可以很方便地为你取消客户订阅。只需要定义如下控制器路由:
route::post('stripe/webhook', 'laravel\cashier\webhookcontroller@handlewebhook');
就是这样!失败的支付将会被该控制器捕获和处理。当 stripe 判断订阅失败(正常情况下尝试支付失败三次后)时该控制器将会取消客户的订阅。不要忘了:你需要在 stripe 控制面板设置中配置相应的 webhook uri,否则不能正常工作。
由于 stripe webhooks 需要通过 laravel 的csrf验证,所以我们将该 uri 置于 verifycsrftoken中间件排除列表中:
protected $except = [ 'stripe/*',];
3.2 其它webhooks 如果你有额外想要处理的 stripe webhook 事件,只需简单继承 webhook 控制器, 你的方法名应该和 cashier 期望的约定一致,尤其是方法应该以“handle”开头并以驼峰命名法命名。例如,如果你想要处理 invoice.payment_succeededwebhook,你应该添加 handleinvoicepaymentsucceeded方法到控制器:
charge(100);
charge方法接收一个数组作为第二个参数,允许你传递任何你想要传递的底层 stripe 账单创建参数:
$user->charge(100, [ 'source' => $token, 'receipt_email' => $user->email,]);
如果支付失败 charge方法将返回 false,这通常表明付款被拒绝:
if ( ! $user->charge(100)) { // the charge was denied...}
如果支付成功,该方法将会返回一个完整的 stripe 响应。
5、发票 你可以使用 invoices方法轻松获取账单模型的发票数组:
$invoices = $user->invoices();
当列出客户发票时,你可以使用发票的辅助函数来显示相关的发票信息。例如,你可能想要在表格中列出每张发票,从而方便用户下载它们:
@foreach ($invoices as $invoice) {{ $invoice->datestring() }} {{ $invoice->dollars() }} id }}>download
@endforeach
生成pdf发票 在生成pdf分票之前,需要安装 php 库 dompdf:
composer require dompdf/dompdf
在路由或控制器中,使用 downloadinvoice方法生成发票的 pdf 下载,该方法将会自动生成相应的 http 响应发送下载到浏览器:
route::get('user/invoice/{invoice}', function ($invoiceid) { return auth::user()->downloadinvoice($invoiceid, [ 'vendor' => 'your company', 'product' => 'your product', ]);});