ケーススタディ:vibefast.appでEコマースサイトを作る——SoapCo
English · 繁中 · Español · 日本語 · Português (BR)
著者: Danko Peng(@dankopeng)
更新: 2026年3月
読了時間: 約10分
SaaSとEコマースの違い
前の記事(InvoiceTrack)ではSaaSプロダクトの構築を見た。この記事ではEコマースサイト「SoapCo」を例に、SaaSとは異なる開発のポイントを紹介する。
主な違いを先に整理しておこう:
| SaaS(InvoiceTrack) | Eコマース(SoapCo) | |
|---|---|---|
| 課金モデル | 月額サブスクリプション | 1回限りの商品購入 |
| データ | ユーザーごとに独立 | 共有商品カタログ + 個別注文 |
| コア体験 | ダッシュボード・業務ツール | 商品ブラウジング・購入フロー |
| Stripe | サブスクリプション | 一度限りの決済 |
| 在庫管理 | 不要 | 必要 |
プロダクト概要:SoapCo
コンセプト: 天然・手作りソープの小規模Eコマースショップ
ターゲット: 自然派・健康志向の消費者
MVPの核心機能:
- 商品一覧と商品詳細ページ
- ショッピングカート(複数商品・数量指定)
- Stripe Checkoutで一度限りの決済
- 注文完了後の確認メール
- 在庫管理(商品ページに残り在庫数を表示)
明確に除外:(MVPでは作らない)
- アカウント登録・ログイン(ゲスト購入のみ)
- 商品レビュー機能
- 割引コード
- 複数の配送オプション
ステップ1:AIとDBスキーマを設計する
先にコードを書かないでください。
天然ソープのEコマースサイト「SoapCo」を作ります。
ゲスト購入のみ(ユーザーログイン不要)。
必要なテーブルを設計してください:
- 商品(カテゴリ付き、在庫管理)
- ショッピングカート(一時保存)
- 注文と注文明細
各テーブルのカラム、型、制約を含めてください。
まだSQLは生成しなくていいです、設計だけ提案してください。AIが提案するスキーマ:
-- カテゴリテーブル
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE
);
-- 商品テーブル
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER,
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
description TEXT,
price INTEGER NOT NULL, -- 円(整数)
stock INTEGER NOT NULL DEFAULT 0,
image_url TEXT,
status TEXT NOT NULL DEFAULT 'active', -- active / inactive
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(id)
);
-- 注文テーブル
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_number TEXT NOT NULL UNIQUE,
customer_email TEXT NOT NULL,
customer_name TEXT NOT NULL,
shipping_address TEXT NOT NULL,
subtotal INTEGER NOT NULL,
shipping_fee INTEGER NOT NULL DEFAULT 0,
total INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'pending', -- pending / paid / shipped / cancelled
stripe_session_id TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
paid_at TEXT
);
-- 注文明細テーブル
CREATE TABLE order_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
product_name TEXT NOT NULL, -- 注文時点の名前を保存
quantity INTEGER NOT NULL,
unit_price INTEGER NOT NULL, -- 注文時点の価格を保存
subtotal INTEGER NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);設計のポイント:order_itemsにproduct_nameとunit_priceを保存するのは重要だ。後で商品の名前や価格が変わっても、過去の注文記録が正確に保持される。
ステップ2:ショッピングカートの設計
EコマースのUI設計で最も重要な判断の一つが「カートをどこに保存するか」だ。
選択肢とトレードオフ:
Cookieベース(vibefast.appの採用方式)
ユーザーのブラウザのCookieにカートデータを保存するメリット:
- サーバーにカートのDBテーブルが不要
- ゲスト購入のみの場合に特に適している
- ブラウザを閉じても残る(Cookie有効期限まで)
デメリット:
- Cookieのサイズ制限(通常4KB)があるため商品数が多いと問題になる
- サーバーサイドでカートを操作できない
D1(データベース)ベース
メリット:
- サーバー側でカートを操作できる
- ユーザーがログインすれば複数デバイス間で同期できる
デメリット:
- カートの有効期限管理が必要(古いカートを定期的に削除するなど)
- ゲスト購入の場合、セッションIDの管理が必要
SoapCoはゲスト購入のみで商品数が少ないため、Cookieベースを選択する。
AIに実装を依頼:
Remix + Cloudflare WorkersでCookieベースのショッピングカートを実装してください。
機能:
- カートに商品を追加(商品ID、数量)
- カートから商品を削除
- 商品数量を変更
- カートの合計金額を計算
- カートの内容をCookieに保存・取得
カートのデータ構造:
{ productId: number, quantity: number }[]
カートの内容は JSON.stringify してCookieに保存、
取得時は JSON.parse して使う。ステップ3:商品一覧と商品詳細ページ
商品一覧ページ(/shop)を実装してください。
- D1からstatus = 'active'の商品を取得
- カテゴリでフィルタリングできる(クエリパラメータ ?category=soap)
- 商品カードグリッド(PC: 3列、モバイル: 1列)
- 各カードに:商品画像、商品名、価格(¥ 記号付き)、在庫切れバッジ(stock = 0の場合)
- 「カートに追加」ボタン(在庫切れ時はdisabled)商品詳細ページ(/shop/:slug)を実装してください。
- slugで商品を取得
- 大きな商品画像
- 商品名、価格、説明文
- 数量選択(1〜在庫数)
- 「カートに追加」ボタン
- 在庫数を表示(「残り○個」)
- 存在しない商品は404を返すステップ4:Stripe Checkoutで複数商品の決済
SaaSと違い、EコマースのStripe Checkoutは複数の商品を含む。
カートの内容を受け取ってStripe Checkout Sessionを作成するAPIを実装してください。
入力:
- cartItems: [{ productId: number, quantity: number }]
- customerEmail: string(任意)
- shippingFee: number(固定 ¥700)
処理:
1. D1から各商品の名前・価格・在庫を確認する
2. 在庫不足の場合はエラーを返す
3. Stripe Checkout Sessionを作成する(line_itemsに各商品を含める)
4. session_idとともにcheckout URLを返す
成功URL:/success?session_id={CHECKOUT_SESSION_ID}
キャンセルURL:/cartAIが生成するコードのサンプル:
export async function handleCheckout(request: Request, env: Env) {
const { cartItems, customerEmail } = await request.json()
// 在庫確認と商品情報取得
const lineItems = []
for (const item of cartItems) {
const product = await env.DB.prepare(
'SELECT * FROM products WHERE id = ? AND status = ?'
).bind(item.productId, 'active').first()
if (!product || product.stock < item.quantity) {
return Response.json(
{ error: `${product?.name || '商品'} の在庫が不足しています` },
{ status: 400 }
)
}
lineItems.push({
price_data: {
currency: 'jpy',
product_data: { name: product.name },
unit_amount: product.price,
},
quantity: item.quantity,
})
}
const stripe = new Stripe(env.STRIPE_SECRET_KEY)
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: lineItems,
mode: 'payment',
customer_email: customerEmail || undefined,
success_url: `${env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${env.APP_URL}/cart`,
metadata: { cartItems: JSON.stringify(cartItems) },
})
return Response.json({ url: session.url })
}ステップ5:Webhookで注文処理
Stripe Webhookで決済完了を受け取り、注文を確定させる:
Stripe checkout.session.completed Webhookを処理するハンドラを実装してください。
処理内容:
1. metadataからcartItemsを取得
2. ordersテーブルに注文レコードを作成
3. order_itemsテーブルに各商品の明細を保存
4. 各商品の在庫数を減らす(stock -= quantity)
5. Resendで注文確認メールを顧客に送信
冪等性の処理:stripe_session_idが同じ注文が既に存在する場合はスキップする。ステップ6:管理画面での注文管理・在庫管理
vibefast.appの管理画面(/admin)に追加する:
管理画面に以下のページを追加してください:
/admin/orders:注文一覧
- 注文番号、顧客名、注文日、金額、ステータス
- ステータスを「発送済み」に更新するボタン
- 発送済みに変更したら顧客に発送通知メールを送る
/admin/products:商品管理
- 商品一覧(名前、価格、在庫数、ステータス)
- 在庫数の一括更新機能
- 商品の有効化・無効化
/admin/products/new:商品追加
/admin/products/:id/edit:商品編集(R2への画像アップロード含む)ステップ7:ビジュアルとブランディング
Eコマースはビジュアルが命だ。機能完成後にデザインを仕上げる:
SoapCoのブランドデザインを整えてください。
ブランドイメージ:ナチュラル、手作り感、温かみ、信頼
カラーパレット:stone-50(背景)+ green-800(アクセント)+ amber-100(暖色)
フォント:システムフォントスタック
優先的に整えるページ:
1. トップページ(ヒーローセクション + 特集商品)
2. 商品一覧ページ(カードデザイン)
3. カートページ(シンプルで使いやすく)
信頼性を示す要素を含めてください:
- 「天然素材」「手作り」「無添加」などのバッジ
- お客様の声(テキストのみ)
- セキュア決済バッジ(Stripe使用を明示)Eコマースで信頼性の表示が重要な理由:ユーザーはクレジットカード情報を入力する前に、このサイトが信頼できるかを判断する。ブランドの一貫性、安全な決済の明示、お客様の声——これらが購入の決め手になる。
SaaSとEコマース、共通のベース
SoapCoとInvoiceTrack、異なるプロダクトだが、共通して使ったvibefast.appの機能:
両方で使ったもの:
- Cloudflare Workers(バックエンドAPI実行)
- D1データベース
- Stripe決済(モードが異なるだけ)
- Resendメール送信
- 管理画面の基本レイアウト
SaaSで使ったもの(Eコマースでは不要):
- ユーザー認証、JWT
- Stripeサブスクリプション
Eコマースで使ったもの(SaaSでは不要):
- R2画像ストレージ
- Cookieベースのカート管理
- 在庫管理ロジック
vibefast.appはこれらの共通基盤を提供していて、プロダクト固有の部分だけを自分で作ればいい。
Eコマースで特に注意すること
在庫の同時更新問題
複数のユーザーが同時に同じ商品を最後の1個に向かって購入しようとした場合——これは「レースコンディション」と呼ばれる問題だ。D1のトランザクションを使って適切に処理する:
// 在庫確認と予約をトランザクションで処理
await env.DB.batch([
env.DB.prepare(
'UPDATE products SET stock = stock - ? WHERE id = ? AND stock >= ?'
).bind(quantity, productId, quantity)
])Webhookの冪等性
Stripe Webhookは稀に同じイベントを2回送ることがある。注文処理でstripe_session_idの重複チェックを必ず実装する。
ゲスト購入の後処理
ゲスト購入の場合、注文確認メールが顧客との唯一の連絡手段だ。メールの内容を充実させる——注文番号、商品一覧、配送予定、問い合わせ方法。
Danko Peng
X · YouTube · Threads
完全なCloudflareフルスタックテンプレートを今すぐ使い始めたい方へ:
👉 vibefast.app — アーリーバード $99、2026年8月1日より $199 に値上がり。