# 機能設計書 126-WhatsApp

## 概要

本ドキュメントは、ChatwootのWhatsApp Business API連携機能の設計仕様を記載する。この機能は、WhatsApp Business APIを通じたメッセージングをChatwootで一元管理するためのチャネル統合機能である。

### 本機能の処理概要

WhatsApp連携は、WhatsApp Business APIを通じてビジネスアカウントのメッセージを受信し、Chatwootの会話として管理する。テンプレートメッセージ、セッションメッセージ、添付ファイルの送受信に対応。プロバイダとして360dialogまたはWhatsApp Cloud APIを選択可能。

**業務上の目的・背景**：WhatsAppは世界中で20億人以上のユーザーを持つメッセージングプラットフォームであり、特にアジア・中南米・欧州での顧客コミュニケーションにおいて重要な役割を果たす。本機能により、WhatsApp経由の顧客対応をChatwootで統合管理でき、グローバルなカスタマーサポート体制を構築できる。

**機能の利用シーン**：
- 顧客からのWhatsAppメッセージを受信・返信する場面
- 24時間経過後のテンプレートメッセージ送信
- 商品カタログ・注文確認などのテンプレート送信
- 画像・動画・ドキュメントの送受信

**主要な処理内容**：
1. WhatsApp Business API（360dialog / Cloud API）との接続設定
2. Webhookを通じたメッセージ・ステータス更新の受信処理
3. セッションメッセージとテンプレートメッセージの送信
4. メッセージテンプレートの同期・管理
5. 連絡先（WhatsAppユーザー）の自動作成・更新

**関連システム・外部連携**：
- WhatsApp Business API（Cloud / 360dialog）
- Facebook Graph API（Cloud API使用時）
- Meta Webhook

**権限による制御**：インボックスの作成・管理にはAdministrator権限が必要。メッセージの送受信はインボックスにアサインされたエージェントが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 49 | チャネル設定画面 | 主画面 | WhatsAppチャネルの設定・認可フロー |
| 11 | 会話詳細画面 | 主画面 | WhatsAppメッセージの送受信 |
| 101 | WhatsAppキャンペーン画面 | 関連画面 | WhatsAppテンプレートを使用したキャンペーン |

## 機能種別

データ連携 / メッセージング

## 入力仕様

### 入力パラメータ（チャネル作成時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| phone_number | String | Yes | WhatsApp電話番号 | 一意性 |
| provider | String | Yes | プロバイダ（default/whatsapp_cloud） | 列挙値 |
| provider_config | JSONB | Yes | プロバイダ固有設定 | プロバイダにより異なる |
| account_id | Integer | Yes | Chatwootアカウント | 必須 |

### provider_config（360dialog）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| api_key | String | Yes | 360dialog APIキー |

### provider_config（WhatsApp Cloud）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| api_key | String | Yes | アクセストークン |
| phone_number_id | String | Yes | 電話番号ID |
| business_account_id | String | Yes | ビジネスアカウントID |
| webhook_verify_token | String | Auto | Webhook検証トークン（自動生成） |

### 入力パラメータ（メッセージ送信時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| content | String | No | メッセージ本文 | - |
| attachments | Array | No | 添付ファイル | - |
| template_params | Object | No | テンプレートパラメータ | テンプレート送信時 |

### 入力データソース

- WhatsApp認可フローからトークン取得
- Chatwoot会話画面からのメッセージ入力

## 出力仕様

### 出力データ（Webhook受信時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message | Message | 作成されたメッセージレコード |
| conversation | Conversation | 関連する会話 |
| contact | Contact | 送信者の連絡先 |

### 出力データ（メッセージ送信時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message_id | String | WhatsApp側のメッセージID（source_idとして保存） |

### 出力先

- Chatwootデータベース（messages, conversations, contacts）
- WhatsApp API（送信メッセージ）

## 処理フロー

### 処理シーケンス（メッセージ受信）

```
1. WhatsApp Webhookからイベント受信
   └─ WhatsappController#process_payloadで処理
2. 非アクティブ番号チェック
   └─ INACTIVE_WHATSAPP_NUMBERSに含まれる場合は拒否
3. Webhook検証（Cloud API）
   └─ webhook_verify_tokenで検証
4. WhatsappEventsJobで処理
   └─ IncomingMessageServiceでメッセージ処理
   └─ ステータス更新処理
```

### 処理シーケンス（メッセージ送信）

```
1. Chatwootでメッセージ作成
   └─ Message#after_create_commitコールバック
2. SendOnWhatsappServiceの実行
   └─ 返信可能かどうかの判定
3. テンプレート/セッションメッセージの分岐
   └─ template_paramsあり or 返信不可 → テンプレート送信
   └─ それ以外 → セッションメッセージ送信
4. プロバイダサービスを通じて送信
   └─ source_idをレスポンスから取得・保存
```

### フローチャート

