# 機能設計書 147-Shopify統合

## 概要

本ドキュメントは、ChatwootとShopify（Eコマースプラットフォーム）を連携する統合機能の設計書である。OAuth認証によりShopifyストアと接続し、顧客の注文履歴をChatwoot上で確認できるようにすることで、カスタマーサポートの品質向上を実現する。

### 本機能の処理概要

**業務上の目的・背景**：Eコマース企業のカスタマーサポートでは、顧客からの問い合わせに対応する際に注文情報を確認する必要が頻繁に発生する。Shopify統合により、Chatwoot上で直接顧客の注文履歴を確認できるようになり、別システムへの切り替えが不要になる。これにより、対応時間の短縮と顧客満足度の向上が期待できる。

**機能の利用シーン**：
- 顧客からの注文状況の問い合わせ対応時に注文履歴を確認
- 配送状況の確認
- 返品・交換対応時の注文情報確認
- 顧客の購買履歴に基づいたサポート提供

**主要な処理内容**：
1. Shopify OAuth認証によるストア接続
2. 連絡先のメールアドレス/電話番号から顧客検索
3. 顧客の注文履歴取得
4. 注文詳細へのリンク生成

**関連システム・外部連携**：
- Shopify Admin API（OAuth認証、顧客検索、注文取得）

**権限による制御**：
- Shopify統合の使用にはshopify_integrationフィーチャーフラグが必要
- SHOPIFY_CLIENT_ID/SHOPIFY_CLIENT_SECRET環境変数の設定が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 71 | Shopify連携画面 | 主画面 | Shopify統合の設定 |
| 65 | 連携アプリ一覧画面 | 参照画面 | Shopify統合の有効化状態表示 |

## 機能種別

データ連携 / OAuth認証 / 注文情報取得

## 入力仕様

### 入力パラメータ（認証）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| shop_domain | String | Yes | Shopifyストアドメイン（例: mystore.myshopify.com） | 空でないこと |

### 入力パラメータ（注文取得）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| contact_id | Integer | Yes | 連絡先ID | 存在する連絡先、email/phone必須 |

### 必要なOAuthスコープ

```
read_customers
read_orders
```

### 入力データソース

- 管理画面のShopify認証フォーム
- 連絡先詳細画面の注文履歴取得ボタン

## 出力仕様

### 出力データ（注文一覧）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| orders | Array | 注文リスト |
| orders[].id | Integer | 注文ID |
| orders[].email | String | 注文時メールアドレス |
| orders[].created_at | DateTime | 注文日時 |
| orders[].total_price | String | 合計金額 |
| orders[].currency | String | 通貨コード |
| orders[].fulfillment_status | String | 配送ステータス |
| orders[].financial_status | String | 支払いステータス |
| orders[].admin_url | String | Shopify管理画面URL |

### 出力先

- APIレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス（認証フロー）

```
1. 管理者がShopify連携を開始
   └─ ShopifyController#auth

2. shop_domainを受け取りstateトークン生成
   └─ generate_shopify_token(account_id)

3. Shopify OAuth認証URLを生成
   └─ https://{shop_domain}/admin/oauth/authorize?...

4. redirect_urlを返却
   └─ フロントエンドがリダイレクト

5. ユーザーが認証を承認

6. コールバック受信（フロントエンド経由）
   └─ /shopify/callback

7. アクセストークン取得・Hook作成
   └─ Hook(app_id: 'shopify', reference_id: shop_domain)
```

### 処理シーケンス（注文取得）

```
1. エージェントが注文履歴を要求
   └─ ShopifyController#orders

2. 連絡先情報取得
   └─ validate_contact

3. Shopifyコンテキスト設定
   └─ setup_shopify_context

4. 顧客検索
   └─ fetch_customers
   └─ email OR phone_number で検索

5. 注文取得
   └─ fetch_orders(customer_id)

6. 管理画面URL付与
   └─ admin_url: https://{shop}/admin/orders/{id}

7. レスポンス返却
   └─ { orders: [...] }
```

### フローチャート

