Shopifyアプリを Amazon EventBridge と統合してみる #shopify

2020.12.17

こんにちは、クラスメソッドの岡です。
今日2020/12/17の16:00から「AWSとReactで始めるShopifyアプリ開発」が開催されます。
この記事は3つ目のセッション「EventBridgeでAWSとShopifyの統合」での統合手順の解説ブログとなります。

Shopifyのアカウント

まず前提の知識として、shopifyアカウントには2種類あります。

  • ショップアカウント
    • ショップを運営・販売するマーチャントが利用するアカウント
  • パートナーアカウント
    • shopifyパートナーがShopifyアプリを開発・販売するアカウント

ShopifyをEventBridgeと統合するには パートナーアカウント が必要になります。
こちらから作成してください。
パートナーのストア管理の画面からストアを追加すれば無料で検証ができる開発ストアとしてショップが利用できます。

Shopifyアプリ

Shopifyアプリには以下の3種類があります。

  • カスタムアプリ
    • 単一のストアでインストールできるアプリ
  • 公開アプリ
    • Shopifyアプリストアで販売できるアプリ
  • プライベートアプリ
    • ストア管理画面上で作成されるアプリ

プライベートアプリではEventBridgeとは統合できません。今回は検証で1店舗のイベントデータをEventBridgeに渡したいので、カスタムアプリを構築します。

パートナー管理画面の操作

開発ストアまで用意できている前提で進めます。

1, パートナーアカウントのアプリ管理画面から「アプリを作成する」をクリック image

2, 今回はカスタムアプリを選択します。 image

3, URLはあとで用意するので一旦仮で入れてそのまま作成します。 image

Shopifyアプリを作成する

Shopifyアプリをストアにインストールする際に設定したURLがhttpsでホストされている必要があります。
しかし今回はEventBridgeと統合するだけで画面も必要ないし常時稼働している必要もありません。今回はインストール用にバイパスだけ用意します。
Shopify App CLIで、一番手軽な方法「Shopify App CLI」でShopifyアプリを作成します。

ちなみにShopify App CLIのコマンドを使ってデプロイした場合はherokuにデプロイされます。しかしここでは Admin API が実行できる環境を準備したいだけなのでデプロイはせずにngrokでローカルサーバーに対してhttpsでアクセスできるようにします。

インストール

$ brew upgrade
$ brew install shopify-cli

アプリを作成

shopify create を実行します。

$ shopify create
? What type of project would you like to create? (You chose: Node.js App)
? Appname > shopify-devio-app
? What type of app are you building? (Choose with ↑ ↓ ⏎, filter with 'f')
  1. Public: An app built for a wide merchant audience.
> 2. Custom: An app custom built for a single client.

今回はNode.js Appを選択、アプリ名「shopify-devio-app」、Shopifyアプリの種類→カスタムアプリとそれぞれ入力します。

プロジェクトフォルダが作成されるので、移動します。

$ cd shopify-devio-app

.envのファイルが生成されているので、それぞれ自分の情報に書き換えます。

  • SHOPIFY_API_KEY: アプリ詳細画面に表示されるAPIキー
  • SHOPIFY_API_SECRET: アプリ詳細画面に表示されるAPIシークレット
  • SHOP: アプリをインストールするストア
  • SCOPES: アプリ内で許可するスコープ(write_products,write_customers,write_draft_orders)
  • HOST: アプリのURL, serveコマンド実行時に更新される

image

ngrokでローカルサーバーを起動

shopify serve を実行するとngrokで起動します。

$ shopify serve
✓ ngrok tunnel running at https://hogehoge.ngrok.io
✓ .env saved to project root
? Do you want to update your application url? (You chose: yes)
✓ Whitelist URLS updated in Partners                                                                                                                                    
⭑ To install and start using your app, open this URL in your browser:
https://hogehoge.ngrok.io/auth?shop=developers-io-store.myshopify.com

開発ストアにアプリをインストール

serveコマンド実行時にインストールリンクが発行されているので開発ストアにサインインした状態でアクセスすると確認画面に遷移します。 https://hogehoge.ngrok.io/auth?shop=developers-io-store.myshopify.com

image

開発ストアのアプリ管理内にアプリの画面が表示されました。 image

image

ここまでで、EventBridgeと統合するためのアプリの用意ができました。

EventBridgeにパートナーイベントソースを作成

ここからが本題です。
パートナーダッシュボードに戻り、アプリ設定の画面を再度開きます。

image

イベントサブスクリプションの項目にEventBridgeのソースを作成するボタンがあるのでクリック

image

アカウントIDとリージョン, ソース名を入力して作成します。

image

EventBridgeのイベントバスと関連付け、ルール作成

イベントバスと関連付け

EventBridgeのコンソールを開いてみると、アプリID/ソース名がパスになったパートナーイベントソースが作成されています。

「イベントバスと関連付ける」をクリックします。 image

ステータスがアクティブに変更されたらOKです。 image

ターゲット用にLambdaを作成

イベントをログ出力するだけのLambdaを用意しておきます。

index.js

