2017 © Pedro Peláez
 

library subscriptions

Subscription handling package for laravel 5 applications

image

vvmalko/subscriptions

Subscription handling package for laravel 5 applications

  • Monday, February 8, 2016
  • by vvMalko
  • Repository
  • 1 Watchers
  • 8 Stars
  • 67 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 3 Forks
  • 0 Open issues
  • 1 Versions
  • 22 % Grown

The README.md

porting from laravel 4 package. Original here: ipunkt/subscriptions, (*1)

Subscriptions package for Laravel 5.x applications

Latest Stable Version Latest Unstable Version License Total Downloads, (*2)

Installation

Add to your composer.json following lines, (*3)

    "require": {
        "vvmalko/subscriptions": "dev-master"
    }

Run php artisan vendor:publish, (*4)

Then edit plans.php and defaults.php in config/vvmalko to your needs. All known plans are still in there., (*5)

Add vvMalko\Subscriptions\SubscriptionsServiceProvider::class, to providers in config/app.php., (*6)

Add 'Subscription' => vvMalko\Subscriptions\SubscriptionsFacade::class, to aliases in config/app.php., (*7)

Run php artisan migrate to migrate the necessary database tables., (*8)

Configuration

Plan configuration

    //  @see src/config/plans.php
    return [
        'PLAN-ID' => [
            'name' => 'TRIAL',
            'description' => 'Trial subscription.',
            'subscription_break' => 0,  // optional for preventing a subscription for X days after last subscription ends
        ],
    ];

The optional property 'subscription_break can be used to prevent a subscriber to subscribe to this plan before a configured count of days will be gone. This is for example to have a TRIAL plan which can be subscribed to only once a year., (*9)

Benefit configuration for a plan

//  @see src/config/plans.php
    return [
        'PLAN-ID' => [
            // [..]         
            'benefits' => [
                'feature-1' => [],  // feature is present
                'feature-2-countable' => [
                    'min' => 10,    // feature is present and has margins/countable range
                ],
                'feature-3-countable' => [
                    'min' => 10,
                    'max' => 50,
                ],
                'feature-4-countable' => [
                    'max' => 50,    // min is automatically 0 (zero)
                ],
            ],
        ],
    ];

Payment options for a plan

    //  @see src/config/plans.php
    return [
        'PLAN-ID' => [
            // [..]         
            'payments' => [
                [
                    'price' => 1,           // for 1.00
                    'quantity' => 12,       // in 12-times
                    'days' => 30,           // of 30-days
                    'methods' => ['paypal'], // allowed payment methods
                ],
                [
                    'price' => 2,           // for 2.00
                    'quantity' => 12,       // in 12-times
                    'days' => 30,           // of 30-days
                    'methods' => ['paypal', 'invoice'],
                ],
            ],
        ],
    ];

Choosing a default plan for all subscribers

For setting a default plan to all subscribers you can use the src/config/defaults.php and set the id for the default plan. So every call on plan-based feature checking will resolve this default plan when the subscriber has no plan yet., (*10)

Customer Model Setup (for example User)

use vvMalko\Subscriptions\Subscription\Contracts\SubscriptionSubscriber;

class User extends Model implements SubscriptionSubscriber
{
    ...
    ...
    ...

    public function getSubscriberId()
        {
            return $this->id;
        }

    public function getSubscriberModel()
    {
        return $this->table; //model_class set us `User`
    }
}

Controller Setup (for example SubscriptionsController)

use vvMalko\Subscriptions\SubscriptionsFacade as Subscription;

class SubscriptionsController extends Controller
{
    ...
    ...
    ...

    private $subscriber;

    public function __construct()
    {
        $this->subscriber = Auth::user();
    }

    //get user(subscriber) plans
    public function plans()
        {
            $plan = Subscription::current($this->subscriber);
            return view('plans');
        }


}

For setting a default plan to all subscribers you can use the src/config/defaults.php and set the id for the default plan. So every call on plan-based feature checking will resolve this default plan when the subscriber has no plan yet., (*11)

Usage

Getting all plans

    /** @var Plan[] $plans */
    $plans = Subscription::plans();

If you use the subscription break in your plan configuration, fetch all plans with the selectablePlans method. This checks the last subscription for each breaking plan., (*12)

    /** @var Plan[] $plans */
    $plans = Subscription::selectablePlans($this->user);

Getting the current plan for a subscriber

    /** @var Plan|null $plan */
    $plan = Subscription::plan($subscriber);

Does a subscription already exists for a subscriber

    Subscription::exists($subscriber); // returns true when a subscription exists

Does a subscription expired? Compared period time and current time

    Subscription::expired($subscriber); // returns true|false or null when a subscription not exists

Each plan can have benefits (features)

    $plan->can('feature');               // returns true or false
    $plan->can('countable-feature', 14); // returns true or false

Or use the Subscription facade instead to check against current subscription plan for a subscriber. This is recommended:, (*13)

    Subscription::can($subscriber, 'feature');               // returns true or false
    Subscription::can($subscriber, 'countable-feature', 14); // returns true or false

Getting all possible payment options for a plan

    /** @var PaymentOption[] $paymentOptions */
    $paymentOptions = $plan->paymentOptions();

Creating a new subscription

    /** creating a subscription for a subscriber, maybe the current authenticated user */
    $subscription = Subscription::create($plan, $paymentOption, SubscriptionSubscriber $subscriber);

For creating a subscription you have to give the Plan or the id of a plan and the selected PaymentOption or the identifier for the payment option. The $subscriber is the entity the subscription belongs to. This can be any morphable eloquent object., (*14)