```mermaid
flowchart TD
    A[メッセージ作成] --> B{チャネル検証}
    B -->|NG| C[処理終了]
    B -->|OK| D{template_paramsあり?}
    D -->|Yes| E[send_template_message]
    D -->|No| F{can_reply?}
    F -->|No| E
    F -->|Yes| G[send_session_message]
    E --> H[TemplateProcessorService]
    H --> I{テンプレート有効?}
    I -->|No| J[failed: Template not found]
    I -->|Yes| K[channel.send_template]
    G --> L[channel.send_message]
    K --> M[source_id保存]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 24時間ルール | 最後の受信メッセージから24時間以内のみセッションメッセージ送信可能 | メッセージ送信時 |
| BR-002 | テンプレート必須 | 24時間経過後はテンプレートメッセージのみ送信可能 | メッセージ送信時 |
| BR-003 | テンプレート同期 | チャネル作成時にテンプレートを自動同期 | チャネル作成時 |
| BR-004 | 非アクティブ番号 | INACTIVE_WHATSAPP_NUMBERSに含まれる番号はWebhook拒否 | Webhook受信時 |
| BR-005 | 重複メッセージ防止 | source_id重複チェックとRedisキャッシュで重複防止 | メッセージ受信時 |

### 計算ロジック

- can_reply?判定: 最後の受信メッセージから24時間以内かどうか（conversation.can_reply?）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| チャネル作成 | channel_whatsapp | INSERT | WhatsAppチャネル情報保存 |
| テンプレート更新 | channel_whatsapp | UPDATE | message_templates, message_templates_last_updated更新 |
| メッセージ受信 | messages | INSERT | 受信メッセージ保存 |
| メッセージ送信 | messages | UPDATE | source_id, status更新 |
| ステータス更新 | messages | UPDATE | status, external_error更新 |
| 連絡先作成 | contacts | INSERT/UPDATE | 送信者情報保存 |

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

#### channel_whatsapp

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | phone_number, provider, provider_config, account_id | 設定フォームから取得 | - |
| UPDATE | message_templates | APIから取得したテンプレート一覧 | JSONB形式 |
| UPDATE | message_templates_last_updated | 同期日時 | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| template_not_found | Validation Error | テンプレート名が無効 | 正しいテンプレート名を指定 |
| invalid_credentials | Auth Error | API認証情報が無効 | チャネル再設定 |
| inactive_number | Business Rule | 非アクティブ番号へのWebhook | 422エラー返却 |
| rate_limit | API Error | レート制限超過 | 時間を置いて再試行 |

### リトライ仕様

- 自動リトライなし
- メッセージステータスがfailedの場合、UIから再送信可能

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

- メッセージ受信時：トランザクション内でメッセージ作成後、Redisキャッシュをクリア
- ステータス更新：独立したトランザクション

## パフォーマンス要件

- Webhook処理はバックグラウンドジョブで実行
- 重複メッセージ防止のためRedisを使用

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

- webhook_verify_tokenは自動生成（SecureRandom.hex(16)）
- provider_config内のapi_keyは暗号化されない（要検討）
- Webhook検証トークンで不正リクエスト防止

## 備考

- PROVIDERS = %w[default whatsapp_cloud]
- Reauthorizableモジュールで認可エラー時の再認可フロー対応
- EmbeddedSignupサービスでMeta埋め込みサインアップに対応

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | whatsapp.rb | `app/models/channel/whatsapp.rb` | チャネルモデルの構造、プロバイダ設定、テンプレート管理 |

**読解のコツ**:
- `provider_service`メソッドでプロバイダに応じたサービスを生成
- `delegate`で送信メソッドをプロバイダサービスに委譲

**主要処理フロー**:
- **24-25行目**: EDITABLE_ATTRS, PROVIDERS定義
- **28行目**: PROVIDERS検証
- **29行目**: phone_numberバリデーション
- **30行目**: validate_provider_config
- **32行目**: 作成後にsync_templates
- **33行目**: 削除前にteardown_webhooks
- **34行目**: 作成後にsetup_webhooks（条件付き）
- **43-49行目**: provider_serviceでプロバイダごとのサービス生成
- **57-61行目**: delegate定義（send_message, send_template, sync_templates, media_url, api_headers）

#### Step 2: メッセージ送信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | send_on_whatsapp_service.rb | `app/services/whatsapp/send_on_whatsapp_service.rb` | 送信処理の分岐ロジック |
| 2-2 | template_processor_service.rb | `app/services/whatsapp/template_processor_service.rb` | テンプレート処理 |

**主要処理フロー（SendOnWhatsappService）**:
- **8-15行目**: perform_reply - テンプレート/セッション分岐
- **17-38行目**: send_template_message - テンプレート処理・送信
- **40-43行目**: send_session_message - セッションメッセージ送信
- **45-47行目**: template_params取得

#### Step 3: メッセージ受信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | whatsapp_controller.rb | `app/controllers/webhooks/whatsapp_controller.rb` | Webhook受信 |
| 3-2 | incoming_message_base_service.rb | `app/services/whatsapp/incoming_message_base_service.rb` | メッセージ処理基底クラス |

**主要処理フロー（IncomingMessageBaseService）**:
- **9-17行目**: perform - ステータス/メッセージ分岐
- **21-39行目**: process_messages - 重複チェック、連絡先・会話・メッセージ作成
- **42-48行目**: process_statuses - ステータス更新
- **83-100行目**: set_contact - 連絡先作成/更新

#### Step 4: プロバイダサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | whatsapp_cloud_service.rb | `app/services/whatsapp/providers/whatsapp_cloud_service.rb` | Cloud API実装 |
| 4-2 | whatsapp_360_dialog_service.rb | `app/services/whatsapp/providers/whatsapp_360_dialog_service.rb` | 360dialog実装 |

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

```
[メッセージ送信フロー]
Message#after_create_commit
    │
    └─ SendReplyJob.perform_later
           │
           └─ Whatsapp::SendOnWhatsappService#perform
                  │
                  ├─ validate_target_channel
                  ├─ outgoing_message? チェック
                  ├─ invalid_message? チェック
                  │
                  └─ perform_reply
                         │
                         ├─ [テンプレート] send_template_message
                         │      │
                         │      ├─ TemplateProcessorService#call
                         │      │      └─ テンプレート検索・パラメータ処理
                         │      │
                         │      └─ channel.send_template
                         │             └─ provider_service.send_template
                         │
                         └─ [セッション] send_session_message
                                │
                                └─ channel.send_message
                                       └─ provider_service.send_message

