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

## 概要

<font color="red">**Shopify Flowとは**</font>
Shopify のエンタープライズプラン「Shopify Plus」 を利用すると使えるようになるオートメーション機能。Shopify で構築した自身のストア上における顧客の行動をトリガーに様々なアクションを自動で行ってくれる。

`Trigger → Condition → Action`

- Trigger（トリガー）
    - ワークフローを開始させるイベント
- Condition（条件）
    - アクションを実行するかどうかの条件
- Action（アクション）
    - 条件が満たされた時に行われる内容

```:ex.
・ ある商品の在庫数が◯個以下になった際に Slack 通知を飛ばす
・ ○円以上の買い物をした顧客に対して特別なタグを付与する
・ 顧客が新規登録されるたびにそのデータをスプレッドシートに記録する
```

> ![スクリーンショット 2022-04-21 23.46.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/b62e0948-e14f-5938-bdfe-d2f8765412b6.png)
> https://apps.shopify.com/flow

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

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

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

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

## カスタムアプリを作成

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

![スクリーンショット 2022-04-22 0.04.28_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/079be26a-b195-4433-3c79-823678ab1444.jpeg)

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

![スクリーンショット 2022-04-22 0.08.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/b709538d-b69a-439b-5076-45551ad3add2.png)

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

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

![スクリーンショット 2022-04-22 0.10.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/ff271d59-8c93-f61b-40b5-329f24a8b25d.png)

- 一般設定
    - アプリ名
        - 任意
    - アプリURL
        - https://localhost （後ほど変更） 
- リダイレクトURL
    - https://localhost （後ほど変更） 

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

## トリガーを作成

![スクリーンショット 2022-04-22 0.13.56_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/0f07e95e-f43d-e911-1c61-2d04e439b68e.jpeg)

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

![スクリーンショット 2022-04-22 0.16.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/87bc550d-37a6-3aca-77fb-8cb1b71f4af3.png)

「作成+」をクリック。

![スクリーンショット 2022-04-22 0.17.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/cf7d804e-911a-d818-3753-a892621714e7.png)

拡張機能タイプは「Flow/トリガー」を選択してください。

![スクリーンショット 2022-04-22 0.19.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/f6f2d44c-70a5-c150-d159-0927aad46f5d.png)

適当な名前をつけて「Save」

![スクリーンショット 2022-04-22 0.20.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/c3041f77-05f8-1acb-ceb6-4436ee2a68ed.png)

トリガーの設定を行うために「Working draft」をクリック。

![スクリーンショット 2022-04-22 0.22.35_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/58bbaae2-b950-ef89-e785-2e89738cec4c.jpeg)

- トリガーのタイトル
    - 任意
- トリガーの説明
    - 任意
- プロパティ
    - お客様

プロパティについては、デフォルトの状態だと

- お客様
- 注文
- 商品

といった３種類が用意されており、Trigger 起動時のリクエストに 

- customer_id
- order_id
- product_id

を含める事で Action 実行時に shopify 上のオブジェクトを参照する事ができます。

[https://shopify.dev/apps/flow/triggers#shopify-properties](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}}).


![スクリーンショット 2022-04-22 0.34.51_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/e12dcb41-acd8-432b-68bd-a125a29e51ad.jpeg)

設定が済んだら右上の「Create version」をクリックし、「Minor version」を選択して「Create」

![スクリーンショット 2022-04-22 0.36.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/c3361f6b-175a-b365-6d0f-8f6c9de9eb7c.png)

作成が完了したら、「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
```

```dockerfile:./Dockerfile
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"]
```

```yml:./docker-compose.yml
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:
      - 4306: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:
```

```sh:./entrypoint.sh
#!/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 "$@"
```

```ruby:./Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails", "~> 6"
```

```ruby:./Gemfile.lock
# 空欄でOK
```

### rails new

APIモードで作成します。

```
$ docker-compose run api rails new . --force --no-deps -d mysql --api
```

### database.ymlを編集

デフォルトの状態だとデータベースとの接続ができないので「database.yml」の一部を書き換えます。

```yml:./config/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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/5324a08f-5cc9-2501-7ebe-9e779f5c1cf3.png)

