Shopify と GASを連携、そして、WMSと連携してみたお話

YAMAP エンジニア Advent Calendar 2019 の16日目になります。

YAMAPで、フロントエンドエンジニア？として活動中の @issyxissy です。
いつもは、フロントエンド周りをやっていますが、 [YAMAP STORE](https://store.yamap.com/) の運用部分も携わっています。

# YAMAP STOREとは？
YAMAPでは、実は、アウトドア・登山のギア(道具)を販売している [YAMAP STORE](https://store.yamap.com/) というサイトがあります。
[YAMAP STORE](https://store.yamap.com/) で販売しているギアは、YAMAPスタッフが目利きを行い、自分たちが良いと思うものを仕入れて、販売しています。

珍しいところでいうと、パーゴワークスさん、ティートンブロスさん、他にもいろいろ取り扱っています。
その他にも、プライベートブランドも開発して、いろんなユーザさんに使ってもらっています。
(宣伝になってきた・・・)

その[YAMAP STORE](https://store.yamap.com/)ですが、[Shopify](https://www.shopify.jp/)というSaas型のEコマースシステムで運用しています。

## Shopifyって？
- Saas型のEコマース
    - 運用楽ちん
- テーマと呼ばれる、テンプレートがあり、いろんなUI・デザインを選ぶことができる
    - そのテーマをカスタマイズもできる
- 決済周りでは、Shopify Paymentを利用して決済も可能
    - 他の決済システムとの連動も可能
    - コンビニ決済や、代引も導入しようとすれば可能
- 越境ECに対応
    - 各国の通貨に合わせた決済が実現可能(バンザイ、越境EC)
- APIも豊富
    - StoreFront APIと呼ばれるUI部分に組み込むことができるAPIの利用も可能
    - 運用面で、REST Admin APIで、いろんなマスタを読み書き可能
- plugableに、機能を増やすことが可能
    - 世界のShopify Developerさんたちが、日々開発して、新しい機能を提供しています
    - MAツールっぽいものもあるので、エンジニアでなくても、容易に導入しやすいです
- Saas型の割に、いろいろカスタマイズ可能
    - 割引などのカート内に関わるプログラムを書くことができる Script Editorや、トリガーをきっかけにアクションを実行できるShopify Flowなど、意外とカスタマイズができます。

# 本題のデータ連携

## 概要
具体的にどんなことをしているかというと、、、

<img width="794" alt="Screen Shot 2019-12-17 at 7.34.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221028/47cb50aa-b8f8-64b8-3355-671013ad538d.png">

のように連携して、出荷業務等を自動化しています（今は半自動化・・・）

連携しているデータは

- 出荷管理
- 入荷管理
- 商品・在庫管理

の3つになります。

## GASを選んだ理由
自動化する上で、GASを選んだ理由・・・なんとなく、手軽だから・・・(ノ ꒪⌓꒪)ノ
というわけではないんですが、仕入れ管理等を行う上で、一からシステムを構築したくないというのがありました。
また、一から構築するとなると、かなりの時間とコストが掛かるため、サービス立ち上げ当初なら、ということで、選びました。

### メリット
- 共有の管理がしやすい
- 履歴も残る
- Scriptも書きやすい
- Spreadsheet / Google Calendarなど、google関連のサービスとの連携が楽ちん
- みんなが慣れている

※メリットは言わずとも、あるあるな感じです

### デメリット
- 基幹システムようなものより安定性にかける・・・
    - たまに、トリガーが動かないが、、、稀ですが・・・( ꒪⌓꒪;
- ES6など、モダンな書き方ができない
    - やろうと思えばできるっぽいけど、後々、そっちに移行したい(ノ ꒪⌓꒪)ノ

## 出荷連携の実装例
あくまでも簡易的ですが、おおよそ↓のようになるのかなと思います。

### Shopify
管理画面から、 アプリ管理 -> プライベートアプリ管理 で、APIキーを発行します。

### GAS
GASでは、Shopifyから注文データを取得します。
（実行状況をSlackに通知するので、SLACK_WEBHOOKの情報もプロジェクトのプロパティに設定しています）

サンプルコードは↓

```javascript
var SLACK_WEBHOOK = PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK');

var SHOPIFY_API_KEY = PropertiesService.getScriptProperties().getProperty('SHOPIFY_API_KEY');
var SHOPIFY_PASSWORD = PropertiesService.getScriptProperties().getProperty('SHOPIFY_PASSWORD');
var SHOPIFY_API_URL = 'https://xxxxxxxxx.myshopify.com/admin/api/xxxx-xx';


function exportOrders() {
  // Shopifyの注文データを取得
  var response = _shopifyAPI('/orders.json?status=open&fulfillment_status=unshipped&limit=250', 'get');
  
  // 注文データを加工
  var orders = [];
  for (var index of response.orders) {
    var order = response.orders[index];
    // ~~ 注文データをごにょごにょ ~~
  }
  
  // WMSへデータ送信
  var response = _wmsAPI(xxxx, 'post', orders);

  // Slackに終わったよを送信
  _postSlackMessage('WMSに出荷データを送信したお');
}

// ShopifyAPIへリクエスト
function _shopifyAPI(url, method, params) {
  var options = {
    method: method,
    payload: JSON.stringify(params),
    headers: {
      "Authorization": "Basic " + Utilities.base64Encode(SHOPIFY_API_KEY + ":" + SHOPIFY_PASSWORD),
      "Content-type": 'application/json',
    },
    muteHttpExceptions: false,
  };
  
  var response = UrlFetchApp.fetch(SHOPIFY_API_URL + url, options);
  return JSON.parse(response.getContentText());
}

// Slackメッセージを送信
function _postSlackMessage(message) {
  var jsonData = {
    username: 'YAMAP STORE bot',
    text: message
  };
  var options ={
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(jsonData)
  };

  UrlFetchApp.fetch(SLACK_WEBHOOK, options);
}

// WMS API へのリクエスト
function _wmsAPI(url, method, params) {
  // ~~~~~~~~~~~~~
  return xxx;
}

```

## 構成の応用
- 履歴管理とかした場合、GASからSpreadsheetに出力しておくと、色々と便利です。

<img width="777" alt="Screen Shot 2019-12-17 at 7.37.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221028/57a27287-68cd-809e-8656-ada79fb4af8b.png">

- また、原価管理とか、データ分析を行うのなら、こんな感じでもよいかもしれません

<img width="776" alt="Screen Shot 2019-12-17 at 7.45.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221028/dbe9aa65-7a26-3ca7-b172-9098c836e77a.png">

# 今後
カート〜チェックアウトフローのUIにこだわったり、他システムとの連携を柔軟にしたいので、↓のことをやってみたいなーって思います。

- チェックアウトフローのUIをこだわって、作ってみたいなー(ノ ꒪⌓꒪)ノ
- headless commerceで、構築してみたいなー(ノ ꒪⌓꒪)ノ
- GraphQLも使ってみたいなー(ノ ꒪⌓꒪)ノ
- 記事周りを柔軟にするために、headless cmsを利用したいなー(ノ ꒪⌓꒪)ノ

# ちなみに
- Shopifyの契約プランに応じて、できることは限られますので、ご注意をください(ノ ꒪⌓꒪)ノ

# 最後に小技
GASで日時指定して、実行したい場合、triggerで次の日時をセットすることで、毎日同じ時間に実行することができます。
例えば、毎日10時に、exportOrders関数を実行した場合は、↓で実現可能です。

```javascript
var trigger = {
  set: function(type, date) {
    ScriptApp.newTrigger(type).timeBased().at(date).create();
  },
  remove: function(type) {
    var triggers = ScriptApp.getProjectTriggers();
    for(var i=0; i < triggers.length; i++) {
      if (triggers[i].getHandlerFunction() == type) {
        ScriptApp.deleteTrigger(triggers[i]);
      }
    }
  }
};

var date = new Date();
date.setDate(date.getDate() + 1);
date.setHours(10);
date.setMinutes(0);
  
trigger.remove('exportOrders');
trigger.set('exportOrders', date);
```