[TOC]
### **1、簡介**
[Laravel](http://laravelacademy.org/tags/laravel "View all posts in Laravel")?[Cashier](http://laravelacademy.org/tags/cashier "View all posts in Cashier")?為通過?[Stripe](https://stripe.com/)?實現[訂閱](http://laravelacademy.org/tags/%e8%ae%a2%e9%98%85 "View all posts in 訂閱")[支付](http://laravelacademy.org/tags/%e6%94%af%e4%bb%98 "View all posts in 支付")服務提供了一個優雅平滑的接口。它封裝了幾乎所有你恐懼編寫的樣板化的訂閱支付代碼。除了基本的訂閱管理外,Cashier還支持處理[優惠券](http://laravelacademy.org/tags/%e4%bc%98%e6%83%a0%e5%88%b8 "View all posts in 優惠券")、訂閱升級/替換、訂閱“數量”、取消寬限期,甚至生成[PDF](http://laravelacademy.org/tags/pdf "View all posts in PDF")[發票](http://laravelacademy.org/tags/%e5%8f%91%e7%a5%a8 "View all posts in 發票")。
#### **1.1 安裝&配置**
##### **Composer**
首先,添加 Cashier 包到?`composer.json`?文件并運行?`composer update`?命令:
~~~
"laravel/cashier": "~6.0"
~~~
##### **服務提供者**
接下來,在?`config/app.php`?配置文件中注冊[服務提供者](http://laravelacademy.org/post/2900.html):`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`?命令,相應修改就會更新到數據庫。
##### **設置模型**
接下來,添加?`Billable`?trait 到?`User`?模型類:
~~~
use Laravel\Cashier\Billable;
class User extends Authenticatable{
use Billable;
}
~~~
##### **[Stripe](http://laravelacademy.org/tags/stripe "View all posts in 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?[計劃](http://laravelacademy.org/tags/%e8%ae%a1%e5%88%92 "View all posts in 計劃"),該值對應 Stripe 中相應計劃的 id。
`create`?方法會自動創建這個 Stripe 訂閱,同時更新數據庫中 Stripe 的客戶 ID(即?`users`?表中的?`stripe_id`)和其它相關的賬單信息。如果你的訂閱計劃有[試用期](http://laravelacademy.org/tags/%e8%af%95%e7%94%a8%e6%9c%9f "View all posts in 試用期"),試用期結束時間也會自動被設置到數據庫相應字段。
##### **額外的用戶信息**
如果你想要指定額外的客戶信息,你可以將其作為第二個參數傳遞給?`create`?方法:
~~~
$user->newSubscription('main', 'monthly')->create($creditCardToken, [
'email' => $email,
'description' => 'Our First Customer'
]);
~~~
要了解更多 Stripe 支持的字段,可以查看 Stripe 關于[創建消費者的文檔](https://stripe.com/docs/api#create_customer)。
##### **優惠券**
如果你想要在創建訂閱的時候使用優惠券,可以使用?`withCoupon`?方法:
~~~
$user->newSubscription('main', 'monthly')
->withCoupon('code')
->create($creditCardToken);
~~~
#### **2.2 檢查訂閱狀態**
用戶訂閱你的應用后,你可以使用各種便利的方法來簡單檢查訂閱狀態。首先,如果用戶有一個有效的訂閱,則`subscribed`?方法返回`true`,即使訂閱現在出于試用期:
~~~
if ($user->subscribed('main')) {
//
}
~~~
`subscribed`?方法還可以用于[路由中間件](http://laravelacademy.org/post/2803.html),基于用戶訂閱狀態允許你對路由和控制器的訪問進行過濾:
~~~
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文檔](https://stripe.com/docs/guides/subscriptions#setting-quantities)。
#### **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、處理 Stripe?[Webhook](http://laravelacademy.org/tags/webhook "View all posts in Webhook")**
#### **3.1 訂閱失敗處理**
如果客戶的[信用卡](http://laravelacademy.org/tags/%e4%bf%a1%e7%94%a8%e5%8d%a1 "View all posts in 信用卡")失效怎么辦?不用擔心—— Cashier 自帶了 Webhook 控制器,該控制器可以很方便地為你取消客戶訂閱。只需要定義如下控制器路由:
~~~
Route::post('stripe/webhook', 'Laravel\Cashier\WebhookController@handleWebhook');
~~~
就是這樣!失敗的支付將會被該控制器捕獲和處理。當 Stripe 判斷訂閱失敗(正常情況下嘗試支付失敗三次后)時該控制器將會取消客戶的訂閱。不要忘了:你需要在 Stripe 控制面板設置中配置相應的 webhook URI,否則不能正常工作。
由于 Stripe webhooks 需要通過 Laravel 的[CSRF驗證](http://laravelacademy.org/post/2784.html#csrf-attack),所以我們將該 URI 置于?`VerifyCsrfToken`?中間件排除列表中:
~~~
protected $except = [
'stripe/*',
];
~~~
#### **3.2 其它Webhooks**
如果你有額外想要處理的 Stripe webhook 事件,只需簡單繼承 Webhook 控制器, 你的方法名應該和 Cashier 期望的約定一致,尤其是方法應該以“handle”開頭并以駝峰命名法命名。例如,如果你想要處理`invoice.payment_succeeded`?webhook,你應該添加?`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)
{
// 處理該事件
}
}
~~~
### **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();
~~~
當列出客戶發票時,你可以使用發票的輔助函數來顯示相關的發票信息。例如,你可能想要在表格中列出每張發票,從而方便用戶下載它們:
~~~
<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>
~~~
#### **生成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',
]);
});
~~~
- 序言
- 發行版本說明
- 升級指南
- 貢獻代碼
- 開始
- 安裝
- 配置
- Laravel Homestead
- 基礎
- HTTP 路由
- HTTP 中間件
- HTTP 控制器
- HTTP 請求
- HTTP 響應
- 視圖
- Blade 模板引擎
- 架構
- 一次請求的生命周期
- 應用目錄結構
- 服務提供者
- 服務容器
- 門面(Facades)
- 數據庫
- 起步
- 查詢構建器
- 遷移
- 填充數據
- Eloquent ORM
- 起步
- 關聯關系
- 集合
- 訪問器&修改器
- 序列化
- 服務
- 用戶認證
- 用戶授權
- Artisan Console
- 訂閱支付實現:Laravel Cashier
- 緩存
- 集合
- 集成前端資源:Laravel Elixir
- 加密
- 錯誤&日志
- 事件
- 文件系統/云存儲
- 哈希
- 輔助函數
- 本地化
- 郵件
- 包開發
- 分頁
- Redis
- 隊列
- Session
- Envoy Task Runner
- 任務調度
- 測試
- 驗證
- 新手入門指南
- 簡單任務管理系統
- 帶用戶功能的任務管理系統