[localhost:3000](http://localhost:3000/) にアクセスしてウェルカムページが表示されればOKです。

### ngrok を起動

```ruby:./config/environments/development.rb
# 追記
config.hosts << ".ngrok.io"
```

```
$ ngrok http 3000
```

`https://` を割り当てるために上記コマンドで起動します。

### アクセストークン取得用の処理を記述

```ruby:./Gemfile
gem "faraday"
```
```
$ docker-compose build
```

HTTP クライアントの「[faraday](https://github.com/lostisland/faraday)」をインストールしてきましょう。

```
$ docker-compose run api bundle exec rails g controller api/shopify
```

```ruby:./app/controllers/api/shopify_controller.rb
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
```

アクセストークン取得用の処理をコントローラーに書いていきます。

### ルーティングを定義

```ruby:./config/routes.rb
Rails.application.routes.draw do
  get "api/shopify/auth", to: "api/shopify#auth"
  get "api/shopify/token", to: "api/shopify#token"
end
```

これで

- https://************.ngrok.io/api/shopify/auth
- https://************.ngrok.io/api/shopify/token

にアクセスした際に先ほど定義した処理が走るようになりました。


### アプリ URL & リダイレクト URL を変更

 「https://localhost/」 で暫定的に埋めていた

- アプリ URL
- リダイレクト URL

を先ほど立ち上げた Web サーバーのものに置き換えます。

![スクリーンショット 2022-04-22 1.20.32_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/d7b6f613-066b-dd8b-c77a-283091025d48.jpeg)

- アプリ URL
    - https://**************.ngrok.io/api/shopify/auth
- リダイレクト URL
    - https://**************.ngrok.io/api/shopify/token


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

![スクリーンショット 2022-04-22 1.08.56_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/eda7407e-2f58-6972-110a-67729c771e95.jpeg)

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

![スクリーンショット 2022-04-22 1.10.55_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/dc2cb487-3d8c-a528-a2c6-265f785811ef.jpeg)

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

![スクリーンショット 2022-04-22 1.23.34_censored (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/34a822ba-5c68-0acf-37f2-cb9e8fa61aa8.jpeg)

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

![スクリーンショット 2022-04-22 1.57.52_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/8c54e7b0-69b8-cf60-1322-e83ddadb5102.jpeg)

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

![スクリーンショット 2022-04-22 2.01.15_censored (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/bba827da-8d5c-287c-d480-48fb9cb490a6.jpeg)

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

```
"{\"access_token\":\"shpca_********************\",\"scope\":\"write_customers\"}"
```

`shpca_`が接頭辞として付いている文字列がアクセストークンなので、メモに控えておいてください。

## ワークフローを作成

![スクリーンショット 2022-04-22 6.24.14_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/2d776970-9fab-cffb-7be6-3279f133f698.jpeg)


ストアにカスタムアプリをインストールできたら、ワークフローを作成します。

![スクリーンショット 2022-04-22 6.26.40_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/83de0db6-b900-e755-af5b-3745bc389924.jpeg)

ワークフロー作成画面が開いたら、トリガーとして先ほど作成したカスタムアプリから「Test Trigger」を選択してください。

![スクリーンショット 2022-04-23 2.37.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a6c8a171-574f-d2e7-4d1b-2efe4c9e3d43.png)

次に、アクションとして「Add customer tags」を開き

- test-trigger

適当な文字列を入力。

![スクリーンショット 2022-04-23 2.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/0a40617e-8df7-73c9-6a1a-7d7f902c4fcb.png)

トリガーとアクションの設定が終わったら、右上の「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

![スクリーンショット 2022-04-23 2.43.15_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/c8878b72-f93a-d084-1b95-39c32997107e.jpeg)

トリガーIDについては、「カスタムアプリ」→「拡張機能」→「Test Trigger」→「Edit draft」を開き、`ペイロードのプレビュー` という部分を確認してください。

上記 Curl コマンドを実行し、

```
{
	"data": {
		"flowTriggerReceive": {
			"userErrors": []
		}
	},
	"extensions": {
		"cost": {
			"requestedQueryCost": 1,
			"actualQueryCost": 1,
			"throttleStatus": {
				"maximumAvailable": 2000,
				"currentlyAvailable": 1999,
				"restoreRate": 100
			}
		}
	}
}
```

こんな感じのレスポンスが返って来ればOKです。

![スクリーンショット 2022-04-23 2.50.21_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/3ca360e0-9dc0-e5ae-c53d-39b2c93cf455.jpeg)

customer_id で指定した顧客情報も確認してみてください。「test-trigger」というタグが付与されているはず。

これで API 経由で Shopify Flow を起動させる事に成功しました。

## あとがき

以上、Shopify Flow のトリガーを自作してみました。

API 経由で起動できる事から、Shopify ストア以外の場所でも何かしらのアクションを起こせるようになり、活用の幅が広がりそうな気がします。