* * * * *
[TOC]
## 簡介
Laravel Cashier 提供了直觀、流暢的接口來接入?[Stripe's](https://stripe.com/)?和?[Braintree's](https://braintreepayments.com/)?訂閱付費服務。它可以處理幾乎所有你寫起來非常頭疼付費訂閱代碼。除了提供基本的訂閱管理之外,Cashier 還可以幫你處理優惠券,更改訂閱計劃, 按量計費訂閱, 已取消訂閱寬限期管理, 甚至生成發票的 PDF 文件。
> {note} 如果你只是需要提供一次性的收費服務,建議直接使用 Stripe 和 Braintree 的 SDK,而無需使用 Cashier。
## 配置
### Stripe
#### Composer
首先,將 Cashier Stripe 擴展包添加到?`composer.json`?文件,然后運行?`composer update`?命令:
~~~
"laravel/cashier": "~7.0"
~~~
#### Service 服務提供者
接下來,需要注冊?`Laravel\Cashier\CashierServiceProvider`?[服務提供者](https://laravel-china.org/docs/laravel/5.4/providers)?到你的?`config/app.php`?配置文件。
#### 數據庫遷移
在使用 Cashier 之前,我們需要?[準備一下數據庫](https://laravel-china.org/docs/laravel/5.4/migrations)。需要在?`users`?表中新增幾列,以及創建一個新的?`subscriptions`表來存儲客戶的訂閱信息:
~~~
Schema::table('users', function ($table) {
$table->string('stripe_id')->nullable();
$table->string('card_brand')->nullable();
$table->string('card_last_four')->nullable();
$table->timestamp('trial_ends_at')->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`?Artisan 命令。
#### Billable 模型
現在,需要添加?`Billable`?trait 到你的模型定義。`Billable`?trait 提供了豐富的方法去允許你完成常見的支付任務,比如創建訂閱,使用優惠券以及更新信用卡信息。
~~~
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
~~~
#### API Keys
最后,你需要在?`services.php`?配置文件中設置你的 Stripe key。你可以在 Stripe 的控制面板獲取到相關的 API keys。
~~~
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
~~~
### Braintree
#### Braintree 注意事項
對于大多數操作,Stripe 和 Braintree 在 Cashier 的實現方法是一致的,都提供對信用卡訂閱計費的支持。但是 Braintree 是通過 Paypal 進行支付的,所以相對 Stripe 來說會缺少一些功能的支持。在選擇該使用 Stripe 還是 Braintree 的時候需要留意這些區別。
* Braintree 支持 PayPal,但是 Stripe 不支持。
* Braintree 并不支持對 subscriptions 使用?`increment`?和?`decrement`?方法。這是 Braintree 本身的限制,并不是 Cahier 的限制。
* Braintree 并不支持基于百分比的折扣。 這也是 Braintree 本身的限制,并不是 Cahier 的限制。
#### Composer
首先,將 Cashier Braintree 擴展包添加到?`composer.json`?文件,然后運行?`composer update`?命令:
~~~
"laravel/cashier-braintree": "~2.0"
~~~
#### 服務提供者
接下來,需要注冊?`Laravel\Cashier\CashierServiceProvider`?[服務提供者](https://laravel-china.org/docs/laravel/5.4/providers)?到你的?`config/app/php`?配置文件。
#### 信用卡優惠計劃
在使用 Cashier 之前,你需要首先在 Braintree 控制面板定義一個?`plan-credit`?折扣。這個折扣會根據用戶選擇的支付選項匹配合適的折扣比例,比如選擇年付還是月付。
在 Braintree 控制面板中配置的折扣總額可以隨意填,Cashier 會在每次使用優惠券的時候根據我們自己的定制覆蓋該默認值。我們需要這個優惠券計劃的原因是 Braintree 并不原生支持按照訂閱頻率來匹配折扣比例。
#### 數據庫遷移
在使用 Cashier 之前,我們需要?[準備一下數據庫](https://laravel-china.org/docs/laravel/5.4/migrations)。需要在?`users`?表中新增幾列,以及創建一個新的?`subscriptions`表來存儲客戶的訂閱信息:
~~~
Schema::table('users', function ($table) {
$table->string('braintree_id')->nullable();
$table->string('paypal_email')->nullable();
$table->string('card_brand')->nullable();
$table->string('card_last_four')->nullable();
$table->timestamp('trial_ends_at')->nullable();
});
Schema::create('subscriptions', function ($table) {
$table->increments('id');
$table->integer('user_id');
$table->string('name');
$table->string('braintree_id');
$table->string('braintree_plan');
$table->integer('quantity');
$table->timestamp('trial_ends_at')->nullable();
$table->timestamp('ends_at')->nullable();
$table->timestamps();
});
~~~
創建好數據庫遷移文件之后,需要運行一下?`migrate`?Artisan 命令。
#### Billable 模型
接下來,添加?`Billable`?trait 到你的模型定義。
~~~
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
~~~
#### API Keys
現在,你需要參考下面的示例在?`services.php`?文件中配置相關選項:
~~~
'braintree' => [
'model' => App\User::class,
'environment' => env('BRAINTREE_ENV'),
'merchant_id' => env('BRAINTREE_MERCHANT_ID'),
'public_key' => env('BRAINTREE_PUBLIC_KEY'),
'private_key' => env('BRAINTREE_PRIVATE_KEY'),
],
~~~
然后你需要將 Braintree SDK 添加到你的?`AppServiceProvider`?的?`boot`?方法中:
~~~
\Braintree_Configuration::environment(config('services.braintree.environment'));
\Braintree_Configuration::merchantId(config('services.braintree.merchant_id'));
\Braintree_Configuration::publicKey(config('services.braintree.public_key'));
\Braintree_Configuration::privateKey(config('services.braintree.private_key'));
~~~
### 貨幣配置
Cashier 使用美元(USD)作為默認貨幣。你可以在某個服務提供者的?`boot`?方法中使用?`Cashier::useCurrency`?方法來修改默認的貨幣設置。`useCurrency`?方法接受兩個字符串參數:貨幣名稱和貨幣符號。
~~~
use Laravel\Cashier\Cashier;
Cashier::useCurrency('eur', '€');
~~~
## 訂閱列表
### 創建訂閱
創建訂閱,首先需要獲取到一個 billabel 模型實例,一般情況下就是?`App\User`?實例。然后你可以使用?`newSubscription`?方法來創建該模型的訂閱:
~~~
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($stripeToken);
~~~
`newSubscription`?方法的第一個參數應該是訂閱的名稱。如果你的應用程序只是提供一個簡單的訂閱,你可以簡單的設置為?`main`?或者?`primary`。第二個參數需要指定用戶的 Stripe / Braintree 訂閱計劃。這里的值應該和在 Stripe 或 Braintree 的中的對應值保持一致。
`create`?方法會創建訂閱并且更新客戶 ID 和相關的支付信息到數據庫。
#### Additional User Details
如果你需要添加額外的客戶細節信息,你可以在?`create`?方法的第二個參數中進行傳遞:
~~~
$user->newSubscription('main', 'monthly')->create($stripeToken, [
'email' => $email,
]);
~~~
查閱一下 Stripe 的?[創建客戶文檔](https://stripe.com/docs/api#create_customer)?或對應的?[Braintree 文檔](https://developers.braintreepayments.com/reference/request/customer/create/php)?了解更多支持的客戶細節信息的內容。
#### 優惠券
如果你想在創建訂閱的時候使用優惠券,你可以使用?`withCoupon`?方法:
~~~
$user->newSubscription('main', 'monthly')
->withCoupon('code')
->create($stripeToken);
~~~
### 查詢訂閱狀態
一旦用戶在你的應用程序中完成了訂閱,你可以使用大量便利的方法來查詢他們的訂閱狀態。首先,?`subscribed`?方法會返回?`true`?如果用戶已經激活了訂閱,即使該訂閱正在試用期內。
~~~
if ($user->subscribed('main')) {
//
}
~~~
也可以在?[路由中間件](https://laravel-china.org/docs/laravel/5.4/middleware)?中使用?`subscribed`?方法,來基于用戶的訂閱狀態過濾用戶請求:
~~~
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed('main')) {
// 用戶并沒有完成支付...
return redirect('billing');
}
return $next($request);
}
~~~
如果你想知道用戶是否在一個常識周期內,你可以使用?`onTrial`?方法。該方法可以用來提示用戶他們還在試用期之內:
~~~
if ($user->subscription('main')->onTrial()) {
//
}
~~~
`subscribedToPlan`?方法可以基于給定的 Stripe / Braintree 計劃 ID 來判斷用戶是否有訂閱該計劃。下面的示例,我們在判斷用戶的?`main`?訂閱是否成功激活訂閱了?`monthly`?計劃。
~~~
if ($user->subscribedToPlan('monthly', 'main')) {
//
}
~~~
#### 取消訂閱的狀態
可以使用?`cancelled`?方法來判斷用戶是否之前已經完成了訂閱,但是又已經取消了他們的訂閱:
~~~
if ($user->subscription('main')->cancelled()) {
//
}
~~~
可以去判斷用戶是否已經取消了他們的訂閱,但是還是在訂閱的「寬限期」直到訂閱完全到期。比方說,如果一個用戶在 3 月 5 號取消了訂閱,但是原本訂閱定于 3 月 10 號到期,那么該用戶就會處于「寬限期」直到 3 月 10 號。需要注意的是,`subscribed`?方法在寬限期還是會返回?`true`?:
~~~
if ($user->subscription('main')->onGracePeriod()) {
//
}
~~~
### 更改訂閱計劃
在用戶完成訂閱之后,有時會更改訂閱計劃。將用戶切換到一個新的訂閱計劃,需要傳遞訂閱計劃的 ID 給?`swap`?方法:
~~~
$user = App\User::find(1);
$user->subscription('main')->swap('provider-plan-id');
~~~
如果用戶在試用期,試用期的期限會被保留。如果訂閱有限量的「份額」存在,該份額數目也會被保留。
如果你想在更改用戶訂閱計劃的時候取消用戶當前訂閱的試用期,可以使用?`skipTrial`?方法:
~~~
$user->subscription('main')
->skipTrial()
->swap('provider-plan-id');
~~~
### 訂閱份額
> {note} 按量計費訂閱方式只支持 Stripe,因為 Braintree 并沒有按量計費訂閱之類的功能。
有些時候訂閱是會受「數量」影響的。舉個例子,你的應用程序的付費方式可能是每個賬戶 $10 /人/月。你可以使用?`incrementQuantity`?和?`decrementQuantity`?方法輕松的增加或者減少你的訂閱數量。
~~~
$user = User::find(1);
$user->subscription('main')->incrementQuantity();
// 當前的訂閱數量加 5
$user->subscription('main')->incrementQuantity(5);
$user->subscription('main')->decrementQuantity();
// 當前的訂閱數量減 5
$user->subscription('main')->decrementQuantity(5);
~~~
另外,你可以使用?`updateQuantity`?方法指定訂閱的數量:
~~~
$user->subscription('main')->updateQuantity(10);
~~~
查閱?[Stripe 文檔](https://stripe.com/docs/guides/subscriptions#setting-quantities)?,了解更多關于按量訂閱的信息。
### 訂閱稅額設置
為了設置每筆訂閱的稅收比例,需要在 billable 模型內實現?`taxPercentage`?方法,該方法返回一個 0 到 100 的數字,精度不超過小數點后兩位。
~~~
public function taxPercentage() {
return 20;
}
~~~
`taxPercentage`?方法可以讓你在模型基礎上應用稅率,這對于用戶群跨越多個國家和稅收的情況非常有幫助。
> {note} 但是需要注意的是?`taxPercentage`?方法僅適用于訂閱付費模式。如果你使用 Cashier 完成一次性收費的業務,你需要手動的指定稅率。
### 取消訂閱
取消訂閱,可以直接在用戶的訂閱上調用?`cancel`?方法:
~~~
$user->subscription('main')->cancel();
~~~
當訂閱被取消之后,Cashier 自動會填充數據庫中的?`ends_at`?列。這列用來指定?`subscribed`?方法什么時候應該返回?`false`。比如,如果一個客戶在 3 月 1 號取消了訂閱,但是訂閱并不會結束直到 3 月 5 號才到期,所以?`subscribed`?方法會繼續返回?`true`?直到 3 月 5 號。
你也可以使用?`onGracePeriod`?方法來判斷用戶是否已經取消了訂閱,但是還在一個「寬限期」:
~~~
if ($user->subscription('main')->onGracePeriod()) {
//
}
~~~
如果你想直接取消一個訂閱,可以直接調用?`cancelNow`?方法:
~~~
$user->subscription('main')->cancelNow();
~~~
### 恢復訂閱
如果一個用戶取消訂閱之后,你可以使用?`resume`?方法來恢復他的訂閱。該方法**只適用于**用戶取消訂閱之后的寬限期:
~~~
$user->subscription('main')->resume();
~~~
如果用戶取消訂閱之后,然后恢復訂閱之時訂閱已經到期,他們并不會被立即支付費用。代替的是,訂閱會被簡單進入重新激活的狀態,需要按照原本的支付流程再次進行支付。
### 更新信用卡信息
`updateCard`?方法被用于更新客戶的信用卡信息。該方法會接受一個 Stripe 的 token,并設置一個新的信用卡作為默認的結算來源:
~~~
$user->updateCard($stripeToken);
~~~
## 試用訂閱
### 必須提供信用卡信息
如果你想用戶在選擇試用訂閱計劃的時候必須提供信用卡信息,可以使用在創建訂閱時使用?`trialDays`?方法:
~~~
$user = User::find(1);
$user->newSubscription('main', 'monthly')
->trialDays(10)
->create($stripeToken);
~~~
該方法會設置一個試用期結束日期在數據庫的訂閱記錄上,同時告知 Stripe / Braintree 在該日期之前并不開始結算。
> {note} 如果客戶沒有在試用期結束之前取消訂閱,訂閱會被自動結算,所以需要在試用期結束之前通知你的客戶。
你可以使用用戶實例的?`onTrial`?方法來判斷用戶是否在試用期內,或者使用訂閱實例上的?`onTrial`方法也可以。下面是示例:
~~~
if ($user->onTrial('main')) {
//
}
if ($user->subscription('main')->onTrial()) {
//
}
~~~
### 非必須提供信用卡
如果你打算提供一個試用期,并且不需要用戶立馬提交信用卡信息,你可以簡單的在數據庫的用戶表記錄中設置?`trial_ends_at`?列為你需要的試用期結束日期。這是用戶注冊過程中的典型手法:
~~~
$user = User::create([
// 填充其他用戶屬性...
'trial_ends_at' => Carbon::now()->addDays(10),
]);
~~~
> {note} 請確保在你的模型中已經為?`trial_ends_at`?添加[日期轉換器](https://laravel-china.org/docs/laravel/5.4/eloquent-mutators#date-mutators)。
Cashier 把這種類型的試用引用為「generic trial」, 因為它并沒有關聯任何已存在的訂閱。`User`?實例上的?`onTrial`會返回?`true`?值,只要當前的日期沒有超過?`trial_ends_at`?的值:
~~~
if ($user->onTrial()) {
// 用戶在試用期內...
}
~~~
你可以使用?`onGenericTrial`?方法來判斷用戶是否在一個「generic」 試用期間,其實并沒有創建任何真實的訂閱:
~~~
if ($user->onGenericTrial()) {
// 用戶在 「generic」 試用期內...
}
~~~
一旦你準備創建一個真實的訂閱給用戶,你通常可以使用?`newSubscription`?方法:
~~~
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($stripeToken);
~~~
## 處理 Stripe Webhooks
Stripe 和 Braintree 都能通過 webhooks 通知大量的事件給你的應用程序。為了處理 Stripe webhooks,定義一個路由指向 Cashier's webhook 控制器。該控制器會處理所有傳來的請求并分發到合適的控制器方法:
~~~
Route::post(
'stripe/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
~~~
> {note} 注冊好路由之后,記得在 Stripe 的控制面板中配置好 webhook 跳轉的 URL。
Cashier 的控制器默認會在多次失敗的支付嘗試后自動取消該訂閱(取決于你的 Stripe 設置);但是,稍后你會發現,你可以根據自己的需要擴展該控制器。
#### Webhooks & CSRF 保護
因為 Stripe webhooks 需要繞過 Laravel 的?[CSRF 保護](https://laravel-china.org/docs/laravel/5.4/csrf)?,所以需要確保將相關的 URI 作為例外添加到你的?`VerifyCsrfToken`?中間件或者在?`web`?中間件集合之外定義相關路由。
~~~
protected $except = [
'stripe/*',
];
~~~
### 定義 Webhook 事件處理器
Cashier 對于失敗的支付自動進行取消訂閱的處理,但是如果你想對 Stripe webhook 事件進行額外的處理,可以簡單的擴展一下 Webhook 控制器。你的方法名稱應該與 Cashier 的預設慣例一直,特別的是,方法名應該以?`handle`?為前綴,然后以 「駝峰」命名的方式加上你要處理的 Stripe webhook 的名稱。比如,如果你希望去處理?`invoice.payment_succeeded`?webhook, 你應該添加一個?`handleInvoicePaymentSucceeded`?方法到控制器:
~~~
<?php
namespace App\Http\Controllers;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
class WebhookController extends CashierController
{
/**
* 處理 Stripe webhook。
*
* @param array $payload
* @return Response
*/
public function handleInvoicePaymentSucceeded($payload)
{
// Handle The Event
}
}
~~~
### 訂閱失敗
如果客戶的信用卡過期了怎么辦?不用擔心 - Cashier 的 webhook 控制器會輕易的取消客戶的訂閱。像上面提到的,你所需要做的知識簡單的指定一個路由到該控制器:
~~~
Route::post(
'stripe/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
~~~
就這么簡單!失敗的支付會被控制器捕捉、并處理。Cashier 控制器會自動取消客戶的訂閱當 Stripe 判斷該訂閱已經失敗(正常會經過三次錯誤的支付嘗試)。
## 處理 Braintree Webhooks
Stripe 和 Braintree 都能通過 webhooks 通知大量的事件給你的應用程序。為了處理 Braintree webhooks,定義一個路由指向 Cashier's webhook 控制器。該控制器會處理所有傳來的請求并分發到合適的控制器方法:
~~~
Route::post(
'braintree/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
~~~
> {note} 注冊好路由之后,記得在 Braintree 的控制面板中配置好 webhook 跳轉的 URL。
Cashier 的控制器默認會在多次失敗的支付嘗試后自動取消該訂閱(取決于你的 Braintree 設置);但是,稍后你會發現,你可以根據自己的需要擴展該控制器。
#### Braintree & CSRF 保護
因為 Stripe webhooks 需要繞過 Laravel 的?[CSRF 保護](https://laravel-china.org/docs/laravel/5.4/csrf)?,所以需要確保將相關的 URI 作為例外添加到你的?`VerifyCsrfToken`?中間件或者在?`web`?中間件集合之外定義相關路由。
~~~
protected $except = [
'braintree/*',
];
~~~
### 定義 Webhook 事件處理器
Cashier 對于失敗的支付自動進行取消訂閱的處理,但是如果你想對 Braintree webhook 事件進行額外的處理,可以簡單的擴展一下 Webhook 控制器。你的方法名稱應該與 Cashier 的預設慣例一直,特別的是,方法名應該以?`handle`?為前綴,然后以 「駝峰」命名的方式加上你要處理的 Braintree webhook 的名稱。比如,如果你希望去處理?`dispute_opened`?webhook, 你應該添加一個?`handleDisputeOpened`?方法到控制器:
~~~
<?php
namespace App\Http\Controllers;
use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
class WebhookController extends CashierController
{
/**
* 處理 Braintree webhook。
*
* @param WebhookNotification $webhook
* @return Response
*/
public function handleDisputeOpened(WebhookNotification $notification)
{
// Handle The Event
}
}
~~~
### 訂閱失敗
如果客戶的信用卡過期了怎么辦?不用擔心 - Cashier 的 webhook 控制器會輕易的取消客戶的訂閱。像上面提到的,你所需要做的知識簡單的指定一個路由到該控制器:
~~~
Route::post(
'braintree/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
~~~
就這么簡單!失敗的支付會被控制器捕捉、并處理。Cashier 控制器會自動取消客戶的訂閱當 Braintree 判斷該訂閱已經失敗(正常會經過三次錯誤的支付嘗試)。不要忘了:你需要在 Braintree 的控制面板正確配置 webhook URI。
## 一次性收費
### 單次付費
> {note} 使用 Stripe 時,?`charge`?方法是以你當前貨幣的**最小面值**作為單位的。但是,使用 Braintree 時,你應該傳遞完整的美元金額給?`charge`?方法:
如果你想對一個已訂閱客戶執行「單次」收費,你可以在 billable 模型實例上使用?`charge`?方法。
~~~
// Stripe 以美分結算...
$user->charge(100);
// Braintree 以美元結算...
$user->charge(1);
~~~
`charge`?方法可以接受一個數據作為第二個參數,允許你在 Stripe / Braintree 支付時傳遞任何可用的參數。查閱 Stripe 或 Braintree 的文檔了解都有哪些可用的參數。
~~~
$user->charge(100, [
'custom_option' => $value,
]);
~~~
當支付結算失敗是?`charge`?方法會拋出一個異常。如果結算成功,會返回完成的 Stripe / Braintree 響應。
~~~
try {
$response = $user->charge(100);
} catch (Exception $e) {
//
}
~~~
### 結算時生成發票
有時候你只是需要完成一個一次性的支付,但是也需要為結算生成一個發票信息,這樣你可以提供 PDF 的票據給你的客戶。`invoiceFor`?方法就是做這個的。例如,讓我們給客戶開具一個 $5.00 的「單次收費」發票:
~~~
// Stripe 以美分結算...
$user->invoiceFor('One Time Fee', 500);
// Braintree 以美元結算...
$user->invoiceFor('One Time Fee', 5);
~~~
生成發票將立即對用戶的信用卡收取費用。`invoiceFor`?方法可以接受一個數據作為第三個參數,允許你在 Stripe / Braintree 結算時傳遞任何可用的參數。
~~~
$user->invoiceFor('One Time Fee', 500, [
'custom-option' => $value,
]);
~~~
> {note}?`invoiceFor`?方法在當多次嘗試失敗支付后也會產生一個 Stripe 發票。如果你不需要為失敗重試的支付生成發票,你需要在第一次失敗結算是就調用 Stripe 的 API 關閉它們。
## 發票
你可以使用?`invoices`?方法輕松獲取 billable 模型的所有發票信息:
~~~
$invoices = $user->invoices();
// 在結果中包含待開發票...
$invoices = $user->invoicesIncludingPending();
~~~
當將發票信息列出來給用戶時,你可能需要使用發票的輔助函數來顯示相關的發票信息。例如,你可能希望將發票列表以表格的形式顯示,允許用戶輕松的下載它們:
~~~
<table>
@foreach ($invoices as $invoice)
<tr>
<td>{{ $invoice->date()->toFormattedDateString() }}</td>
<td>{{ $invoice->total() }}</td>
<td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
</tr>
@endforeach
</table>
~~~
### 生成發票的 PDFs
在生成發票的 PDF 文件之前,你需要先安裝?`dompdf`?PHP 庫:
~~~
composer require dompdf/dompdf
~~~
然后,在路由或控制器內,使用?`downloadInvoice`?方法來生成一個發票的 PDF 的下載響應。瀏覽器會自動開始下載該文件:
~~~
use Illuminate\Http\Request;
Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) {
return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
});
~~~
- 前言
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 請求周期
- 開發環境部署
- Homestead
- Valet
- 核心概念
- 服務容器
- 服務提供者
- Facades
- Contracts
- HTTP層
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- Session
- 表單驗證
- 前端
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- Passport OAuth 認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 錯誤與日志
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Scout 全文搜索
- Socialite 社會化登錄