RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-03-17上級

Rork MaxでECアプリを1から構築する【Supabase + Stripe + プッシュ通知】

Rork MaxでフルスタックECアプリを1から構築。Supabaseで商品管理・認証、Stripe決済、プッシュ通知まで実装する実践チュートリアル。

Rork Max229ECアプリSupabase33Stripe17プッシュ通知19アプリ開発77チュートリアル23

Rork Maxを使えば、従来なら複数の専門エンジニアが必要だったフルスタックのECアプリを、個人でも短期間で構築できます。本チュートリアルでは、商品一覧・カート・決済・注文管理・プッシュ通知をすべて備えた本格的なECアプリを、Rork MaxとSupabase、Stripeを組み合わせてゼロから実装していきます。

完成するアプリの主な機能:

  • 商品一覧 / 詳細画面(Supabase Storageの画像表示)
  • カート管理(ローカル状態 + Supabase同期)
  • Stripe決済(クレジットカード / Apple Pay / Google Pay)
  • 注文履歴・ステータス管理
  • プッシュ通知(発送・到着通知)

この記事はRork Maxの基本操作を理解している方を対象としています。まだRorkに慣れていない方は Rorkの始め方ガイド を先にご確認ください。


前提知識・環境準備

必要なもの

ツール用途料金
Rork Maxアプリ開発・AI生成有料プラン
SupabaseDB・認証・ストレージ無料枠あり
Stripe決済処理手数料制
Expo EASビルド・プッシュ通知無料枠あり

Supabaseプロジェクトのセットアップ

まず Supabase ダッシュボードで新規プロジェクトを作成し、以下のテーブルを作成します。Rork Maxのチャットで「Supabase のテーブル設計を手伝って」と入力すると、AIがSQL生成を補助してくれます。

-- 商品テーブル
CREATE TABLE products (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  name TEXT NOT NULL,
  description TEXT,
  price INTEGER NOT NULL, -- 円単位(例: 2980 = ¥2,980)
  stock INTEGER DEFAULT 0,
  image_url TEXT,
  category TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
 
-- 注文テーブル
CREATE TABLE orders (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  status TEXT DEFAULT 'pending', -- pending / paid / shipped / delivered
  total_amount INTEGER NOT NULL,
  stripe_payment_intent_id TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
 
-- 注文明細テーブル
CREATE TABLE order_items (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
  product_id UUID REFERENCES products(id),
  quantity INTEGER NOT NULL,
  unit_price INTEGER NOT NULL
);
 
-- Row Level Security を有効化
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY "ユーザーは自分の注文のみ参照可能"
  ON orders FOR SELECT
  USING (auth.uid() = user_id);

Rork Maxプロジェクトの初期化

Rorkを開き、新規プロジェクトを作成します。プロンプトには以下のように入力してください:

ECアプリを作りたい。
- 商品一覧・詳細・カート・注文確認・マイページ画面が必要
- Supabase(URL: xxx, anon key: xxx)と連携
- React Native / Expo ベース
- タブナビゲーション(ホーム・カート・マイページ)

Rork MaxのAIがプロジェクト全体のスキャフォールドを生成します。生成後に細部を調整していく方法が最も効率的です。


概念と設計思想

ECアプリのアーキテクチャ

本チュートリアルで構築するアプリは、以下のアーキテクチャを採用しています。

[React Native / Expo アプリ]
       ↕ (Supabase JS Client)
[Supabase]
  ├── Auth(メール/SNSログイン)
  ├── Database(商品・注文データ)
  └── Storage(商品画像)
       ↕ (API呼び出し)
[Stripe]
  ├── Payment Intent(決済意図の作成)
  └── Webhook(決済完了通知)
       ↕
[Expo Push Notifications]
  └── 注文ステータス変更時に通知

状態管理の設計

カート状態はReact Context + useReducerで管理し、ログイン済みの場合はSupabaseのcartsテーブルにも同期します。この設計により、ゲストとログイン後でシームレスにカートを引き継げます。


ステップバイステップ実装

Step 1: 商品一覧画面

Rork MaxのAIに「商品一覧画面を作って」と指示した後、生成されたコードに以下の修正を加えます。

// screens/ProductListScreen.tsx
import { useEffect, useState } from 'react';
import {
  FlatList, View, Text, Image,
  TouchableOpacity, StyleSheet, ActivityIndicator
} from 'react-native';
import { supabase } from '../lib/supabase';
 
type Product = {
  id: string;
  name: string;
  price: number;
  image_url: string;
  stock: number;
};
 
export default function ProductListScreen({ navigation }: any) {
  const [products, setProducts] = useState<Product[]>([]);
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    fetchProducts();
  }, []);
 
  async function fetchProducts() {
    const { data, error } = await supabase
      .from('products')
      .select('*')
      .gt('stock', 0)        // 在庫あり商品のみ
      .order('created_at', { ascending: false });
 
    if (!error && data) setProducts(data);
    setLoading(false);
  }
 
  if (loading) return <ActivityIndicator style={{ flex: 1 }} />;
 
  return (
    <FlatList
      data={products}
      numColumns={2}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <TouchableOpacity
          style={styles.card}
          onPress={() => navigation.navigate('ProductDetail', { product: item })}
        >
          <Image source={{ uri: item.image_url }} style={styles.image} />
          <Text style={styles.name}>{item.name}</Text>
          <Text style={styles.price}>¥{item.price.toLocaleString()}</Text>
        </TouchableOpacity>
      )}
    />
  );
}
 
