Laravel Cashier
為 Stripe
的訂購(gòu)單據(jù)服務(wù)提供了一個(gè)優(yōu)雅的、平滑的接口。它處理了幾乎所有你恐懼編寫的樣板化的訂購(gòu)單據(jù)代碼。除了基本的訂購(gòu)管理外,Cashier
還支持處理優(yōu)惠券、交換訂購(gòu)、訂購(gòu)“數(shù)量”、取消寬限期,甚至生成PDF發(fā)票。
首先,添加 Cashier
包到 composer.json
文件并運(yùn)行 composer update 命令:
"laravel/cashier": "~5.0" (For Stripe SDK ~2.0, and Stripe APIs on 2015-02-18 version and later)
"laravel/cashier": "~4.0" (For Stripe APIs on 2015-02-18 version and later)
"laravel/cashier": "~3.0" (For Stripe APIs up to and including 2015-02-16 version)
接下來(lái),在 app
配置文件中注冊(cè)服務(wù)提供者Laravel\Cashier\CashierServiceProvider
。
實(shí)現(xiàn) Cashier
之前,我們需要添加額外的字段到數(shù)據(jù)庫(kù)。別擔(dān)心,你可以使用 Artisan
命令 cashier:table
來(lái)創(chuàng)建遷移以添加必要的字段。例如,要添加該字段到用戶表運(yùn)行如下命令:
php artisan cashier:table users
創(chuàng)建好遷移后,只需簡(jiǎn)單運(yùn)行 migrate
命令。
接下來(lái),添加 Billable trait
和相應(yīng)的日期調(diào)整器到模型定義:
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
實(shí)例格式字段而不是原生字符串。
最后,在配置文件 services.php
中設(shè)置 Stripe
鍵:
'stripe' => [
'model' => 'User',
'secret' => env('STRIPE_API_SECRET'),
],
要?jiǎng)?chuàng)建一個(gè)訂購(gòu),首先獲取一個(gè)賬單模型的實(shí)例,通常是 App\User
的實(shí)例。獲取到該模型實(shí)例之后,你可以使用 subscription
方法來(lái)管理模型的訂購(gòu):
$user = User::find(1);
$user->subscription('monthly')->create($creditCardToken);
create
方法將會(huì)自動(dòng)創(chuàng)建 Stripe
訂購(gòu),并使用 Stripe
客戶 ID
和其他相關(guān)單據(jù)信息更新數(shù)據(jù)庫(kù)。如果你在 Stripe
中的計(jì)劃有一個(gè)試用配置,那么用戶記錄中的試用結(jié)束時(shí)間也會(huì)自動(dòng)設(shè)置。
如果你想要實(shí)現(xiàn)試用期,但是完全在 Laravel
應(yīng)用中管理試用而不是將其定義在 Stripe
中,你必須手動(dòng)設(shè)置試用結(jié)束時(shí)間:
$user->trial_ends_at = Carbon::now()->addDays(14);
$user->save();
如果你想要指定額外的客戶詳情,你可以將其作為第二個(gè)參數(shù)傳遞給 create
方法:
$user->subscription('monthly')->create($creditCardToken, [
'email' => $email,
'description' => 'Our First Customer'
]);
要了解更多 Stripe
支持的字段,可以查看 Stripe
關(guān)于客戶創(chuàng)建的文檔。
如果你想要在創(chuàng)建訂購(gòu)的時(shí)候使用優(yōu)惠券,可以使用 withCoupon
方法:
$user->subscription('monthly')
->withCoupon('code')
->create($creditCardToken);
用戶訂購(gòu)你的應(yīng)用后,你可以使用各種便利的方法來(lái)簡(jiǎn)單檢查訂購(gòu)狀態(tài)。首先,如果用戶有一個(gè)有效的訂購(gòu),則 subscribed
方法返回 true
,即使訂購(gòu)現(xiàn)在出于試用期:
if ($user->subscribed()) {
//
}
subscribed
方法還可以用于路由中間件,基于用戶訂購(gòu)狀態(tài)允許你對(duì)路由和控制器的訪問(wèn)進(jìn)行過(guò)濾:
public function handle($request, Closure $next){
if ($request->user() && ! $request->user()->subscribed()) {
// This user is not a paying customer...
return redirect('billing');
}
return $next($request);
}
如果你想要判斷一個(gè)用戶是否還在試用期,可以使用 onTrial
方法,該方法在為還處于試用期的用戶顯示警告信息很有用:
if ($user->onTrial()) {
//
}
onPlan 方法可用于判斷用戶是否基于 Stripe ID
訂購(gòu)了給定的計(jì)劃:
if ($user->onPlan('monthly')) {
//
}
要判斷用戶是否曾經(jīng)是有效的訂購(gòu)者,但現(xiàn)在取消了訂購(gòu),可以使用 cancelled
方法:
if ($user->cancelled()) {
//
}
你還可以判斷用戶是否曾經(jīng)取消過(guò)訂購(gòu),但現(xiàn)在仍然在“寬限期”直到訂購(gòu)?fù)耆А@?,如果一個(gè)用戶在 3 月 5 號(hào)取消了一個(gè)實(shí)際有效期到 3 月 10 號(hào)的訂購(gòu),該用戶處于“寬限期”直到 3 月 10 號(hào)。注意 subscribed
方法在此期間仍然返回 true
。
if ($user->onGracePeriod()) {
//
}
everSubscribed
方法可以用于判斷用戶是否訂購(gòu)過(guò)應(yīng)用的計(jì)劃:
if ($user->everSubscribed()) {
//
}
用戶訂購(gòu)應(yīng)用后,偶爾想要改變到新的訂購(gòu)計(jì)劃,要將用戶切換到新的訂購(gòu),使用 swap
方法。例如,我們可以輕松切換用戶到 premium
訂購(gòu):
$user = App\User::find(1);
$user->subscription('premium')->swap();
如果用戶在試用,試用期將會(huì)被維護(hù)。還有,如果訂購(gòu)存在數(shù)量,數(shù)量也可以被維護(hù)。當(dāng)交換訂購(gòu)計(jì)劃,還可以使用 prorate
方法來(lái)表明費(fèi)用是按比例的,此外,你可以使用 swapAndInvoice
方法立即為用戶計(jì)劃改變開(kāi)發(fā)票:
$user->subscription('premium')
->prorate()
->swapAndInvoice();
有時(shí)候訂購(gòu)也會(huì)被數(shù)量影響,例如,你的應(yīng)用每個(gè)賬戶每月需要付費(fèi)$10
,要簡(jiǎn)單增加或減少訂購(gòu)數(shù)量,使用 increment
和 decrement
方法:
$user = User::find(1);
$user->subscription()->increment();
// 當(dāng)前訂購(gòu)數(shù)量+5...
$user->subscription()->increment(5);
$user->subscription()->decrement();
// 當(dāng)前訂購(gòu)數(shù)量-5...
$user->subscription()->decrement(5);
你也可以使用 updateQuantity
方法指定數(shù)量:
$user->subscription()->updateQuantity(10);
想要了解更多訂購(gòu)數(shù)量信息,查閱相關(guān)Stripe
文檔。
在 Cashier
中,提供 tax_percent
值發(fā)送給 Stripe
很簡(jiǎn)單。要指定用戶支付訂購(gòu)的稅率,實(shí)現(xiàn)賬單模型的 getTaxPercent
方法,并返回一個(gè)在 0 到 100 之間的數(shù)值,不要超過(guò)兩位小數(shù):
public function getTaxPercent() {
return 20;
}
這將使你可以在模型基礎(chǔ)上使用稅率,對(duì)跨越不同國(guó)家的用戶很有用。
要取消訂購(gòu),可以調(diào)用用戶訂購(gòu)上的 cancel
方法:
$user->subscription()->cancel();
當(dāng)訂購(gòu)被取消時(shí),Cashier
將會(huì)自動(dòng)設(shè)置數(shù)據(jù)庫(kù)中的 subscription_ends_at
字段。該字段用于了解 subscribed
方法什么時(shí)候開(kāi)始返回 false。例如,如果客戶 3 月 1 號(hào)份取消訂購(gòu),但訂購(gòu)直到 3 月 5 號(hào)才會(huì)結(jié)束,那么
subscribed `方法繼續(xù)返回 true 直到 3 月 5 號(hào)。
你可以使用 onGracePeriod
方法判斷用戶是否已經(jīng)取消訂購(gòu)但仍然在“寬限期”:
if ($user->onGracePeriod()) {
//
}
如果用戶已經(jīng)取消訂購(gòu)但你想恢復(fù),使用 resume
方法:
$user->subscription('monthly')->resume($creditCardToken);
如果用戶取消訂購(gòu)然后在訂購(gòu)失效前恢復(fù),將不會(huì)立即付款,相反,他們的訂購(gòu)會(huì)重新激活,他們將會(huì)在正常的付款周期進(jìn)行支付。
如果客戶的信用卡失效怎么辦?不用擔(dān)心——Cashier
包含了 Webhook
控制器,該控制器可以輕易的為你取消客戶訂購(gòu)。只需要設(shè)置下到該控制器的路由:
Route::post('stripe/webhook', 'Laravel\Cashier\WebhookController@handleWebhook');
就這樣!失敗的支付將會(huì)被該控制器捕獲和處理。當(dāng) Stripe
判斷訂購(gòu)失敗(正常情況下嘗試支付失敗三次后)時(shí)該控制器將會(huì)取消客戶的訂購(gòu)。不要忘了:你需要在 Stripe
控制面板設(shè)置中配置 webhook URI
。
由于 Stripe webhooks
需要通過(guò) Laravel
的CSRF
驗(yàn)證,確保將該 URI
置于 VerifyCsrfToken
中間件排除列表中:
protected $except = [
'stripe/*',
];
如果你有額外想要處理的 Stripe webhook
事件,只需簡(jiǎn)單繼承 Webhook
控制器, 你的方法名應(yīng)該和 Cashier
期望的約定一致,尤其是方法應(yīng)該以“handle”開(kāi)頭并以駝峰命名法命名。例如,如果你想要處理 invoice.payment_succeeded webhook
,你應(yīng)該添加 handleInvoicePaymentSucceeded
方法到控制器:
<?php
namespace App\Http\Controller;
use Laravel\Cashier\WebhookController as BaseController;
class WebhookController extends BaseController{
/**
* 處理 stripe webhook.
*
* @param array $payload
* @return Response
*/
public function handleInvoicePaymentSucceeded($payload)
{
// 處理該事件
}
}
如果你想要使用訂購(gòu)客戶的信用卡一次性結(jié)清賬單,可以使用單據(jù)模型實(shí)例上的 charge
方法,該方法接收付款金額(應(yīng)用使用的貨幣的最小單位對(duì)應(yīng)的金額數(shù)值)作為參數(shù),例如,下面的例子要使用信用卡支付 100 美分,或 1 美元:
$user->charge(100);
charge
方法接收一個(gè)數(shù)組作為第二個(gè)參數(shù),允許你傳遞任何你想要傳遞的底層 Stripe
賬單創(chuàng)建參數(shù):
$user->charge(100, [
'source' => $token,
'receipt_email' => $user->email,]);
如果支付失敗 charge
方法將返回 false
,這通常表明付款被拒絕:
if ( ! $user->charge(100)) {
// The charge was denied...
}
如果支付成功,該方法將會(huì)返回一個(gè)完整的 Stripe
響應(yīng)。
你可以使用 invoices
方法輕松獲取賬單模型的發(fā)票數(shù)組:
$invoices = $user->invoices();
當(dāng)列出客戶發(fā)票時(shí),你可以使用發(fā)票的幫助函數(shù)來(lái)顯示相關(guān)的發(fā)票信息。例如,你可能想要在表格中列出每張發(fā)票,從而允許用戶方便的下載它們:
<table>
@foreach ($invoices as $invoice)
<tr>
<td>{{ $invoice->dateString() }}</td>
<td>{{ $invoice->dollars() }}</td>
<td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
</tr>
@endforeach
</table>
在路由或控制器中,使用 downloadInvoice
方法生成發(fā)票的 PDF
下載,該方法將會(huì)自動(dòng)生成合適的 HTTP
響應(yīng)發(fā)送下載到瀏覽器:
Route::get('user/invoice/{invoice}', function ($invoiceId) {
return Auth::user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
});