Using credits in Spike

Credits are the first currency in Spike. It helps manage and account for the usage of your app, while providing easy and understandable ways to top up and renew credits via subscriptions and products.

Each of your users (billables) has their own credit balance, which they can spend to use your app features, such as API requests, downloads, file generations, etc. Spike handles the credit balance top ups and history, while giving you the freedom to spend the user's credits any way you like.

There are two ways to access the Spike's CreditManager:

  • on the billable directly - $user->credits(), or
  • using the Facade - Opcodes\Spike\Facades\Credits::billable($user)

Let's use the simple form of $user->credits() in the examples below.

Checking the balance

$user->credits()->balance() returns an int and allows you to check the balance of any billable.

$balance = $user->credits()->balance();

If you have multiple types of credits configured, you can specify which credit type you'd like to work with.

$balance = $user->credits('sms')->balance();
// or
$balance = $user->credits()->type('sms')->balance();

// Not providing a type will default to the first-configured credit type.
$balance = $user->credits()->balance();

Adding credits

$user->credits()->add(int $credits, string $note = null) allows you to add credits to the billable's balance.

Although Spike handles the purchases of credits from products and subscriptions, sometimes you may wish to add additional credits to the billable's balance. This will appear as an adjustment in the Usage page for the user, along with the note provided.

// Add 500 credits to the user
$user->credits()->add(500, 'Free gift');

The second argument can be an associative array instead, allowing you to also specify the date when the credits would expire:

// Add 500 credits that expire after 1 week
$user->credits()->add(500, [
    'notes' => 'Free gift (use it quickly!)',
    'expires_at' => now()->addWeek(),
]);

Again, if there are multiple types of credits configured, you can specify the type first before adding credits to it.

$user->credits()->type('sms')->add(500);

Removing credits

$user->credits()->remove(int $credits, string $note = null) allows you to remove credits from the billable's balance.

If it's not related to the regular usage of credits, sometimes you may wish to remove credits from the billable's balance. This will appear as an adjustment in the Usage page for the user, along with the note provided.

If the user's balance is not high enough to cover the negative adjustment, an Opcodes\Spike\Exceptions\NotEnoughBalanceException exception will be thrown, unless Negative balances are allowed.

// Remove 300 credits from the user
$user->credits()->remove(300, 'penalty for misuse of the API');

// optionally, define the credit type to remove
$user->credits()->type('sms')->remove(20);

Spending credits

Spike provides a method to spend the user's available credits, but it is still up to you to figure out where, when, and how many credits should be spent.

$user->credits()->spend(int $credits) allows you to conveniently spend the credits of the default billable. This appears as usage type in the Usage page for the user, helping them understand how many credits were spent each day.

// Spend a single credit
$user->credits()->spend();

// Spend five credits
$user->credits()->spend(5);

// Spend a specific type of credit
$user->credits()->type('sms')->spend(
    $message->numberOfSmsParts()
);

If the user's balance is not high enough to cover the credits to be spent, an Opcodes\Spike\Exceptions\NotEnoughBalanceException exception will be thrown, unless Negative balances are allowed.

You can first check whether the user has enough credits with the canSpend method:

if ($user->credits()->canSpend(50)) {
    $user->credits()->spend(50);
}

Let's cover a few common scenarios for spending credits.

Scenario: pay per request

In an example scenario, where you would like to charge for each API request, you might want to call the $user->credits()->spend() method after each successful response to the API. For this, you can utilise Laravel middleware and create a new middleware that spends a credit after each request:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Opcodes\Spike\Facades\Spike;
 
class SpendSpikeCredit
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $billable = Spike::resolve();

        if (!$billable->credits()->canSpend()) {
            // the user does not have any credits remaining for this request.
        }

        $response = $next($request);

        if ($response->status() < 300) {
            // We only want to spend credits on successful responses
            $billable->credits()->spend();
        }

        return $response;
    }
}

You can then assign this middleware on any API route that you wish to use a credit.

If you want to learn more about using and assigning middleware, check out the Laravel documentation.

Scenario: pay per contents

In some scenarios, the credit usage is varied, depending on the request. For example, you may allow the user to send text messages, but you want to charge them for each of the SMS parts sent, rather than once for a single request.

In this example, we check the SMS message sent and calculate its length. For simplicity's sake, let's assume that a single SMS part can consist of 153 characters.

$contentLength = strlen($sms->content);
$costInCredits = ceil($contentLength / 153);

$user->credits()->spend($costInCredits);

Checking if billable can spend

By default, calling credits()->remove() and credits()->spend() will throw exceptions if the billable does not have enough credits to fulfill the expenditure. You can either catch the exception, or check whether they can spend the credits beforehand.

$user->credits()->canSpend(int $credits) returns a bool allowing you to check whether the user has enough credits to spend for this request.

// Can the user spend one credit?
$user->credits()->canSpend();

// Can the user spend 5 credits?
$user->credits()->canSpend(5);

Support

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