const styles = StyleSheet.create({
  card: { flex: 1, margin: 8, backgroundColor: '#fff', borderRadius: 12,
          overflow: 'hidden', elevation: 2 },
  image: { width: '100%', aspectRatio: 1 },
  name:  { fontSize: 14, fontWeight: '600', padding: 8 },
  price: { fontSize: 16, color: '#e53e3e', paddingHorizontal: 8, paddingBottom: 8 },
});

期待する出力: 2カラムのグリッドに商品が表示され、在庫切れ商品は表示されません。

Step 2: Stripe決済の実装

Stripeの決済はEdge Functionを介して実装します。Rork Maxで「Supabase Edge FunctionでStripe Payment Intentを作成するコードを書いて」と指示すると雛形が生成されます。

// supabase/functions/create-payment-intent/index.ts
import Stripe from 'npm:stripe@14.21.0';
import { createClient } from 'npm:@supabase/supabase-js@2';
 
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!);
 
Deno.serve(async (req) => {
  const { amount, currency, items, userId } = await req.json();
 
  try {
    // 1. Payment Intent を作成
    const paymentIntent = await stripe.paymentIntents.create({
      amount,           // 円単位の合計金額
      currency: currency ?? 'jpy',
      automatic_payment_methods: { enabled: true },
      metadata: { userId, itemCount: items.length.toString() },
    });
 
    // 2. Supabase に注文を仮登録(status: pending)
    const supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
    );
    const { data: order } = await supabase
      .from('orders')
      .insert({
        user_id: userId,
        total_amount: amount,
        status: 'pending',
        stripe_payment_intent_id: paymentIntent.id,
      })
      .select()
      .single();
 
    // 3. 注文明細を登録
    const orderItems = items.map((item: any) => ({
      order_id: order.id,
      product_id: item.productId,
      quantity: item.quantity,
      unit_price: item.price,
    }));
    await supabase.from('order_items').insert(orderItems);
 
    return new Response(JSON.stringify({
      clientSecret: paymentIntent.client_secret,
      orderId: order.id,
    }), { headers: { 'Content-Type': 'application/json' } });
 
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 400,
      headers: { 'Content-Type': 'application/json' }
    });
  }
});

期待する出力: clientSecretorderId がJSONで返却されます。

フロントエンドではこの clientSecret を使って @stripe/stripe-react-nativeconfirmPayment() を呼び出します:

