何を作ったか
投稿、検索、認証、決済、Wallet、Ledger、Unlock、監視・障害追跡の仕組みと運用手順を設計したWebアプリケーションです。
Webアプリケーション / 技術ケーススタディ
一次情報をもとに意思決定を支えるWebサービスの設計・実装ケース
投稿、検索、認証、決済、Wallet、Ledger、Unlock、監視・障害追跡の仕組みと運用手順を設計したWebアプリケーションです。
自分に近い人の経験・結果・助言を見て、選択前の判断材料を得たい利用者。
要件整理、情報設計、画面実装、API、DB連携、決済連携、テスト、公開準備までを横断して扱いました。
事業成果ではなく、設計・実装・検証内容を確認するための主力技術ケーススタディとして掲載しています。
| 種別 | Webアプリケーション |
|---|---|
| 担当 | 要件整理 / 情報設計 / 画面 / API / DB / 決済 / テスト / 公開準備 |
| 主な技術 | Next.js / TypeScript / Supabase / PostgreSQL / Stripe / Playwright |
| 技術課題 | 決済、台帳、冪等性、権限、監視、CI |
公開可能な範囲で確認した実画面です。クリックすると拡大できます。
認証、DB、決済、監視、テストを含むWebアプリケーションとして整理しています。
採用担当者にも流れが分かるよう、実現したかったこと、設計、理由、防いでいる問題、検証方法に分けています。
目的: 購入後の残高反映と有料開放を重複なく扱うこと。設計: Checkout、Webhook署名検証、冪等キー、Purchase / Ledger記録、Wallet更新、Unlock判定を分けています。
目的: 残高、購入履歴、有料開放履歴の参照範囲を分けること。設計: Supabase Auth、RLS、RPCを使い、更新処理をDB側へ寄せています。
目的: 決済や解錠の失敗時に原因を追えること。設計: Sentry、Vercel Logs、Webhook failure playbook、refund playbook、admin_actionsを組み合わせています。
目的: 変更時に画面、型、ビルド、主要導線の崩れを早めに見つけること。設計: TypeScript、Playwright、GitHub Actions、Vercel確認を組み合わせています。
残高表示用のwalletsと、追跡用のwallet_ledgerを分け、表示速度と後追い確認を両立しています。
残高確認、消費、Unlock作成をDB側に寄せ、API処理途中の不整合を抑えています。
Webhook失敗や返金時の確認手順をplaybook化し、障害時に確認すべき情報を追えるようにしています。
採用担当者が設計意図を確認できる短い抜粋に限定しています。
Stripe Webhook署名検証
目的: Stripeから届いたイベントが正しい送信元のものか確認する。防いでいる問題: なりすまし通知による不正な残高反映。検証: handler、Sentry送信、失敗時レスポンスを確認。参照元: app/api/stripe/webhook/route.ts
const sig = req.headers.get("stripe-signature");
const rawBody = await req.text();
const event = stripe.webhooks.constructEvent(
rawBody,
sig,
webhookSecret,
);Unlock RPC呼び出し
目的: 残高確認、台帳追加、Unlock作成をDB側のRPCへ寄せる。防いでいる問題: API途中失敗による残高と解錠状態の不一致。検証: Route HandlerとmigrationのRPC定義を確認。参照元: app/api/open-post/route.ts
const { data, error } = await admin.rpc("open_content_with_wallet", {
p_user_id: userId,
p_content_id: postId,
p_coin_cost: coinCost,
});購入モーダルE2E
目的: PCとスマートフォン幅で購入モーダルの表示と主要CTAを確認する。防いでいる問題: 決済導線に入る前のUI崩れ。検証: Playwrightで複数viewportを確認。参照元: tests-e2e/purchase-modal.spec.ts
await expect(dialog).toBeVisible();
await expect(dialog.locator("h2").first()).toHaveText(scenario.heading);
const box = await dialog.boundingBox();
expect(box?.width).toBeLessThanOrEqual(viewport.width + 1);要件・仕様・優先順位はYuta Matsufujiが決め、Codex/OpenAIは調査、実装、レビュー支援として使います。受入判断は人間側で行い、型チェック、テスト、ビルド、画面確認、コードレビューで確認してから採用します。
本ページは、実装コード、テスト、設計資料から確認できる内容を掲載しています。利用者数や売上等の事業成果ではなく、設計・実装・検証内容をケーススタディとして紹介しています。