logo

计划

定义付款计划

有关为您的应用程序定义付款计划的更多信息,请参阅 计划配置文档

试用期

默认情况下,您应用程序的 config/spark.php 配置文件中的计划配置包含一个值为 5trial_days 选项。此配置选项决定用户在免费试用期内可以使用您的应用程序的时间。您可以根据应用程序的需要自由修改此配置值。

实际上,此配置选项仅决定可计费模型的 onTrial 方法何时开始返回 false 而不是 true

php
$user = Auth::user();

if ($user->onTrial()) {
    // The user is still within their trial period...
}

确定计划资格

有时,您可能希望对特定计费计划设置限制。例如,项目管理应用程序可能会限制特定计费计划上的用户最多创建 10 个项目,而价格更高的计划可能会允许创建最多 20 个项目。

如果您选择采用这种计费方式,您需要指示 Spark 如何确定给定的可计费模型是否有资格加入特定计费计划。您可以通过修改应用程序的 App\Providers\SparkServiceProvider 类中注册的 checkPlanEligibility 回调来实现这一点。当可计费模型尝试订阅或切换到新的订阅计划时,将调用此回调。

php
use App\Models\User;
use Illuminate\Validation\ValidationException;
use Spark\Plan;
use Spark\Spark;

Spark::billable(User::class)->checkPlanEligibility(function ($billable, Plan $plan) {
    if (count($billable->projects) > 10 && $plan->name == 'Basic') {
        throw ValidationException::withMessages([
            'plan' => 'You have too many projects for the selected plan.'
        ]);
    }
});

按席位计费计划

一些应用程序按“席位”收费,而不是固定月费。例如,项目管理应用程序可能会收取每月 每个项目 10 美元,因此如果用户管理了五个项目,他们将被每月收取 50 美元。

如果您的应用程序将使用按席位计费,您可能会在应用程序的 config/spark.php 配置文件中定义一个单一的每月计划。此外,您需要指示 Spark 如何计算可计费模型当前使用的“席位”数量。

您可以在配置计费模型时,通过 chargePerSeat 方法指示 Spark 如何计算计费模型当前使用的“座位”数量。通常,此方法应在应用程序 App\Providers\SparkServiceProvider 类的 boot 方法中调用。

php
use App\Models\User;
use Spark\Spark;

Spark::billable(User::class)->chargePerSeat('project', function ($billable) {
    return $billable->projects()->count();
});

chargePerSeat 方法接受的第一个参数是您的应用程序用来指代“座位”的术语。对于项目管理应用程序,这将是“项目”。传递给 chargePerSeat 方法的第二个参数应该是一个闭包,它接受计费模型并返回该模型当前占用的“座位”数量。

配置按座位计费后,Spark 会自动更新应用程序计费门户中的措辞,以告知用户计费是按座位计算的。

增加/减少座位数量

上面解释的 chargePerSeat 回调将告知 Spark 在客户启动订阅时应使用的当前座位数量。但是,当用户使用您的应用程序时,您仍然需要告知 Spark 何时添加或删除座位。例如,如果您正在构建一个按项目计费的项目管理应用程序,则需要在用户创建或删除项目时告知 Spark。您可以通过调用 addSeatremoveSeat 方法来实现这一点。

php
$user->addSeat();

$user->removeSeat();

Paddle 不允许订阅数量低于“1”。因此,您永远不应该将座位数量减少到零。相反,您可以取消订阅计划。

确定订阅状态

在构建应用程序时,您经常需要检查用户的订阅状态和计划,以确定他们是否被允许执行给定的操作。例如,您可能不希望允许用户创建项目,如果他们订阅的计费计划只允许创建特定数量的项目。首先,您应该查看 Spark 提供的 订阅验证中间件

此外,您始终可以使用 Laravel Cashier 提供的方法 手动检查计费模型的订阅状态,这对于验证用户是否订阅了特定计划特别有用。

php
if ($user->subscribed()) {
    // The user has an active subscription...
}

if ($user->subscribedToPrice($priceId = 'pri_monthly')) {
    // The user has a subscription to a plan with a Paddle price ID of pri_monthly...
}

if ($user->subscribedToProduct($productId = 'pro_hobby')) {
    // The user has a subscription to a product with a Paddle product ID of pro_hobby...
}

定义计划激励文本

Spark 允许您定义计划“激励”文本以显示给您的用户。例如,您可能希望显示选择使用年度计划与每月计划相比节省的金额。激励文本显示在计划卡片的右上角。

Incentive text example

要定义激励文本,您可以在计划定义中添加 monthly_incentive 和/或 yearly_incentive 配置选项。

php
[
    'name' => 'Standard',
    'short_description' => 'This is a short, human friendly description of the plan.',
    'monthly_id' => env('SPARK_STANDARD_MONTHLY_PLAN', 'price_id'),
    'yearly_id' => env('SPARK_STANDARD_YEARLY_PLAN', 'price_id'),
    'yearly_incentive' => 'Save 10%',
    'features' => [
        'Feature 1',
        'Feature 2',
        'Feature 3',
    ],
],

访问可计费的 Spark 计划

有时您可能希望访问给定可计费的 Spark 计划实例,以确定该计划有哪些选项可用。例如,如果您的计划定义包含以下内容

php
[
    'name' => 'Standard',
    'short_description' => 'This is a short, human friendly description of the plan.',
    'monthly_id' => env('SPARK_STANDARD_MONTHLY_PLAN', 'price_id'),
    'yearly_id' => env('SPARK_STANDARD_YEARLY_PLAN', 'price_id'),
    'features' => [
        // ...
    ],
    'options' => [
        'database_backups' => true,
    ],
],

您可以像这样访问计划和选项

php
if ($user->sparkPlan()) {
    $canCreateBackups = $user->sparkPlan()->options['database_backups'] ?? false;
}

存档计划

如果您计划“存档”或停用应用程序的特定计划,您应该将archived配置选项添加到计划的配置数组中。如果您允许已订阅该计划的应用程序现有用户继续其订阅,则不应完全删除该计划的配置

php
'plans' => [
    [
        'name' => 'Standard',
        // ...
        'archived' => true,
    ],
],

帐户删除

如果您的应用程序允许用户完全删除其帐户数据,您应该确保在删除其数据之前取消用户已订阅的任何订阅计划。否则,即使您已删除其数据,用户也可能会继续被计费。您可以使用Laravel Cashier的典型订阅管理方法取消其订阅。根据您的应用程序使用的可计费类型,您可能需要根据您的应用程序的独特需求调整此代码

php
if (optional($user->subscription())->recurring()) {
    $user->subscription()->cancelNow();
}