// screens/CheckoutScreen.tsx(抜粋)
import { useStripe } from '@stripe/stripe-react-native';
 
export default function CheckoutScreen({ cartItems }: any) {
  const { confirmPayment } = useStripe();
 
  async function handlePay() {
    const totalAmount = cartItems.reduce(
      (sum: number, item: any) => sum + item.price * item.quantity, 0
    );
 
    // Edge Function 経由で Payment Intent 作成
    const res = await fetch(`${SUPABASE_URL}/functions/v1/create-payment-intent`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json',
                 'Authorization': `Bearer ${session.access_token}` },
      body: JSON.stringify({ amount: totalAmount, items: cartItems,
                             userId: session.user.id }),
    });
    const { clientSecret, orderId } = await res.json();
 
    // Stripe決済フローを起動
    const { error } = await confirmPayment(clientSecret, {
      paymentMethodType: 'Card',
    });
 
    if (error) {
      Alert.alert('決済エラー', error.message);
    } else {
      // 決済成功 → 注文完了画面へ
      navigation.navigate('OrderComplete', { orderId });
    }
  }
 
  // ...
}

Step 3: プッシュ通知の実装

注文ステータスが変わったときにプッシュ通知を送る仕組みを実装します。

// lib/notifications.ts
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import { supabase } from './supabase';
 
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});
 
// デバイストークンを取得してSupabaseに保存
export async function registerPushToken(userId: string) {
  if (!Device.isDevice) return;
 
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== 'granted') return;
 
  const token = (await Notifications.getExpoPushTokenAsync({
    projectId: 'YOUR_EXPO_PROJECT_ID',
  })).data;
 
  // ユーザーのプッシュトークンをSupabaseに保存
  await supabase
    .from('user_push_tokens')
    .upsert({ user_id: userId, token, updated_at: new Date().toISOString() });
 
  return token;
}

Supabase Edge FunctionのWebhookをStripeと連携させ、決済完了時に注文ステータスをpaidに更新し、Expo Push APIで通知を送る仕組みを構築します。


応用パターン

リアルタイム在庫管理

Supabaseのリアルタイム機能を使うと、他のユーザーが購入した瞬間に在庫数が自動更新されます。

// 在庫変更をリアルタイムで監視
useEffect(() => {
  const channel = supabase
    .channel('product-stock')
    .on(
      'postgres_changes',
      { event: 'UPDATE', schema: 'public', table: 'products',
        filter: `id=eq.${product.id}` },
      (payload) => {
        setStock(payload.new.stock);
        if (payload.new.stock === 0) {
          Alert.alert('お知らせ', 'この商品は売り切れになりました');
        }
      }
    )
    .subscribe();
 
  return () => supabase.removeChannel(channel);
}, [product.id]);

クーポン・割引コード機能

Stripeのクーポン機能と組み合わせて割引コードを実装できます。Rork Maxに「Stripeクーポンコードを検証するEdge Functionを追加して」と指示すると、検証ロジックを自動生成してくれます。

マルチ通貨対応

海外展開を見据えるなら、ユーザーのロケールに応じて通貨を切り替える仕組みを追加しましょう。Stripeは160以上の通貨に対応しており、currencyパラメータを変更するだけで対応できます。


トラブルシューティング

決済が完了しても注文ステータスが更新されない

原因: StripeのWebhookがSupabase Edge Functionに届いていません。 対処:

  1. Stripeダッシュボード → Webhooks でエンドポイントURLが正しいか確認
  2. Edge FunctionのログでWebhookの受信を確認(supabase functions logs
  3. Webhook署名検証をスキップしていないか確認(必ず検証すること)

Supabaseのクエリが遅い

原因: インデックスが設定されていません。 対処: 以下のインデックスを追加することで検索速度が大幅に改善します。

-- よく使う検索条件にインデックスを追加
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);

プッシュ通知が届かない

確認リスト:

  • iOS: APNs証明書がEAS Buildに設定されているか
  • Android: FCMのサーバーキーが設定されているか
  • expo-notificationsのパーミッション取得が完了しているか
  • デバイストークンがSupabaseに保存されているか(デバッグ: Supabase TableのUI確認)

