こんにちは、UUUM に入社してからちょうど2年半くらいの kazama です。
弊社には数々のプロジェクトが存在しており、それぞれにエンジニアが数名アサインされているわけですが、自分は少し前まで EC 部門のプロジェクトに関わっていました。
そこでは世界最大級の e コマースプラットフォーム「Shopify」をベースとした運用・開発が行われており、個人的になかなか貴重な体験ができたので今回はその辺の知見をアウトプットしておきたいと思います。
タイトル通り、テーマは「API 経由で起動する Shopify Flow のトリガーを自作してみる」です。
Shopify Flow とは
Shopify のエンタープライズプラン「Shopify Plus」 を利用すると使えるようになるオートメーション機能の事。
Shopify で構築した自身のストア上における顧客の行動をトリガーに様々なアクションを自動で行ってくれます。
Trigger → Condition → Action
- Trigger(トリガー)
- ワークフローを開始させるイベント
- Condition(条件)
- アクションを実行するかどうかの条件
- Action(アクション)
- 条件が満たされた時に行われる内容
例) ・ ある商品の在庫数が◯個以下になった際に Slack 通知を飛ばす ・ ○円以上の買い物をした顧客に対して特別なタグを付与する ・ 顧客が新規登録されるたびにそのデータをスプレッドシートに記録する
この Shopify Flow を回し始めるには「トリガー」と呼ばれるきっかけが必要になるのですが、Shopify が標準で用意しているトリガーは基本的にストア上の顧客行動が対象となっているため、それ以外の要因で引き金を引く事が難しくなっています。
もちろん、特に奇を衒った事をしようとしなければそれでも十分だと思いますが、個人的に「外部からちょこっと API を叩くだけで任意に起動できるトリガーを作れないかなぁ」みたいな願望があったため、色々調べてみました。
結果、なんとかなりそうな方法があったので紹介してみたいと思います。
API 経由でワークフローを回す事ができるようになれば、Shopify ストアとは別の自社アプリ内などに組み込んで色々と連携が捗ったり運用の幅が広がるかもしれません。
カスタムアプリを作成
https://www.shopify.com/jp/partners
Shopify Parnters のダッシュボードに入り、「アプリ管理」→「すべてのアプリ」へと進み、「アプリを作成する」ボタンをクリックします。
- カスタムアプリ
- 公開アプリ
の2種類ありますが、今回は「カスタムアプリ」の方を選択してください。
- 一般設定
- アプリ名
- 任意
- アプリURL
- https://localhost (後ほど変更)
- アプリ名
- リダイレクトURL
- https://localhost (後ほど変更)
諸々入力したら、右上の「アプリを作成」ボタンをクリックしましょう。
トリガーを作成
カスタムアプリの作成に成功するとアプリ詳細画面に飛ぶはずなので、「拡張機能」という部分をクリックしてください。
「作成+」をクリック。
拡張機能タイプは「Flow/トリガー」を選択してください。
適当な名前をつけて「Save」
トリガーの設定を行うために「Working draft」をクリック。
- トリガーのタイトル
- 任意
- トリガーの説明
- 任意
- プロパティ
- お客様
プロパティについては、デフォルトの状態だと
- お客様
- 注文
- 商品
といった3種類が用意されており、Trigger 起動時のリクエストに
- customer_id
- order_id
- product_id
を含める事で Action 実行時に shopify 上のオブジェクトを参照する事ができます。
https://shopify.dev/apps/flow/triggers#shopify-properties
When you create a trigger, you add the properties that your trigger sends to Shopify Flow. You can add a custom property or a predefined Shopify property. Shopify property lets you send the identifier of a Shopify resource to Shopify Flow. Merchants can then use the entire resource in their conditions and actions. You can add one of each Shopify property to a trigger: For example, your trigger sends a customer ID to Shopify Flow. The merchant can create a condition that checks the customer's total spend amount. In their action, the merchant can include the template variables for customers (such as {{customer.email}}).
設定が済んだら右上の「Create version」をクリックし、「Minor version」を選択して「Create」
作成が完了したら、「Publish」をクリックしましょう。
アクセストークンを取得 & ストアにインストール
諸々の準備が整ったので、自身のストアに先ほど作ったカスタムアプリをインストールしていきます。
と、その前に、カスタムアプリと通信を行うためのアクセストークンを取得しなければなりません。
アクセストークンの取得方法は色々ありますが、今回は
- Ruby
- Ruby on Rails
- ngrok
を使って Web サーバーを準備し、そこで取得していきます。
各種ディレクトリ & ファイルを作成
$ mkdir shopify-access-token-getter && cd shopify-access-token-getter $ touch Dockerfile docker-compose.yml entrypoint.sh Gemfile Gemfile.lock
FROM ruby:3.0 RUN curl https://deb.nodesource.com/setup_14.x | bash RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn ENV APP_PATH /myapp RUN mkdir $APP_PATH WORKDIR $APP_PATH COPY Gemfile $APP_PATH/Gemfile COPY Gemfile.lock $APP_PATH/Gemfile.lock RUN bundle install COPY . $APP_PATH COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"]
version: "3" services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: password volumes: - mysql-data:/var/lib/mysql - /tmp/dockerdir:/etc/mysql/conf.d/ ports: - 3306:3306 api: build: context: . dockerfile: Dockerfile command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp - ./vendor/bundle:/myapp/vendor/bundle environment: TZ: Asia/Tokyo RAILS_ENV: development ports: - "3000:3000" depends_on: - db volumes: mysql-data:
#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"
# frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem "rails", "~> 6"
# 空欄でOK
rails new
APIモードで作成します。
$ docker-compose run api rails new . --force --no-deps -d mysql --api
database.ymlを編集
デフォルトの状態だとデータベースとの接続ができないので「database.yml」の一部を書き換えます。
default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # デフォルトだと空欄になっているはずなので変更 host: db # デフォルトだとlocalhostになっているはずなので変更 development: <<: *default database: myapp_development test: <<: *default database: myapp_test production: <<: *default database: <%= ENV["DATABASE_NAME"] %> username: <%= ENV["DATABASE_USERNAME"] %> password: <%= ENV["DATABASE_PASSWORD"] %>
コンテナを起動 & データベースを作成
$ docker-compose build $ docker-compose up $ docker-compose run api bundle exec rails db:create
localhost:3000 にアクセス
localhost:3000 にアクセスしてウェルカムページが表示されればOKです。
ngrok を起動
# 追記 config.hosts << ".ngrok.io"
$ ngrok http 3000
https://
を割り当てるために上記コマンドで起動します。
アクセストークン取得用の処理を記述
gem "faraday"
$ docker-compose build
HTTP クライアントの「faraday」をインストールしてきましょう。
$ docker-compose run api bundle exec rails g controller api/shopify
class Api::ShopifyController < ApplicationController def auth shop = params[:shop] client_id = "CUSTOM_APP_API_KEY" # カスタムアプリの API キー scope = "write_customers,read_customers" redirect_uri = "https://************.ngrok.io/api/shopify/token" # ngrok で起動したサーバーの URL state = SecureRandom.random_bytes(16) query = { client_id: client_id, scope: scope, redirect_uri: redirect_uri, state: state }.to_query url = "https://#{shop}/admin/oauth/authorize?#{query}" redirect_to url end def token shop = params[:shop] code = params[:code] client_id = "CUSTOM_APP_API_KEY" # カスタムアプリの API キー client_secret = "CUSTOM_APP_API_SECRET_KEY" # カスタムアプリの API シークレットキー res = Faraday.post("https://#{shop}/admin/oauth/access_token", client_id: client_id, client_secret: client_secret, code: code ) p res.body end end
アクセストークン取得用の処理をコントローラーに書いていきます。
ルーティングを定義
Rails.application.routes.draw do get "api/shopify/auth", to: "api/shopify#auth" get "api/shopify/token", to: "api/shopify#token" end
これで
にアクセスした際に先ほど定義した処理が走るようになりました。
アプリ URL & リダイレクト URL を変更
「https://localhost/」 で暫定的に埋めていた
- アプリ URL
- リダイレクト URL
を先ほど立ち上げた Web サーバーのものに置き換えます。
- アプリ URL
- リダイレクト URL
インストールリンクを作成
諸々の準備ができたので、いよいよカスタムアプリをストアにインストールしていきます。
インストール先のストアドメインを入力し、「リンクを生成する」をクリックしてください。
あとは生成されたリンクをコピーし、アドレスバーにペーストしてアクセスしてみましょう。
上手くいけばこんな感じのインストール画面が出てくるので、右上の「アプリをインストール」をクリック。
インストールが完了したら、アプリケーションのログを見てみましょう。
"{\"access_token\":\"shpca_********************\",\"scope\":\"write_customers\"}"
shpca_
が接頭辞として付いている文字列がアクセストークンなので、メモに控えておいてください。
ワークフローを作成
ストアにカスタムアプリをインストールできたら、ワークフローを作成します。
ワークフロー作成画面が開いたら、トリガーとして先ほど作成したカスタムアプリから「Test Trigger」を選択してください。
次に、アクションとして「Add customer tags」を開き
- test-trigger
適当な文字列を入力。
トリガーとアクションの設定が終わったら、右上の「Turn on workflow」からワークフローを稼働させ始めます。(条件(Condition)は今回割愛しました)
動作確認
curl -X POST "https://*******.myshopify.com/admin/api/2022-04/graphql.json" \ -H "Content-Type: application/graphql" \ -H "X-Shopify-Access-Token: shpca_******************************" \ -d ' mutation { flowTriggerReceive(body: "{ \"trigger_id\": \"**********-****-****-****-**********\", \"properties\": { \"customer_id\": ***************** } }") { userErrors { field, message } } }'
- *******.myshopify.com
- 自身のストアドメイン
- X-Shopify-Access-Token
- 事前に取得したアクセストークン
- trigger_id
- トリガーID
- customer_id
- 対象の顧客ID
トリガーIDについては、「カスタムアプリ」→「拡張機能」→「Test Trigger」→「Edit draft」を開き、ペイロードのプレビュー
という部分を確認してください。
上記 Curl コマンドを実行し、
{ "data": { "flowTriggerReceive": { "userErrors": [] } }, "extensions": { "cost": { "requestedQueryCost": 1, "actualQueryCost": 1, "throttleStatus": { "maximumAvailable": 2000, "currentlyAvailable": 1999, "restoreRate": 100 } } } }
こんな感じのレスポンスが返って来ればOKです。
customer_id で指定した顧客情報も確認してみてください。「test-trigger」というタグが付与されているはず。
これで API 経由で Shopify Flow を起動させる事に成功しました。
あとがき
以上、Shopify Flow のトリガーを自作してみました。
API 経由で起動できる事から、Shopify ストア以外の場所でも何かしらのアクションを起こせるようになり、活用の幅が広がりそうな気がします。 何かのお役に立てれば幸いです。