# 販売管理を Stripe 中心で組む案（2026-10-09）

- 読み手: キヨさん、およびキヨさんが使う AI
- 目的: すきま予備校の販売管理（教材などの有料販売）の体制について、これまでの調査と検討の結果を共有し、進め方を擦り合わせる
- 作成: 澤田（2026-10-09）

---

## 1. 現状の棚卸し（事実）

| 項目 | 現状 |
|---|---|
| 売上の経路 | 教材は今も note.com で販売（月 ¥2,000 台）。会員サイト（members.educarelog.net）経由の売上は 0 円。 |
| 会員の正本 | 本番 D1 `educare-poc` の `users`。会員サイトの Worker と、管理画面（Cloudflare Pages `educare-admin`）の会員一覧・会員詳細が同じ D1 を直接読んでいる。 |
| 自社の課金基盤 | 9/30 にお渡しした枝 `handoff/commerce-kiyo-20260930`（line-harness リポジトリ）。商品・値段・注文・権利・返金・異議・日次照合の表（migration 126〜130）と、Stripe の通知（Webhook）の受け口がある。ステージングの課金 Worker と管理画面の販売管理で動作確認済み。 |
| Stripe での実地確認 | Stripe のテスト環境で、購入 → 権利付与 → 閲覧 → 返金 → 閲覧停止 → 異議（チャージバック）→ 停止 → 勝訴 → 閲覧復帰、を一周通している。本番の鍵・本番の Webhook は未設定。 |
| 値段の正本 | 今の設計では自社の表（`offers` / `offer_prices`）。決済のたびに Stripe へ金額を渡している（Checkout の `price_data`）。Stripe 側の商品マスターは使っていない。 |
| 自社が持つ情報 | 課金の表にカード番号・下 4 桁・有効期限・口座・住所・電話・メールは無い。Checkout に渡すのは会員 ID・品目・金額だけで、カード情報は Stripe の画面で入力され自社サーバーを通らない。一方、会員表にはメール等の個人情報があり、購入履歴も個人に結び付く。 |
| 閲覧可否の判定 | 本番では 4 系統に分かれている。教材＝`enrollments`＋`user_memberships`、問題集＝`user_plans`（書き込むコードは無い）、過去問＝環境変数の許可リスト、AI 面接＝`interview_access`。 |
| 止まっている所 | 既存の閲覧判定（上の 4 系統）への接続と、既存会員の利用権の新しい権利表への移し替え。 |

## 2. 要約

1. 外部のカート・講座プラットフォーム・オープンソースを広く調べたが、今回の目的に合うものは無く、**Stripe を中心にまとめる**ことにした。
2. 「カードや口座の情報を持ちたくない」は、Stripe の決済画面（Checkout）を使う今の作りで既に満たしている。
3. 方針は「**Stripe にまとめられるものは Stripe に、どうしても自社が要るものだけ自社に**」。商品・値段・決済画面・返金・売上確認は Stripe、権利の付与と停止・教材の中身・会員との紐付けは自社。
4. 自社には薄い「**販売台帳**」（Stripe の値段 ID と教材の対応・販売状態）を置き、権利はそこから付ける。Stripe の商品に書いたメモ（metadata）だけで権利を付けるのは危ないため。
5. 9/30 の基盤のうち、自社の値段管理と商品編集画面は捨て、Webhook・注文・権利・返金／異議・日次照合は使い回す。
6. 最初の本番は「公開済み教材の単品と固定セットを、カードの単発払いで売る」だけに絞る。
7. 独立した 2 本の設計レビューの判定は、どちらも「条件付き採用可」。条件は 6 章。

## 構築イメージ（図解）

カード情報と取引の記録は Stripe 側だけにあり、自社は「誰がどの教材を見てよいか」と会員の紐付けだけを持つ。

### 図 1 全体の構成
```mermaid
flowchart LR
  M[会員（ブラウザ）] --> S[会員サイト members.educarelog.net]
  S -- ① 購入の要求 --> L
  subgraph OWN[自社（Cloudflare Workers + D1）]
    L[販売台帳: Stripe の値段 ⇔ 教材]
    O[注文と固定明細]
    E[権利（閲覧の可否）]
    C[会員 CRM（正本）: 会員 ⇔ Stripe 顧客]
    W[通知の受け口・毎日の照合]
  end
  subgraph ST[Stripe]
    P[商品・値段]
    K[決済画面 Checkout（カード入力）]
    T[取引の記録: 決済・返金・異議・入金]
    D[管理画面: 返金・売上・権限]
  end
  O -- ② 決済画面を作る --> K
  M -- ③ カード入力（自社を通らない） --> K
  T -- ④ 決済完了・返金の通知 --> W
  A[運営] -- 販売開始・停止 --> L
  A -- 商品・値段の作成・返金 --> D
```

### 図 2 購入の流れ
```mermaid
sequenceDiagram
  participant M as 会員
  participant O as 自社
  participant S as Stripe
  M->>O: 1. 購入する（表示した値段の ID）
  O->>O: 2. ログイン・台帳で販売中・重複所有・未完了決済を確認し、買う中身を固定した注文の下書きを作る
  O->>S: 3. 決済画面を作る（値段 ID・顧客）
  M->>S: 4. カード入力・3D セキュア（自社を通らない）
  S-->>O: 5. 決済完了の通知（署名つき）
  O->>S: 6. 状態を確認（通知を信じ切らない）→ 固定明細どおりに権利を付与
  O->>M: 7. 教材が読める
  Note over O,S: 取りこぼした通知は毎日の照合で拾う
```

### 図 3 返金・異議のとき
運営が Stripe の管理画面で返金 → Stripe が記録して通知 → 自社はその注文の権利だけを止める → 会員はその教材が読めなくなる。最初の本番は全額返金の成功で停止し、一部返金・返金失敗は例外の一覧で人が処理。異議に勝てば権利は自動で戻る。

## 3. Stripe で何ができるか（調査結果）

出典は Stripe 公式（docs.stripe.com・stripe.com/jp/pricing、2026-10-08〜09 に取得）。

### 3.1 商品と値段
- **Product（商品）と Price（値段）** を管理画面または API で作れる。1 商品に複数の値段を付けられ、単発用とサブスク用の両方がある。
- 値段の改定は「新しい Price を作って古い方を無効にする」運用。過去の購入は買ったときの Price に結び付いたまま残る。
- 任意の情報を **metadata** として付けられる（1 件あたり最大 50 項目・キー 40 文字・値 500 文字）。Stripe は中身を検証しない。
- Product・Price の `active`（有効）は「新しい購入に使えるか」だけを意味する。

### 3.2 決済ページ
- **Checkout**: 自社サーバーが購入ごとに決済セッションを作り、Stripe がホストする画面へ送る。カード情報は自社を通らない。会員 ID は `client_reference_id` で渡せる。Stripe の Customer を指定すると、その Customer のメールは決済画面で編集できない。
- **Payment Links**: 管理画面で商品ごとの決済 URL をコードなしで作れる。`client_reference_id` は URL のパラメータで渡す（英数字・ダッシュ・アンダースコア、最大 200 文字）ため、買い手が書き換えられる。二重購入の事前チェックもできない。
- 決済完了は Webhook `checkout.session.completed` で届く。品目（line_items）は通知の本文に含まれず、API で取り直す必要がある。コンビニのように後から入金が確定する支払いは、`checkout.session.async_payment_succeeded` / `async_payment_failed` を別に受ける。

### 3.3 取引の記録と運用
- 決済・返金・異議・手数料・入金予定・領収書・顧客の記録が Stripe に残り、管理画面で検索・CSV 出力できる。SQL での集計は有料の Sigma。
- 返金・異議への対応・領収書メールは管理画面から行える。
- **プロモーションコード**（クーポン）は Checkout と Payment Links で使える。
- **Customer Portal** は、サブスクの解約・支払い方法の変更・請求書の確認を顧客自身にさせる画面。
- **Entitlements API**（Stripe 側の権利機能）はサブスク専用で、単発購入の権利には使えない。

### 3.4 セキュリティ
- Checkout（ホスト型）は、PCI DSS の自己問診でいちばん軽い区分（SAQ A）にあたる。加盟店側にも一定の責任は残る。
- 3D セキュア（日本のクレジットカード・セキュリティガイドラインで求められる本人認証）は、Checkout と Payment Links の単発決済なら追加対応が要らない。
- 管理画面は役割（管理者・返金担当・異議担当・閲覧のみ など）でチームの権限を分けられ、チーム全員に 2 段階認証を必須にできる。管理者は他の人を招待できるため、管理者アカウントが乗っ取られた時の被害が大きいと Stripe 自身が注意喚起している。
- API キーは権限を絞った「制限付きキー」を作れる。
- Webhook は署名（`Stripe-Signature`、HMAC-SHA256）で本物か確かめる。届く順番は保証されず、同じ通知が複数回届くことがある。失敗時の自動再送は本番で最長 3 日。
- Cloudflare Workers からは `constructEventAsync`（Web Crypto）で署名を検証できる。

### 3.5 日本の決済手段と手数料（標準料金・2026-10-08 時点）
| 手段 | 手数料 | 制約 |
|---|---|---|
| カード | 3.6% | 3D セキュアは上記のとおり対応 |
| コンビニ | 3.6%（最低 ¥120） | サブスクの Checkout では使えない。¥500 の商品なら手数料は実質 24% |
| PayPay | 3.98%（デジタルコンテンツ事業は 9.48%） | 継続課金は不可。教材が「デジタルコンテンツ事業」に当たるかは未確認 |
| Stripe Billing（サブスク） | 取引額の 0.7% | — |

### 3.6 使わないと決めたもの
- **Managed Payments**（Stripe が販売元になる方式）: テスト環境では既定で有効になり、決済手段の指定と衝突したため無効化して運用している。
- **Payment Links**: 上記 3.2 の理由で、会員向けの販売には使わない（会員以外への単発キャンペーン程度なら可）。

## 4. 外部システムの比較調査（なぜ入れないか）

| 種類 | 主な候補 | 結果 |
|---|---|---|
| 日本の D2C・定期通販カート | ecforce、スマレジEC・リピート（旧 楽楽リピート）、Shopify、BASE、MakeShop ほか | 物販前提。決済代行は GMO-PG・SB ペイメント等の固定が多く、Stripe を選べる記載は見つからない。カート側に購入者の個人情報が集まる。例: ショップサーブ（Eストアー）は 2026 年 8 月に約 885 万件の不正アクセスを公表。 |
| 講座・会員サイト系 | UTAGE、MyASP、Teachable、Thinkific、Kajabi ほか | コンテンツと会員ごと移す前提。決済ページだけを切り出して使える確証が無く、会員 ID と学習記録が二重になる。日本円・コンビニ・特商法表示に対応しないものも多い。 |
| オープンソース | EC-CUBE、Medusa、Saleor、Vendure、Lago ほか | Cloudflare Workers・D1 に載らず、別サーバーの運用（OS・DB の更新、バックアップ、防御設定）が増える。取引データの置き場所も増える。公開済みのセキュリティ勧告も多い（Vendure 28 件・うち重大 6 件、Sylius 17 件、Saleor 12 件、EC-CUBE は 2026 年 3 月に管理画面の 2 段階認証すり抜け）。 |
| 課金 SaaS・販売代行 | Paddle、Lemon Squeezy、Chargebee、Recurly ほか | 日本円・コンビニ・PayPay への対応を確認できない、または別に決済代行が要る。 |
| 日本の決済代行 | KOMOJU、PAY.JP、GMO ほか | KOMOJU が次点（署名付き通知あり）だが料率非公開。Stripe から乗り換える理由は無い。 |

参考として挙がった CAM UP（cammacs.jp/contents/camup8/）は製品ではなく、物販向けクラウド ERP「キャムマックス」を提供する株式会社キャムの記事「おすすめのカートシステム 15 選」だった。

外部システムで置き換えられる部分（決済画面・商品管理・取引記録）は、既に Stripe と自社基盤で大半ができている。時間がかかっている部分（既存の閲覧判定への接続・既存会員の利用権の移し替え）は、どの外部システムを入れても自社に残る。

## 5. 販売管理体制案

### 5.1 置き場所の仕分け
| 機能 | 置き場所 | 備考 |
|---|---|---|
| 商品名・説明・値段・値段の改定 | Stripe（Product / Price） | 管理画面で作成 |
| 決済ページ | Stripe Checkout | 自社サーバーが販売台帳の Price ID で作成 |
| 領収書・返金・異議・売上確認 | Stripe の管理画面 | 管理画面に売上画面は作らない |
| クーポン | Stripe のプロモーションコード | 最初の本番では使わない（5.4） |
| 販売台帳（Stripe の値段 → 教材の対応・販売状態） | 自社 | 権利付与の正本 |
| 販売開始・停止 | 自社の管理画面 | 教材が公開済み・本文ありかを検査してから台帳に載せる |
| 決済完了 → 権利付与 | 自社 | Webhook → 決済状態を API で確認 → 台帳の固定明細から付与（冪等） |
| 返金・異議 → 権利の停止・復帰 | 自社 | Webhook で自動。最初は全額返金の成功だけで停止 |
| 教材の中身・下書きと公開 | 自社 | Stripe は中身を持てない |
| 会員との紐付け | 自社（会員の正本＝`users`） | 5.3 |

### 5.2 購入の流れ
1. 会員画面の値段は、販売台帳の写しから表示する（Stripe を表示のたびには叩かない。Stripe 障害を閲覧に波及させないため）。
2. 購入ボタン → 自社サーバーが、①ログイン会員か ②台帳で販売中の Price か（表示した Price ID と照合）③同じ教材を既に持っていないか ④未完了の決済が無いか、を確かめる。
3. 購入内容（教材の集合・金額・台帳の版）を固定した注文の下書きを作り、その会員の Stripe Customer と Price ID で Checkout セッションを作る。
4. Webhook で決済完了を受けたら、通知の中身を信用せず Stripe API で状態を取り直し、固定した明細から権利を付ける。
5. 日次の照合で「払ったのに権利が無い」「返金したのに権利が残っている」「台帳に無い決済」を検出する。

### 5.3 会員との紐付け
- Stripe の Customer は自社が先に作る（同じ会員で二重に作らないよう冪等に）。Customer の metadata に会員 ID を入れ、自社側に「会員 ↔ Stripe Customer」の対応表を持つ。
- 対応は **1 対多**にする。名寄せ（2 会員の統合、統合済みの会員は `status='merged'` で残る）の後も、過去の Customer は残るため。権利の付与先は統合先の会員へ辿って決める。Stripe の Customer は消さない。
- Customer を指定するとメールが決済画面で編集できないため、会員のメール変更を Stripe へ同期する。
- 退会時は未完了の決済を失効させる。再登録した会員へ権利を自動で移さない。メールの一致で名寄せしない。
- 管理画面の会員詳細に、注文・権利・付与の理由・同期の異常・Stripe の顧客ページへのリンクを出す。退会済みの会員も注文 ID から追えるようにする。

### 5.4 最初の本番の範囲
- 公開済み教材の **単品と固定セット**（セットも Stripe の商品 1 つ）を、**カードの単発払い・税込固定・数量 1・割引なし**で販売。
- 購入はログイン会員だけ。会員ごとに未完了の決済は 1 つまで。同じ教材の重複購入は止める。
- 権利は新しい権利表（`entitlements`）に付け、**教材の閲覧判定だけ**をそこへ切り替える。既存会員の教材の利用権は保持する。
- 全額返金と異議に連動して権利を止める・戻す。一部返金・返金失敗などは例外として一覧に出し、人が処理する。
- 日次照合と、照合や Webhook 処理が止まったことの検知。
- 後回し: クーポン、サブスク、面接チケット、問題集・過去問の販売、コンビニ払い、PayPay、可変セット。

## 6. 独立レビューで付いた条件と、既存コードの修正点

この案は、異なる 2 つの AI モデルに独立に防御レビューを依頼し、どちらも「条件付き採用可」だった。条件は 5 章に反映済み。要点:

1. **Stripe の metadata を権利付与の正本にしない**。管理画面で誰でも商品を作れ、metadata は検証されないため、付け忘れや書き間違いが「払ったのに読めない」に直結する。→ 自社の販売台帳を正本にする。
2. **Stripe で商品を有効にしても、教材が下書きのことがある**。→ 販売開始を自社の操作にして公開検査を通す。教材を下書きに戻したら台帳を止め、Stripe の Price も無効にする（自社 → Stripe の一方向）。購入者のいる教材は公開版を消せないようにする。
3. **日次照合は残す**（Webhook の順序・重複・取りこぼしのため）。台帳と紐付かない通知は「解決待ち」として残し、後から再処理できるようにする。
4. **権利は `enrollments` に書かない**。`enrollments` には状態の列が無く、返金で止めるには行を消すしかないため。
5. **Stripe 側の守り**: 個人別アカウント・2 段階認証必須・最小権限。Worker の鍵は返金の書き込み権限を持たない制限付きキー 1 本にし、テストと本番で鍵・Webhook の署名値を分ける。

既存コードで確認した修正点（返金時の権利停止の条件・二重購入の判定範囲・管理画面の有料会員数の数え方の3点）は、セキュリティ上の理由でこのページには書かず、リポジトリの設計メモ `docs/COMMERCE_STRIPE_CENTRIC_PROPOSAL_20261009.md` §6 に記載しています。

## 7. 9/30 の基盤から捨てるもの・残すもの

| 判断 | 対象 |
|---|---|
| 捨てる | 自社を値段の正本にする仕組み（`offer_prices`・`price_revision_intents`・Checkout の `price_data`）、管理画面の商品・値段の編集画面、セットを重ねる構造（migration 129・130） |
| 薄くして残す | 教材との対応・販売承認・変更履歴（＝販売台帳。Stripe の商品カタログは複製しない） |
| 残す | Webhook の受け口・署名検証・イベントの重複排除、`checkout_intents`（固定明細）、注文、注文別の権利、監査ログ、日次照合（migration 128 の途中再開・異常台帳）、教材の閲覧判定（教材だけの `checkAccess`）、公開・下書き・プレビューの保護、教材の販売可否検査（`checkCourseSale`） |
| 直して残す | 返金・異議の状態遷移（6 章）、名寄せ時の注文の持ち主の扱い |

## 8. 決めたいこと

1. この体制（Stripe 中心・自社は販売台帳と権利と会員の紐付け）で進めてよいか。
2. 要件の変更: 以前の要件「管理画面だけで商品作成を 5 分以内に完結」を、「Stripe で商品と値段を作り、管理画面で教材を結んで販売開始」に変えてよいか。
3. Stripe 管理画面の権限の運用: 誰が商品・値段を作り、誰が返金・異議に対応するか。

## 9. 参考（リポジトリ内）
- 課金基盤: line-harness リポジトリの枝 `handoff/commerce-kiyo-20260930`（`packages/db/migrations/126〜130`、`packages/db/src/commerce.ts`、`apps/worker/src/lib/commerce-stripe.ts`、`apps/worker/src/routes/commerce.ts`）
- 管理画面の会員一覧: `educare_admin/site/functions/_lib/crm_db.ts`
