|
| 1 | +# Shopify embedded app |
| 2 | + |
| 3 | +Start with a basic rails application. Then do the following: |
| 4 | + |
| 5 | +* Setup the secrets for the embedded app |
| 6 | +* Add the gems |
| 7 | +* Setup omniauth |
| 8 | +* |
| 9 | + |
| 10 | + |
| 11 | +## Secrets |
| 12 | + |
| 13 | +Edit your `config/secrets.yml` |
| 14 | + |
| 15 | + defaults: &defaults |
| 16 | + shopify_app_api_key: <%= ENV["SHOPIFY_APP_API_KEY"] %> |
| 17 | + shopify_app_api_secret: <%= ENV["SHOPIFY_APP_API_SECRET"] %> |
| 18 | + |
| 19 | +Add the environment variables to the `.env`: |
| 20 | + |
| 21 | + SHOPIFY_APP_API_KEY=12345abcdef.... |
| 22 | + SHOPIFY_APP_API_SECRET=12345abcdef.... |
| 23 | + |
| 24 | + |
| 25 | +## Gems |
| 26 | + |
| 27 | +Add the following to your `Gemfile`: |
| 28 | + |
| 29 | + gem 'shopify_api' |
| 30 | + gem 'omniauth' |
| 31 | + gem 'omniauth-shopify-oauth2', git: 'git://github.com/jeffrafter/omniauth-shopify-oauth2' |
| 32 | + |
| 33 | +Then `bundle`. |
| 34 | + |
| 35 | +## Setup omniauth |
| 36 | + |
| 37 | +Omniauth is middleware which is setup in an initializer. Create `config/initializers/omniauth.rb`: |
| 38 | + |
| 39 | + # See http://docs.shopify.com/api/tutorials/oauth for all scopes |
| 40 | + SHOPIFY_SCOPES = [ |
| 41 | + 'read_content', |
| 42 | + 'write_content', |
| 43 | + 'read_products', |
| 44 | + 'write_products', |
| 45 | + 'read_customers', |
| 46 | + 'write_customers', |
| 47 | + 'read_orders' |
| 48 | + ] |
| 49 | + |
| 50 | + Rails.application.config.middleware.use OmniAuth::Builder do |
| 51 | + provider :shopify, |
| 52 | + Rails.application.secrets.shopify_app_api_key, |
| 53 | + Rails.application.secrets.shopify_app_api_secret, |
| 54 | + :scope => SHOPIFY_SCOPES.join(', '), |
| 55 | + :setup => lambda {|env| |
| 56 | + params = Rack::Utils.parse_query(env['QUERY_STRING']) |
| 57 | + site_url = "https://#{params['shop']}" |
| 58 | + env['omniauth.strategy'].options[:client_options][:site] = site_url |
| 59 | + } |
| 60 | + end |
| 61 | + |
| 62 | + |
| 63 | +## Shopify Models |
| 64 | + |
| 65 | + rails g model shop_session shop_id:integer uid:string access_token:string ip:string user_agent:string access_at:datetime logout_at:datetime revoked_at:datetime |
| 66 | + |
| 67 | + |
| 68 | + class CreateShopSessions < ActiveRecord::Migration |
| 69 | + def change |
| 70 | + create_table :shop_sessions do |t| |
| 71 | + t.string :url |
| 72 | + t.string :uid |
| 73 | + t.string :access_token |
| 74 | + t.string :ip |
| 75 | + t.string :user_agent |
| 76 | + t.datetime :access_at |
| 77 | + t.datetime :logout_at |
| 78 | + t.datetime :revoked_at |
| 79 | + |
| 80 | + t.timestamps null: false |
| 81 | + end |
| 82 | + |
| 83 | + add_index :shop_sessions, [:uid, :access_token] |
| 84 | + add_index :shop_sessions, [:revoked_at, :logout_at, :access_at] |
| 85 | + end |
| 86 | + end |
| 87 | + |
| 88 | + |
| 89 | + Add a site method for the API |
| 90 | + |
| 91 | + def site |
| 92 | + "https://#{self.url}/admin" |
| 93 | + end |
| 94 | + |
| 95 | + def token |
| 96 | + self.access_token |
| 97 | + end |
| 98 | + |
| 99 | +## Shopify Controller |
| 100 | + |
| 101 | +In order to handle the interactions in Shopify you will want a specific controller. Create `app/controllers/shopify_controller.rb`: |
| 102 | + |
| 103 | + |
| 104 | +https://gist.github.com/jeffrafter/5f3184de69d6737d2898 |
| 105 | + |
| 106 | + |
| 107 | +# Shopify Layout |
| 108 | + |
| 109 | +https://gist.github.com/jeffrafter/a4ac26761d6a5e0552ce |
| 110 | + |
| 111 | +# Views |
| 112 | + |
| 113 | + |
| 114 | +New: |
| 115 | + |
| 116 | + Shopify new |
| 117 | + |
| 118 | + <div class="page-header" id="login-header"> |
| 119 | + <h1>Install This App</h1> |
| 120 | + <h1><small>This app requires you to login to start using it.</small></h1> |
| 121 | + </div> |
| 122 | + |
| 123 | + <div style="width:340px; margin:0 auto;"> |
| 124 | + <%= form_tag :shopify_login, method: :get, class: 'form-search' do %> |
| 125 | + <%= text_field_tag 'shop', nil, placeholder: 'Shop URL', class: 'btn-large' %> |
| 126 | + <%= submit_tag 'Install', class: 'btn btn-primary btn-large' %> |
| 127 | + <% end %> |
| 128 | + </div> |
| 129 | + |
| 130 | + <p align="center" style="margin-top:30px"> |
| 131 | + <%= image_tag 'shopify.png' %> |
| 132 | + </p> |
| 133 | + |
| 134 | +Welcome: |
| 135 | + |
| 136 | + |
| 137 | + Shopify welcome! |
| 138 | + <br> |
| 139 | + <br> |
| 140 | + [<%= shop_session.shop %>] |
| 141 | + <br> |
| 142 | + <br> |
| 143 | + <strong><%= flash[:notice] %></strong> |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | +# Routes |
| 152 | + |
| 153 | + Rails.application.routes.draw do |
| 154 | + root "shopify#welcome" |
| 155 | + |
| 156 | + get '/shopify/welcome', to: 'shopify#welcome', as: :shopify_welcome |
| 157 | + get '/shopify/products', to: 'shopify#products', as: :shopify_products |
| 158 | + |
| 159 | + get '/shopify/login', to: 'shopify#new', as: :shopify_login |
| 160 | + post '/shopify/login', to: 'shopify#create' |
| 161 | + get '/shopify/logout', to: 'shopify#destroy', as: :shopify_logout |
| 162 | + get '/auth/shopify/callback', to: 'shopify#create', as: :shopify_callback |
| 163 | + end |
| 164 | + |
| 165 | + |
| 166 | + |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | +Now go to this url: |
| 171 | + |
| 172 | +https://snowe-shopify.herokuapp.com/shopify/login?shop=embedded-test |
| 173 | + |
| 174 | + |
| 175 | +https://docs.shopify.com/api/authentication/oauth |
| 176 | + |
| 177 | + |
| 178 | + |
| 179 | + |
| 180 | + |
| 181 | + |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | + |
| 189 | + |
| 190 | + |
| 191 | + |
| 192 | + |
| 193 | + |
| 194 | + |
| 195 | + |
| 196 | +http://embedded-test.myshopify.com/admin/admin/api_clients/new?shop=embedded-test |
| 197 | + |
| 198 | +https://embedded-test.myshopify.com/admin/api_clients/new?shop=embedded-test&api_client[return_url]=http://snowe-shopify.herokuapp.com/shopify/login |
| 199 | + |
| 200 | + |
| 201 | + |
| 202 | +https://embedded-test.myshopify.com/admin/oauth/authorize?client_id=9498a7cd6d0e1a56547c5c618bd8c004&scope=read_content,write_content,read_products,write_products, read_customers,write_customers,read_orders&redirect_uri=https://snowe-shopify.herokuapp.com/welcome |
| 203 | + |
| 204 | +read_content,write_content,read_products,write_products, read_customers,write_customers,read_orders |
| 205 | + |
| 206 | + |
| 207 | + |
| 208 | + |
| 209 | +https://snowe-shopify.herokuapp.com/auth/shopify/callback?code=5bb8f92ff8c7da37eaba890020ed2f45&hmac=6dfa0f1e0b56bb3c4d8390c96634d693a699be896ba7c6e211e14f630d2d502d&shop=embedded-test.myshopify.com&signature=b80e78f1c664bca5dc2c2178905709cb×tamp=1428965125 |
| 210 | + |
| 211 | + |
| 212 | +# Safari and IE embedded IFrame Cookies |
| 213 | + |
| 214 | +http://www.mendoweb.be/blog/internet-explorer-safari-third-party-cookie-problem/ |
| 215 | + |
| 216 | + |
| 217 | +# Validating the HMAC |
| 218 | + |
| 219 | +The problem is that in many requests the HMAC is blank. For example lets say you go to |
| 220 | + |
| 221 | + https://<your-site>/shopify/welcome |
| 222 | + |
| 223 | +This will hit your controller and render the welcome page (without an HMAC). When this page renders it will render the JavaScript which runs the shopify app embed which will reframe your page within the Shopify Admin: |
| 224 | + |
| 225 | + https://<your-shop>.myshopify.com/admin/apps/<your-app-no>/shopify/welcome |
| 226 | + |
| 227 | +This page will re-render your welcome page but this time with the shop and the hmac params included: |
| 228 | + |
| 229 | + https://<your-site>/shopify/welcome?hmac=12345&signature=12345×tamp=12345&shop=<your-shop>.myshopify.com |
| 230 | + |
| 231 | + |
| 232 | + |
0 commit comments