After a subscription was created successfully an event of type vvMalko\Subscriptions\Subscription\Events\SubscriptionWasCreated gets fired., (*15)

The underlying repository controls for duplicates itself. So for existing subscriptions it will update the current subscription and fires an event of type vvMalko\Subscriptions\Subscription\Events\SubscriptionWasUpdated instead., (*16)

You can upgrade the subscription to any other plan. The same method Subscription::create() handles this upgrade., (*17)

The fired events have both the current subscription, the selected plan and the payment option as properties. So you can listen on these events and do your own stuff., (*18)

Getting the current subscription for a subscriber

    /** @var Subscription|null $subscription */
    $subscription = Subscription::current($subscriber);

Check subscriber on a Trial

    /** be careful because current() can return null when no subscription existing */
    $onTrial = Subscription::current($subscriber)->onTrial();

Check subscription paid

    $subscription = Subscription::current($subscriber);
    $isPaid = $subscription->paid(); // or Subscription::paid($subscriber);

Getting all periods for a subscription

    /** @var Period[] $periods */
    $periods = $subscription->periods;

Userland code

Fitting in you controllers

We use the laracasts/commander package for handling business commands and events., (*19)

    class SubscriptionsController extends \Controller
    {
        /**
         * use commandbus to execute commands
         */
        use Laracasts\Commander\CommanderTrait;

        // display an overview of all subscriptions
        public function index()
        {
            $subscribed = Subscription::exists($this->user);// $this->user represents a SubscriptionSubscriber interface
            if ( ! $subscribed) {
                $plans = Subscription::selectablePlans($this->user);    // unselectable plans filtered out already
                $defaultPlan = Subscription::plan($this->user);

                return View::make('subscriptions.create', compact('plans', 'defaultPlan'));
            }

            $plan = Subscription::plan($this->user);
            $subscription = Subscription::current($this->user);

            $paid = $subscription->paid();

            $subscriptions = Subscription::all($this->user);

            return View::make('subscriptions.index', compact('subscribed', 'plan', 'subscription', 'subscriptions', 'paid'));
        }

        //  create a plan (form)
        public function create($plan)
        {
            $plan = Subscription::findPlan($plan);

            $subscription = Subscription::all($this->user)->last();
            if (null !== $subscription && $subscription->subscription_ends_at->isPast())
                $subscription = null;

            $startDate = (null === $subscription) ? Carbon::now() : $subscription->subscription_ends_at->addSeconds(1);

            return View::make('subscriptions.create_plan', compact('plan', 'startDate'));
        }

        //  store the plan as subscription for user
        public function store()
        {
            try {
                $this->validate(Input::all());
            } catch (FormValidationException $e)
            {
                return Redirect::back()->withInput()->withErrors($e->getErrors());
            }

            $plan = Subscription::findPlan(Input::get('plan'));
            if (null === $plan)
                throw (new ModelNotFoundException('No plan ' . Input::get('plan') . ' found.'))->setModel(Plan::class);

            $this->execute(CreateSubscriptionCommand::class, Input::all());

            Flash::success('subscriptions.subscription_created');

            return Redirect::route('subscriptions.index');
        }
    }

And the corresponding command CreateSubscriptionCommandHandler is here (The CreateSubscriptionCommand is only a DTO for the input values):, (*20)

    class CreateSubscriptionCommandHandler implements Laracasts\Commander\CommandHandler
    {
        use Laracasts\Commander\Events\DispatchableTrait;

        /**
         * authenticated user
         *
         * @var \Illuminate\Auth\Guard
         */
        private $auth;

        /**
         * @param AuthManager $auth
         */
        public function __construct(\Illuminate\Auth\AuthManager $auth)
        {
            $this->auth = $auth;
        }

        /**
         * Handle the command
         *
         * @param CreateSubscriptionCommand $command
         * @return mixed
         */
        public function handle($command)
        {
            /** @var User|SubscriptionSubscriber $user */
            $user = $this->auth->user();

            //  store invoice data

            //  create subscription
            $subscription = Subscription::create($command->plan, $command->payment_option, $user);

            //  fire event for "subscription created" or "subscription updated"
            $this->dispatchEventsFor($subscription);
        }
    }

Nearly the same you have to do for extending or upgrading a plan. You can use the same command, handler and controller action. The subscription repository handles automatically an update or create for a subscription plan., (*21)

Registering a Listener

    # in your app/listeners.php for example
    Event::listen('vvMalko.Subscriptions.Subscription.Events.*', 'App\Subscriptions\Listeners\EmailNotifier');

    //  we use the laracasts/commander package, so you can inform you about a listener too
    class EmailNotifier extends Laracasts\Commander\Events\EventListener
    {
        /**
         * will be called when event SubscriptionWasCreated was fired
         *
         * @param SubscriptionWasCreated $event
         */
        public function whenSubscriptionWasCreated(SubscriptionWasCreated $event)
        {
            //  do something when a subscription was created (a new plan was set up and no plan exists before 
            //  or every plan subscription before was in the past)
        }

        /**
         * will be called when event SubscriptionWasUpdated was fired
         *
         * @param SubscriptionWasUpdated $event
         */
        public function whenSubscriptionWasUpdated(SubscriptionWasUpdated $event)
        {
            //  do something when a subscription was updated (e.g. smaller plan before gets upgraded to a more-featured
            //  plan or a subscription was extended to get longer running)
        }
    }

The Versions

08/02 2016

dev-master

9999999-dev

Subscription handling package for laravel 5 applications

  Sources   Download

MIT

The Requires

 

by Vladimir Malko

laravel php laravel5 subscriptions plan