RLSポリシーエラー(403 Unauthorized)

原因: Row Level Securityが有効なのにポリシーが設定されていません。 対処: 管理者からのみ書き込み可能にしたい場合は、Service Role Keyを使用するEdge Function経由で更新します。絶対にフロントエンドのコードにService Role Keyを含めないこと。


公開・収益化のポイント

App Store / Google Playへの申請

ECアプリをApp Storeに申請する際は、以下の点に特に注意が必要です:

  1. 外部決済の扱い: App StoreでのデジタルコンテンツはIn-App Purchaseが必須。物理商品の場合はStripeなど外部決済が許可されます。
  2. プライバシーポリシー: 決済情報を扱うため、必ずプライバシーポリシーのURL提出が必要です。
  3. カスタマーレビュー機能: 偽レビュー誘導はガイドライン違反のため、expo-store-reviewを使って適切なタイミングでのみレビュー依頼を表示すること。

アプリ公開の詳細な手順は App Store申請完全ガイド をご参照ください。

収益モデルの設計

ECアプリの収益化には複数のモデルがあります:

  • 直販型: 自社商品を直接販売(マージン最大)
  • マーケットプレイス型: 出品手数料を収益源にする(Stripe Connectを活用)
  • SaaS型: ECプラットフォームとして月額課金(Stripeサブスクリプション)

マーケットプレイス型で複数の出品者を管理する場合は Rorkサブスクリプション収益化ガイド も参考にしてください。

パフォーマンス最適化

ECアプリで最も重要なのは 画像の表示速度です。Supabase Storageに保存した商品画像は、Cloudflare Image Resizingと組み合わせることで、デバイスサイズに最適化された画像を配信できます。詳細は Rork + Cloudflare AIバックエンド構築 をご覧ください。


個人開発者の視点から(実体験メモ)

まとめ

本チュートリアルでは、Rork MaxとSupabase、Stripeを組み合わせてフルスタックECアプリをゼロから構築してきました。要点を整理します:

  • アーキテクチャ: Rork Max(フロントエンド)+ Supabase(バックエンド)+ Stripe(決済)の組み合わせが最もコスパが高い
  • 認証・RLS: Supabase Authと Row Level Securityで安全なデータアクセスを実装
  • 決済: Edge Function経由でPayment Intentを作成し、フロントエンドからconfirmPayment()を呼び出す
  • プッシュ通知: Expo Push Token をSupabaseに保存し、Webhookトリガーで送信
  • リアルタイム: Supabase Realtimeで在庫変更をリアルタイム反映

次のステップとして、売上ダッシュボードの実装やSEO対策(Next.jsへの移行)、AIを使った商品レコメンデーション機能の追加を検討してみましょう。個人開発でのECアプリ収益化については ソロ開発者のアプリビジネス戦略 も参考にしてください。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-03-19
Rork Max で作る本格学習アプリ(EdTech)実装ガイド:コース管理・進捗追跡・Stripe課金まで
Rork Max を使って本格的な学習アプリをゼロから構築する上級ガイド。Supabase でコース・進捗管理、Stripe で課金フロー、オフライン対応まで完全実装。5000文字超の実践チュートリアル。
開発ツール2026-04-12
Rork × Stripe Connect でマーケットプレイスアプリの決済を構築する
Rork と Stripe Connect を組み合わせ、出品者・購入者間の決済が動くマーケットプレイスアプリを構築する方法を体系的に解説。アカウント登録から収益分配・手数料設定まで完全網羅。
開発ツール2026-04-01
RorkでB2B SaaSアプリを構築する — マルチテナント設計・RBAC・Stripe組織課金の実装
RorkでB2B SaaSアプリを構築するための完全ガイド。マルチテナントアーキテクチャ、ロールベースアクセス制御(RBAC)、組織ごとのStripe課金、招待フローを実装する方法を詳細コード付きで解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →