Collecting customer information

You can get a lot of information about the shop from the shopify API.

To store the information create a migration.

bundle exec rails g migration capture_store_information

This migration will add a lot of fields to the Shop model to store all of the information from Shopify.

class CaptureStoreInformation < ActiveRecord::Migration[5.0]
  def change
    change_table :shops do |t|
      t.integer :shopify_id, limit: 8, null: true, after: :shopify_token
      t.string :name, limit: 255, null: true, after: :shopify_id
      t.string :domain, limit: 255, null: true, after: :name
      t.string :shop_owner, limit: 255, null: true, after: :domain
      t.string :email, limit: 255, null: true, after: :shop_owner
      t.string :customer_email, limit: 255, null: true, after: :email
      t.string :plan_name, limit: 120, null: true, after: :customer_email
      t.string :iana_timezone, limit: 255, null: true, after: :plan_name
      t.string :timezone, limit: 255, null: true, after: :iana_timezone
      t.string :primary_locale, limit: 255, null: true, after: :timezone
      t.string :currency, limit: 3, null: true, after: :primary_locale
      t.string :money_format, limit: 255, null: true, after: :currency
      t.string :money_with_currency_format, limit: 255, null: true, after: :money_format
      t.string :address1, limit: 255, null: true, after: :money_with_currency_format
      t.string :address2, limit: 255, null: true, after: :address1
      t.string :city, limit: 120, null: true, after: :address2
      t.string :province_code, limit: 255, null: true, after: :city
      t.string :country_code, limit: 2, null: true, after: :province_code
      t.string :zip, limit: 50, null: true, after: :country_code
      t.string :phone, limit: 30, null: true, after: :zip
      t.string :source, limit: 255, null: true, after: :phone
      t.boolean :password_enabled, null: true, default: false, after: :source
      t.boolean :has_discounts, null: true, default: false, after: :password_enabled
      t.boolean :has_gift_cards, null: true, default: false, after: :has_discounts
      t.boolean :has_storefront, null: true, default: false, after: :has_gift_cards
      t.boolean :setup_required, null: true, default: false, after: :has_storefront
      t.boolean :force_ssl, null: true, default: false, after: :setup_required
      t.boolean :tax_shipping, null: true, default: false, after: :force_ssl
      t.boolean :taxes_included, null: true, default: false, after: :tax_shipping
      t.boolean :county_taxes, null: true, default: false, after: :taxes_included
      t.datetime :store_created_at, null: true, after: :county_taxes
    end
  end
end

Also add some validation to the Shop model

class Shop < ActiveRecord::Base
  include ShopifyApp::Shop

  validates :address1, length: {maximum: 255}
  validates :address2, length: {maximum: 255}
  validates :city, length: {maximum: 120}
  validates :country_code, length: {maximum: 2}
  validates :currency, length: {maximum: 3}
  validates :customer_email, length: {maximum: 255}
  validates :domain, length: {maximum: 255}
  validates :email, length: {maximum: 255}
  validates :iana_timezone, length: {maximum: 255}
  validates :money_format, length: {maximum: 255}
  validates :money_with_currency_format, length: {maximum: 255}
  validates :name, length: {maximum: 255}
  validates :phone, length: {maximum: 30}
  validates :plan_name, length: {maximum: 120}
  validates :primary_locale, length: {maximum: 255}
  validates :province_code, length: {maximum: 255}
  validates :shopify_domain, length: {maximum: 255}, presence: true, uniqueness: true
  validates :shop_owner, length: {maximum: 255}
  validates :shopify_token, length: {maximum: 255}, presence: true, uniqueness: true
  validates :source, length: {maximum: 255}
  validates :timezone, length: {maximum: 255}
  validates :zip, length: {maximum: 50}

  scope :active, -> { where(uninstalled_at: nil) }
end

Now create a new app_installed job. We’ll run this in the background so that it doesn’t slow down the installation process.

bundle exec rails g job app_installed

Your app installed job should call the Shopify shop API, grab the shop details and store them with shop record.

app/jobs/app_installed_job.rb

