Skip to content

Creating a Billable App

Nathan Pope edited this page Nov 23, 2018 · 22 revisions

Introduction

First of all, its best to read up on Shopify's documentation to get to know how it works internally.

To enable billing on your app, there is configuration already in config/shopify-app.php which is based on environment variables. Using these variables, here is what you need to setup.

Options / Setup

Note: Below are examples all based on env variables, you're free to use the standard config/shopify-app.php file as well

Here is an example setup to get a basic app into billable mode:

SHOPIFY_BILLING_ENABLED=1

Here is an explanation of the configuration, many are already set with sane-defaults:

billing_enabled (required)

SHOPIFY_BILLING_ENABLED=0 to disable billing state for application (default)

SHOPIFY_BILLING_ENABLED=1 to enable billing state for application

billing_freemium_enabled

SHOPIFY_BILLING_FREEMIUM_ENABLED=0 to disallow freemium use (default)

SHOPIFY_BILLING_FREEMIUM_ENABLED=1 to enable freemium use (skip billing section for a plan on install)

billing_redirect (required)

SHOPIFY_BILLING_REDIRECT="/url/path" for setting the path to handle the accepting or declining of the application charge (default: /billing/process)

Note: Its not recommended to change this unless you're using custom logic or overriding the billing controller.

Creating Plans

All plans are stored in the plans table.

Values

  • type; either 1 for recurring, 2 for single (required)
  • name; the name of the plan (required)
  • price; the price of the plan (required)
  • capped_amount; the maximum the plan will charge the shop for usage charges (required only for recurring type plans and if you plan to issue usage charges)
  • terms; the terms to display for the usage charge/capped_amount (required only for recurring type plans and if you plan to issue usage charges)
  • trial_days; number of trial days for your plan
  • test; boolean value of if the plan is test mode or not (good for development)
  • on_install; boolean value of if this plan will be presented on install (only one plan can have on_install as true)

Example Setup

# Create a recurring "Demo" plan for $5.00, which will be presented on install to the shop and have the ability to issue usage charges to a maximum of $10.00
INSERT INTO `plans` (`type`, `name`, `price`, `capped_amount`, `terms`, `trial_days`, `test`, `on_install`) VALUES (1, 'Demo', 5.00, 10.00, 'Demo terms', 7, false, true);

On-Install

Its important to flag one plan's on_install to true, so a shop can be charged billing during installation.

Notes

If you plan on creating a "free" recurring plan, and only utilizing usage charges, then you must set the plan's price to 0.00, set the capped_amount and the terms for Shopify to accept the 0.00 charge through the API.

Flow

Once you've set the required environment variables, you're ready to go.

Here's how the flow works:

  1. Shop installs the app
  2. Shop accepts the app's permissions
  3. Shop is presented with the billing screen to accept or decline
    • 3a. If they accept, charge is activated, freemium is disabled, charge ID is saved to database, shop is sent to your app's homepage
    • 3b. If they decline, charge is marked declined, shop is sent to error screen saying they did not pay.

Note: If billing is being applied on top of an existing app, when the shop accesses your app, they will sent to the billing screen.

Grandfather Mode

Grandfather mode is useful when you wish to give a shop free access to your app. A simple boolean flag in the database (grandfathered) tells the billable middleware to let them through.

You can check a shop's grandfathered status via: $shop->isGrandfathered().

Freemimum Mode

This mode, if enabled through the config, will allow shops to by-pass billing and use the app in freemium mode, and you can present a plan to them later.

You can check a shop's freemium status via: $shop->isFreemium().

Upgrading or Downgrading Plans (recurring/one-time)

Shopify internally handles cancelling the previous plan. You simply need to direct the shop to the billing screen to accept the new charge.

<p><a href="{{ route('billing', ['plan_id' => 2]) }}">Upgrade</a></p>

The above will direct the shop to the billing screen and present them with plan ID #2 from the plans table. You can also simply issue a redirect in a controller.

Creating Usage Charges

If a shop is on a recurring plan, with usage charge abilities, you can direct the shop (either through POST/GET) to the usage charge route. The package will take care of issuing the charge, storing it in the database, and redirection.

Example GET:

# App/Http/Controllers/Example
# ...
public function index()
{
    // Description and price of usage charge (the only two parameters required)
    $charge = [
        'description' => 'Five e-mails',
        'price'       => 1.00,
        'redirect'    => route('example.success') // Optional, if not supplied redirect goes back to previous page with flash `success`=`true`
    ];

    // Create a signature to prevent tampering
    $signature = ShopifyApp::createHmac(['data' => $charge, 'buildQuery' => true]);

    // Create the route
    $usageChargeRoute = route('billing.usage_charge', array_merge($charge, ['signature' => $signature]));

    return view('example.index', compact('usageChargeRoute'));
}

In the view:

<a href="{{ $usageChargeRoute }}">Add more emails!</a>

Billable Middleware

The middleware will check the following:

  1. If billing is enabled
  2. If the shop is not grandfthered
  3. If the shop is not set to freemium
  4. If the shop is not on a billing plan

If all checks pass, the request will be redirected to the billing flow

Usage

Currently its used only on the app's home route. In most cases, this is all you will need because every time a shop clicks your app from their Shopify admin, it will go through to the home route by default which includes the shop authorization middleware, and the billable middleware.

If you'd like to use it on more routes, here's an example usage:

Route::get(
    '/settings',
    'App\Controllers\SettingsController@index'
)
->middleware(['auth.shop', 'billable']) // <---
->name('settings');
You can’t perform that action at this time.