```mermaid
flowchart TD
    A[エージェントが注文履歴を要求] --> B{連絡先にemail/phoneあり?}
    B -->|No| C[エラー: Contact information missing]
    B -->|Yes| D[Shopifyコンテキスト設定]
    D --> E[顧客検索]
    E --> F{顧客が見つかった?}
    F -->|No| G[空配列を返却]
    F -->|Yes| H[注文取得]
    H --> I[管理画面URL付与]
    I --> J[注文一覧を返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-147-01 | フィーチャーフラグ | shopify_integrationが有効なアカウントのみ利用可能 | 全操作 |
| BR-147-02 | 環境設定 | SHOPIFY_CLIENT_IDが設定されている場合のみ利用可能 | 全操作 |
| BR-147-03 | 連絡先情報必須 | 注文取得にはemail/phone_numberのいずれかが必要 | 注文取得時 |
| BR-147-04 | OR検索 | email OR phone_number で顧客を検索 | 顧客検索時 |
| BR-147-05 | 全注文取得 | status: 'any' で全ステータスの注文を取得 | 注文取得時 |
| BR-147-06 | 管理画面リンク | 各注文にShopify管理画面へのリンクを付与 | 注文取得時 |

### 計算ロジック

特になし

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 認証 | integrations_hooks | INSERT | Shopify Hook作成 |
| 削除 | integrations_hooks | DELETE | Shopify Hook削除 |
| 注文取得 | contacts | SELECT | 連絡先情報取得 |

### テーブル別操作詳細

#### integrations_hooks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | app_id = 'shopify' AND account_id = ? | Hook取得 |
| INSERT | app_id | 'shopify' | 固定値 |
| INSERT | reference_id | shop_domain | ストアドメイン |
| INSERT | access_token | OAuth取得トークン | 暗号化保存 |
| DELETE | - | id = ? | Hook削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Unprocessable Entity | shop_domainが空 | ストアドメインを入力 |
| 422 | Unprocessable Entity | 連絡先にemail/phoneなし | 連絡先情報を更新 |
| 422 | Unprocessable Entity | Shopify API エラー | エラーメッセージを確認 |
| 404 | Not Found | Hook が存在しない | Shopify統合を設定 |

### リトライ仕様

Shopify API呼び出し自体にはリトライ仕様は存在しない

## トランザクション仕様

- 各操作は単一リクエストで処理
- 外部API呼び出しはトランザクション外

## パフォーマンス要件

- 顧客検索・注文取得: 5秒以内

## セキュリティ考慮事項

- access_tokenは暗号化して保存
- OAuth認証時のstateパラメータでCSRF防止
- Shopify API バージョン: 2025-01

## 備考

- ShopifyAPI gemを使用
- API バージョンは定期的に更新が必要
- 顧客検索はemail/phone_numberのOR条件

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Shopify統合の設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | apps.yml | `config/integration/apps.yml` | Shopifyアプリ設定（204-209行目） |

**読解のコツ**:
- **apps.yml 204-209行目**: Shopifyのhook_type(account)、allow_multiple_hooks(false)を確認

#### Step 2: エントリーポイントを理解する

Shopify統合コントローラーを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | shopify_controller.rb | `app/controllers/api/v1/accounts/integrations/shopify_controller.rb` | 各種API操作 |

**主要処理フロー**:
1. **7-22行目**: `auth`でOAuth認証URLを生成
2. **24-32行目**: `orders`で注文一覧取得
3. **34-39行目**: `destroy`でHook削除
4. **55-67行目**: `fetch_customers`で顧客検索
5. **69-82行目**: `fetch_orders`で注文取得
6. **84-95行目**: `setup_shopify_context`でAPI設定

#### Step 3: ヘルパーを理解する

Shopify統合ヘルパーを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | integration_helper.rb | `app/helpers/shopify/integration_helper.rb` | トークン生成等 |

### プログラム呼び出し階層図

```
ShopifyController
    │
    ├─ #auth
    │      ├─ generate_shopify_token(account_id)
    │      └─ OAuth認証URL生成
    │             └─ { redirect_url: ... }
    │
    ├─ #orders
    │      ├─ validate_contact
    │      │      └─ contact.email/phone_number チェック
    │      ├─ setup_shopify_context
    │      │      └─ ShopifyAPI::Context.setup
    │      ├─ fetch_customers
    │      │      └─ shopify_client.get('customers/search.json')
    │      └─ fetch_orders
    │             └─ shopify_client.get('orders.json')
    │             └─ admin_url付与
    │
    └─ #destroy
           └─ hook.destroy!
```

### データフロー図

```
[入力]                          [処理]                              [出力]

shop_domain            ───▶ auth                       ───▶ redirect_url
                               │                              (OAuth URL)
                               ▼
OAuthコールバック       ───▶ Hook作成                  ───▶ integrations_hooks
(access_token)                                               (access_token)

contact_id             ───▶ orders                     ───▶ orders配列
                               │
                               ├─ fetch_customers
                               │      └─ Shopify API
                               │
                               └─ fetch_orders
                                      └─ Shopify API
                                      └─ admin_url付与
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| shopify_controller.rb | `app/controllers/api/v1/accounts/integrations/shopify_controller.rb` | コントローラー | Shopify統合API |
| integration_helper.rb | `app/helpers/shopify/integration_helper.rb` | ヘルパー | トークン生成等 |
| apps.yml | `config/integration/apps.yml` | 設定 | Shopifyアプリ設定 |
