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方法到控制器:
4、一次性付款
如果你想要使用訂閱客戶的信用卡一次性結清賬單,可以使用賬單模型執行個體上的 charge方法,該方法接收付款金額(應用使用的貨幣的最小單位對應的金額數值)作為參數,例如,下面的例子使用信用卡支付100美分,或1美元:
$user->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', ]);});