---
title: ポイント・電子マネーの残高DB設計
description: 単一カラムの差分更新ではなくイベント履歴テーブルで設計する。経理・監査・供託判定に耐える構造
sidebar:
  label: 残高DB設計
---

| 項目 | 内容 |
|---|---|
| **疑うべき法令・基準** | 会計基準（＋資金決済法の供託判定） |
| **確認タイミング** | DB・API設計時 |

## なにが問題になるか

ユーザーテーブルに `point_balance` カラムを持ち増減のたびに直接更新する設計は、シンプルですが「現在の残高はわかるが、なぜその値になったのかを追えない」状態を生みます。経理から「先月のポイント付与総額は？」「失効処理はいつ走ったか？」と聞かれても答えられません。

基本は「残高そのもの」ではなく、**「残高が変化したイベント」を記録する**ことです。

```sql
CREATE TABLE point_transactions (
  id          BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id     BIGINT NOT NULL,
  event_type  ENUM('earn','use','refund','expire','charge') NOT NULL,
  amount      INT NOT NULL,         -- 正: 増加、負: 減少
  balance_after INT NOT NULL,       -- 事後残高（照合・デバッグ用）
  ref_id      BIGINT,               -- 関連するorder_idなど
  expires_at  TIMESTAMP,            -- 有効期限付き残高のみ
  created_at  TIMESTAMP NOT NULL
);
```

`event_type` の分類は経理処理と対応しています。

| event_type | 内容 | 経理上の処理 |
|---|---|---|
| `charge` | 先払いチャージ | 前受金（負債）の増加 |
| `earn` | 購入特典の付与 | ポイント引当金の計上 |
| `use` | 決済への充当 | 前受金の消込・売上認識 |
| `refund` | 返品時の復元 | 前受金の復活または現金返金 |
| `expire` | 有効期限切れ | 雑収入または前受金消込 |

このテーブルは会計でいう「仕訳帳」に相当し、**一度記録したデータの物理削除やUPDATEは禁止**（Append Only）です。誤って付与したポイントの修正も、誤レコードの削除ではなく「付与取消」をマイナスの `amount` の新イベントとして発行します（会計の逆仕訳に相当）。削除・更新を許すとイベントの連続性が崩れ、監査で「この期間に何が起きたか」を証明できなくなります。

`balance_after`（事後残高）は冗長に見えますが、`SUM(amount)` の理論残高と突き合わせることで「トランザクションの記録漏れ」「並列更新によるデータ不整合」を自前で検知できます。日次バッチで検証し、不一致でアラートを発火する設計が推奨されます。

未使用残高の総量を集計できる設計は、資金決済法の供託義務（1,000万円超判定）にも直結します。

## 経営層の問いに答えられるか（管理会計）

経営企画やCFOが定期的に確認したい数字が3つあります。設計段階でこれらの集計クエリに答えられるかを確認してください。

- **未使用残高（負債残高）**：ユーザーが保有するチャージ残高と未使用ポイントの総量。供託判断（1,000万円超）に直結し、BS上の負債として把握が必要。`balance_after` のユーザーごと最新値の集計、またはサマリーテーブルの定期更新で対応
- **失効予測**：今後3ヶ月・6ヶ月以内に有効期限が切れるポイントの総量。`expires_at` へのインデックスが必要。失効前リマインドなどマーケ施策にも使える
- **ポイント利用率**：付与されたポイントのうち実際に使われた割合。`earn` と `use` のコホート分析で算出。付与型ポイントが購買インセンティブとして機能しているかの判断材料

イベントがBS（貸借対照表）とPL（損益計算書）のどちらに効くかの対応も押さえておくと、経営層との数字の議論がスムーズになります。

| event_type | 財務上の位置づけ | 科目例 |
|---|---|---|
| `charge` | BS負債の増加 | 前受金 |
| `earn` | BS負債の増加（引当） | ポイント引当金 |
| `use` | BS負債の減少＋PL収益 | 前受金消込・売上高 |
| `expire` | BS負債の減少＋PL収益 | 雑収入 |
| `refund` | BS負債の復活 | 前受金（再計上） |

全イベントの毎回集計はデータ量が増えると現実的でないため、日次・月次バッチでサマリーテーブルに書き出す設計が有効です。ただし「バッチが走っていない間のリアルタイム集計は信頼できない」という前提を経営層と共有しておいてください。

## 確認すべきこと

- [ ] ポイント・電子マネーの残高変化をイベント履歴テーブルで記録する設計になっている
- [ ] `event_type` の分類が経理処理に対応している（`charge` / `earn` / `use` / `refund` / `expire`）
- [ ] 履歴テーブルはAppend Only（追記専用）で運用する設計になっている（UPDATE・DELETE禁止）
- [ ] `balance_after`（事後残高）カラムを持ち、整合性チェックバッチを設計している
- [ ] 未使用ポイント残高の総量を集計できるクエリまたはサマリーテーブルを設計している（供託義務の判定用）
- [ ] 失効（`expire`）イベントを正確に記録し、失効直前のポイント総量を把握できる
- [ ] 未使用残高・失効予測・利用率の集計に答えられる（インデックス・サマリーテーブルの設計）

## こんな仕様が出たらアラート

- ポイント・チャージ残高を単一カラムの差分更新で管理している
- 残高の変化履歴（イベント種別・日時・金額）を記録していない
- 月次の経理集計に特殊なクエリが都度必要になっている

## あわせて確認

- 供託義務・届出の前提 → [チャージ型電子マネー](/features/money/prepaid-emoney)
- 返金処理の設計 → [複合払い（ポイント＋カード）の返金処理](/features/accounting/mixed-payment-refund)

{/* 実務メモ: 書籍にない独自の追加情報はこの下に追記していく */}
