laravel 會(huì)計(jì)員(Cashier)
簡(jiǎn)介
Laravel Cashier 提供了一種表現(xiàn)流利的接口來(lái)支持 Stripe 和 Braintree 的計(jì)費(fèi)服務(wù)。它封裝了許多你畏懼編寫(xiě)的認(rèn)購(gòu)計(jì)費(fèi)樣板。除了基本的認(rèn)購(gòu)管理,Cashier 還可以處理優(yōu)惠券、服務(wù)升級(jí)、服務(wù)替換、認(rèn)購(gòu)份額、取消并保留期限內(nèi)可用、甚至是生成 PDF 類(lèi)型的發(fā)票。
Stripe 配置
Composer
首先,添加 Strpe 的 Cashier 包到你的 composer.json 文件,然后運(yùn)行 composer update 命令:
"laravel/cashier": "~6.0"
服務(wù)提供者
然后在 app 配置文件中注冊(cè) Laravel\Cashier\CashierServiceProvider 服務(wù)提供者。
數(shù)據(jù)庫(kù)遷移
在使用 Cashier 之前,我們也需要去準(zhǔn)備一下數(shù)據(jù)庫(kù)。我們需要在 users 表中添加幾列,并且創(chuàng)建一個(gè)新的 subscriptions 表來(lái)保留客戶(hù)的認(rèn)購(gòu):
Schema::table('users', function ($table) {
$table->string('stripe_id')->nullable();
$table->string('card_brand')->nullable();
$table->string('card_lost_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();
});
一旦遷移表創(chuàng)建完畢,你可以通過(guò) artisan 命令 migrate 進(jìn)行遷移操作。
模型起步
接著,你需要在你的模型定義中添加 Billable trait:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
提供秘鑰
然后你需要在 services.php 配置文件中對(duì)你的 Stripe 進(jìn)行配置:
'stripe' => [
'model' => App\User::class,
'secret' => env('SIRIPE_SECRET'),
];
Braintree 配置
注意事項(xiàng)
對(duì)于大多數(shù)的操作,Stripe 和 Braintree 在 Cashier 中的方法實(shí)現(xiàn)是相同的,兩種服務(wù)都提供了對(duì)使用信用卡進(jìn)行認(rèn)購(gòu)的計(jì)費(fèi)支持。但是 Braintree 也支持通過(guò) PayPal 的支付方式。Braintree 也相應(yīng)的缺乏一些 Stripe 所支持的特性。你需要根據(jù)自身的需求去對(duì)比選擇使用 Stripe 還是 Braintree。
- Braintree 支持 PayPal 支付,而 Stripe 不支持。
- Braintree 在認(rèn)購(gòu)時(shí)不支持
increment和decrement,這是 Braintree 自身的限制,而不是 Cashier 的局限性。 - Braintree 不支持百分比的折扣。這是 Braintree 自身的限制,而不是 Cashier 所限制的。
Composer
首先,你需要在你的 composer.json 文件中進(jìn)行添加 Cashier 的文件包并使用 composer update 命令進(jìn)行安裝:
"laravel/cashier-braintree": "~1.0"
服務(wù)提供者
接著,注冊(cè) laravel\Cashier\CashierServiceProvider 服務(wù)提供者到你的 app 配置文件。
安排信用優(yōu)惠
在你使用 Braintree 的 Cashier 之前,你需要先在你的 Braintree 控制面板中定義一個(gè) plan-credit 折扣。這個(gè)折扣會(huì)被用于正確的按比例從年度改為按月計(jì)費(fèi)或者從月到年計(jì)費(fèi)。你可以在控制面板中定義這個(gè)折扣為任意你所希望的值,Cashier 會(huì)在我們使用優(yōu)惠時(shí)進(jìn)行正確的結(jié)算。
數(shù)據(jù)庫(kù)遷移
在使用 Cashier 之前,我們也需要預(yù)先構(gòu)建好數(shù)據(jù)庫(kù)。我們需要在 users 表中添加幾列并且創(chuàng)建一個(gè) subscriptions 表來(lái)處理所有的用戶(hù)認(rèn)購(gòu):
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();
});
一旦遷移的表結(jié)構(gòu)構(gòu)建完成,你就可以通過(guò) artisan 命令 migrate 運(yùn)行遷移操作。
模型起步
接著,在你的模型中添加 Billable trait:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
提供配置
然后,你需要在你的 services.php 配置文件中進(jìn)行相關(guān)配置:
'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'),
];
你還需要在你的 AppServiceProvider 服務(wù)提供者的 boot 方法中運(yùn)行下面的 Braintree SDK 的方法:
Braintree_Configuration::environment(env('BRAINTREE_ENV'));
Braintree_Configuration::merchantId(env('BRAINTREE_MERCHANT_ID'));
Braintree_Configuration::publicKey(env('BRAINTREE_PUBLIC_KEY'));
Braintree_Configuration::privateKey(env('BRAINTREE_PRIVATE_KEY'));
訂閱/認(rèn)購(gòu)
創(chuàng)建一個(gè)認(rèn)購(gòu)
為了創(chuàng)建一個(gè)認(rèn)購(gòu),你首先需要先從 billable 模型中提取一個(gè)實(shí)例,通常情況下它是 App\User 的一個(gè)實(shí)例。一旦你擁有了一個(gè)模型的實(shí)例,你就可以使用 newSubscription 方法來(lái)創(chuàng)建一個(gè)該模型的認(rèn)購(gòu):
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($creditCardToken);
傳遞到 newSubscription 方法的第一個(gè)參數(shù)應(yīng)該是認(rèn)購(gòu)的名稱(chēng)。如果你的應(yīng)用僅僅只提供一種單一功能的訂閱,你或許可以定義為 main 或者 primary。而第二個(gè)參數(shù)則是用戶(hù)需要認(rèn)購(gòu)的 Stripe / Braintree 的付費(fèi)計(jì)劃。這個(gè)值應(yīng)該和你的 Stripe 或者 Braintree 中的計(jì)劃標(biāo)識(shí)相對(duì)應(yīng)。
create 方法會(huì)自動(dòng)的創(chuàng)建一個(gè)認(rèn)購(gòu),并且同客戶(hù)的 ID 和 其他賬單相關(guān)的信息更新到數(shù)據(jù)庫(kù)。
其他用戶(hù)詳細(xì)信息
如果你需要指定添加一些額外的用戶(hù)信息,你可以在 create 方法中傳遞第二個(gè)參數(shù):
$user->newSubscription('main', 'monthly')->create($creditCardToken, [
'email' => $email,
]);
你可以查看 Stripe 和 Braintree 的文檔來(lái)獲取其所支持的額外字段。
優(yōu)惠
如果你需要在創(chuàng)建一個(gè)認(rèn)購(gòu)時(shí)使用優(yōu)惠,你可以使用 withCoupon 方法:
$user->newSubscription('main', 'monthly')
->withCoupon('code')
->create($creditCardToken);
檢查認(rèn)購(gòu)狀態(tài)
一旦訂閱了你的應(yīng)用,你可以通過(guò)各種簡(jiǎn)單方便的方法來(lái)檢查他們的認(rèn)購(gòu)狀態(tài)。首先,你可以通過(guò) subscribed 方法來(lái)檢查用戶(hù)是否激活了認(rèn)購(gòu),即使用戶(hù)是在試用期中,該方法也會(huì)返回 true:
if ($user->subscribed('main')) {
//
}
通過(guò) subscribed 方法,我們可以創(chuàng)建一個(gè)極好的路由中間件,用來(lái)過(guò)濾只有處于訂閱狀態(tài)的用戶(hù)可以訪(fǎng)問(wèn)路由或者控制器:
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);
}
如果你需要判斷用戶(hù)是不是還在試用期之中,你可以使用 onTrial 方法,該方法通常是用來(lái)向用戶(hù)提醒其還在試用期之中:
if ($user->subscription('main')->onTrial()) {
//
}
subscribedToPlan 方法用來(lái)判斷用戶(hù)是否訂閱了所給定的 Stripe / Braintree 計(jì)劃。比如,我們需要判斷用戶(hù)的 main 認(rèn)購(gòu)是不是通過(guò) monthly 計(jì)劃來(lái)進(jìn)行付費(fèi)的:
if ($user->subscribedToPlan('monthly', 'main')) {
//
}
取消認(rèn)購(gòu)狀態(tài)
你可以通過(guò) cancelled 方法來(lái)判斷用戶(hù)是否曾經(jīng)進(jìn)行過(guò)認(rèn)購(gòu),但是卻在使用的過(guò)程中取消了繼續(xù)付費(fèi)訂閱:
if ($user->subscription('main')->cancelled()) {
//
}
或許,你需要判斷一個(gè)用戶(hù)取消了持續(xù)訂閱,但是他的訂閱期還未過(guò)期。比如,一個(gè)用戶(hù)在 3 月 5 號(hào)取消了持續(xù)訂閱,但是他的上次付費(fèi)服務(wù)還可以繼續(xù)使用到 3 月 10 號(hào)。他還在其可用期內(nèi)。你應(yīng)該注意只要是在可用期內(nèi),不論是否取消了持續(xù)訂閱或者還是在試用期內(nèi),subscribed 方法都會(huì)返回 true :
if ($user->subscription('main')->onGracePeriod()) {
//
}
改變付費(fèi)計(jì)劃
在用戶(hù)認(rèn)購(gòu)了你的應(yīng)用之后,有時(shí)候或許他們想要變更付費(fèi)的計(jì)劃。你可以使用 swap 方法來(lái)變更到一個(gè)新的認(rèn)購(gòu)計(jì)劃。比如,我們可以非常簡(jiǎn)單的切換當(dāng)前用戶(hù)到 premium 認(rèn)購(gòu):
$user = App\User::find(1);
$user->subscription('main')->swap('provider-plan-id');
如果用戶(hù)還處于試用期之中,那么即使切換付費(fèi)計(jì)劃,他們的試用期也是會(huì)被保持。還有,如果之前認(rèn)購(gòu)的份數(shù)存在,相應(yīng)的認(rèn)購(gòu)的飛鼠也會(huì)被保持:
$user->subscription('main')->swap('provider-plan-id');
如果你想要在切換付費(fèi)方案的同時(shí)取消之前的試用期,你可以使用 skipTrial 方法:
$user->subscription('main')
->skipTrial()
->swap('provider-plan-id');
認(rèn)購(gòu)份額
注意:認(rèn)購(gòu)份額只支持 Stripe 版本的 Cashier。Braintree 并沒(méi)有 Stripe 份額的概念。
有時(shí)候認(rèn)購(gòu)時(shí)受份額影響的。比如,你的應(yīng)用可能是按照每用戶(hù)每月 $10 來(lái)進(jìn)行計(jì)費(fèi)的。要輕松的遞增或遞減認(rèn)購(gòu)的數(shù)量,你可以使用 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)
另外,你也可以通過(guò)使用 updateQuantity 放法來(lái)設(shè)置一個(gè)指定的份額:
$user->subscription('main')->updateQuantity(10);
關(guān)于更多的認(rèn)購(gòu)份額信息,請(qǐng)查看 Strpe documentation。
認(rèn)購(gòu)稅率
在 Cashier 中,可以非常簡(jiǎn)單的提供 tax_percent 值到 Stripe / Braintree。為了指定用戶(hù)支付認(rèn)購(gòu)時(shí)的稅率百分比,你需要實(shí)現(xiàn) taxPercentage 方法到你的 billable 模型中,并且在該方法中返回一個(gè) 0 - 100 的值,該值不應(yīng)該多于 2 個(gè)小數(shù):
public function taxPercentage() {
return 20;
}
這使你可以應(yīng)用在多種模型間計(jì)算不同的稅率,這意味著你可以跨越多個(gè)國(guó)家的用戶(hù)群來(lái)設(shè)置結(jié)算稅率。
取消訂閱
你可以簡(jiǎn)單的調(diào)用 cancel 方法來(lái)取消用戶(hù)訂閱:
$user->subscription('main')->cancel();
當(dāng)認(rèn)購(gòu)被取消時(shí),Cashier 會(huì)自動(dòng)的設(shè)置 ends_at 列到數(shù)據(jù)庫(kù)中。該列會(huì)被用來(lái)了解當(dāng)調(diào)用 subscribed 方法時(shí)何時(shí)應(yīng)該返回 false。比如,如果用戶(hù)在 3 月 1 號(hào)取消了認(rèn)購(gòu),但是上一次認(rèn)購(gòu)期應(yīng)該是在 3 月 5 號(hào)結(jié)束,所以, subscribed 方法會(huì)持續(xù)返回 true 直到 3 月 5 號(hào)。
你可以通過(guò) onGracePeriod 方法來(lái)判斷一個(gè)已經(jīng)取消訂閱的用戶(hù)是否還在其可用期內(nèi):
if ($user->subscription('main')->onGracePeriod()) {
//
}
恢復(fù)訂閱
如果用戶(hù)取消了其訂閱,并且想要恢復(fù)訂閱,那么你可以使用 resume 方法。用戶(hù)必須是在其可用期內(nèi)才可以恢復(fù)訂閱:
$user->subscription('main')->resume();
如果用戶(hù)取消了訂閱,并在其可用期內(nèi)恢復(fù)了訂閱,那么認(rèn)購(gòu)不會(huì)立即進(jìn)行計(jì)費(fèi)支付。訂閱只是被重新激活,其付費(fèi)周期還是基于原先的計(jì)費(fèi)周期。
試用
提供試用并記錄信用卡信息
如果你想要在提供試用期的同時(shí)還收集支付方式的信息。你應(yīng)該在你創(chuàng)建用戶(hù)認(rèn)購(gòu)時(shí)使用 trialDays 方法:
$user = User::find(1);
$user->newSubscription('main', 'monthly')
->trialDays(10)
->create($creditCardToken);
該方法會(huì)設(shè)置一個(gè)試用結(jié)束日期到數(shù)據(jù)庫(kù)的認(rèn)購(gòu)記錄中。并且會(huì)通知 Stripe / Braintree 暫不計(jì)費(fèi),直到超過(guò)了該日期才開(kāi)始進(jìn)行計(jì)費(fèi)。
注意:如果你的客戶(hù)并沒(méi)有在試用期結(jié)束之前取消訂閱,那么他們會(huì)在試用期結(jié)束時(shí)自動(dòng)扣費(fèi),所以你應(yīng)該在試用期結(jié)束前那天進(jìn)行用戶(hù)通知。
你可以通過(guò)用戶(hù)實(shí)例的 onTrial 方法或者認(rèn)購(gòu)實(shí)例的 onTrial 方法來(lái)判斷用戶(hù)是否在試用期:
if ($user->onTrial('main')) {
//
}
if ($user->subscription('main')->onTrial()) {
//
}
提供試用的同時(shí)不記錄支付信息
如果你想要在提供試用期的同時(shí)而不收集用戶(hù)的支付方法信息,你可以簡(jiǎn)單的設(shè)置 trial_ends_at 列到你的用戶(hù)記錄中。你應(yīng)該在該列中設(shè)置你所期望的試用結(jié)束的日期。比如,這一般是用戶(hù)注冊(cè)過(guò)程中的典型做法:
$user = User::create([
// Populate other user properties...
'trial_ends_at' => Carbon::now()->addDays(10),
]);
Cashier 會(huì)認(rèn)為這中類(lèi)型的試用是一種通用的試用期,因?yàn)檫@個(gè)日期并不會(huì)被關(guān)聯(lián)到任何已有的訂閱中。如果當(dāng)前日期并沒(méi)有超過(guò) trial_ends_at 所設(shè)置的值,在 User 實(shí)例中的 onTrial 方法將會(huì)返回為 true:
if ($user->onTrial()) {
// User is within their trial period...
}
你也可以通過(guò) onGenericTrial 方法來(lái)判斷用戶(hù)是否還沒(méi)有進(jìn)行任何認(rèn)購(gòu),并且還在通用期內(nèi):
if ($user->onGenericTrial()) {
// User is within their 'generic' trial period...
}
一旦你準(zhǔn)備好去創(chuàng)建一個(gè)真實(shí)的認(rèn)購(gòu)到用戶(hù),你通常應(yīng)該使用 newSubscription 方法:
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($creditCardToken);
處理 Stripe Webhooks
失敗的認(rèn)購(gòu)
如果用戶(hù)的信用卡過(guò)期了怎么辦?不用擔(dān)心,Cashier 為你提供了便捷的取消用戶(hù)訂閱的 Webhook 控制器。你只需要將控制器添加到路由就可以了:
Route::post(
'stripe/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
就那么簡(jiǎn)單!失敗的支付會(huì)在這個(gè)控制器中捕獲和處理。這個(gè)控制器會(huì)在 Stripe 確定用戶(hù)的訂閱支付失敗時(shí)(通常是在三次支付失?。┤∠脩?hù)的訂閱。不要忘記在你的 Stripe 控制面板中配置 webhook 的 URI。
因?yàn)?Stripe webhooks 需要穿過(guò) laravel 的 CSRF 中間件,所以你應(yīng)該將你的路由抽離出 web 中間件組,或者在你的 VerifyCsrfToken 中間件中添加排除的 URI:
protected $except = [
'stripe/*',
];
其他 Webhooks
如果你想要處理額外的 Stripe webhook 事件,你可以簡(jiǎn)單的繼承 Webhook 控制器。你所繼承的控制器名中的方法名稱(chēng)應(yīng)該與 Cashier 的預(yù)期約定想對(duì)應(yīng)。特別的,方法名稱(chēng)應(yīng)該有 handle 前綴,并且以大寫(xiě)駝峰的方式命名你所期望捕獲的 Stripe webhook 事件名稱(chēng)。比如,你想要處理 invoice.payment_succeeded webhook, 那么你應(yīng)該在控制器中添加 handleInvociePaymentSucceeded 方法:
<?php
namespace App\Http\Controllers;
use Laravel\Cashier\Http\Controllers\WebhookController as BaseController;
class WebhookController extends BaseController
{
/**
* Handle a Stripe webhook.
*
* @param array $payload
* @return Response
*/
public function handleInvoicePaymentSucceeded($payload)
{
// Handle The Event
}
}
處理 Braintree Webhooks
失敗的訂閱
如果用戶(hù)的信用卡過(guò)期了怎么辦?不用擔(dān)心,Cashier 為你提供了便捷的取消用戶(hù)訂閱的 Webhook 控制器。你只需要將控制器添加到路由就可以了:
Route::post(
'braintree/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
就那么簡(jiǎn)單!失敗的支付會(huì)在這個(gè)控制器中捕獲和處理。這個(gè)控制器會(huì)在 Braintree 確定用戶(hù)的訂閱支付失敗時(shí)(通常是在三次支付失?。┤∠脩?hù)的訂閱。不要忘記在你的 Braintree 控制面板中配置 webhook 的 URI。
因?yàn)?Stripe webhooks 需要穿過(guò) laravel 的 CSRF 中間件,所以你應(yīng)該將你的路由抽離出 web 中間件組,或者在你的 VerifyCsrfToken 中間件中添加排除的 URI:
protected $except = [
'braintree/*',
];
其他 Webhooks
如果你想要處理額外的 Braintree webhook 事件,你可以簡(jiǎn)單的繼承 Webhook 控制器。你所繼承的控制器名中的方法名稱(chēng)應(yīng)該與 Cashier 的預(yù)期約定想對(duì)應(yīng)。特別的,方法名稱(chēng)應(yīng)該有 handle 前綴,并且以大寫(xiě)駝峰的方式命名你所期望捕獲的 Stripe webhook 事件名稱(chēng)。比如,你想要處理 dispute_opened webhook, 那么你應(yīng)該在控制器中添加 handleDisputeOpened 方法:
<?php
namespace App\Http\Controllers;
use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController as BaseController;
class WebhookController extends BaseController
{
/**
* Handle a Braintree webhook.
*
* @param WebhookNotification $webhook
* @return Response
*/
public function handleDisputeOpened(WebhookNotification $notification)
{
// Handle The Event
}
}
一次性付費(fèi)
簡(jiǎn)單的付費(fèi)
注意:在使用 Stripe 時(shí),
charge方法接收的金額可以是相應(yīng)貨幣的最小單位。而 Braintree,你應(yīng)該傳遞美元到charge方法:
如果你想要對(duì)訂閱的用戶(hù)施行一次性付費(fèi),你可以使用 charge 方法:
// Stripe Accepts Charges In Cents...
$user->charge(100);
// Braintree Accepts Charges In Dollars...
$user->charge(1);
charge 方法也可以接收一個(gè)數(shù)組作為其第二個(gè)參數(shù),這個(gè)參數(shù)將傳遞給底層的 Stripe / Braintree 用于支付賬單的創(chuàng)建:
$user->charge(100, [
'custom_option' => $value,
]);
當(dāng)支付失敗時(shí),charge 方法將會(huì)拋出一個(gè)異常,如果支付成功則完整的 Stripe / Braintree 響應(yīng)將會(huì)被返回:
try {
$response = $user->charge(100);
} catch (Exception $e) {
//
}
支付并提供發(fā)票
有時(shí)候,你可能需要做一次性的支付,并且也要生成一個(gè)發(fā)票給用戶(hù)。你可以使用 invoiceFor 方法來(lái)生成發(fā)票并提供一個(gè) PDF 的收據(jù)給用戶(hù):
// Stripe Accepts Charges In Cents...
$user->invoiceFor('One Time Fee', 500);
// Braintree Accepts Charges In Dollars...
$user->invoiceFor('One Time Fee', 5);
開(kāi)具發(fā)票會(huì)立即向用戶(hù)的信用卡進(jìn)行收費(fèi)。invoiceFor 方法也接收一個(gè)數(shù)組作為第三個(gè)參數(shù),用于傳遞給底層的 Stripe / Braintree 支付賬單創(chuàng)建時(shí)使用:
$user->invoiceFor('One Time Fee', 500, [
'custom-option' => $value,
]);
注意:
invoiceFor方法會(huì)創(chuàng)建 Stripe 發(fā)票,這將重新嘗試失敗的計(jì)費(fèi)。如果你不希望開(kāi)具發(fā)票的同時(shí)嘗試失敗的支付,你需要使用 Stripe API 在他們首次失敗時(shí)就關(guān)閉這個(gè)支付。
發(fā)票
你可以通過(guò)使用 invoices 方法來(lái)檢索一個(gè)可計(jì)費(fèi)模型的發(fā)票數(shù)組:
$invoices = $user->invoices();
當(dāng)你為客戶(hù)列出其發(fā)票時(shí),你可以使用 invoice 幫助方法來(lái)顯示相應(yīng)的發(fā)票詳情。比如,你想要在表格中列出所有的發(fā)票,從而允許用戶(hù)方便的下載:
<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>
生成 PDF 類(lèi)型的發(fā)票
你需要安裝 dompdf PHP 類(lèi)庫(kù)來(lái)生成 PDF:
composer require dompdf/dompdf
在路由或者控制器中,使用 downloadInvoice 方法來(lái)生成一個(gè)供下載的 PDF 類(lèi)型的發(fā)票。該方法會(huì)自動(dòng)的生成正確的下載響應(yīng)到瀏覽器中:
Route::get('user/invoice/{invoice}', function ($invoiceId) {
return Auth::user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
});