# 機能設計書 27-SimpleFIN連携

## 概要

本ドキュメントは、Actual Budget における SimpleFIN連携機能の設計仕様を記述する。この機能は、SimpleFINプロトコルを使用して北米の銀行口座と連携し、取引データを自動的に取得することを目的とする。

### 本機能の処理概要

SimpleFIN連携機能は、SimpleFIN Bridge APIを通じて北米の金融機関の口座にアクセスし、取引データを自動取得する。SimpleFINはユーザーが設定したトークンをベースにアクセスキーを生成し、そのキーを使用して口座情報と取引を取得する。

**業務上の目的・背景**：北米の金融機関はOpen Banking規制が異なるため、GoCardlessでは対応できない場合がある。SimpleFINは北米の金融機関に特化したサービスを提供し、ユーザーはSimpleFIN Bridgeを通じて銀行口座を連携できる。

**機能の利用シーン**：
- 北米の銀行口座をActualに連携する
- 定期的に銀行取引を自動取得する
- 連携の問題発生時の再設定
- アクセストークンのリセット

**主要な処理内容**：
1. 設定状態の確認：SimpleFINトークンの設定確認
2. 口座一覧の取得：連携された口座を取得
3. 取引の取得：指定期間の取引データを取得
4. アクセスキーの取得：トークンからアクセスキーを生成

**関連システム・外部連携**：
- SimpleFIN Bridge API
- 同期サーバー（プロキシとして動作）

**権限による制御**：SimpleFINトークンとアクセスキーはサーバー側でシークレットとして管理される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 銀行同期管理画面（デスクトップ） | 主画面 | SimpleFIN連携設定 |
| 26 | 銀行同期管理画面（モバイル） | 主画面 | モバイルでのSimpleFIN連携管理 |

## 機能種別

データ連携 / 外部API連携

## 入力仕様

### 入力パラメータ

#### /status

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| - | - | - | パラメータなし | - |

#### /accounts

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| - | - | - | パラメータなし | - |

#### /transactions

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| accountId | string/string[] | Yes | 口座ID（単一または配列） | SimpleFIN形式 |
| startDate | string/string[] | Yes | 取得開始日（accountIdと対応） | YYYY-MM-DD |

### 入力データソース

- サーバー設定（SimpleFINトークン、アクセスキー）
- SimpleFIN Bridge（ユーザー設定）

## 出力仕様

### 出力データ

#### /status レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| configured | boolean | SimpleFINが設定されているか |

#### /accounts レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| accounts | Account[] | 連携された口座リスト |

#### /transactions レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| balances | Balance[] | 口座残高 |
| startingBalance | number | 開始残高（セント単位） |
| transactions.booked | Transaction[] | 確定取引 |
| transactions.pending | Transaction[] | 保留取引 |
| transactions.all | Transaction[] | 全取引 |

### 出力先

- クライアント：取引一覧、残高情報
- ローカルDB：インポートされた取引

## 処理フロー

### 処理シーケンス

```
1. 初回設定
   └─ ユーザーがSimpleFIN Bridgeでトークンを取得
   └─ トークンをサーバーに設定

2. アクセスキー生成
   └─ トークンからアクセスキーを生成
   └─ アクセスキーをサーバーに保存

3. 口座取得
   └─ /accounts でアクセスキーを使用して口座一覧取得
   └─ 口座をActualの口座と紐付け

4. 取引同期
   └─ /transactions で取引データを取得
   └─ booked/pendingに分類してレスポンス
```

### フローチャート