exports.handler = async (event) => {
    console.log(event);
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

ルールの作成

左ペインからルールを選択して、作成したイベントバスを選択、「ルールを作成する」をクリックします。 image

任意のルール名 image

ターゲットにLambdaを選択します。 image

ルールが作成できたらLambdaのトリガーにShopifyのアイコンが表示されます。 image

ここまででEventBridge側の準備ができました。
ただこのままだとShopifyアプリ→EventBridgeへイベントが届きますがShopifyショップ→ShopifyアプリへのwebhookはShopifyのAdmin APIで登録する必要があります。

ShopifyのAdmin APIでwebhook登録

Admin APIはOAuth 2.0の authorization code grant flow を使って認証します。

なのでアクセストークンの前に認可コードを取得する必要があります。

認証コードを取得

https://{shop}.myshopify.com/admin/oauth/authorize に対してリクエストを送ります。

クエリパラメータに以下を付与します。

  • client_id: ShopifyアプリのAPIキー
  • scope: 要求するスコープ()
  • redirect_uri: ShopifyのリダイレクトURL
  • state: 一意の文字列
  • grant_options[]: オフラインかオンラインの設定(オプション)

実際には以下のようなURLになります。

https://developers-io-store.myshopify.com/admin/oauth/authorize?client_id=xxxxx&scope=write_products,write_customers,write_draft_orders&redirect_uri=https://hogehoge.ngrok.io/auth&state=5b407360-4009-11eb-9c4e-acde48001122

ショップアカウントでサインインしたブラウザでURLにアクセスすると、インストール時と同じように権限を確認ページに遷移します。

image

アプリを更新するを押すと、リダイレクトURLのクエリパラメータに code が含まれて返ってくるのでこのコードを使ってアクセストークンを取得します。

アクセストークン取得

ここではREST APIで実行します。

https://{shop}.myshopify.com/admin/oauth/access_token に対してPOSTリクエストを送ります。

$ curl -X POST -H "Content-Type: application/json" https://developers-io-store.myshopify.com/admin/oauth/access_token -d "$(cat <<EOS
{
    "client_id": "APIキー",
    "client_secret": "APIシークレット",
    "code": "上で取得した認証コード"
}
EOS
)" | jq .

webhook登録

エンドポイント address には EventBridge の パートナーイベントソースの ARNを入力してください。

$ curl -X POST -H "Content-Type: application/json" -H "X-Shopify-Access-Token: xxxxx" https://developers-io-store.myshopify.com/admin/api/2020-07/webhooks.json -d "$(cat <<EOS
{
  "webhook": {
    "topic": "products/update",
    "address": "arn:aws:events:ap-northeast-1::event-source/aws.partner/shopify.com/9999999/shopify-devio-app",
    "format": "json"
  }
}
EOS
)" | jq .

以下のようにレスポンスが返ってきます。

{
    "webhook": {
        "id": 123456789,
        "address": "arn:aws:events:ap-northeast-1::event-source/aws.partner/shopify.com/9999999/shopify-devio-app",
        "topic": "products/update",
        "created_at": "2020-12-16T08:11:01-05:00",
        "updated_at": "2020-12-16T08:11:01-05:00",
        "format": "json",
        "fields": [],
        "metafield_namespaces": [],
        "api_version": "2020-10",
        "private_metafield_namespaces": []
    }
}

動作確認

ここまででShopify上にある開発ストアのイベントをEventBridgeで受け取れるようになりました。
Developers.IO Storeの商品を実際に更新して動作確認してみます。

商品の説明を追加してみます。

image

Lambdaのロググループに以下のようにデータが出力されています。

image

{
  version: '0',
  id: 'dsofhijmcise',
  'detail-type': 'shopifyWebhook',
  source: 'aws.partner/shopify.com/9999999/shopify-devio-app',
  account: '123456789012',
  time: '2020-12-16T15:18:57Z',
  region: 'ap-northeast-1',
  resources: [],
  detail: {
    payload: {
      id: 5853201367192,
      title: '花咲ひさきTシャツ',
      body_html: 'てすと',
      vendor: 'Developers.IO Store',
      product_type: '',
      created_at: '2020-09-29T22:52:13-04:00',
      handle: '花咲ひさきtシャツ',
      updated_at: '2020-12-16T10:18:54-05:00',
      published_at: '2020-09-29T22:52:15-04:00',
      template_suffix: '',
      status: 'active',
      published_scope: 'web',
      tags: '',
      admin_graphql_api_id: 'gid://shopify/Product/5853201367192',
      variants: [Array],
      options: [Array],
      images: [Array],
      image: [Object]
    },
    metadata: {
      'Content-Type': 'application/json',
      'X-Shopify-Topic': 'products/update',
      'X-Shopify-Shop-Domain': 'developers-io-store.myshopify.com',
      'X-Shopify-Product-Id': '5853201367192',
      'X-Shopify-Hmac-SHA256': 'efdwiqTOJJSDJcLY+wXI4=',
      'X-Shopify-API-Version': '2020-10'
    }
  }
}

まとめ

今回は簡易的な構成で試してみましたが、もう少し業務側のユースケースも含めたパターンを試した記事も出していこうと思います!