UUUMエンジニアブログ

UUUMのエンジニアによる技術ブログです

API 経由で起動する Shopify Flow のトリガーを自作してみる

こんにちは、UUUM に入社してからちょうど2年半くらいの kazama です。

弊社には数々のプロジェクトが存在しており、それぞれにエンジニアが数名アサインされているわけですが、自分は少し前まで EC 部門のプロジェクトに関わっていました。

そこでは世界最大級の e コマースプラットフォーム「Shopify」をベースとした運用・開発が行われており、個人的になかなか貴重な体験ができたので今回はその辺の知見をアウトプットしておきたいと思います。

タイトル通り、テーマは「API 経由で起動する Shopify Flow のトリガーを自作してみる」です。

Shopify Flow とは

Shopify のエンタープライズプラン「Shopify Plus」 を利用すると使えるようになるオートメーション機能の事。

Shopify で構築した自身のストア上における顧客の行動をトリガーに様々なアクションを自動で行ってくれます。

Trigger → Condition → Action

  • Trigger(トリガー)
    • ワークフローを開始させるイベント
  • Condition(条件)
    • アクションを実行するかどうかの条件
  • Action(アクション)
    • 条件が満たされた時に行われる内容
例)
・ ある商品の在庫数が◯個以下になった際に Slack 通知を飛ばす
・ ○円以上の買い物をした顧客に対して特別なタグを付与する
・ 顧客が新規登録されるたびにそのデータをスプレッドシートに記録する

https://apps.shopify.com/flow

この Shopify Flow を回し始めるには「トリガー」と呼ばれるきっかけが必要になるのですが、Shopify が標準で用意しているトリガーは基本的にストア上の顧客行動が対象となっているため、それ以外の要因で引き金を引く事が難しくなっています。

もちろん、特に奇を衒った事をしようとしなければそれでも十分だと思いますが、個人的に「外部からちょこっと API を叩くだけで任意に起動できるトリガーを作れないかなぁ」みたいな願望があったため、色々調べてみました。

結果、なんとかなりそうな方法があったので紹介してみたいと思います。

API 経由でワークフローを回す事ができるようになれば、Shopify ストアとは別の自社アプリ内などに組み込んで色々と連携が捗ったり運用の幅が広がるかもしれません。

カスタムアプリを作成

https://www.shopify.com/jp/partners

Shopify Parnters のダッシュボードに入り、「アプリ管理」→「すべてのアプリ」へと進み、「アプリを作成する」ボタンをクリックします。

  • カスタムアプリ
  • 公開アプリ

の2種類ありますが、今回は「カスタムアプリ」の方を選択してください。

諸々入力したら、右上の「アプリを作成」ボタンをクリックしましょう。

トリガーを作成

カスタムアプリの作成に成功するとアプリ詳細画面に飛ぶはずなので、「拡張機能」という部分をクリックしてください。

「作成+」をクリック。

拡張機能タイプは「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 にアクセス

スクリーンショット 2021-07-24 0.30.48.png

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 サーバーのものに置き換えます。

インストールリンクを作成

諸々の準備ができたので、いよいよカスタムアプリをストアにインストールしていきます。

インストール先のストアドメインを入力し、「リンクを生成する」をクリックしてください。

あとは生成されたリンクをコピーし、アドレスバーにペーストしてアクセスしてみましょう。

上手くいけばこんな感じのインストール画面が出てくるので、右上の「アプリをインストール」をクリック。

インストールが完了したら、アプリケーションのログを見てみましょう。

"{\"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 ストア以外の場所でも何かしらのアクションを起こせるようになり、活用の幅が広がりそうな気がします。 何かのお役に立てれば幸いです。