Skip to content

Edge Functions Overview

This page catalogs the Supabase Edge Functions under supabase/functions/: what each one does, how it is triggered, and which flows depend on it.

All edge functions run in Deno with the service role key. They must never be called directly from untrusted clients without appropriate auth checks.


Billing & subscriptions (Stripe)

stripe-webhook

Path: supabase/functions/stripe-webhook/index.ts
Trigger: HTTPS webhook from Stripe
HTTP method: POST (Stripe webhooks)

Purpose: Central handler for Stripe events.

Responsibilities:

  1. Verify the stripe-signature header using STRIPE_WEBHOOK_SECRET.
  2. Enforce idempotency via the webhook_events_processed table.
  3. Resolve company_id via:
    • event.data.object.metadata.company_id
    • Subscription metadata
    • Checkout Session metadata
    • Existing subscriptions row
    • Customer metadata
  4. Handle key event types:
    • checkout.session.completed → create/activate subscription row in subscriptions.
    • customer.subscription.created / updated → keep subscription row in sync.
    • customer.subscription.deleted → mark subscription as canceled (with “upgrade cancellation” nuance).
    • invoice.payment_succeeded → insert into payment_history.
  5. Record outcome in webhook_events_processed with status and error info.

Inputs:

  • Raw Stripe webhook payload.
  • STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY env vars.

Outputs:

  • 2xx on success or already-processed events.
  • Error response on invalid signature or missing company_id.

Used by flows:


create-checkout-session

Path: supabase/functions/create-checkout-session/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Create a Stripe Checkout Session for subscription signup/upgrade.

Responsibilities:

  1. Authenticate the caller via Supabase JWT (Authorization: Bearer <token>).
  2. Use Supabase service role to:
    • Verify user and tenant.
    • Load company info from companies.
    • Check for existing stripe_customer_id in subscriptions, or create a new Stripe customer.
  3. Choose a Stripe Price ID based on plan_name using env vars:
    • STRIPE_STARTER_PRICE_ID
    • STRIPE_PROFESSIONAL_PRICE_ID
  4. Create a Stripe Checkout Session with:
    • Mode: subscription
    • Payment method collection: always (collect card at trial start).
    • Metadata: company_id, plan_name, source, feature, user_id, referral.
    • Success URL: /checkout/success (includes session_id).
    • Cancel URL: /checkout/cancel.
  5. Return session.url to the frontend.

Inputs (JSON body):

  • company_id (required)
  • plan_name (required, e.g. starter or professional)
  • Optional: source, feature, user_id, referral

Outputs:

  • { url: string } – Stripe Checkout URL.

Used by flows:


create-portal-session

Path: supabase/functions/create-portal-session/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Create a Stripe Billing Portal Session for managing subscriptions.

Responsibilities:

  1. Authenticate user via Supabase JWT.
  2. Look up the tenant’s current subscription in subscriptions.
  3. Ensure stripe_customer_id is known:
    • If missing, fetch subscription from Stripe and persist stripe_customer_id.
  4. Create a Billing Portal session with:
    • customer = stripe_customer_id.
    • return_url pointing back to the dashboard.
  5. Return portal url.

Inputs:

  • JSON body: { company_id: string }.

Outputs:

  • { url: string } – Stripe Billing Portal URL.

Used by flows:


get-stripe-subscription

Path: supabase/functions/get-stripe-subscription/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Fetch the current subscription from Stripe for accuracy.

Responsibilities:

  1. Authenticate user.
  2. Look up latest subscriptions row for company_id.
  3. Call Stripe subscriptions.retrieve(stripe_subscription_id).
  4. Map Stripe’s status and fields to an app-level object, including:
    • status (trial, active, past_due, canceled, expired).
    • Amount, currency, interval.
    • current_period_start, current_period_end.
    • trial_end, trial_days_remaining.

Inputs:

  • JSON body: { company_id: string }.

Outputs:

  • Subscription summary object returned as JSON.

Used by flows:

  • Billing UI to show accurate subscription state.

get-upcoming-invoice

Path: supabase/functions/get-upcoming-invoice/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Fetch upcoming invoice amounts from Stripe.

Responsibilities:

  1. Authenticate user.
  2. Look up stripe_subscription_id and stripe_customer_id for company_id.
  3. If subscription is in trial, return 0 due.
  4. Call stripe.invoices.retrieveUpcoming.
  5. Return:
    • amount_due (formatted as dollars).
    • currency.
    • next_payment_date.
    • is_trial flag.

Inputs:

  • JSON body: { company_id: string }.

Outputs:

  • { amount_due, currency, next_payment_date, is_trial }.

Used by flows:

  • Billing UI for “next payment” info.

get-customer-invoices

Path: supabase/functions/get-customer-invoices/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Fetch historical invoices from Stripe for a given customer.

Responsibilities:

  1. Authenticate user.
  2. Accept stripe_customer_id and optional status filter.
  3. Call stripe.invoices.list({ customer, status?, limit }).
  4. Map Stripe invoices to a simple object:
    • amount, currency
    • status (normalized)
    • invoice_url
    • paid_at, created_at
    • invoice_number

Inputs:

  • JSON body: { stripe_customer_id: string, status?: string }.

Outputs:

  • { invoices: [...], total }.

