Spike Providables

A providable is a special class that defines something that is provided to the user once they have purchased a product, or their subscription is renewed.

When configuring products or subscriptions, you've come across the respective provides and provides_monthly properties, which contain a list of items provided to the user. A CreditAmount is an example of an in-built providable, which provides different types of credits to the user upon purchase.

You can build your own providables and include them in your product/subscription configuration, and you just need to implement a simple interface.

CreditAmount providable

Spike comes with a CreditAmount providable built-in, which allows you to provide different types of credits to your users very easily.

For example, assuming you have configured the credit types so that there are email and sms credits, you can provide any mix of these in your products and subscription plans, like so:

use Opcodes\Spike\CreditAmount;

'subscriptions' => [
    [
        'id' => 'standard',
        // ...
        'provides_monthly' => [
            CreditAmount::make(5_000, 'emails'),
            CreditAmount::make(1_000, 'sms'),
        ],
    ],
    
    [
        'id' => 'business',
        // ...
        'provides_monthly' => [
            CreditAmount::make(20_000, 'emails'),
            CreditAmount::make(5_000, 'sms'),
        ],
    ],

    // other plans...
],

When selling products, you can also define an expiration period for your credits, like so:

'products' => [
    [
        'id' => '10_dollars',
        // ...
        'provides' => [
            CreditAmount::make(5_000, 'emails')->expiresAfterMonths(6),
            CreditAmount::make(2_000, 'sms'),
        ],
    ],

    //
],

Subscriptions ignore any expiration set on credits, because they are renewed monthly already and any unused credits are expired at the end of the period.

Types of providables

Currently, Spike supports two types of providables - Providable and CountableProvidable.

  • Providable - something that isn't countable, such as access to a VIP area, unlocking special behaviour or a higher API rate-limit.
  • CountableProvidable - something that is countable, such as credits, projects, tasks, team members, etc.

Now let's look at how to implement each of these providable types.

Providable

Let's say you want to allow your users to purchase a higher API rate-limit, so they can spend more of your credits, faster!

First, we create a new class, named anything you like and anywhere you like, and we implement the Providable interface.

namespace App\Providables;

use Opcodes\Spike\Contracts\Providable;

class CustomApiRateLimit implements Providable
{
    //
}

Now, let's define the necessary methods:

namespace App\Providables;

use Opcodes\Spike\Contracts\Providable;

class CustomApiRateLimit implements Providable
{
    public function __construct(
        public int $requestsPerMinute
    ) {}

    public static function __set_state(array $data): Providable
    {
        return new static($data['requestsPerMinute']);
    }

    public function key(): string
    {
        return 'custom-api-rate-limit';
    }

    public function name(): string
    {
        return 'Custom Rate-Limit';
    }

    public function icon(): ?string
    {
        return '<svg>...</svg>';
    }

    public function toString(): string
    {
        return $this->requestsPerMinute . ' requests per minute';
    }

    public function isSameProvidable(Providable $providable): bool
    {
        return get_class($providable) === get_class($this);
    }

    // This is where we give the feature to the billable, because it has been purchased via a product
    public function provideOnceFromProduct(Product $product, $billable): void
    {
        $billable->setApiRequestsPerMinute($this->requestsPerMinute);
    }

    // This is where we give the feature to the billable, because their subscription plan has this providable
    public function provideMonthlyFromSubscriptionPlan(SubscriptionPlan $subscriptionPlan, $billable): void
    {
        $billable->setApiRequestsPerMinute($this->requestsPerMinute);
    }
}

Now that we have defined the class for the providable, we can start including it in our product and subscription configuration in config/spike.php, like so:

use App\Providables\CustomApiRateLimit;

'subscriptions' => [
    [
        'id' => 'standard',
        'name' => 'Standard',
        // ...
        'provides_monthly' => [
            new CustomApiRateLimit(60),
        ],
    ],
    
    [
        'id' => 'business',
        'name' => 'Business',
        // ...
        'provides_monthly' => [
            new CustomApiRateLimit(180),
        ],
    ],

    // other plans...
],

'products' => [
    [
        'id' => 'high_rate_limit',
        'name' => 'High rate-limit',
        'short_description' => 'Unlock a higher rate-limit for your business',
        // ...
        'provides' => [
            new CustomApiRateLimit(200)
        ],
    ],
    
    [
        'id' => '50_dollars',
        'name' => 'Business pack',
        // ...
        'provides' => [
            new CustomApiRateLimit(200),
            
            CreditAmount::make(50_000),
        ],
    ],

    //
],

Countable Providable

A CountableProvidable just needs a couple more methods in order to make calculations when purchasing products or renewing subscriptions.

Taking the above example, if we want to make the CustomApiRateLimit countable (so the user can stack more of these limits), then we just implement the CountableProvidable interface and the two additional methods:

namespace App\Providables;

use Opcodes\Spike\Contracts\CountableProvidable;

class CustomApiRateLimit implements CountableProvidable
{
    public function __construct(
        public int $requestsPerMinute
    ) {}

    // ... other methods we previously implemented
    
    public function setAmount(int $amount): CountableProvidable
    {
        $this->requestsPerMinute = $amount;
    }

    public function getAmount(): int
    {
        return $this->requestsPerMinute;
    }
}

And that's it! Now the user in the billing portal can purchase multiple of these, with the value stacking.

For example, let's assume you're selling a product that gives an additional 60 requests per minute for $49. Your product configuration would look like this:

use App\Providables\CustomApiRateLimit;

'products' => [
    [
        'id' => 'additional_rate_limit',
        'name' => 'Additional API rate-limit',
        'short_description' => 'Unlock a higher rate-limit for your increasing needs',
        // ...
        'provides' => [
            new CustomApiRateLimit(60)
        ],
    ],

    //
],

Then, your user will be able to add one, two, three or even more of these in their card, and the value of this providable will be multiplied by the number of items in their shopping cart.

E.g. if they put three of these products in their cart, they will pay $49 x 3, and they will get a 60 x 3 rate-limit.

Support

If you have any questions, feedback, or need any help setting up Spike within your project, feel free to reach out to me.