keoby / laravel-plans
Laravel Plans 是一个用于需要管理计划、功能、订阅和计划的或有限、可计数的功能的 SaaS 应用的包。
Requires
- php: ^8.1
- doctrine/dbal: ^2.8.0|^3.0
- laravel/framework: 5.7.*|5.8.*|^6.0|^7.0|^8.0|~9
Requires (Dev)
- laravel/legacy-factories: ^1.1
- laravel/pint: ^1.0
- nunomaduro/collision: ^6.0
- nunomaduro/larastan: ^2.0.1
- orchestra/database: 3.8.*|^7.0|^6.6
- orchestra/testbench: ^7.0
- pestphp/pest: ^1.21
- pestphp/pest-plugin-laravel: ^1.1
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-06 16:02:10 UTC
README
Laravel Plans 是一个用于需要管理计划、功能、订阅和计划的或有限、可计数的功能的 SaaS 应用的包。
Laravel Cashier
虽然 Laravel Cashier 完成这项工作非常出色,但还有一些功能对于 SaaS 应用可能很有用
- 可计数、有限的功能 - 如果您计划限制订阅者可以拥有的资源数量并跟踪使用情况,这个包为您做到这一点。
- 事件驱动 - 您可以监听事件。如果用户及时支付账单,您是否可以给他们提供3天免费试用期呢?
安装
安装包
$ composer require keoby/laravel-plans
如果您的 Laravel 版本不支持包发现,请在您的 config/app.php
文件的 providers
数组中添加此行
Keoby\LaravelPlans\LaravelPlansServiceProvider::class,
发布配置文件和迁移文件
$ php artisan vendor:publish --tag=laravel-plans-migrations
迁移数据库
$ php artisan migrate
将 HasPlans
特性添加到您的 Eloquent 模型中
use Keoby\LaravelPlans\Traits\HasPlans; class User extends Model { use HasPlans; ... }
创建计划
类似订阅的系统的基本单位是计划。您可以使用 Keoby\LaravelPlans\Models\Plan
或您自己的模型创建它,如果您实现了自己的模型。
$plan = Plan::create([ 'name' => 'Enterprise', 'description' => 'The biggest plans of all.', 'duration' => 30, // in days ]);
功能
每个计划都有功能。它们可以是可计数的,这些是有限的或无限的,或者只是存储信息,例如特定权限。
使用以下方式标记功能类型
feature
,是一个单个字符串,不需要计数。例如,您可以存储权限。limit
,是一个数字。对于这类功能,limit
属性将被填充。它旨在衡量用户从本订阅中消费了多少此类功能。例如,您可以计算用户在本月(或在本例中的周期,即 30 天)中消费了多少构建分钟。
注意:对于无限功能,limit
字段将被设置为任何负值。
要将功能附加到您的计划,您可以使用关系 features()
并传递所需的 Keoby\LaravelPlans\Models\Feature
实例数量。
$plan->features()->saveMany([ new Feature([ 'name' => 'Vault access', 'code' => 'vault.access', 'description' => 'Offering access to the vault.', 'type' => 'feature', ]), new Feature([ 'name' => 'Build minutes', 'code' => 'build.minutes', 'description' => 'Build minutes used for CI/CD.', 'type' => 'limit', 'limit' => 2000, ]), new Feature([ 'name' => 'Users amount', 'code' => 'users.amount', 'description' => 'The maximum amount of users that can use the app at the same time.', 'type' => 'limit', 'limit' => -1, // or any negative value ]), ... ]);
稍后,您可以直接从订阅中检索权限。
$subscription->features()->get(); // All features $subscription->features()->code($codeId)->first(); // Feature with a specific code. $subscription->features()->limited()->get(); // Only countable/unlimited features. $subscription->features()->feature()->get(); // Uncountable, permission-like features.
订阅计划
您的用户可以订阅计划,为期一定天数或直到某个日期。
$subscription = $user->subscribeTo($plan, 30); // 30 days $subscription->remainingDays(); // 29 (29 days, 23 hours, ...)
默认情况下,计划被标记为 recurring
,因此它可以在到期后延长,如果您计划这样做,请参阅下面的 续订 部分。
如果您不希望有续订订阅,可以将 false
作为第三个参数传递。
$subscription = $user->subscribeTo($plan, 30, false); // 30 days, non-recurrent
如果您计划在某个日期前订阅用户,可以传递包含日期、日期时间或 Carbon 实例的字符串。
如果您的订阅是续订的,续订周期的天数是到期日期和当前日期之间的差异。
$user->subscribeToUntil($plan, '2018-12-21'); $user->subscribeToUntil($plan, '2018-12-21 16:54:11'); $user->subscribeToUntil($plan, Carbon::create(2018, 12, 21, 16, 54, 11)); $user->subscribeToUntil($plan, '2018-12-21', false); // no recurrency
注意:如果用户已经订阅,则 subscribeTo()
将返回 false。为了避免这种情况,请升级或延长订阅。
升级订阅
升级当前订阅的计划可以通过两种方式完成:要么延长当前订阅的天数,要么创建另一个订阅,作为当前订阅的扩展。
无论哪种方式,您都必须传递一个布尔值作为第三个参数。默认情况下,它将延长当前订阅。
// The current subscription got longer with 60 days. $currentSubscription = $user->upgradeCurrentPlanTo($anotherPlan, 60, true); // A new subscription, with 60 days valability, starting when the current one ends. $newSubscription = $user->upgradeCurrentPlanTo($anotherPlan, 60, false);
就像订阅方法一样,升级也支持日期作为第三个参数,如果您计划在当前订阅结束时创建新的订阅。
$user->upgradeCurrentPlanToUntil($anotherPlan, '2018-12-21', false); $user->upgradeCurrentPlanToUntil($anotherPlan, '2018-12-21 16:54:11', false); $user->upgradeCurrentPlanToUntil($anotherPlan, Carbon::create(2018, 12, 21, 16, 54, 11), false);
如果您的第三个参数为 false
,则可以传递第四个参数,如果您想将新订阅标记为循环订阅,则应该传递它。
// Creates a new subscription that starts at the end of the current one, for 30 days and recurrent. $newSubscription = $user->upgradeCurrentPlanTo($anotherPlan, 30, false, true);
扩展当前订阅
升级使用扩展方法,因此它使用相同的参数,但您不需要将计划模型作为第一个参数传递。
// The current subscription got extended with 60 days. $currentSubscription = $user->extendCurrentSubscriptionWith(60, true); // A new subscription, which starts at the end of the current one. $newSubscription = $user->extendCurrentSubscriptionWith(60, false); // A new subscription, which starts at the end of the current one and is recurring. $newSubscription = $user->extendCurrentSubscriptionWith(60, false, true);
扩展也可以与日期一起使用
$user->extendCurrentSubscriptionUntil('2018-12-21');
取消订阅
您可以取消订阅。如果订阅尚未完成(尚未过期),它将被标记为 待取消
。当过期日期超过当前时间并且仍然处于取消状态时,它将被完全取消。
// Returns false if there is not an active subscription. $user->cancelCurrentSubscription(); $lastActiveSubscription = $user->lastActiveSubscription(); $lastActiveSubscription->isCancelled(); // true $lastActiveSubscription->isPendingCancellation(); // true $lastActiveSubscription->isActive(); // false $lastActiveSubscription->hasStarted(); $lastActiveSubscription->hasExpired();
使用可计数的特性
要使用 limit
类型特性,您必须在订阅实例中调用 consumeFeature()
方法。
要获取订阅实例,您可以在实现该特性的用户中调用 activeSubscription()
方法。作为一个前置检查,别忘了从用户实例中调用 hasActiveSubscription()
来确保它已经订阅了。
if ($user->hasActiveSubscription()) { $subscription = $user->activeSubscription(); $subscription->consumeFeature('build.minutes', 10); $subscription->getUsageOf('build.minutes'); // 10 $subscription->getRemainingOf('build.minutes'); // 1990 }
consumeFeature()
方法将返回
false
如果特性不存在,特性不是limit
或数量超过当前特性配额true
如果消费成功
// Note: The remaining of build.minutes is now 1990 $subscription->consumeFeature('build.minutes', 1991); // false $subscription->consumeFeature('build.hours', 1); // false $subscription->consumeFeature('build.minutes', 30); // true $subscription->getUsageOf('build.minutes'); // 40 $subscription->getRemainingOf('build.minutes'); // 1960
如果 consumeFeature()
遇到无限特性,它将消耗它,并且也会像数据库中的正常记录一样跟踪使用情况,但永远不会返回 false
。剩余的数量对于无限特性始终为 -1
。
consumeFeature()
方法的逆操作方法是 unconsumeFeature()
。它的工作方式相同,但相反。
// Note: The remaining of build.minutes is 1960 $subscription->consumeFeature('build.minutes', 60); // true $subscription->getUsageOf('build.minutes'); // 100 $subscription->getRemainingOf('build.minutes'); // 1900 $subscription->unconsumeFeature('build.minutes', 100); // true $subscription->unconsumeFeature('build.hours', 1); // false $subscription->getUsageOf('build.minutes'); // 0 $subscription->getRemainingOf('build.minutes'); // 2000
在无限特性上使用 unconsumeFeature()
方法也会减少使用,但它永远不会达到负值。
事件
当使用订阅计划时,您想监听事件来自动运行可能会更改您应用的代码。
事件使用起来很简单。如果您不熟悉,可以查看 Laravel 官方文档中的事件。
您需要做的只是在其 EventServiceProvider.php
文件中实现以下事件。每个事件都将有自己的成员,可以在监听器的 handle()
方法中的 $event
变量中访问。
$listen = [ ... \Keoby\Plans\Events\CancelSubscription::class => [ // $event->model = The model that cancelled the subscription. // $event->subscription = The subscription that was cancelled. ], \Keoby\Plans\Events\NewSubscription::class => [ // $event->model = The model that was subscribed. // $event->subscription = The subscription that was created. ], \Keoby\Plans\Events\NewSubscriptionUntil::class => [ // $event->model = The model that was subscribed. // $event->subscription = The subscription that was created. ], \Keoby\Plans\Events\ExtendSubscription::class => [ // $event->model = The model that extended the subscription. // $event->subscription = The subscription that was extended. // $event->startFromNow = If the subscription is exteded now or is created a new subscription, in the future. // $event->newSubscription = If the startFromNow is false, here will be sent the new subscription that starts after the current one ends. ], \Keoby\Plans\Events\ExtendSubscriptionUntil::class => [ // $event->model = The model that extended the subscription. // $event->subscription = The subscription that was extended. // $event->expiresOn = The Carbon instance of the date when the subscription will expire. // $event->startFromNow = If the subscription is exteded now or is created a new subscription, in the future. // $event->newSubscription = If the startFromNow is false, here will be sent the new subscription that starts after the current one ends. ], \Keoby\Plans\Events\UpgradeSubscription::class => [ // $event->model = The model that upgraded the subscription. // $event->subscription = The current subscription. // $event->startFromNow = If the subscription is upgraded now or is created a new subscription, in the future. // $event->oldPlan = Here lies the current (which is now old) plan. // $event->newPlan = Here lies the new plan. If it's the same plan, it will match with the $event->oldPlan ], \Keoby\Plans\Events\UpgradeSubscriptionUntil::class => [ // $event->model = The model that upgraded the subscription. // $event->subscription = The current subscription. // $event->expiresOn = The Carbon instance of the date when the subscription will expire. // $event->startFromNow = If the subscription is upgraded now or is created a new subscription, in the future. // $event->oldPlan = Here lies the current (which is now old) plan. // $event->newPlan = Here lies the new plan. If it's the same plan, it will match with the $event->oldPlan ], \Keoby\Plans\Events\FeatureConsumed::class => [ // $event->subscription = The current subscription. // $event->feature = The feature that was used. // $event->used = The amount used. // $event->remaining = The total amount remaining. If the feature is unlimited, will return -1 ], \Keoby\Plans\Events\FeatureUnconsumed::class => [ // $event->subscription = The current subscription. // $event->feature = The feature that was used. // $event->used = The amount reverted. // $event->remaining = The total amount remaining. If the feature is unlimited, will return -1 ], ];
测试
composer test
变更日志
有关最近更改的更多信息,请参阅 变更日志。
贡献
有关详细信息,请参阅 贡献指南。
安全漏洞
有关如何报告安全漏洞的详细信息,请查看 我们的安全策略。
鸣谢
- Keoby
- Georgescu Alexandru 初始工作
- Musa Kurt
- Dukens Thelemaque
- 所有贡献者
许可协议
MIT 许可协议(MIT)。有关更多信息,请参阅 许可文件。