Supabase第三方集成 #

一、Vercel集成 #

1.1 一键部署 #

text
Vercel集成步骤
├── 1. 访问 Vercel Marketplace
├── 2. 搜索 Supabase
├── 3. 点击 Add Integration
├── 4. 选择 Vercel 项目
├── 5. 选择 Supabase 项目
└── 6. 自动配置环境变量

1.2 手动配置 #

bash
# 在Vercel项目设置中添加环境变量
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

1.3 Vercel CLI #

bash
# 拉取环境变量
vercel env pull .env.local

# 添加环境变量
vercel env add NEXT_PUBLIC_SUPABASE_URL

二、Netlify集成 #

2.1 环境变量配置 #

bash
# netlify.toml
[build]
  command = "npm run build"
  publish = "dist"

[build.environment]
  NEXT_PUBLIC_SUPABASE_URL = "https://xxx.supabase.co"
  NEXT_PUBLIC_SUPABASE_ANON_KEY = "your-anon-key"

2.2 Netlify Functions #

javascript
// netlify/functions/hello.js
const { createClient } = require('@supabase/supabase-js')

exports.handler = async (event) => {
  const supabase = createClient(
    process.env.SUPABASE_URL,
    process.env.SUPABASE_SERVICE_ROLE_KEY
  )

  const { data } = await supabase.from('posts').select('*')

  return {
    statusCode: 200,
    body: JSON.stringify(data),
  }
}

三、Stripe集成 #

3.1 数据库表设计 #

sql
-- 订阅表
CREATE TABLE subscriptions (
    id BIGSERIAL PRIMARY KEY,
    user_id UUID REFERENCES auth.users(id) NOT NULL,
    stripe_customer_id TEXT UNIQUE,
    stripe_subscription_id TEXT UNIQUE,
    status TEXT NOT NULL,
    price_id TEXT,
    current_period_start TIMESTAMPTZ,
    current_period_end TIMESTAMPTZ,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 支付历史
CREATE TABLE payment_history (
    id BIGSERIAL PRIMARY KEY,
    user_id UUID REFERENCES auth.users(id) NOT NULL,
    stripe_payment_intent_id TEXT UNIQUE,
    amount INTEGER NOT NULL,
    currency TEXT NOT NULL,
    status TEXT NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

3.2 Webhook处理 #

typescript
// functions/stripe-webhook/index.ts
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

Deno.serve(async (req) => {
  const signature = req.headers.get('stripe-signature')
  const body = await req.text()

  // 验证Webhook签名
  // ...

  const event = JSON.parse(body)

  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )

  switch (event.type) {
    case 'customer.subscription.created':
    case 'customer.subscription.updated':
      const subscription = event.data.object
      await supabase.from('subscriptions').upsert({
        user_id: subscription.metadata.user_id,
        stripe_subscription_id: subscription.id,
        stripe_customer_id: subscription.customer,
        status: subscription.status,
        current_period_start: new Date(subscription.current_period_start * 1000),
        current_period_end: new Date(subscription.current_period_end * 1000),
      })
      break

    case 'customer.subscription.deleted':
      await supabase
        .from('subscriptions')
        .update({ status: 'canceled' })
        .eq('stripe_subscription_id', event.data.object.id)
      break
  }

  return new Response(JSON.stringify({ received: true }), {
    headers: { 'Content-Type': 'application/json' },
  })
})

3.3 创建订阅 #

typescript
// functions/create-checkout/index.ts
Deno.serve(async (req) => {
  const { priceId } = await req.json()
  const authHeader = req.headers.get('Authorization')

  // 获取用户
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_ANON_KEY')!,
    { global: { headers: { Authorization: authHeader! } } }
  )

  const { data: { user } } = await supabase.auth.getUser()

  // 创建Stripe Checkout Session
  const response = await fetch('https://api.stripe.com/v1/checkout/sessions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('STRIPE_SECRET_KEY')}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      'mode': 'subscription',
      'payment_method_types[]': 'card',
      'line_items[0][price]': priceId,
      'line_items[0][quantity]': '1',
      'success_url': `${Deno.env.get('SITE_URL')}/success`,
      'cancel_url': `${Deno.env.get('SITE_URL')}/cancel`,
      'metadata[user_id]': user!.id,
    }),
  })

  const session = await response.json()

  return new Response(JSON.stringify({ url: session.url }), {
    headers: { 'Content-Type': 'application/json' },
  })
})

四、GitHub集成 #

4.1 OAuth配置 #

text
GitHub OAuth设置
├── 1. GitHub Settings > Developer settings > OAuth Apps
├── 2. 创建新应用
├── 3. 设置回调URL
│   └── https://xxx.supabase.co/auth/v1/callback
└── 4. 获取Client ID和Secret

4.2 Supabase配置 #

text
Dashboard > Authentication > Providers > GitHub

配置
├── Enable GitHub provider
├── Client ID
├── Client Secret
└── Redirect URL

4.3 使用GitHub登录 #

typescript
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: {
    scopes: 'repo user',
  },
})

五、OpenAI集成 #

5.1 聊天功能 #

typescript
// functions/chat/index.ts
Deno.serve(async (req) => {
  const { messages } = await req.json()

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'gpt-4-turbo-preview',
      messages,
    }),
  })

  const data = await response.json()

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  })
})

5.2 向量嵌入 #

typescript
// functions/embed/index.ts
Deno.serve(async (req) => {
  const { text } = await req.json()

  const response = await fetch('https://api.openai.com/v1/embeddings', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'text-embedding-ada-002',
      input: text,
    }),
  })

  const data = await response.json()
  const embedding = data.data[0].embedding

  // 存储到Supabase
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )

  await supabase.from('documents').insert({
    content: text,
    embedding,
  })

  return new Response(JSON.stringify({ success: true }), {
    headers: { 'Content-Type': 'application/json' },
  })
})

六、Twilio集成 #

6.1 发送短信 #

typescript
// functions/send-sms/index.ts
Deno.serve(async (req) => {
  const { to, message } = await req.json()

  const accountSid = Deno.env.get('TWILIO_ACCOUNT_SID')
  const authToken = Deno.env.get('TWILIO_AUTH_TOKEN')
  const fromNumber = Deno.env.get('TWILIO_PHONE_NUMBER')

  const response = await fetch(
    `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Basic ${btoa(`${accountSid}:${authToken}`)}`,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        To: to,
        From: fromNumber!,
        Body: message,
      }),
    }
  )

  const data = await response.json()

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  })
})

七、Resend邮件集成 #

7.1 发送邮件 #

typescript
// functions/send-email/index.ts
Deno.serve(async (req) => {
  const { to, subject, html } = await req.json()

  const response = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('RESEND_API_KEY')}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: 'noreply@yourdomain.com',
      to,
      subject,
      html,
    }),
  })

  const data = await response.json()

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  })
})

八、总结 #

第三方集成要点:

服务 用途
Vercel 部署托管
Netlify 部署托管
Stripe 支付订阅
GitHub OAuth登录
OpenAI AI功能
Twilio 短信服务
Resend 邮件服务

下一步,让我们学习自托管部署!

最后更新:2026-03-28