class AppInstalledJob < ApplicationJob
  def perform(shop_id)
    shop = Shop.find(shop_id)

    ShopifyAPI::Session.setup({:api_key => ENV['SHOPIFY_API_KEY'], :secret => ENV['SHOPIFY_SHARED_SECRET']})
    session = ShopifyAPI::Session.new(shop.shopify_domain, shop.shopify_token)
    ShopifyAPI::Base.activate_session(session)

    current_shop = ShopifyAPI::Shop.current
    shop.update(
      shopify_id: current_shop.id,
      name: current_shop.name,
      domain: current_shop.domain,
      shop_owner: current_shop.shop_owner,
      email: current_shop.email,
      customer_email: current_shop.customer_email,
      plan_name: current_shop.plan_name,
      iana_timezone: current_shop.iana_timezone,
      timezone: current_shop.timezone,
      primary_locale: current_shop.primary_locale,
      currency: current_shop.currency,
      money_format: current_shop.money_format,
      money_with_currency_format: current_shop.money_with_currency_format,
      address1: current_shop.address1,
      address2: current_shop.address2,
      city: current_shop.city,
      province_code: current_shop.province_code,
      country_code: current_shop.country_code,
      zip: current_shop.zip,
      phone: current_shop.phone,
      source: current_shop.source,
      password_enabled: current_shop.password_enabled,
      has_discounts: current_shop.has_discounts,
      has_gift_cards: current_shop.has_gift_cards,
      has_storefront: current_shop.has_storefront,
      setup_required: current_shop.setup_required,
      force_ssl: current_shop.force_ssl,
      tax_shipping: current_shop.tax_shipping,
      taxes_included: current_shop.taxes_included,
      county_taxes: current_shop.county_taxes,
      store_created_at: current_shop.created_at)
  end
end

Now run your migrations

bundle exec rails db:migrate
bundle exec rails db:migrate RAILS_ENV=test

You then need to update the store method of your custom shop repository app/models/shop_session_repository.rb so that it runs the AppInstalledJob.

    def store(session)
      shop = Shop.find_or_initialize_by(shopify_domain: session.url)
      shop.shopify_token = session.token
      shop.save!

      AppInstalledJob.perform_later(shop.id)

      shop.id
    end

Now create a new job to handle the shop/update webhook that’s called when the shop details are changed.

bundle exec rails g job ShopUpdate

This job should take the store parameters passed to it and update the relevant shop record.

class ShopUpdateJob < ApplicationJob
  def perform(*webhooks)
    # Just in case we don't get an array
    return unless webhooks.is_a?(Array)

    webhooks.each do |webhook|
      # Try the next one if we don't have a shop_domain
      next unless webhook.key?(:shop_domain)

      # Find the shop and try the next one if we can't find it
      shop = Shop.active.find_by(shopify_domain: webhook[:shop_domain])
      next if shop == nil

      shop_data = webhook[:webhook]
      shop.update(
        shopify_id: shop_data[:id],
        name: shop_data[:name],
        domain: shop_data[:domain],
        shop_owner: shop_data[:shop_owner],
        email: shop_data[:email],
        customer_email: shop_data[:customer_email],
        plan_name: shop_data[:plan_name],
        iana_timezone: shop_data[:iana_timezone],
        timezone: shop_data[:timezone],
        primary_locale: shop_data[:primary_locale],
        currency: shop_data[:currency],
        money_format: shop_data[:money_format],
        money_with_currency_format: shop_data[:money_with_currency_format],
        address1: shop_data[:address1],
        address2: shop_data[:address2],
        city: shop_data[:city],
        province_code: shop_data[:province_code],
        country_code: shop_data[:country_code],
        zip: shop_data[:zip],
        phone: shop_data[:phone],
        source: shop_data[:source],
        password_enabled: shop_data[:password_enabled],
        has_discounts: shop_data[:has_discounts],
        has_gift_cards: shop_data[:has_gift_cards],
        has_storefront: shop_data[:has_storefront],
        setup_required: shop_data[:setup_required],
        force_ssl: shop_data[:force_ssl],
        tax_shipping: shop_data[:tax_shipping],
        taxes_included: shop_data[:taxes_included],
        county_taxes: shop_data[:county_taxes],
        store_created_at: shop_data[:created_at])
    end
  end
end

Update config/initializers/shopify_app.rb so that you now handle the shop/update webhook.

config.webhooks = [
  {topic: 'app/uninstalled', address: "#{ENV['SHOPIFY_WEBHOOKS_BASE_URL']}/app_uninstalled"},
  {topic: 'shop/update', address: "#{ENV['SHOPIFY_WEBHOOKS_BASE_URL']}/shop_update"}
]

After installing the store you can now see the store information in the shops table. When a store owner updates shop information you shops table record will be updated too.

Now commit everything to version control.

git add app db spec config
git commit -m "Added background jobs to capture store information on install and shop update"