Laravel Cashier (Stripe)
简介
Laravel Cashier Stripe 为 Stripe 的订阅计费服务提供了一个富有表现力、流畅的接口。它处理了几乎所有你害怕编写的订阅计费样板代码。除了基本的订阅管理,Cashier 还可以处理优惠券、交换订阅、订阅 「数量」、取消宽限期,甚至生成发票 PDF。
升级 Cashier
升级到新版本的 Cashier 时,请务必仔细阅读 升级指南。
注意:为了防止破坏性变更,Cashier 使用固定的 Stripe API 版本。 Cashier 13 使用 Stripe API 版本 2020-08-27
。Stripe API 版本将在次要版本上更新,以利用新的 Stripe 功能和改进。
安装
首先,使用 Composer 为 Stripe 安装 Cashier 扩展包:
composer require laravel/cashier
注意:为确保 Cashier 正确处理所有 Stripe 事件,请记得 设置 Cashier 的 webhook。
数据库迁移
Cashier 的服务提供器注册了自己的数据库迁移目录,因此请记住在安装此包后迁移数据库。Cashier 迁移将向 users
表中添 加多个列,并创建一个新的 subscriptions
表来保存客户的所有订阅:
php artisan migrate
如果需要覆盖 Cashier 附带的迁移,可以使用 vendor:publish
Artisan 命令发布它们:
php artisan vendor:publish --tag="cashier-migrations"
如果你想阻止 Cashier 的迁移完全运行,可以使用 Cashier 提供的ignoreMigrations
方法。通常应在 AppServiceProvider
类的 register
方法中调用此方法:
use Laravel\Cashier\Cashier;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Cashier::ignoreMigrations();
}
注意:Stripe 建议用于存储 Stripe 标识符的任何列都应区分大小写。因此,在使用 MySQL 时,应该确保将 stripe_id
列排序规则设置为 utf8_bin
。更多关于这方面的信息可以在 Stripe 文档 中找到。
配置
订单模型
在使用 Cashier 之前,需要将 Billable
trait 添加到可订单模型定义中 。通常会放在 App\Models\User
模型中。这个特性提供了多个方法以便执行常用支付任务,如创建订阅、应用优惠券和更新支付方法信息:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
Cashier 默认假设你的 Billable 模型是 Laravel 自带的 App\Models\User
类。如果需要修改可以在 useCustomerModel
方法定义一个不同的模型。通常此方法在 AppServiceProvider
类的boot
方法中被调用:
use App\Models\Cashier\User;
use Laravel\Cashier\Cashier;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Cashier::useCustomerModel(User::class);
}
注意:如果你使用的不是 Laravel 自带的 App\Models\User
模型,需要发布并修改默认的 Cashier 迁移 文件以匹配你使用模型对应的表名。
API 秘钥
接下来需要在 .env
文件中配置 Stripe 秘钥,可以在 Stripe 后台控制面板中获取Stripe API 秘钥:
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
货币配置
Cashier 默认货币是美元 (USD),可以在 .env
中设置 CASHIER_CURRENCY
环境变量来修改默认的货币配置:
CASHIER_CURRENCY=eur
除了配置 Cashier 的货币之外,还可以在格式化用于显示在发票上的金额时指定本地化配置。在底层,Cashier 使用了 PHP 的 NumberFormatter
类 来设置本地货币:
CASHIER_CURRENCY_LOCALE=nl_BE
注意:为了使用本地化配置而不是 en
,需要确保安装了 PHP ext-intl
PHP 扩展并在服务器上启用配置。
税务配置
感谢Stripe 税务,可以自动计算 Stripe 生成的所有发票的税费。 可以通过应用程序的 App\Providers\AppServiceProvider
类的 boot
方法中调用 calculateTaxes
来启用自动税务计算:
use Laravel\Cashier\Cashier;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Cashier::calculateTaxes();
}
启动税务计算后,任何新订阅和生成的一次性发票都会进行自动税务计算。
为了使这个功能正常使用,客户的账单明细中例如客户姓名、住址、发票 ID 需要同步到 Stripe。你可以使用 Cashier 提供的 客户数据同步 和 Tax ID 方法来完成此操作。
注意:遗憾的是,目前不支持计算 单笔交易 或 单笔交易支付。此外 Stripe Tax 目在测试期间仅限“受邀”使用。你可以通过 Stripe Tax 网站请求访问 Stripe 税务。
日志
Cashier 允许你指定日志通道来记录所有与 Stripe 相关的异常。可以通过在 .env
中配置 CASHIER_LOGGER
来指定:
CASHIER_LOGGER=stack
对 Stripe 的 API 调用生成的异常将通过应用程序的默认日志通道记录。
使用自定义模型
你可以通过定义自己的模型并扩展相应的 Cashier
模型来自由扩展 Cashier 内部的模型,增加一些方法:
use Laravel\Cashier\Subscription as CashierSubscription;
class Subscription extends CashierSubscription
{
// ...
}
定义模型后,可以通过 Laravel\Cashier\Cashier
类配置 Cashier 使用自定义的模型。通常还需要在 App\Providers\AppServiceProvider
类的 boot
中注册一下:
use App\Models\Cashier\Subscription;
use App\Models\Cashier\SubscriptionItem;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Cashier::useSubscriptionModel(Subscription::class);
Cashier::useSubscriptionItemModel(SubscriptionItem::class);
}
消费者
查询消费者
你可以使用 Cashier::findBillable
方法通过 Stripe ID 查询消费者信息。该方法返回的是一个 billable 模型实例:
use Laravel\Cashier\Cashier;
$user = Cashier::findBillable($stripeId);
创建消费者
有时候,你可能希望在不开始订阅的情况下创建一个 Stripe 消费者。这可以通过 createAsStripeCustomer
方法来实现:
$stripeCustomer = $user->createAsStripeCustomer();
消费者在 Stripe 中创建后,可以过一段时间再开始订阅。还可以使用可选的 $options
数组传入所有 Stripe API 支持的创建消费者参数 额外支持的参数:
$stripeCustomer = $user->createAsStripeCustomer($options);
如果你要返回消费者对象,你可以使用 asStripeCustomer
方法 :
$stripeCustomer = $user->asStripeCustomer();
此外,可以使用 createOrGetStripeCustomer
方法来获取不确定查询的 Stripe 消费者在 Stripe 中是否已经存在。如果不存在,这个方法会创建一个消费者:
$stripeCustomer = $user->createOrGetStripeCustomer();
更新消费者
有时候,你可能想要使用额外的信息直接更新 Stripe 顾客信息,可以使用 updateStripeCustomer
方法来完成。这个方法接受 Stripe API 数组:
$stripeCustomer = $user->updateStripeCustomer($options);
余额
Stripe 允许你贷记或借记客户的「余额」。稍后,此余额将在新发票上贷记或借记。要检查客户的总余额 ,你可以使用balance
可用于计费模型的方法。该balance
方法将返回以客户货币表示的余额的格式化字符串表示:
$balance = $user->balance();
要记入客户的余额,可以为该creditBalance
方法提供一个值。如果你愿意,还可以提供描述:
$user->creditBalance(500, 'Premium customer top-up.');
为该方法提供一个值debitBalance将从客户的余额中扣除:
$user->debitBalance(300, 'Bad usage penalty.');
applyBalance
方法会创建一条客户余额流水记录。可以通过调用 balanceTransactions
方法获取余额交易记录,这有助于提供借记或贷记记录给客户查看:
// 检索所有交易...
$transactions = $user->balanceTransactions();
foreach ($transactions as $transaction) {
// Transaction amount...
$amount = $transaction->amount(); // $2.31
// Retrieve the related invoice when available...
$invoice = $transaction->invoice();
}
税号
Cashier 提供了一种管理客户税号的简便方法。taxIds
例如,taxIds
方法可用于检索作为集合分配给客户的所有 税号:
$taxIds = $user->taxIds();
您还可以通过标识符检索客户的特定税号:
$taxId = $user->findTaxId('txi_belgium');
您可以通过向 createTaxId
方法提供有效的 type 和值来创建新的税号:
$taxId = $user->createTaxId('eu_vat', 'BE0123456789');
createTaxId
方法将立即将增值税 ID 添加到客户的帐户中。 增值税 ID 的验证也由 Stripe 完成; 然而,这是一个异步的过程。 您可以通过订阅 customer.tax_id.updated
webhook 事件并检查 [增值税 ID verification
参数](https://stripe.com/docs/api/customer_tax_ids/object#tax_id_object- 确认)。 有关处理 webhook 的更多信息,请参阅 有关定义 webhook 处理程序的文档。
您可以使用 deleteTaxId
方法删除税号:
$user->deleteTaxId('txi_belgium');
使用 Stripe 同步客户数据
通常,当您的应用程序的用户更新他们的姓名、电子邮件地址或其他也由 Stripe 存储的信息时,您应该通知 Stripe 更新。 这样一来,Stripe 的信息副本将与您的应用程序同步。
要自动执行此操 作,您可以在计费模型上定义一个事件侦听器,以响应模型的「更新」事件。然后,在您的事件监听器中,您可以在模型上调用 syncStripeCustomerDetails
方法:
use function Illuminate\Events\queueable;
/**
* 模型的「引导」方法。
*
* @return void
*/
protected static function booted()
{
static::updated(queueable(function ($customer) {
if ($customer->hasStripeId()) {
$customer->syncStripeCustomerDetails();
}
}));
}
现在,每次更新您的客户模型时,其信息都会与 Stripe 同步。 为方便起见,Cashier 会在初始创建客户时自动将您客户的信息与 Stripe 同步。
您可以通过覆盖 Cashier 提供的各种方法来自定义用于将客户信息同步到 Stripe 的列。 例如,当 Cashier 将客户信息同步到 Stripe 时,您可以重写 stripeName
方法来自定义应该被视为客户「姓名」的属性:
/**
* 获取应同步到 Stripe 的客户名称。
*
* @return string|null
*/
public function stripeName()
{
return $this->company_name;
}
同样,您可以复写 stripeEmail
、stripePhone
和 stripeAddress
方法。 当更新 Stripe 客户对象 时,这些方法会将信息同步到其相应的客户参数。 如果您希望完全控制客户信息同步过程,您可以复写 syncStripeCustomerDetails
方法。
订单入口
Stripe 提供了一个简单的方式来设置订单入口以便用户可以管理订阅、支付方法、以及查看历史账单。你可以在控制器或路由中使用 redirectToBillingPortal
方法将用户重定向到账单入口:
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
});
默认情况下,当用户完成对订阅的管理后,会将能够通过 Stripe 计费门户中的链接返回到应用的 home 路由,你可以通过传递 URL 作为 redirectToBillingPortal
方法的参数来自定义用户返回的 URL:
use Illuminate\Http\Request;
Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal(route('billing'));
});
如果你只想要生成订单入口的 URL,可以使用 billingPortalUrl
方法:
$url = $request->user()->billingPortalUrl(route('billing'));
支付方式
存储支付方式
为了使用 Stripe 创建订阅或者进行「一次性」支付,你需要存储支付方法并从 Stripe
中获取对应的标识符。这种方式可用于实现你是否计划使用这个支付方法进行订阅还是单次收费,下面我们分别来介绍这两种方法。
用于订阅的支付方法
当我们为消费者存储信用卡支付方式以便将来使用时,必须使用 Stripe Setup Intents API 来安全地收集顾客的支付方式细节,比如回调错误信息 。「Setup Intents」用于告知 Stripe 使用顾客的支付方法进行收费的意图。Cashier 的 Billable
Trait 包含了 createSetupIntent
方法来创建新的「Setup Intent」,你需要在渲染收集顾客支付方法细节表单的路由或控制器方法中调用这个方法:
return view('update-payment-method', [
'intent' => $user->createSetupIntent()
]);
创建完 Setup Intent 并将其传递给视图之后,你需要在收集支付方法的元素中添加它的 secret。例如,参考下面这个「更新支付方法」表单:
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Update Payment Method
</button>
接下来,会通过 Stripe.js 库添加一个 Stripe 元素到表单,并安全地收集顾客的支付细节:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
然后,使用 Stripe 的 handleCardSetup 方法验证信用卡并从 Stripe 获取一个安全的「支付方法标识符」:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// 显示错误信息给用户...
} else {
// 信用卡验证成功...
}
});
在 Stripe 验证卡后,您可以将生成的 setupIntent.payment_method
标识符传递给您的 Laravel 应用程序,并在其中将其附加到客户。支付方式可以是添加为新的支付方式 或用于更新默认支付方式。您还可以立即使用付款方式标识符来创建新订阅。
技巧:如果你想要了解更多关于 Setup Intents 以及获取顾客支付细节的信息,可以参考 Stripe 官方文档。
用于单次付费的支付方法
当然,如果消费者支付方法使用的是单次付费,我们只需要使用支付方法标识符一次即可。由于 Stripe 本身的限制,你不可以使用存储的默认顾客支付方法进行单次付费,必须允许顾客通过 Stripe.js 库进入他们的支付方法细节。例如,参考下面这个表单:
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button">
Process Payment
</button>
接下来跟上面文档相似,通过 Stripe.js 库添加 Stripe 元素 到这个表单,并安全地收集顾客的支付细节:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
然后,使用 Stripe 的 createPaymentMethod 方法验证信用卡并获取一个安全的「支付方法标识符」:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
cardButton.addEventListener('click', async (e) => {
const { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
如果信用卡验证成功,就可以传递 paymentMethod.id
到你的 Laravel 应用并处理一次性付费。
获取支付方法
Billable 模型实例上的 paymentMethods
方法会返回 Laravel\Cashier\PaymentMethod
实例集合:
$paymentMethods = $user->paymentMethods();
默认情况下,此方法将返回 card
类型的付款方式。 要检索不同类型的付款方式,您可以将 type
作为参数传递给该方法:
$paymentMethods = $user->paymentMethods('sepa_debit');
要获取消费者默认的支付方法,可以使用 defaultPaymentMethod
方法:
$paymentMethod = $user->defaultPaymentMethod();
还可以使用 findPaymentMethod
方法通过 Billable 模型获取指定支付方法:
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
判断消费者是否拥有支付方法
要判断某个 Billable
模型对应账户是否有默认的支付方法,可以使用 hasDefaultPaymentMethod
方法:
if ($user->hasDefaultPaymentMethod()) {
//
}
要判断某个 Billable
模型对应账户是否有支付方法,可以使用 hasPaymentMethod
方法:
if ($user->hasPaymentMethod()) {
//
}
此方法将确定计费模型是否具有 card
类型的付款方式。 要确定模型是否存在另一种类型的付款方式,您可以将 type
作为参数传递给该方法:
if ($user->hasPaymentMethod('sepa_debit')) {
//
}
更新默认支付方式
更新顾客的默认支付方式信息 可以用 updateDefaultPaymentMethod
方法 ,该方法接收一个 Stripe 支付方法标识符并将新的支付方法分配为默认的支付方法:
$user->updateDefaultPaymentMethod($paymentMethod);
要同步应用的默认支付方法信息到 Stripe 顾客的默认支付方式信息,可以使用 updateDefaultPaymentMethodFromStripe
方法:
$user->updateDefaultPaymentMethodFromStripe();
注意:消费者的默认支付方式只能用于发票和创建新的订阅,由于 Stripe 的限制,不能将其用于单次付费。
添加支付方式
要添加新的支付方式,可以调用 Billable 用户的 addPaymentMethod
方法,并传递支付方法标识符:
$user->addPaymentMethod($paymentMethod);
技巧:要了解如何获取支付方法标识符,请参考支付方式存储文档.
删除支付方式
删除一个支付方法,你可以调用要删除的 Laravel\Cashier\PaymentMethod
实例上的 delete
方法:
$paymentMethod->delete();
删除指定 Billable 模型上的所有支付方法信息可以使用 deletePaymentMethods
方法:
$user->deletePaymentMethod('pm_visa');
deletePaymentMethods
方法将删除计费模型的所有付款方式信息:
$user->deletePaymentMethods();
默认情况下,此方法将删除 card
类型的付款方式。 要删除不同类型的付款方式,您可以将 type
作为参数传递给该方法:
$user->deletePaymentMethods('sepa_debit');
注意:如果用户有活动订阅,您的应用程序不应允许他们删除其默认付款方式。
订阅内容
订阅提供了一种为消费者设置定期付款的方式。由收银员管理的 Stripe 订阅 提供对多个订阅计划、订阅数量、试用等的支持。
创建订阅
创建一个订阅,首先要获取一个账单模型的实例,通常是 App\Models\User
的实例。获取到该模型实例之后,可以使用 newSubscription
方法来创建该模型的订阅:
use Illuminate\Http\Request;
Route::post('/user/subscribe', function (Request $request) {
$request->user()->newSubscription(
'default', 'price_monthly'
)->create($request->paymentMethodId);
// ...
});
newSubscription
方法的第一个参数是该订阅的名字,如果应用只有一个订阅,可以将其称作 default
或 primary
,第二个参数用于指定用户订阅的计划,这个值对应 Stripe
中相应计划的标识符。
create
方法接收 Stripe 支付方法标识符或者 Stripe PaymentMethod
对象的 create
方法会自动创建这个 Stripe 订阅,同时更新数据库中 Stripe 的消费者 ID(即 users
表中的 stripe_id
)和其它相关的账单信息。
注意:直接传递支付方法标识符到 create () 订阅方法还会自动将其添加到用户存储的支付方法中。
通过发票电子邮件收集定期付款
您可以指示 Stripe 在每次定期付款到期时通过电子邮件将发票发送给客户,而不是自动收取客户的定期付款。 然 后,客户可以在收到发票后手动支付发票。 通过发票收取定期付款时,客户无需预先提供付款方式:
$user->newSubscription('default', 'price_monthly')->createAndSendInvoice();
客户在取消订阅之前必须支付发票的时间取决于您在 Stripe 仪表板 中的订阅和发票设置。
数量
如果你想要在创建订阅时设置计划的具体数量,可以使用 quantity
方法:
$user->newSubscription('default', 'price_monthly')
->quantity(5)
->create($paymentMethod);
其它细节
如果你想要指定其它消费者或者订阅细节,你可以将其作为第二个参数传递给 create
方法:
$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [
'email' => $email,
], [
'metadata' => ['note' => 'Some extra information.'],
]);
优惠券
如果你想在创建订阅的时候使用优惠券,你可以使用 withCoupon
方法:
$user->newSubscription('default', 'price_monthly')
->withCoupon('code')
->create($paymentMethod);
或者,如果你想使用 Stripe 推广代码,可以使用 withPromotionCode
方法:
$user->newSubscription('default', 'price_monthly')
->withPromotionCode('promo_code')
->create($paymentMethod);
添加订阅
如果你想要为已经有默认支付方式的消费者添加订阅,可以在使用 newSubscription
方法时使用 add
方法:
use App\Models\User;
$user = User::find(1);
$user->newSubscription('default', 'price_monthly')->add();
从 Stripe 面板中创建订阅
你也可以从 Stripe 面板中创建订阅。在面板中创建订阅时候,收银员将同步新添加的订阅,并为它们分配一个名称为 default
的订阅。要自定义分配给仪表板创建订阅的订阅名,扩展’ WebhookController ‘并覆盖 newSubscriptionName
方法。
此外,只能通过 Stripe 面板中创建一种订阅类型。如果应用程序提供使用不同名称的多个订阅,则只能通过 Stripe 面板添加一种订阅类型。
最后,应该始终确保对应用程序提供的每种订阅类型只添加一个活动订阅。如果消费者有两个 default
订阅,那么收银员只会使用最近添加的订阅,即使这两个订阅都将与应用程序的数据库同步。
检查订阅状态
客户订阅您的应用程序后,您可以使用各种便捷的方法检查他们的订阅状态。首先,如果客户有活动订阅,则 subscribed
方法会返回 true
,即使订阅当前处于试用期也是如此 。subscribed
方法接受订阅的名称作为它的第一个参数:
if ($user->subscribed('default')) {
//
}
subscribed
方法也可以用于路由中间件 , 基于消费者订阅状态允许你对路由和控制器的访问进行过滤:
<?php
namespace App\Http\Middleware;
use Closure;
class EnsureUserIsSubscribed
{
/**
* 处理传入的请求。
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed('default')) {
// 该用户不是付费用户...
return redirect('billing');
}
return $next($request);
}
}
如果你想要判断一个消费者是否还在试用期,可以使用 onTrial
方法,该方法对于还处于试用期的用户显示警告信息很有用:
if ($user->subscription('default')->onTrial()) {
//
}
subscribedToProduct
方法可用于根据给定 Stripe 产品的标识符确定用户是否订阅了给定产品。 在 Stripe 中,产品是价格的集合。 在此示例中,我们将确定用户的 默认
订阅是否主动订阅了应用程序的 高级
产品。 给定的 Stripe 产品标识符应与您在 Stripe 仪表板中的产品标识符之一相对应:
if ($user->subscribedToProduct('prod_premium', 'default')) {
//
}
if ($user->subscribedToProduct(['prod_basic', 'prod_premium'], 'default')) {
//
}
subscribedToPrice
方法可用于确定客户的订阅是否对应于给定的价格 ID:
if ($user->subscribedToPrice('price_basic_monthly', 'default')) {
//
}
recurring
方法可用于确定用户当前是否已订阅并且不再处于试用期内:
if ($user->subscription('default')->recurring()) {
//
}
注意:如果消费者有两个具有相同名称的订阅,则 subscription 方法将始终返回最近的订阅。例如,消费者可能有两条名为 default 的订阅记录;但是,其中一个订阅可能是旧的、过期的订阅,而另一个是当前的、活动的订阅。最新的订阅将始终返回,而旧的订阅将保留在数据库中以进行历史回顾。
已取消的订阅状态
要判断消费者是否曾经是有效的订阅者,但现在取消了订阅,可以使用 cancelled
方法:
if ($user->subscription('default')->canceled()) {
//
}
还可以判断消费者是否曾经取消过订阅,但现在仍然在「宽限期」直到完全失效。例如,如果一个消费者在 3 月 5 号取消了一个实际有效期到 3 月 10 号的订阅,该消费者处于「宽限期」直到 3 月 10 号。注意 subscribed
方法在此期间仍然返回 true
。
if ($user->subscription('default')->onGracePeriod()) {
//
}
要去定消费者已经取消订阅并且不在「宽限期」内,可以使用 ended
方法:
if ($user->subscription('default')->ended()) {
//
}
未完成和过期状态
如果某个订阅要求创建完订阅后进行二次付款操作,将被标记为 incomplete
。订阅状态被存储在 Cashier subscriptions
数据表的 stripe_status
字段。
类似的,如果在切换订阅计划时也需要进行二次付款 操作,对应的订阅会被标记为 past_due
。当你的订阅处于这种状态时,只有等到消费者确认支付后它们才会被激活。我们可以使用 Billable
模型或者订阅实例的 hasIncompletePayment
方法来检查某个订阅是否存在未完成支付:
if ($user->hasIncompletePayment('default')) {
//
}
if ($user->subscription('default')->hasIncompletePayment()) {
//
}
如果某个订阅存在未完成支付,你需要引导消费者到 Cashier 的支付确认页面,并传递 latestPayment
标识符。你可以使用订阅实例上的 latestPayment
方法来获取这个标识符:
<a href="{{ route('cashier.payment', $subscription->latestPayment()->id) }}">
Please confirm your payment.
</a>
如果你想要订阅在 past_due
状态下依然有效,可以使用 Cashier 提供的 keepPastDueSubscriptionsActive
方法,通常,该方法需要在 AppServiceProvider
的 boot
方法中调用:
use Laravel\Cashier\Cashier;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Cashier::keepPastDueSubscriptionsActive();
}
注意:当某个订阅处于 incomplete
状态,只有等到支付被确认后才能进行修改。因此,当订阅处于 incomplete
状态时,执行 swap
和 updateQuantity
方法会抛出异常。
订阅范围
大多数订阅状态也可用作查询范围,以便你可以轻松地查询数据库中处于给定状态的订阅:
// 获取所有有效订阅...
$subscriptions = Subscription::query()->active()->get();
// Get all of the canceled subscriptions for a user...
$subscriptions = $user->subscriptions()->canceled()->get();
所有内置的订阅查询范围列表如下:
Subscription::query()->active();
Subscription::query()->canceled();
Subscription::query()->ended();
Subscription::query()->incomplete();
Subscription::query()->notCanceled();
Subscription::query()->notOnGracePeriod();
Subscription::query()->notOnTrial();
Subscription::query()->onGracePeriod();
Subscription::query()->onTrial();
Subscription::query()->pastDue();
Subscription::query()->recurring();