Skip to main content

Overview

The create-checkout-session edge function creates a Stripe checkout session for organization owners to purchase event or attendee tokens. It handles dynamic pricing based on quantity tiers and integrates with Stripe for payment processing.

Endpoint

POST /functions/v1/create-checkout-session

Authentication

Requires a valid Bearer token. The authenticated user must be an organization owner.
Authorization: Bearer <supabase_access_token>

Request Body

token_type
string
required
Type of tokens to purchase. Must be either "event" or "attendee".
quantity
number
required
Number of tokens to purchase. Must be at least 1 and match available pricing tiers.

Example Request

curl -X POST 'https://<project-ref>.supabase.co/functions/v1/create-checkout-session' \
  -H 'Authorization: Bearer <user_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "token_type": "event",
    "quantity": 10
  }'

Response

Success Response (200 OK)

url
string
Stripe checkout session URL where the user should be redirected to complete payment
{
  "url": "https://checkout.stripe.com/c/pay/cs_test_..."
}

Error Responses

400 Bad Request

Returned when request parameters are invalid.
{
  "error": "Invalid token_type or quantity"
}
{
  "error": "token_type must be 'event' or 'attendee'"
}
{
  "error": "No pricing tier for quantity 1000"
}

401 Unauthorized

Returned when authentication fails.
{
  "error": "Unauthorized"
}

404 Not Found

Returned when the user’s organization is not found.
{
  "error": "Organization not found"
}

500 Internal Server Error

Returned when checkout session creation fails.
{
  "error": "Payment session creation failed"
}

Pricing Tiers

The function dynamically calculates pricing based on the token_pricing table. It finds the appropriate tier based on min_quantity and max_quantity for the requested token type. From source/supabase/functions/create-checkout-session/index.ts:54-69:
const { data: tiers } = await adminClient
  .from("token_pricing")
  .select("*")
  .eq("type", token_type)
  .order("min_quantity");

const tier = tiers.find(
  (t: any) => quantity >= t.min_quantity && quantity <= t.max_quantity
);

if (!tier) throw new Error(`No pricing tier for quantity ${quantity}`);

const unitAmountCents = Math.round(Number(tier.price_per_unit) * 100);
const totalAmount = unitAmountCents * quantity;

Stripe Integration

Checkout Session Creation

From source/supabase/functions/create-checkout-session/index.ts:89-116:
const session = await stripe.checkout.sessions.create({
  customer: customerId,
  customer_email: customerId ? undefined : email,
  line_items: [
    {
      price_data: {
        currency: tier.currency.toLowerCase(),
        product_data: {
          name: `${token_type === "event" ? "Event" : "Attendee"} Tokens (x${quantity})`,
          description: `${quantity} ${token_type} tokens for ${org.name}`,
        },
        unit_amount: unitAmountCents,
      },
      quantity,
    },
  ],
  mode: "payment",
  success_url: `${req.headers.get("origin")}/purchase?status=success`,
  cancel_url: `${req.headers.get("origin")}/purchase?status=cancelled`,
  metadata: {
    organization_id: org.id,
    token_type,
    quantity: String(quantity),
    price_per_unit: String(tier.price_per_unit),
    total_amount: String(totalAmount / 100),
    user_id: userId,
  },
});

Transaction Tracking

The function creates a pending transaction record that will be completed by the stripe-webhook function:
await adminClient.from("token_transactions").insert({
  organization_id: org.id,
  type: token_type,
  quantity,
  amount: totalAmount / 100,
  currency: tier.currency,
  status: "pending",
  stripe_session_id: session.id,
  payment_method: "stripe",
});

Flow

  1. Client requests checkout session with token type and quantity
  2. Function verifies user owns an organization
  3. Function retrieves pricing tier for requested quantity
  4. Function calculates total amount based on tier pricing
  5. Function creates or retrieves Stripe customer
  6. Function creates Stripe checkout session with metadata
  7. Function creates pending transaction record
  8. Client redirects user to Stripe checkout URL
  9. After payment, Stripe webhook completes the transaction (see stripe-webhook)
The pending transaction must be completed by the Stripe webhook. Ensure your STRIPE_WEBHOOK_SECRET is properly configured.

Build docs developers (and LLMs) love