[メッセージ受信フロー]
WhatsApp Webhook
    │
    └─ Webhooks::WhatsappController#process_payload
           │
           ├─ inactive_whatsapp_number? チェック
           │
           └─ WhatsappEventsJob.perform_later
                  │
                  └─ IncomingMessageBaseService#perform
                         │
                         ├─ [ステータス] process_statuses
                         │      └─ update_message_with_status
                         │
                         └─ [メッセージ] process_messages
                                │
                                ├─ 重複チェック（DB + Redis）
                                ├─ set_contact
                                ├─ set_conversation
                                └─ create_messages
```

### データフロー図

```
[送信フロー]
[Chatwoot]              [処理]                    [WhatsApp]

Message作成     ───▶ SendReplyJob
                      │
                      ▼
                SendOnWhatsappService
                      │
                      ├─▶ [24h内] send_session_message
                      │      │
                      │      └─▶ provider_service.send_message
                      │
                      └─▶ [24h後/テンプレート] send_template_message
                             │
                             ├─▶ TemplateProcessorService
                             │
                             └─▶ provider_service.send_template
                      │
                      ▼
                WhatsApp API           ───▶ WhatsApp
                      │
                      ▼
                message_id受信         ◀───  API Response
                      │
                      ▼
                source_id保存

[受信フロー]
[WhatsApp]              [処理]                    [Chatwoot]

Webhook Event   ───▶ WhatsappController
                      │
                      ├─▶ 非アクティブ番号チェック
                      │
                      └─▶ WhatsappEventsJob
                             │
                             └─▶ IncomingMessageBaseService
                                    │
                                    ├─▶ [ステータス] update_message_with_status
                                    │
                                    └─▶ [メッセージ]
                                           ├─ 重複チェック
                                           ├─ Contact作成/取得
                                           ├─ Conversation作成/取得
                                           └─ Message作成
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| whatsapp.rb | `app/models/channel/whatsapp.rb` | モデル | チャネル定義 |
| send_on_whatsapp_service.rb | `app/services/whatsapp/send_on_whatsapp_service.rb` | サービス | メッセージ送信処理 |
| template_processor_service.rb | `app/services/whatsapp/template_processor_service.rb` | サービス | テンプレート処理 |
| incoming_message_base_service.rb | `app/services/whatsapp/incoming_message_base_service.rb` | サービス | メッセージ受信処理 |
| whatsapp_cloud_service.rb | `app/services/whatsapp/providers/whatsapp_cloud_service.rb` | サービス | Cloud APIプロバイダ |
| whatsapp_360_dialog_service.rb | `app/services/whatsapp/providers/whatsapp_360_dialog_service.rb` | サービス | 360dialogプロバイダ |
| webhook_setup_service.rb | `app/services/whatsapp/webhook_setup_service.rb` | サービス | Webhook設定 |
| webhook_teardown_service.rb | `app/services/whatsapp/webhook_teardown_service.rb` | サービス | Webhook解除 |
| whatsapp_controller.rb | `app/controllers/webhooks/whatsapp_controller.rb` | コントローラー | Webhook受信 |
| whatsapp_events_job.rb | `app/jobs/webhooks/whatsapp_events_job.rb` | ジョブ | Webhook処理 |
| authorizations_controller.rb | `app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb` | コントローラー | 認可 |
