• #Cloudflare Workers
  • #Better Auth
  • #Polar.sh
  • #Resend
  • #認証
  • #決済

Chat Story 実装ガイド

対象サービス: https://chat-story.number55number55.workers.dev/
作成日: 2025年12月11日
プラットフォーム: Cloudflare Workers


概要

本ドキュメントは Chat Story サービスに以下の機能を実装するための技術選定と実装方針をまとめたものです。

機能推奨技術代替選択肢
認証(Auth)Better AuthClerk, Auth0, Lucia
決済(Payment)Polar.shCreem.io, Stripe + Tax
メール送信ResendSendGrid, Mailgun

1. 認証(Authentication): Better Auth

選定理由

Better Auth は TypeScript 向けの包括的な認証フレームワークで、以下の特徴があります。

  • フレームワーク非依存: React, Vue, Svelte, Astro, Hono など多くのフレームワークに対応
  • 豊富な機能が標準搭載: 2FA, パスキー, マルチテナント, セッション管理など
  • プラグインエコシステム: 機能拡張が容易
  • ユーザーあたりの課金なし: Auth0/Clerk と違い、ユーザー数に関係なく無料
  • データは自分のDB: ユーザーデータがサードパーティに渡らない

料金

項目Better AuthAuth0Clerk
基本料金無料(OSS)無料〜$35/月無料〜$25/月
MAU課金なし7,000MAU以降課金10,000MAU以降課金
セルフホスト××

主要機能

✅ メール/パスワード認証
✅ ソーシャルログイン(Google, GitHub, Discord, Twitter など)
✅ 2要素認証(2FA)
✅ パスキー / WebAuthn
✅ マジックリンク
✅ セッション管理
✅ マルチテナント / 組織管理
✅ レート制限

実装例(Cloudflare Workers + Hono)

// auth.ts
import { betterAuth } from "better-auth";
import { D1Adapter } from "better-auth/adapters/d1";

export const auth = betterAuth({
  database: D1Adapter(env.DB),
  emailAndPassword: {
    enabled: true,
  },
  socialProviders: {
    google: {
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
    },
  },
});
// クライアント側
import { createAuthClient } from "better-auth/client";

export const authClient = createAuthClient({
  baseURL: "https://chat-story.number55number55.workers.dev",
});

// サインアップ
await authClient.signUp.email({
  email: "user@example.com",
  password: "securepassword",
  name: "User Name",
});

// サインイン
await authClient.signIn.email({
  email: "user@example.com",
  password: "securepassword",
});

セットアップ手順

  1. パッケージインストール
    npm install better-auth
    
  2. DBスキーマ生成
    npx @better-auth/cli generate
    npx @better-auth/cli migrate
    
  3. 環境変数設定
    BETTER_AUTH_SECRET=your-secret-key
    GOOGLE_CLIENT_ID=xxx
    GOOGLE_CLIENT_SECRET=xxx
    

参考リンク


2. 決済(Payment): Polar.sh

Stripe vs MoR(Merchant of Record)の違い

決済システムを選ぶ際、最も重要な判断ポイントは Stripe(決済代行)MoR(販売代行) かの選択です。

項目Stripe(決済代行)Polar.sh / Creem(MoR)
法的な販売者あなたMoR事業者
税務処理(VAT/GST)自分で申告・納付自動で全世界対応
手数料約3.6%約4% + 40¢
請求書発行自分で対応自動発行
チャージバック対応自分で対応MoRが対応
コンプライアンス自分で対応MoRが対応

推奨: Polar.sh

選定理由

  1. オープンソース(Apache 2.0)で透明性が高い
  2. 手数料が安い: 4% + 40¢(従来MoRより20%安い)
  3. 開発者向け機能が充実:
    • ライセンスキー自動発行
    • GitHubリポジトリアクセス管理
    • Discordロール自動付与
    • ファイルダウンロード配信
  4. DXが優秀: API/SDK設計が洗練されている
  5. $10M調達済み(Accel主導)で信頼性あり

料金体系

