背景
マルチロケーション対応の連携アプリを構築する際、複数のShopifyストアを運営しているケースにも対応する必要がありました。
それぞれの店舗で異なるNextEngine店舗IDや異なるAPIトークンを使用するため、店舗ごとに設定を分離して管理する仕組みが求められました。
設計上の課題
複数店舗を扱う際には以下の課題がありました:
- 設定の分離 - 店舗ごとに異なるAPIトークンやWebhook秘密鍵を管理
- 自動判別 - 受信したWebhookがどの店舗からのものか自動で判別
- セキュリティ - 認証情報を安全に保存
店舗ごとの設定分離
各店舗の特性に合わせた設定ができる仕組みを構築しました。たとえば、A店舗は通常配送のみ、B店舗は冷蔵配送にも対応、といった違いを設定で吸収できます。
新しい店舗を追加する際も、既存店舗の設定に影響を与えることなく、独立して設定できます。
店舗の自動判別
Webhookを受信したとき、どの店舗からの注文かを自動的に判別する機能を実装しました。これにより、適切な店舗の設定が自動的に適用されます。
運営者が「この注文はA店舗のものだから...」と意識する必要がなくなり、店舗数が増えても業務の複雑さが増しません。
店舗データ構造
各店舗は以下の情報を保持しています:
| 項目 | 説明 | 例 |
|---|---|---|
| 店舗ID | システム内部の識別子 | UUID形式 |
| 店舗名 | 表示用の名前 | "本店"、"楽天店" |
| ドメイン | ShopifyストアのURL | xxx.myshopify.com |
| 管理トークン | Shopify API認証用(暗号化保存) | *** |
| Webhook秘密鍵 | 署名検証用(暗号化保存) | *** |
| NextEngine店舗ID | 受注管理側の店舗ID | "1", "2" |
| 有効フラグ | 店舗の有効/無効 | true/false |
店舗判別のフロー
Webhookを受信した際、どの店舗からの注文かを自動で判別します:
Shopifyからデータ受信
ヘッダーから送信元ドメインを取得
ドメインで店舗を検索
店舗が見つかった場合:
- 店舗専用のWebhook秘密鍵で署名検証
- 店舗専用のアップロードパターンIDを使用
- 店舗専用のAPIトークンで処理
暗号化の仕組み
認証情報を平文で保存するのは危険です。万が一データが漏洩した場合、即座に悪用されてしまいます。
そこで、業界標準の強力な暗号化アルゴリズム(AES-256-GCM)を採用しています。
保存時の処理
APIトークンを受け取る
AES-256-GCMで暗号化
テキスト形式に変換
安全に保存
取得時の処理
暗号化データを読み込み
バイナリに変換
AES-256-GCMで復号
API呼び出しに使用
セキュリティ特徴
- 暗号化キーの分離 - 暗号化キーは環境変数で管理し、コードには含めない
- ランダムな初期化ベクトル - 毎回異なるIVを生成し、同じ平文でも異なる暗号文に
- 改ざん検知 - 認証タグによりデータの改ざんを検知
- 表示時のマスク - APIレスポンスではトークンをマスク表示し、設定の有無のみ確認可能
アーキテクチャ比較
個別管理
個別管理
個別管理
店舗A設定 / 店舗B設定 / 店舗C設定
独立した設定
独立した設定
独立した設定
店舗追加の手順
新しい店舗を追加する場合の手順は以下のとおりです:
- 店舗情報の登録 - 店舗名、ドメイン、NextEngine店舗IDを入力
- APIトークンの設定 - Shopify管理画面からトークンを取得して登録
- Webhook秘密鍵の設定 - ShopifyでWebhookを設定し、秘密鍵を登録
- 有効化 - 有効フラグをオンにして運用開始
既存店舗への影響なく、数分で新店舗を追加できます。
後方互換性
複数店舗管理機能を追加した際、既存の単一店舗運用にも対応しています。
店舗情報がデータベースに登録されていない場合は、従来どおり環境変数の設定を使用します。これにより、既存の運用を変更せずにシステムをアップデートできました。
メリット
この設計により、以下のメリットが得られました:
- 一元管理 - すべての店舗設定を1箇所で管理
- スケーラビリティ - 店舗が増えても管理工数は増えない
- セキュリティ - 認証情報の暗号化で漏洩リスクを軽減
- 独立性 - 店舗ごとに独立した設定で柔軟な運用が可能
注意点
暗号化キーの管理
暗号化キーを紛失すると、保存されたトークンを復号できなくなります。キーは安全な場所にバックアップを取っておくことをお勧めします。
店舗の無効化
店舗を閉鎖する場合は、有効フラグをオフにするだけで処理対象から除外されます。データを削除する必要はなく、再開時にはフラグをオンに戻すだけで済みます。