Used by flows:

  • Billing UI, owner analytics, support tools.

reactivate-subscription

Path: supabase/functions/reactivate-subscription/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Help resubscribe tenants whose subscriptions were canceled or expired.

Responsibilities:

  1. Authenticate user.
  2. Look up most recent canceled/expired subscription for company_id.
  3. Determine plan, trial status, and whether they are still in a paid period.
  4. Create a Stripe Checkout Session for resubscribe, with metadata marking it as is_resubscribe.
  5. Handle remaining trial days or active paid period:
    • If still in paid period, schedule new subscription start after current_period_end.
    • If in trial and trial not expired, maintain remaining trial days.

Inputs:

  • JSON body: { company_id: string }.

Outputs:

  • { url: string } – resubscribe checkout URL.

Used by flows:

  • Future “reactivate subscription” buttons in billing UI.

sync-stripe-subscriptions

Path: supabase/functions/sync-stripe-subscriptions/index.ts
Trigger: Internal (cron) via HTTP with secret or service role
HTTP methods: POST

Purpose: Daily batch sync to keep subscriptions in line with Stripe.

Responsibilities:

  1. Authenticate using CRON_SECRET or service role token.
  2. Fetch active/trial/past_due subscriptions from Supabase.
  3. For each, call stripe.subscriptions.retrieve.
  4. Update:
    • status
    • amount, currency, interval
    • current_period_start, current_period_end
    • cancel_at_period_end, canceled_at
    • trial_days_remaining
    • stripe_customer_id (ensuring it is set)

Used by flows:

  • Background data hygiene; supports all billing-related views.

sync-payment-history

Path: supabase/functions/sync-payment-history/index.ts
Trigger: HTTPS call from authenticated frontend or internal task
HTTP methods: OPTIONS, POST

Purpose: Backfill or repair payment_history from Stripe.

Responsibilities:

  1. Authenticate user.
  2. Accept company_id and stripe_customer_id.
  3. Call stripe.invoices.list({ customer, status: 'paid' }).
  4. For each invoice:
    • Skip if payment_history already has stripe_invoice_id.
    • Insert a new row with amount, currency, status, invoice_url, paid_at, created_at.

Used by flows:

  • Operations runbooks and support tools.

upgrade-checkout-session

Path: supabase/functions/upgrade-checkout-session/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Change plan (upgrade/downgrade) for an existing subscription by updating Stripe subscription items (no new checkout).

Responsibilities:

  1. Authenticate user.
  2. Look up current subscription for company_id.
  3. Validate plan change using PLAN_HIERARCHY:
    • starterprofessional (upgrade) allowed.
    • professionalstarter (downgrade) allowed.
  4. Retrieve Stripe subscription.
  5. Update subscription’s price to target plan price ID.
  6. Respect proration and trial/downgrade behavior as implemented.

Inputs:

  • JSON body: { company_id: string, plan_name: 'starter' | 'professional' | 'enterprise', source?, feature?, user_id?, referral? }.

Outputs:

  • Updated subscription summary.

Used by flows:

  • Future “change plan” actions from billing settings.

Nameplates & affiliates

generate-nameplate

Path: supabase/functions/generate-nameplate/index.ts
Trigger: HTTPS call from authenticated frontend
HTTP methods: OPTIONS, POST

Purpose: Generate AI-powered nameplate designs and store them.

Responsibilities:

  1. Authenticate user via Supabase JWT.
  2. Resolve tenant_id from user_profiles.
  3. Enforce monthly usage limits via nameplate_usage (default: 20 per user per month).
  4. Accept parameters:
    • name, metalType, purity, withDiamonds, font.
  5. Call OpenAI image API (DALL·E) with a generated prompt.
  6. Store image in Supabase Storage (nameplates bucket).
  7. Insert nameplates and nameplate_usage rows.
  8. Return image URL and metadata.

Inputs:

  • JSON body: { name, metalType, purity?, withDiamonds, font }.

Outputs:

  • { imageUrl, nameplateId, usageCount, usageLimit } (shape inferred; see function code).

Used by flows:

  • Design studio / nameplate generation (surface in Library and design flows).

submit-affiliate-application

Path: supabase/functions/submit-affiliate-application/index.ts
Trigger: Public HTTPS call from marketing/affiliate landing
HTTP methods: OPTIONS, POST

Purpose: Securely collect affiliate applications with rate limiting and duplication protections.

Responsibilities:

  1. Enforce request size limit.
  2. Derive client IP from headers.
  3. Rate-limit via affiliate_rate_limits table (e.g., 5 submissions/hour per IP; block for 24h on abuse).
  4. Validate and sanitize fields:
    • Name, email, phone, website, business info.
  5. Prevent duplicate submissions using a submission_hash (hash of email+IP).
  6. Insert into affiliate_applications with sanitized data.

Inputs:

  • JSON body with affiliate form fields.

Outputs:

  • { success: true } on accepted submission.

Used by flows:

  • Affiliate signup from marketing site.

Summary

These edge functions provide the “backend glue” between the Vite frontend, Supabase, and external services like Stripe and OpenAI.

  • Stripe-related functions handle subscriptions, billing, webhooks, and historical sync.
  • Nameplate generation uses OpenAI + Storage to provide design assets.
  • Affiliate submission safely handles public form data.

For table-level details referenced above, see: