ClearNets

Navigation

ホーム

トップ / プロダクト一覧

会社紹介

ClearNets の考え方

ブログ

開発ノート・設計思想

コツコツ。

和モダン習慣トラッカー

Merci

チップ決済 SaaS

Kotodama

AI 占い × キャラ

Paircon

家族見守り

家族おでかけガイド

週末のおでかけ先

コツコツ。を試す

← ブログ·技術·

未成年マーケットプレイスで Stripe Connect Express を運用する — Separate Charges & Transfers + 保護者名義出金 + 資金プール実装 (2026 年版)

Z 世代の稼ぐ手段として注目される『未成年マーケットプレイス』の商用実装は、決済と法規制の 2 面で難易度が跳ね上がります。Stripe Connect の 3 モデル (Standard / Express / Custom) から Express を選ぶ理由、Separate Charges & Transfers の分岐設計、未成年名義で Connect アカウントが作れない問題を保護者名義出金 + consent_token で解決する法務設計、資金プールと application_fee (15% Free / 12% Pro) の階層、返金と Reverse Transfer の TOCTOU 対策 (teen-earn B558 実バグ修正)、Account/Connect 2 系統 Webhook の二鍵署名 (B549)、通報 hold_period と takedown 後の grant 有効性 (B559)、特商法 + 資金決済法 + 未成年契約取消 + 児童売春禁止法 + 著作権の法務チェックリスト、FAQ 10 問を、sukikatsu (teen-earn) 本番実装知見でまとめます。

1. 未成年マーケットプレイスの需要 — Z 世代の稼ぐ手段と何が難しいか

2020 年代の Z 世代 (概ね 1997-2012 年生まれ) は SNS ネイティブで、 創作 / ハンドメイド / スキル販売を高校生のうちから始めるケースが 増えています。BOOTH のようなクリエイター向けマーケット、ココナラの ようなスキルマーケット、minne / Creema のようなハンドメイド EC が 既に成立しているものの、これらは 18 歳以上を前提とした利用規約で、未成年専用のマーケットプレイスは商用市場に空白が あります。

ClearNets の teen-earn プロジェクト (sukikatsu.clearnets.org) は、 この未成年ニッチに特化したマーケットプレイスを設計しています。 しかし実装で早々にぶつかる 2 つの壁があります。

  • 決済の壁: Stripe / PayPay / LINE Pay 等の主要 決済事業者は KYC で 18 歳以上を要求。未成年名義で決済アカウントを 作ることが実質不可能。
  • 法規制の壁: 民法 5 条 2 項の未成年契約取消権、 児童売春禁止法・児童福祉法の対面取引規制、特商法の販売業者表示 義務、資金決済法の前払式支払手段規制、著作権法上の二次創作対応、 の 5 領域を同時に満たす設計が必要。

本記事はこのうち 決済の壁 を Stripe Connect Express + 保護者名義出金 + 資金プール設計で解消する実装知見と、法規制の壁を 技術設計に落とし込む方法を、teen-earn 本番実装で得た知見でまとめます。

2. Stripe Connect の 3 モデル比較 — なぜ未成年案件で Express が最適か

Stripe Connect は 3 つの connected account タイプを提供しており、 個人開発者のマーケットプレイスにとって選定が最初の重要判断です。

項目StandardExpressCustom
KYC 収集Stripe Dashboard (自社実装不要)Onboarding UI 委譲 (自社実装不要)全て自社実装 (PCI DSS 対応)
DashboardStripe 標準 Dashboard簡易版 Express Dashboard全て自社実装
プラットフォーム制御弱 (connected account 主導)中 (transfer/payout 制御可)強 (全操作制御可)
開発工数最小 (数時間)中 (数日)大 (数週間 + 監査)
PCI DSSSAQ-ASAQ-ASAQ-D (自社対応必須)
未成年適合性△ (制御不足)◎ (最適)△ (オーバーヘッド)

未成年マーケットプレイスで Express が最適な理由は 3 つ。

  1. KYC を Stripe に完全委譲: 18 歳未満の名義では KYC を通せないので、保護者名義で Express アカウントを作成する 設計が組みやすい。Custom で自社 KYC を組むと保護者判定ロジックを 全て自社実装する必要があり工数肥大。
  2. カード情報の非保持: 未成年案件は消費者庁 / 個人 情報保護委員会の関心対象になりやすく、機微情報を自社サーバに保持 しない設計 (PCI DSS SAQ-A) が説明しやすい。Custom は SAQ-D 要件 で年 1 回の外部監査が入る。
  3. 手数料の明示性: 保護者に「Stripe 手数料 3.6% + プラットフォーム take rate 15%」と Express Dashboard で明示できる。 Standard だと connected account が自身の Dashboard を持ち、 プラットフォーム側で手数料が見えにくい。

Merci (飲食店チップ SaaS) も同じ理由で Express を採用しており、 小規模事業者向けの Stripe Connect 実装では Express が国内標準的な 選択になっています。

3. Separate Charges & Transfers パターンの詳細

Stripe Connect の charge / transfer には 2 つの主要パターンがあります。

  • Destination Charges: charge 作成時に destination パラメータで connected account を指定。charge 完了と同時に自動で 振替が走る「シンプル方式」。
  • Separate Charges & Transfers: charge はプラット フォームが受け取り、後で明示的に transfer.create() で connected account に振替。「柔軟な資金プール方式」。

未成年マーケットプレイスでは Separate 一択です。 理由は、買主決済 → モデレーション審査 → hold_period 経過 → 通報 なし → transfer 実行という 3 段階の判定を挟む必要があるため。 Destination だと charge と同時に自動振替が走り、通報時の Reverse Transfer が返金全額按分で複雑化します。

Separate パターンの実装コード (Node.js 例):

// 1) 買主決済 (プラットフォームが charge を受ける)
const charge = await stripe.paymentIntents.create({
  amount: 1000,               // ¥1,000
  currency: "jpy",
  payment_method: pmId,
  confirm: true,
  metadata: {
    listing_id: "listing_123",
    buyer_user_id: "user_buyer_456",
    seller_user_id: "user_seller_789",
    parent_stripe_account: "acct_parent_abc",  // 保護者名義 connected account
  },
  on_behalf_of: "acct_parent_abc",  // 統計・払戻先の見せ方
}, {
  idempotencyKey: `purchase-${orderId}`,
});

// 2) hold_period (7 日) 待機 & モデレーション審査 (別プロセス)

// 3) 通報なし & 審査通過 → 明示的に transfer 実行
const application_fee = calculateApplicationFee(1000);  // ¥150 (15%)
const transfer = await stripe.transfers.create({
  amount: 1000 - stripe_fee - application_fee,  // 出品者手取り
  currency: "jpy",
  destination: "acct_parent_abc",
  source_transaction: charge.latest_charge,  // balance_transactions で紐付け
  metadata: {
    listing_id: "listing_123",
    seller_user_id: "user_seller_789",
    payout_lock_released_at: new Date().toISOString(),
  },
}, {
  idempotencyKey: `transfer-${orderId}`,  // 二重 transfer 防止
});

重要なパラメータ:

  • on_behalf_of: 決済上の見せ方を connected account 名義に する。買主のカード明細に『(サービス名) via ClearNets Marketplace』 のように表示される。統計上の税務処理にも影響。
  • source_transaction: transfer が特定の charge に由来する ことを Stripe balance_transactions に記録。追跡性を担保。
  • idempotencyKey: 二重 transfer を防ぐ冪等キー。orderId 単位で振ることで、リトライ時の重複実行を Stripe 側でブロック。

4. 未成年名義で Connect アカウントが作れない問題と保護者名義出金

Stripe Connect Onboarding は KYC 段階で 18 歳以上を要求します。 日本の場合、Individual アカウント作成時に運転免許・マイナンバー カード・パスポート等の生年月日情報を照合し、未成年名義では capability を有効化できません (transfers capability が pending から進まない)。

解決策は 保護者名義で Express アカウントを作成し、 未成年ユーザーの売上を保護者アカウントに transfer する 『保護者名義出金』設計です。データベース設計は以下。

-- users テーブル (未成年ユーザー)
CREATE TABLE users (
  id BIGSERIAL PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  age INT NOT NULL,
  is_minor BOOLEAN GENERATED ALWAYS AS (age < 18) STORED,
  parent_stripe_account_id VARCHAR(64),  -- 保護者の Stripe acct_xxx
  parent_consent_token UUID,              -- 保護者同意の hash
  parent_consent_at TIMESTAMPTZ,
  parent_consent_ip INET,                 -- 法務エビデンス
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- parent_consents テーブル (詳細エビデンス)
CREATE TABLE parent_consents (
  id BIGSERIAL PRIMARY KEY,
  minor_user_id BIGINT NOT NULL REFERENCES users(id),
  parent_email VARCHAR(255) NOT NULL,
  parent_name VARCHAR(128) NOT NULL,
  parent_stripe_account_id VARCHAR(64) NOT NULL,
  consent_token UUID NOT NULL DEFAULT gen_random_uuid(),
  consent_scope JSONB NOT NULL,            -- {marketplace: true, payout: true, ...}
  consent_at TIMESTAMPTZ NOT NULL,
  consent_ip INET NOT NULL,
  consent_user_agent TEXT,
  revoked_at TIMESTAMPTZ                    -- 保護者が同意撤回した場合
);

保護者同意フロー (parent consent flow) の流れ:

  1. 未成年ユーザー登録時に「保護者メールアドレス」を入力させる。
  2. 保護者宛にマジックリンクを送信 (Resend / React Email)、 有効期限 72 時間 + 単発トークン。
  3. 保護者がリンクをクリック → 同意画面でStripe Connect Onboarding URL を生成、保護者に Express アカウント作成を促す。
  4. Onboarding 完了後、account.updated Webhook で transfers capability が active になったことを検知、parent_stripe_account_id を users テーブルに保存。
  5. 同時に parent_consents に consent_token + IP + UA を 記録し、法務エビデンスに残す。

重要な運用ポイント: 保護者メール不達への対応。 Gmail の迷惑メール振り分けや、キャリアメール (docomo / au / softbank) のフィルタで届かないケースが 5-10% 発生します。対策は (1) SPF / DKIM / DMARC の完全設定 (Resend + Cloudflare DNS)、(2) 送信元 ドメインを 3 ヶ月以上のウォームアップ済にする、(3) 不達時に SMS 経由 で consent_token を DB 直参照して手動 approve できる管理画面を用意、 の 3 段構え。teen-earn では (3) の Ops 画面で月 10-20 件の手動対応が 発生しています。

5. 資金プール設計 — Application Fee / min_payout / hold_period

マーケットプレイスの資金フローには 4 つの重要パラメータがあります。

  • Application Fee (take rate): プラットフォームが 取る手数料率。teen-earn は Free プラン 15% / Pro プラン (¥500/月) 12% の階層設計。
  • Stripe 手数料: 国内カード 3.6%、JCB / 一部 +¥40 相当。プラットフォームが吸収するか、出品者に転嫁するか設計判断。
  • min_payout: 最小出金額。teen-earn は ¥1,000。 少額を溜め込むことで振込手数料の頻度を下げる。
  • hold_period: 決済から transfer 実行までの待機 期間。teen-earn は 7 日 (通報期間としても機能)。

Application Fee 計算例 (¥1,000 の取引 / Free 15% / 国内カード):

取引額:              ¥1,000
Stripe 手数料 (3.6%): -¥36
残額:                ¥964
Application Fee:      -¥144  (964 × 15%)
出品者手取り:         ¥820

* 振込手数料 ¥250 は月 1 回の payout でまとめて負担 (小分けにしない)
* min_payout ¥1,000 に達しない場合は次月へ繰越

Pro プラン (¥500/月 サブスク) では take rate が 12% に下がる設計 で、これにより ¥1,000 取引の Application Fee が ¥116 になり、 出品者手取りが ¥848 (+¥28)。月 20 件以上取引する出品者にとっては Pro プランが合理的になる価格設計です。

6. 返金 & 部分返金の Reverse Transfer 実装 (teen-earn B558 実バグ修正)

マーケットプレイス運営で最も事故りやすいのが返金処理です。 teen-earn の B558 バグは 3 件の二重出金が発生した実インシデントで、 修正過程で 3 つの重要な学びを得ました。

学び 1: Reverse Transfer フラグ必須

// ❌ 悪い例 (二重出金の原因)
await stripe.refunds.create({
  payment_intent: piId,
  amount: 1000,
});
// → プラットフォーム側で charge は返金されるが、
//    connected account の残高は減らない = 二重出金

// ✅ 良い例 (Reverse Transfer)
await stripe.refunds.create({
  payment_intent: piId,
  amount: 1000,
  reverse_transfer: true,          // ← 必須
  refund_application_fee: true,    // ← application_fee も按分返金
}, {
  idempotencyKey: `refund-${orderId}`,
});

reverse_transfer: true を指定すると、Stripe が自動で connected account の残高から返金分を引き戻します。これを忘れると プラットフォームは全額返金するのに connected account は売上を 保持し続ける『二重出金』状態になり、事業として損失。

学び 2: TOCTOU 対策 (冪等キー)

charge の status を確認して refund を発行する間に、別のプロセス (ユーザーが返金ボタンを 2 回押した / Ops が同時操作した) が同じ charge に refund を発行するとレース条件で二重返金が起きます。 対策は idempotencyKey を orderId 単位で振ること。 Stripe API は同じ idempotencyKey で 24 時間以内に来た request は キャッシュされた response を返すため、二重実行が API 層でブロック されます。

学び 3: 部分返金の application_fee 按分

// ¥1,000 の取引 → ¥500 部分返金
// application_fee = ¥150 (charge 時)
// 按分後 application_fee 返金額 = ¥150 × (500/1000) = ¥75

await stripe.refunds.create({
  payment_intent: piId,
  amount: 500,                     // 部分返金額
  reverse_transfer: true,
  refund_application_fee: true,    // ← Stripe が自動で ¥75 分の fee を返金
});

// 内部 ledger も同時更新 (transaction 内)
await db.transaction(async (tx) => {
  await tx.insert(refunds).values({
    order_id: orderId,
    amount: 500,
    fee_refunded: 75,
    reason: 'partial_customer_request',
  });
  await tx.update(orders)
    .set({ status: 'partial_refunded', refunded_amount: 500 })
    .where(eq(orders.id, orderId));
});

部分返金では refund_application_fee: true を明示すると Stripe が自動で按分計算 (¥150 × 0.5 = ¥75) してくれます。手動で 計算するとバグの温床になるので、Stripe の按分機能を信頼するのが 推奨。

7. Webhook 二鍵署名 — Account/Connect 2 系統 (teen-earn B549 の学び)

Stripe Connect プラットフォームでは Webhook が 2 系統存在します。

  • Account Webhook: プラットフォーム自身の Stripe アカウントに関するイベント (charge / payout / dispute)。
  • Connect Webhook: 接続された全 connected account の イベント (account.updated / capability.updated / payout.paid)。

それぞれ独立した signing secret を持ちます。teen-earn B549 は 1 つの endpoint (/api/stripe/webhook) で両系統を受けようとしたが、 片方の secret でしか署名検証していなかったバグで、Connect イベントの 50-70% が 401 で拒否される事態が発生。修正は endpoint を分離。

// ✅ 修正後: endpoint を 2 本に分離

// /api/stripe/webhook-account (Account Webhook 専用)
export async function POST(req: Request) {
  const sig = req.headers.get("stripe-signature");
  const body = await req.text();
  const event = stripe.webhooks.constructEvent(
    body, sig,
    process.env.STRIPE_WEBHOOK_SECRET_ACCOUNT  // ← Account 用 secret
  );
  // ...
}

// /api/stripe/webhook-connect (Connect Webhook 専用)
export async function POST(req: Request) {
  const sig = req.headers.get("stripe-signature");
  const body = await req.text();
  const event = stripe.webhooks.constructEvent(
    body, sig,
    process.env.STRIPE_WEBHOOK_SECRET_CONNECT  // ← Connect 用 secret
  );
  // ...
}

Stripe Dashboard 側の設定も 2 系統を別々に登録:

  1. Account Webhook: Developers → Webhooks → Add endpoint → URL /api/stripe/webhook-account、Listen to: 「Events on your account」を選択、対象イベントは payment_intent.* / charge.* / dispute.* / payout.* 等。
  2. Connect Webhook: 同じ画面で「Events on Connected accounts」を選択、URL /api/stripe/webhook-connect、 対象イベントは account.updated / capability.updated / person.updated / payout.paid 等。

署名検証の失敗率をモニタリングするのが運用のコツ。Datadog / Sentry で 401 応答率が 1% を超えたらアラート、5% を超えたら緊急対応 (secret ローテ / endpoint 設定確認) の SOP を組みます。

8. Payout ロック / 通報保留 / takedown 後の grant 有効性 (B559 の学び)

マーケットプレイスの運営上、通報や違反判定時に売上を止める設計が 必要になります。teen-earn B559 で整理した 3 段の grant 有効性 チェック:

Step 1: 通報受理時の hidden 化 + payout_lock

// 通報 API
async function receiveReport(listingId, reason, reporterId) {
  await db.transaction(async (tx) => {
    // 出品を非表示化
    await tx.update(listings)
      .set({ status: 'hidden_pending_moderation', hidden_at: new Date() })
      .where(eq(listings.id, listingId));

    // pending transfer に payout_lock フラグ立て
    await tx.update(pending_transfers)
      .set({ payout_lock: true, payout_lock_reason: 'report_received' })
      .where(and(
        eq(pending_transfers.listing_id, listingId),
        eq(pending_transfers.status, 'pending'),
      ));

    // 通報レコード
    await tx.insert(reports).values({
      listing_id: listingId,
      reason, reporter_id: reporterId,
      status: 'pending_moderation',
    });
  });
}

Step 2: モデレーション審査中の transfer ブロック

// transfer 実行前の事前チェック
async function executeTransfer(orderId) {
  const pt = await db.query.pending_transfers.findFirst({
    where: eq(pending_transfers.order_id, orderId),
  });

  // payout_lock 中は事前ブロック
  if (pt.payout_lock) {
    throw new Error(`Transfer blocked: ${pt.payout_lock_reason}`);
  }

  // hold_period (7 日) 未経過なら待機
  if (Date.now() < pt.created_at.getTime() + 7 * 24 * 60 * 60 * 1000) {
    throw new Error('Hold period not elapsed');
  }

  // 通過 → transfer.create() 実行
  await stripe.transfers.create({ /* ... */ });
}

Step 3: takedown 確定時の返金 + fee 全額返金

// takedown 確定 (モデレーターの判断)
async function takedownConfirmed(listingId) {
  const orders = await db.query.orders.findMany({
    where: and(
      eq(orders.listing_id, listingId),
      eq(orders.status, 'paid'),
    ),
  });

  for (const order of orders) {
    // 全額返金 + Reverse Transfer + fee 全額返金
    await stripe.refunds.create({
      payment_intent: order.stripe_payment_intent,
      reverse_transfer: true,
      refund_application_fee: true,  // fee も全額返金
      reason: 'requested_by_customer',
      metadata: { takedown_case_id: listingId },
    }, {
      idempotencyKey: `takedown-refund-${order.id}`,
    });
  }

  // 出品者 KYC 記録に事案 log
  await db.insert(seller_violations).values({
    seller_id: listing.seller_id,
    violation_type: 'takedown_confirmed',
    listing_id: listingId,
    log: 'Full refund + fee returned',
  });
}

逆に takedown 見送り確定時は payout_lock を解除して transfer 実行。 Payout の hold_period (7 日) を通報期間としても機能させることで、 越境返金 (transfer 実行後の返金で Reverse Transfer が connected account の残高不足で失敗するケース) が発生しない設計になります。

9. 法務チェックリスト — 5 領域の弁護士確認事項

未成年マーケットプレイスの日本法務チェックポイントを 5 領域で 整理します。

領域法律技術対応
特商法特定商取引に関する法律各出品ページに販売業者名 (or 保護者名) + 住所 + 連絡先。プラットフォーム自体も特商法表記必須。
資金決済法資金決済に関する法律プラットフォーム内 wallet を実装しない (Stripe 即時 transfer 方式)。前払式支払手段該当を回避。
未成年契約取消民法 5 条 2 項保護者同意 (consent_token) を DB 記録。同意撤回 API + 30 日以内取消受付フロー。
児童売春禁止法児童福祉法 34 条 / 児童売春禁止法未成年プロフィール: 連絡先非公開 / 対面取引禁止 / 実名不要 / DM 機能制限 / 通報 SLA 24h。
著作権法著作権法 (二次創作)DMCA / 侵害申告窓口 + takedown SOP 24h。二次創作ガイドライン明示。

相場感: 初回法務レビュー 30-50 万円 (規約 + プライバシーポリシー + 特商法表記 + 保護者同意設計)、その後の月次相談 3-10 万円。金融 法制と青少年保護法制の両方に強い弁護士は少なく、事前に法律事務所 3-5 社に電話ヒアリングして選定するのが推奨。

10. FAQ

本記事の要点を 10 問の FAQ にまとめました。JSON-LD の FAQPage 構造化データも埋め込んでいるため、Google 検索でリッチリザルト 表示の対象になります。

Q1. 未成年ユーザーは Stripe Connect アカウントを作れますか?

Stripe の Connect Onboarding は 18 歳以上を前提としており、日本の 場合も KYC (本人確認) 段階で運転免許・マイナンバーカード等の年齢 確認が入るため、未成年名義で Express / Custom アカウントを作成 することは実質不可能です。teen-earn (sukikatsu) では保護者名義で Stripe Express アカウントを作成し、未成年ユーザーの売上を保護者 アカウントに transfer する『保護者名義出金』設計を採用しています。 事前に規約で『18 歳未満は保護者同意 + 保護者名義口座への出金』を 明示し、consent_token を DB 保存して法務エビデンスに残します。

Q2. Standard / Express / Custom のうちどれを選ぶべきですか?

未成年マーケットプレイスなら Express が最適です。理由は (1) KYC 収集を Stripe 側の Onboarding UI に完全委譲でき自社実装不要、 (2) カード情報保持義務 (PCI DSS SAQ-D) を回避でき自社で機微情報を 扱わない、(3) 手数料が明示的で保護者への説明が容易、の 3 点。 Standard は connected account が自身の Dashboard を持ちプラット フォームの制御が弱い、Custom は KYC・ダッシュボード全て自社実装で 個人開発者にはオーバーヘッド過大。Merci (飲食店チップ SaaS) も 同じ理由で Express を採用しています。

Q3. Separate Charges & Transfers と Destination Charges の使い分けは?

未成年マーケットプレイスは Separate Charges & Transfers 一択です。 Destination Charges は charge 時に destination パラメータで自動的に connected account に振替が走り、返金時の Reverse Transfer が同時 実行される『シンプルだが柔軟性が低い』方式。Separate は (1) プラット フォームがまず charge を受け、(2) 判定 (通報保留 / 資金プール保留 / hold period) を通過した後に、(3) 明示的に transfer を実行する 3 段階。 未成年案件では『買主決済後にモデレーション審査を挟む』『通報中は payout を止める』要件があり、Separate でないと柔軟な資金プール管理が できません。

Q4. Application Fee (take rate) はどう設計するのが標準ですか?

国内マーケットプレイス相場は 10-20% で、teen-earn (sukikatsu) は Free プラン 15%、Pro プラン 12% の階層設計を採用しています。Stripe の決済手数料 (国内カード 3.6%) + 振込手数料 (¥250/回) はプラット フォーム側が負担する『表示価格 = 出品者手取り』方式が UX 上優位。 Application Fee の計算式は (取引額 - Stripe 手数料 - 振込手数料) × take_rate、min_payout (¥1,000) と hold_period (7 日) をあわせて設計 します。数式で application_fee_amount を毎回計算し、charge 作成時に amount と同時に指定します。

Q5. Reverse Transfer で返金する時の注意点は?

3 つの落とし穴があります。(1) refund.create() 実行時に reverse_transfer: true を明示しないと、プラットフォーム側で charge は返金されるが connected account の残高は減らず『二重出金』状態に なる。(2) TOCTOU (Time of Check to Time of Use) — charge の status を 確認して refund 発行するまでの間に、別のプロセスが同 charge に対して refund を発行するとレース条件で二重返金が起きるため冪等キー (idempotency_key) 必須。(3) 部分返金では refund_application_fee: true を指定し application_fee 分も按分返金する。teen-earn の B558 バグ 修正は (1) が原因で 3 件の二重出金が発生し、Reverse Transfer フラグの 追加 + 冪等キー付与 + 部分返金の按分ロジック整備で解消しました。

Q6. Webhook の二鍵署名検証とは何ですか?

Stripe Connect プラットフォームでは Account Webhook と Connect Webhook の 2 系統があり、それぞれ独立した signing secret を持ちます。 Account Webhook は自プラットフォームの Stripe アカウントに関する イベント (charge / payout / dispute) を通知、Connect Webhook は接続 された connected account の全イベント (account.updated / capability.updated / payout.paid) を通知。teen-earn B549 のバグは 1 つの endpoint で両系統を受けていたが片方の secret でしか署名検証 していなかったケースで、Connect イベントが 401 で拒否される事態が 発生。修正は endpoint を 2 本に分離し、Stripe Dashboard の Webhook 設定でも Account 用と Connect 用を別途登録、それぞれ STRIPE_WEBHOOK_SECRET_ACCOUNT / STRIPE_WEBHOOK_SECRET_CONNECT 環境変数で検証。

Q7. 通報や takedown 後の transfer / payout の扱いはどうしますか?

teen-earn B559 で整理した設計は 3 段の grant 有効性チェックです。 (1) 通報受理時に該当出品を hidden 化し、既存の pending transfer に payout_lock フラグ立て、(2) モデレーション審査中は当該 connected account の transfer.create() 呼び出しを事前ブロック、(3) takedown 確定時は返金 (Reverse Transfer) + application_fee 全額返金 + 出品者 KYC 記録に事案 log。逆に takedown 見送り確定時は payout_lock を解除 して transfer 実行。Payout の hold_period (7 日) を通報期間として 活用することで、越境返金が発生しない設計になります。

Q8. Apple / Google の IAP 手数料 30% と Stripe Connect 3.6% + take rate の使い分けは?

Apple / Google のガイドライン上『物理的商品・実体サービスの決済』は Stripe 等の外部決済が許容、『デジタルコンテンツ・アプリ内消費物』は 原則 IAP 必須というルール分岐です。teen-earn (sukikatsu) は『イラスト 受注 / ハンドメイド / 手作りグッズ』が中心のためスキン/コスメ/物理 商品扱いで Stripe Connect が使えます。逆に Kotodama (AI 占い) や kotsukotsu (習慣トラッカー Pro) はサブスク型デジタルサービスで IAP 必須。Merci (飲食店チップ) は『実店舗での接客対価』で Stripe 使用可。 ガイドライン解釈は年に 1-2 回改正されるため、リリース前に App Store Review Guidelines 3.1.1 / 3.1.3 を最新版で確認するのが必須です。

Q9. 少額決済 (¥100-500) の最適設計は?

Stripe の国内カード決済手数料は 3.6% + ¥40 相当 (JCB / 一部) で、 少額決済ほど手数料負担率が高くなります。¥100 の決済に対して手数料 ¥43 で take rate 15% を乗せると、出品者手取りが ¥42 になり事業として 成立しません。対策は (1) 最低取引額を ¥500 or ¥1,000 に設定、(2) 複数商品のカート決済で 1 charge にまとめる、(3) プリペイド残高 (Stripe Customer Balance) で少額を溜め込み、¥3,000 以上でまとめて transfer、の 3 手法。teen-earn は (1) を採用し最低取引額 ¥500 の運用で 開始、成長に応じて (2) (3) を追加する段階設計です。

Q10. 日本の法務チェックポイントは何を確認すべきですか?

5 つを弁護士確認します。(1) 特定商取引法 — マーケットプレイス表示 事項 (販売業者名・住所・連絡先) を各出品ページで表示、プラット フォーム自体も特商法表記必須。(2) 資金決済法 — Stripe 経由の即時 transfer は前払式支払手段に該当しないが、プラットフォーム内 wallet を 実装するとアウト。(3) 未成年契約取消 — 民法 5 条 2 項『法定代理人の 同意なき法律行為は取消可能』のため、事前に保護者同意 + consent_token DB 保存が必須。(4) 児童売春禁止法 — 未成年出品者のプロフィール要件 (連絡先非公開 / 対面取引禁止 / 実名不要) を規約強制。(5) 著作権法 — 二次創作・トレース疑義への対応窓口 + takedown SOP。相場は初回法務 レビュー 30-50 万円、その後の運用相談で月 3-10 万円です。

11. まとめ — Express + Separate + 保護者名義出金が国内標準解

本記事の要点を 5 つに集約します。

  1. 未成年マーケットプレイスでは Stripe Connect Express + 保護者名義出金 + Separate Charges & Transfersが国内標準解。KYC を Stripe 委譲、機微情報非保持、柔軟な資金プール 管理が同時に実現可能。
  2. 保護者同意フロー は consent_token + IP + UA を DB 保存し法務 エビデンス化。メール不達 5-10% への Ops 手動 approve フロー必須。
  3. Reverse Transfer + 冪等キー + application_fee 按分返金 の 3 点を返金処理で明示。 teen-earn B558 の二重出金バグは全て Reverse Transfer フラグ漏れが原因。
  4. Account/Connect の Webhook 二鍵署名 を分離実装。1 endpoint 2 secret は運用崩壊。 teen-earn B549 で 50-70% イベント喪失事故。
  5. 5 領域の法務チェック (特商法 / 資金決済法 / 未成年契約取消 / 児童売春禁止法 / 著作権) を 初回 30-50 万円、月次 3-10 万円の弁護士予算で運用継続。

ClearNets の teen-earn (sukikatsu.clearnets.org) は本記事の設計を 全て実装した未成年専用マーケットプレイスとして稼働開始しています。 並行して、Free ユーザー向けにはより軽量な習慣化 / セルフ管理アプリ として kotsukotsu.clearnets.org も提供中で、Z 世代のセルフ管理と マネタイズの両輪を目指しています。Stripe Connect の実装に迷って いる個人開発者・スタートアップの参考にしてください。


ClearNets のマネタイズ支援プロダクト

sukikatsu (teen-earn) と kotsukotsu

本記事の実装設計を全て組み込んだ未成年専用マーケットプレイス sukikatsu (Stripe Connect Express + 保護者名義出金 + Separate Charges & Transfers) と、Z 世代向けセルフ管理 PWA kotsukotsu (¥400/月 Stripe Payment Link) が稼働中です。

この記事を書いた背景

teen-earn (sukikatsu) 本番実装で得た Stripe Connect Express の 実運用知見と、B558 / B549 / B559 の実バグ修正の学びを一次情報 として整理しました。本記事は法的アドバイスではなく事業設計の 参考情報です。実際の事業立ち上げ前には必ず金融法制・青少年保護法制 両方に強い弁護士のレビューを受けてください。特商法・資金決済法・ 民法・児童福祉法・著作権法は改正が続く分野で、最新情報は消費者庁 (caa.go.jp) / 金融庁 (fsa.go.jp) / こども家庭庁 (cfa.go.jp) の 公式発表を参照ください。


← ブログ一覧に戻る