```mermaid
flowchart TD
    A[SimpleFIN連携開始] --> B{トークン設定済み?}
    B -->|No| C[トークン未設定エラー]
    B -->|Yes| D{アクセスキー存在?}
    D -->|No| E[getAccessKey]
    D -->|Yes| F[/accounts]
    E --> F
    F --> G{取得成功?}
    G -->|No| H[エラー処理]
    G -->|Yes| I[口座選択・紐付け]
    I --> J[/transactions]
    J --> K{取得成功?}
    K -->|No| L[エラー処理]
    K -->|Yes| M[取引分類・整形]
    M --> N[Actualに取引登録]
    N --> O[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-27-1 | トークン必須 | SimpleFINトークンがサーバーに設定されている必要がある | 全API呼び出し時 |
| BR-27-2 | アクセスキー自動生成 | トークンがあればアクセスキーを自動生成 | /accounts呼び出し時 |
| BR-27-3 | 配列パラメータ整合性 | accountIdとstartDateは両方配列または両方文字列 | /transactions時 |
| BR-27-4 | pending取引判定 | posted=0またはpendingフラグでpending判定 | 取引分類時 |

### 計算ロジック

**残高計算**:
```javascript
const startingBalance = parseInt(account.balance.replace('.', ''));
```

**取引日の決定**:
- bookedの場合：`posted`タイムスタンプを使用
- pendingの場合：`transacted_at`タイムスタンプを使用

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|-----|---------|------|
| シークレット取得 | secretsテーブル | SELECT | トークン、アクセスキー取得 |
| シークレット保存 | secretsテーブル | INSERT/UPDATE | アクセスキー保存 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| INVALID_ACCESS_TOKEN | 認証エラー | トークン/アクセスキー無効 | トークンをリセット |
| ACCOUNT_MISSING | 口座エラー | 指定口座が見つからない | 口座を再リンク |
| ACCOUNT_NEEDS_ATTENTION | 連携エラー | 銀行側で注意が必要 | SimpleFIN Bridgeで確認 |
| SERVER_DOWN | サーバーエラー | SimpleFINサーバー通信失敗 | 時間をおいて再試行 |

### リトライ仕様

- サーバーダウン時：クライアント側で通知し、手動リトライ
- 認証エラー時：トークンのリセットが必要

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

- SimpleFIN APIへのリクエストはトランザクション外
- エラー時は個別口座単位でエラーを記録

## パフォーマンス要件

- 口座取得：5秒以内
- 取引取得：口座数と期間に依存

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

- トークンとアクセスキーはサーバー側のシークレットとして管理
- 通信はHTTPS（Basic認証）

## 備考

- SimpleFIN Bridgeはユーザーが別途契約・設定が必要
- 一部の金融機関はSimpleFINでサポートされていない場合がある

---

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

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

### 推奨読解順序

#### Step 1: APIエンドポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app-simplefin.js | `packages/sync-server/src/app-simplefin/app-simplefin.js` | 全APIエンドポイント |

**読解のコツ**:
- **14-27行目**: `/status` - 設定状態確認
- **29-66行目**: `/accounts` - 口座一覧取得、アクセスキー自動生成
- **68-138行目**: `/transactions` - 取引取得
- **147-261行目**: `getAccountResponse` - 口座レスポンス生成
- **289-309行目**: `parseAccessKey` - アクセスキーのパース
- **311-329行目**: `getAccessKey` - トークンからアクセスキー生成
- **347-405行目**: `getAccounts` - SimpleFIN APIからの口座取得

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

```
Client
    │
    ├─ POST /simplefin/status
    │       │
    │       └─ secretsService.get(simplefin_token)
    │
    ├─ POST /simplefin/accounts
    │       │
    │       ├─ secretsService.get(simplefin_accessKey)
    │       │
    │       ├─ (なければ) getAccessKey(token)
    │       │      │
    │       │      └─ https.request(token) → accessKey
    │       │
    │       └─ getAccounts(accessKey, null, null, null, true)
    │               │
    │               └─ SimpleFIN API (GET /accounts?balances-only=1)
    │
    └─ POST /simplefin/transactions
            │
            ├─ secretsService.get(simplefin_accessKey)
            │
            └─ getTransactions(accessKey, accountIds, startDate)
                    │
                    └─ getAccounts(accessKey, accounts, startDate, endDate)
                            │
                            └─ SimpleFIN API (GET /accounts?start-date=...&pending=1)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| app-simplefin.js | `packages/sync-server/src/app-simplefin/app-simplefin.js` | ソース | APIエンドポイントと通信処理 |
| secrets-service.js | `packages/sync-server/src/services/secrets-service.js` | ソース | シークレット管理 |