基本手数料: 4% + 40¢ / 取引
- 追加の国際カード手数料: なし
- 月額固定費: なし
- 初期費用: なし

主要機能

✅ サブスクリプション(定期課金)
✅ 単発購入
✅ 使用量ベース課金(Usage-based billing)
✅ グローバル税務コンプライアンス(VAT/GST/消費税)
✅ ライセンスキー発行
✅ ファイル配信(最大10GB)
✅ 顧客ポータル
✅ Webhook
✅ 分析ダッシュボード

実装例

// サーバー側: チェックアウトセッション作成
import { Polar } from "@polar-sh/sdk";

const polar = new Polar({
  accessToken: env.POLAR_ACCESS_TOKEN,
});

// チェックアウトURL生成
const checkout = await polar.checkouts.create({
  productId: "prod_xxx",
  successUrl: "https://chat-story.../success",
  customerEmail: user.email,
});

return Response.redirect(checkout.url);
// Webhook処理
app.post("/webhook/polar", async (c) => {
  const payload = await c.req.json();
  const signature = c.req.header("polar-signature");
  
  // 署名検証
  const event = polar.webhooks.constructEvent(payload, signature, env.POLAR_WEBHOOK_SECRET);
  
  switch (event.type) {
    case "subscription.created":
      // サブスクリプション開始処理
      await activatePremium(event.data.customer.email);
      break;
    case "subscription.canceled":
      // サブスクリプション解約処理
      await deactivatePremium(event.data.customer.email);
      break;
  }
  
  return c.json({ received: true });
});

代替選択肢: Creem.io

Polar.sh が合わない場合の代替として Creem.io も検討可能です。

項目Polar.shCreem.io
手数料4% + 40¢3.9% + 40¢
オープンソース×
PMF前無料×◯(0%手数料)
主なターゲット開発者・OSSSaaS全般
設立2023年2024年

参考リンク


3. メール送信: Resend

選定理由

Resendは開発者向けに設計されたモダンなメールAPIサービスです。

  1. React Email統合: Reactコンポーネントでメールテンプレート作成
  2. 優れたDX: シンプルなAPI、充実したSDK
  3. 高い到達率: SPF/DKIM/DMARC対応、専用IPオプション
  4. リアルタイム追跡: 開封、クリック、バウンスのWebhook通知
  5. 無料枠が充実: 月3,000通まで無料

料金体系

プラン月額送信数特徴
Free$03,000通/月開発・小規模向け
Pro$2050,000通/月SSO対応
Scale$100200,000通/月専用IP(オプション)

Chat Story での活用シーン

📧 ウェルカムメール(新規登録時)
🔐 パスワードリセット
🔔 サブスクリプション開始/解約通知
📊 利用状況レポート
💳 決済完了/失敗通知

実装例

基本的なメール送信

import { Resend } from "resend";

const resend = new Resend(env.RESEND_API_KEY);

// シンプルなメール送信
await resend.emails.send({
  from: "Chat Story <noreply@chat-story.com>",
  to: user.email,
  subject: "Chat Storyへようこそ!",
  html: `
    <h1>ご登録ありがとうございます</h1>
    <p>${user.name}さん、Chat Storyへようこそ!</p>
  `,
});

React Emailでテンプレート作成

// emails/welcome.tsx
import { Html, Head, Body, Container, Heading, Text, Button } from "@react-email/components";

export function WelcomeEmail({ name, loginUrl }: { name: string; loginUrl: string }) {
  return (
    <Html>
      <Head />
      <Body style={{ fontFamily: "sans-serif" }}>
        <Container>
          <Heading>ようこそ、{name}さん!</Heading>
          <Text>Chat Storyへのご登録ありがとうございます。</Text>
          <Button href={loginUrl} style={{ background: "#3b82f6", color: "#fff", padding: "12px 24px" }}>
            ログインする
          </Button>
        </Container>
      </Body>
    </Html>
  );
}
// メール送信
import { WelcomeEmail } from "./emails/welcome";
import { render } from "@react-email/render";

