rokde/laravel-subscription-manager

这是我开发的包SubscriptionManager

v1.1.0 2024-05-06 17:05 UTC

This package is auto-updated.

Last update: 2024-09-05 07:29:07 UTC


README

Latest Version on Packagist run-tests Check & fix styling PHPLint Total Downloads

不适用于生产环境

待办事项

  • 抛出订阅生命周期事件:ended,cycle,...

  • 计量功能:限制功能的数字使用:只有10个客户可以管理

    • 在检查订阅时有限制
    • 有升级的可能性
    • 有事件:quota-reached,quota-exceeded

Laravel的订阅管理器应处理所有基于订阅的内容,而无需处理任何支付。与cashier等知名的支付处理包相反,我们不支持任何支付处理。我们只支持带有功能的计划、订阅、试用、按比例计费或进入宽限期等。

为了传达变化,我们抛出了大量事件,并配备了一系列工具,包括中间件、blade条件和其他请求功能接受服务。

它不是什么

  • 处理价格
  • 处理支付
  • 执行结账
  • 处理优惠券或代金券
  • 处理营销数据
  • 打印发票

它是什么

  • 将功能组合到计划中
  • 将用户(或其它模型)订阅到一个或多个计划
    • 向现有订阅添加或删除功能
  • 检查给定用户(或其它模型)的功能付费状态(如guard)
  • 显示带详细信息的订阅

这究竟是怎么回事?

术语表

可订阅是一个可以订阅一组功能的实体。

在现实生活中,这可能是一个支付金钱以获得访问某些高级内容的人。

订阅是一个可订阅对象和授予其一组功能有限访问权限之间的关系。

在现实生活中,这可能是具有时间限制和重复定义的合同,如手机合同。

功能是一个用户无法访问的高级区域或功能名称。

在现实生活中,你将获得访问隐藏级别的访问密钥。

计划是一组功能的虚拟组合。

在现实生活中,计划可能是具有访问少数、更多或所有功能的S、M或L包。

定义

在定义方面,你有功能 - 由slug代码标识。功能用于控制。想想权限或访问受保护应用程序部分的访问权限。功能可以虚拟地按计划分组,但不必如此。

因为我们不处理价格,所以你不必有计划或功能捆绑。

因此,每个功能可以是没有任何计划、一个计划、多个计划或所有计划的一部分。一个计划可以存在,多个计划可以存在,但不必存在。在授权方面考虑角色。

订阅

订阅与计划松散耦合,并具有未链接的订阅功能列表。因此,松散耦合的计划更像是数据链接的信息。功能slug列表是订阅中的重要信息。

订阅无限期地运行(ends_at为null)或直到结束日期。此外,您可以设置试用期限(trial_ends_at不为null),试用在指定的timestamp结束时结束。在此之前,您处于试用状态。

订阅周期是一个按时间段存在的虚拟对象。默认情况下,订阅周期为1年。因此,每个订阅至少有一个周期列表。每个周期有一个最大长度,如果订阅立即取消,则可能更短。

具有无限周期的订阅只有一个周期且不重复。

安装

您可以通过composer安装此包

composer require rokde/laravel-subscription-manager

您必须使用以下命令发布并运行迁移

php artisan vendor:publish --provider="Rokde\SubscriptionManager\SubscriptionManagerServiceProvider" --tag="laravel-subscription-manager-migrations"
php artisan migrate --step

您可以使用以下命令发布配置文件

php artisan vendor:publish --provider="Rokde\SubscriptionManager\SubscriptionManagerServiceProvider" --tag="laravel-subscription-manager-config"

您可以配置以下部分(文档包含在提供的配置文件中)

  • 中间件

用法

如果您的订阅可订阅对象是您的用户,那么您必须执行以下操作

1.) 将Subscribable特质添加到您的User模型中。

// \App\Models\User
class User extends \Illuminate\Foundation\Auth\User {
    use \Rokde\SubscriptionManager\Models\Concerns\Subscribable;
}

2.) 订阅某个对象

//  using the SubscriptionBuilder, presented by Subscribable trait
/** @var \Rokde\SubscriptionManager\Models\Factory\SubscriptionBuilder $builder */
$builder = $user->newSubscription();    // without any plan
// or
$plan = \Rokde\SubscriptionManager\Models\Plan::byName('Superior');
$builder = $user->newSubscription($plan);    // subscribing to a plan -> the list of features will be taken from the plan
// or
$builder = $user->newFeatureSubscription(['feature-1', 'feature-2']);    // just a list of features, must not exist in database

$builder->periodLength('P1M')   // set period length to a month (default is 1 year)
    ->infinitePeriod()          // or set an infinite period
    ->trialDays(30)             // set 30 days for trial
    ->skipTrial()               // or skip trial (default)
    ->create();                 // and create a subscription

3.) 使用各种检查之一。

请参阅下面的检查章节

4.) 询问有关订阅的任何信息

订阅可以与一个计划相关联。它应该有一个功能列表——但空数组也很好。您可以获取一个订阅周期的数组。每个订阅周期代表订阅内的一个时间段。

$subscription->circles(); // resolves a list of SubscriptionCircles 

每个订阅周期都与订阅相关联,有它自己的开始和结束日期。您可以得到一个周期和索引号(基于1)的周期。它尊重订阅的过去或未来的结束。在活跃订阅上的最后一个订阅周期将包含其范围内的当前时间戳。因此,当您想要进入未来时,您必须自己添加周期。

5.) 取消订阅

可以通过几种方式取消订阅,所有这些都在\Rokde\SubscriptionManager\Models\Concerns\HandlesCancellation特质中提供。

$user->subscription->cancel();              // cancel at the end of the current circle or at the end of the trial when you are on a trial
// or
$user->subscription->cancelNow();           // cancel just right now
// or
$user->subscription->cancelAt($datetime);   // cancel at a concrete time

6.) 恢复已取消的订阅

$user->subscription->resume();  // resume a cancelled subscription within grace period

消费功能

1.) 通过操作设置可计量功能的配额(或通过订阅构建器直接设置)

$meteredFeature = (new CreateFeatureAction())->execute('users-count', true);

$subscription = (new CreateSubscriptionFromFeaturesAction())
    ->execute([$meteredFeature], request()->user(), function (SubscriptionBuilder $builder) {
        $builder->setQuota('users-count', 10); // allow 10 users
    });

检查

使用中间件检查

我们注册了一个名为subscribed的中间件作为路由中间件。您可以通过发布配置并修改subscription-manager::middleware配置键来更改它。

您可以通过检查您的可订阅对象是否具有活跃的订阅来检查路由访问

// /routes/web.php
Route::group(['middleware' => 'subscribed'], function () {
    // here you can define your premium routes
});

如果您想要更具体一些,您可以检查您的可订阅对象是否订阅了某个功能

// /routes/web.php
Route::group(['middleware' => 'subscribed:track-time'], function () {
    // here you can define your routes for all users which have subscribed to a feature "track-time"
});

在可订阅对象上检查

// @var \App\Models\User|\App\Models\Team $subscribable
//  just currently active subscriptions
$hasAnyActiveSubscription = $subscribable->subscribed();
$isActivelySubscribedToAConcreteFeature = $subscribable->subscribed('feature-1');

//  active and past subscriptions
$hasAnySubscriptionEver = $subscribable->everSubscribed();
$wasSubscribedToAConcreteFeature = $subscribable->everSubscribed('feature-1');

获取所有已订阅的功能

您可以检索所有已订阅功能的数组。

// @var \App\Models\User|\App\Models\Team $subscribable
$allFeatures = $subscribable->subscribedFeatures(); // ['feature-1', 'feature-2']

检索不同的可订阅对象

默认情况下,可订阅对象总是Auth::user()。但您可以通过在AppServiceProvider中注册另一个闭包来更改这种行为。

如果您使用的是JetStream或另一个基于团队的订阅可订阅选项,则可以这样操作

