Gold
Gold is Helium’s approach to predictably and accurately billing merchants for Shopify apps.
For more information about the goals and design of this project, please review Gold's design document.
Tour
Gold is comprised of a few pieces:
Gold::BillingController
allows merchants to select tiers, set up charges, and review their billing information.Gold::Billing
ties together all pieces of a shop's billing information.Gold::Machine
defines the state that a shop is in and what transitions can be made from that state to others.Gold::Transition
records these state transitions in the database.Gold::Tier
holds definitions of tiers that are available for merchants to select from. Apps using Gold define these in theirconfig/tiers.yml
file.Gold::Concerns::Gilded
is a mixin to an app's shop class to give it Gold's functionality. This app-specific shop class is configured with theGold
module's#shop_class=
setter.
Installation
Add this line to your application's Gemfile:
gem "shopify-gold", require: "gold"
And then execute:
$ bundle
You will need to apply migrations to your app's Shop
class and bring in the
other Gold models to your database:
$ rake gold_engine:install:migrations
$ rake db:migrate
Mount the engine in your config/routes.rb
file:
mount Gold::Engine, at: "/billing"
Note that by default, the engine's name is gold_engine
. If you must change this,
you'll need to define a new helper method gold_engine
and reference your custom
as
param.
Add module to your Shop
class:
include Gold::Concerns::Gilded
Add the following methods to your Shop
class:
#shopify_domain
, which responds with the *.myshopify.com domain of the shop
#shopify_plan_name
, which responds to the plan name of the Shopify store (e.g. "unlimited", "affiliate")
#qualifies_for_tier?(_tier)
, return if the shop should qualify to a tier
#with_shopify_session
, provided by the Shopify App gem
#installed?
, return if the app is installed in the merchant's Shopify admin
After shop properties have been updated (say, after a shop/update webhook), call:
shop.billing.after_shop_update!
. This tells Gold something has changed about the
shop, which will run any operations (example affiliate -> paid) that might be relevant.
Call the AfterAuthenticateJob inline when a user logs in.
In shopify_app.rb
initializer:
config.after_authenticate_job = { job: Gold::AfterAuthenticateJob, inline: true }
Or in your own job:
Gold::AfterAuthenticateJob.perform_now(shop_domain: shop_domain)
Background tasks
Gold runs background jobs to queue events under the gold
queue. For example, if
you're using Sidekiq, run:
$ bundle exec sidekiq -q gold
Configuration
Add gold.rb
within the initializers
folder of your project. Below are Gold's
configuration options:
Gold.configure do |config|
# The class name (string) of the the app's Active Record Shop model
config.shop_class = "Shop"
# The attribute of the *.myshopify.com domain on the Shop model
config.shop_domain_attribute = "shopify_domain"
# Should Gold create test charges? Helpful in a development environment
config.test_charge = false
# The logger Gold will use
config.logger = Logger.new(STDOUT)
# How many days we'll wait before cleaning up an uninstalled shop
config.days_until_cleanup = 30
# How many days we'll wait before we uninstall a shop without payment
config.days_until_delinquent = 7
# The name of the app using gold (used in the charge name)
config.app_name = "My App"
# The email Gold uses to send emails from
config.contact_email = "[email protected]"
# The URL to a plan comparison page for the app
config.plan_comparison_url = "https://heliumdev.com/pricing"
# The API version used by Shopify (https://help.shopify.com/en/api/versioning)
config.shopify_api_version = "2019-04"
# If Gold is allowed to cancel charges (paid -> free) automatically
config.allow_automated_charge_cancellation = true
config.admin_credentials = {
user: "admin",
password: "password123"
}
end
Hooks
You can hook into Gold's lifecycle by providing a Proc. Available hooks:
Gold.configure do |config|
config.on_install = proc {
puts "Installing Gold..."
}
config.on_terms = proc {
puts "Accepted terms from Gold..."
}
config.on_apply_tier = proc { |billing, tier_id|
puts "Applied tier..."
}
config.on_activate_charge = proc { |billing, charge|
puts "Activated charge..."
}
config.on_uninstall = proc {
puts "Uninstalling Gold..."
}
config.on_cleanup = proc { |billing|
puts "Cleaning up shop"
}
config.on_check_charge = proc { |billing, active_charge|
puts "Check charge for shop"
}
end
Adding tiers
To add tiers to your app, add a config/tiers.yml
file. Here's an example file:
- id: basic
name: "Basic"
description: "Shops just gettings started with a few number of custom fields."
features:
feature_1: Yes
featured_2: No
pricing:
trial_days: 14
monthly: "10.00"
qualifications:
max_customers: 1000
Controller and views
Ok, so it's time to get paid. Let's add our module to show merchants tier choices.
Include the following in your ApplicationController
:
include Gold::Concerns::MerchantFacing
When you want to put an action or controller behind a paywall, add:
before_action :confront_mandatory_billing_action
You can override any view file by matching Gold's path pattern under views
. The
most notable are likely views/gold/billing/terms.html
and views/gold/billing/tier.html
Admin interface
Gold can manage billing for each shop through an admin interface. First, you'll want to setup some basic HTTP auth credentials in your Gold config initializer.
Gold.configure do |config|
config.admin_credentials = {
user: "admin",
password: "password123"
}
end
Mount the engine in routes:
mount Gold::AdminEngine, at: '/admin', as: 'gold_admin_engine'
You can inject a partial anywhere in your own admin interface to include Gold's
admin management by providing an instance of Billing
:
render "gold/admin/billing/overview", billing: @shop.billing
This partial expects the following instance variables to be defined from your controller:
@active_charge = ShopifyAPI::RecurringApplicationCharge.current
@credits = ShopifyAPI::ApplicationCredit.all || []
Migration
If you have existing charges that you want to migrate to Gold on a specific plan,
use the BillingMigrator.new([Shop], [tier_id], [start_at])
class. Example:
Gold::BillingMigrator.new(@shop, "basic", 5.days.ago)
The shop will automatically be pushed through the state machine (new, accepted_terms, etc).
Note that the tier_id
must match a tier setup in Gold. If that shop does not
have a charge that matches the tier's price, a new charge will be requested.
Referral affiliates
Gold supports the ability to track affiliate codes that link back to a shop. A referral can
apply a discount to a shop when they sign up with a code. It works by sending merchants to a
link like /referral?code=HELIUM
. They will be redirected to the URL setup in config.
The admin referral controller provides some basic CRUD operations for managing affiliates, but your app will need to implement the logic for rendering referrals.
Contributing
Run tests with:
$ bin/rails test
Run lint/style checks with:
$ bundle exec rubocop
If you want to see a visual representation of Gold::Machine
, you can run
bin/rails app:gold:diagram
. You will need to have the Graphviz tool installed,
which you can do on Mac via Homebrew with brew install graphviz
Release
To release gem, run gem release
, which depends on having gem-release
installed
on your system.
Changelog
Version 4.0.0
- Added referral affiliates
Version 3.2.0
- Removed
force_embedded_redirect
config option