const html = await render(WelcomeEmail({ 
  name: user.name, 
  loginUrl: "https://chat-story.../login" 
}));

await resend.emails.send({
  from: "Chat Story <noreply@chat-story.com>",
  to: user.email,
  subject: "Chat Storyへようこそ!",
  html,
});

Better Auth との統合

// auth.ts
import { betterAuth } from "better-auth";
import { Resend } from "resend";

const resend = new Resend(env.RESEND_API_KEY);

export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    sendResetPassword: async ({ user, url }) => {
      await resend.emails.send({
        from: "Chat Story <noreply@chat-story.com>",
        to: user.email,
        subject: "パスワードリセット",
        html: `<a href="${url}">こちらをクリックしてパスワードをリセット</a>`,
      });
    },
    sendVerificationEmail: async ({ user, url }) => {
      await resend.emails.send({
        from: "Chat Story <noreply@chat-story.com>",
        to: user.email,
        subject: "メールアドレスの確認",
        html: `<a href="${url}">こちらをクリックしてメールアドレスを確認</a>`,
      });
    },
  },
});

セットアップ手順

  1. Resendアカウント作成: https://resend.com/signup
  2. ドメイン認証(DNS設定)
  3. APIキー取得
  4. パッケージインストール
    npm install resend @react-email/components
    

参考リンク


4. 実装フェーズとタスク

Phase 1: 認証(優先度: 高)

📝 Google OAuth の詳細な設定手順は Google OAuth 設定ガイド を参照

□ Better Auth セットアップ
  □ D1データベーススキーマ作成
  □ 認証エンドポイント実装
  □ クライアント側認証フック作成
□ ログイン/サインアップUI作成
  □ メール/パスワード
  □ Google OAuth(オプション)→ [設定ガイド](/2025-12-11/google-oauth-setup-guide)
□ セッション管理
  □ 保護されたルート実装
  □ ログアウト処理

Phase 2: メール送信(優先度: 中)

□ Resend セットアップ
  □ ドメイン認証
  □ APIキー設定
□ メールテンプレート作成
  □ ウェルカムメール
  □ パスワードリセット
  □ メール確認
□ Better Auth との統合

Phase 3: 決済(優先度: 中)

□ Polar.sh アカウント作成
  □ 組織設定
  □ 商品/プラン作成
□ チェックアウトフロー実装
  □ 料金ページ作成
  □ チェックアウトリダイレクト
  □ 成功/キャンセルページ
□ Webhook処理
  □ エンドポイント作成
  □ サブスクリプション状態管理
□ 顧客ポータルリンク

5. 環境変数一覧

# Better Auth
BETTER_AUTH_SECRET=your-32-char-secret
BETTER_AUTH_URL=https://chat-story.number55number55.workers.dev

# Google OAuth(オプション)
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx

# Resend
RESEND_API_KEY=re_xxx

# Polar.sh
POLAR_ACCESS_TOKEN=polar_xxx
POLAR_WEBHOOK_SECRET=whsec_xxx
POLAR_ORGANIZATION_ID=org_xxx

6. 参考リソース

公式ドキュメント

サービスURL
Better Authhttps://www.better-auth.com/docs
Polar.shhttps://docs.polar.sh
Resendhttps://resend.com/docs
React Emailhttps://react.email/docs

ボイラープレート・サンプル

Cloudflare Workers 関連


7. 注意事項・Tips

認証

  • セッショントークンは HttpOnly Cookie で管理(XSS対策)
  • CSRF対策は Better Auth が自動対応
  • パスワードは Argon2 でハッシュ化(Better Auth標準)

決済

  • 本番環境移行前にテストモードで十分にテスト
  • Webhookは冪等性を考慮した実装を
  • 日本での消費税はPolar.shが自動処理

メール

  • 送信ドメインは必ず認証(SPF/DKIM)
  • 開発中はテストモードでリアル送信を防止
  • バウンスメールの自動処理を設定

このドキュメントは Chat Story サービスの実装ガイドとして作成されました。技術選定は2025年12月時点の情報に基づいています。