// \App\Providers\AppServiceProvider::register()
\Rokde\SubscriptionManager\SubscribableResolver::resolveSubscribable(function () {
    return optional(\Illuminate\Support\Facades\Auth::user())->currentTeam;
});

现在,您可以将Subscribable特质添加到您的Team模型中

// \App\Models\Team
class Team extends \Laravel\Jetstream\Team {
    use \Rokde\SubscriptionManager\Models\Concerns\Subscribable;
}

现在,您可以使用各种检查之一。

事件

您可以在订阅生命周期期间监听各种事件。

以下事件被分发

  • \Rokde\SubscriptionManager\Events\SubscriptionCreated 当订阅被创建时
  • \Rokde\SubscriptionManager\Events\SubscriptionCanceled 当订阅被取消时
  • \Rokde\SubscriptionManager\Events\SubscriptionResumed 当取消的订阅被恢复时
  • \Rokde\SubscriptionManager\Events\SubscriptionUpdated 当订阅被更新时
  • \Rokde\SubscriptionManager\Events\SubscriptionDeleted 当订阅被删除时(它是软删除的)
  • \Rokde\SubscriptionManager\Events\SubscriptionPurged 当订阅最终被移除时
  • \Rokde\SubscriptionManager\Events\SubscriptionRestored 当软删除的订阅被恢复时

洞察

客户洞察

您必须使用Rokde\SubscriptionManager\Insights\Customer

您可以获取以下客户洞察:

  • 在某个时间段或当前时间戳内是哪些客户?
  • 在给定时间段内哪些客户会流失?

通常使用滚动30天或月度周期来检查指标。

订阅直方图

可以通过Rokde\SubscriptionManager\Insights\SubscriptionHistory访问历史数据。

默认情况下,用于检索上个月按周分组的统计数据。

$history = new \Rokde\SubscriptionManager\Insights\SubscriptionHistory();
$histogram = $history->get(); // keyed-map

每个分组的分区具有以下数据

'2021-01-01' => [
    'start' => '2021-01-01 00:00:00',   // start of period
    'end' => '2021-02-01 00:00:00',     // end of period
    'count' => 3,                       // number of subscriptions within the period
    'new' => 2,                         // newly created subscriptions within the period
    'trial' => 2,                       // number of subscriptions being on trial within the period
    'grace' => 2,                       // number of subscriptions being on grace period within the period
    'ended' => 2,                       // subscriptions finally ended within the period
],

测试

composer test

常见问题解答

问题:为什么您不使用订阅与功能模型之间的任何模型关系?

答案:计划和功能表只是为了订阅的售卖方面。您可以通过填充这些表以您的套餐和功能集合来使用这个结构。但是,您不必订阅用户到一组功能。

您的业务模型可以通过团队级别售卖订阅,但您可以向每个用户提供个人功能,用户可以订阅,无论团队有什么功能。所以,您不需要担心这个功能是否已经在计划或您的销售页面上。

示例:您有一个HR应用程序,您可以销售公司范围的订阅,让所有员工都能访问您的应用程序,公司支付费用。一个用户团队想要跟踪他们自己的私人待办事项列表。现在您可以为这些用户订阅一个特殊的功能——例如解锁一个特殊的菜单项。

问题:为什么您不处理计划或功能或订阅上的价格?

答案:因为在全球范围内使用价格包很难。您有多种价格(含税、不含税、含费、各种货币、汇率等)。

向已发货的迁移(表)添加字段,并从包模型继承以满足您处理价格和货币的需求。有时,在营销页面上仅以HTML显示价格,比向个人用户或商业客户显示带有或不带有税的时间相关多货币价格更容易。

也许在营销销售页面上介绍一些独特的卖点(USP)功能更好,但为了处理订阅访问,即使是CRUD操作,也应该有原子功能——就像基于权限的应用程序。

变更日志

有关最近更改的更多信息,请参阅变更日志

贡献

有关详细信息,请参阅贡献指南

安全漏洞

有关如何报告安全漏洞的详细信息,请参阅我们的安全策略

鸣谢

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件