0% found this document useful (0 votes)
6 views

Create Checkout Function

This document outlines a server implementation using Deno that handles Stripe checkout sessions, including user authentication through Supabase. It manages CORS, retrieves pricing information, and creates customer profiles in Stripe, while logging each step for debugging. The server responds with session details or error messages based on the checkout process outcome.

Uploaded by

Ismail Hussain
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Create Checkout Function

This document outlines a server implementation using Deno that handles Stripe checkout sessions, including user authentication through Supabase. It manages CORS, retrieves pricing information, and creates customer profiles in Stripe, while logging each step for debugging. The server responds with session details or error messages based on the checkout process outcome.

Uploaded by

Ismail Hussain
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 5

import { serve } from "https://deno.land/[email protected]/http/server.

ts";
import { createClient } from "https://esm.sh/@supabase/[email protected]";
import Stripe from "https://esm.sh/[email protected]";

const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-
type",
};

// Helper function for logging


const logStep = (step: string, details?: any) => {
const detailsStr = details ? ` - ${JSON.stringify(details)}` : '';
console.log(`[CREATE-CHECKOUT] ${step}${detailsStr}`);
};

serve(async (req) => {


// Handle CORS preflight requests
if (req.method === "OPTIONS") {
return new Response(null, { headers: corsHeaders });
}

try {
logStep("Function started");

// Get Stripe key


const stripeKey = Deno.env.get("STRIPE_SECRET_KEY");
if (!stripeKey) {
throw new Error("STRIPE_SECRET_KEY is not configured");
}
logStep("STRIPE_SECRET_KEY found");

// Initialize Stripe
const stripe = new Stripe(stripeKey, {
apiVersion: "2023-10-16",
});
logStep("Stripe initialized");

// Test Stripe connection


const customerCount = await stripe.customers.list({ limit: 1 });
logStep("Stripe connection test successful", { customerCount:
customerCount.data.length });

// Get request body


let requestData;
try {
requestData = await req.json();
logStep("Request body parsed", requestData);
} catch (parseError) {
logStep("Error parsing request body", parseError);
throw new Error("Invalid request format");
}

const { priceId, paymentType = "subscription", successRedirectUrl,


cancelRedirectUrl } = requestData;

if (!priceId) {
throw new Error("Price ID is required");
}

// Verify price exists


const price = await stripe.prices.retrieve(priceId);
if (!price || !price.active) {
throw new Error(`Invalid or inactive price: ${priceId}`);
}
logStep("Price found in Stripe", { priceId, active: price.active, currency:
price.currency, productId: price.product });

// Initialize Supabase client to get user info


const supabaseUrl = Deno.env.get("SUPABASE_URL");
const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY");

if (!supabaseUrl || !supabaseAnonKey) {
throw new Error("Supabase environment variables not configured");
}

const supabase = createClient(supabaseUrl, supabaseAnonKey, {


global: {
headers: { Authorization: req.headers.get("Authorization") || "" },
},
});

// Get the user from the request


const { data: { user }, error: userError } = await supabase.auth.getUser();

if (userError) {
logStep("Error getting user", userError);
}

// Set up redirect URLs


const origin = req.headers.get("origin");
logStep("Using request origin", { origin });

const productionDomain = Deno.env.get("APP_DOMAIN") ||


"https://crisiscraft.org";
const baseUrl = origin || productionDomain;
logStep("Using origin for redirect URLs", { origin });

const successUrl = `${baseUrl}/payment-success?


session_id={CHECKOUT_SESSION_ID}`;
const cancelUrl = cancelRedirectUrl || `${baseUrl}/pricing`;
logStep("Redirect URLs", { successUrl, cancelUrl });

// Build session config with improved identification


const sessionConfig: any = {
line_items: [
{
price: priceId,
quantity: 1,
},
],
mode: paymentType === "subscription" ? "subscription" : "payment",
success_url: successUrl,
cancel_url: cancelUrl,
billing_address_collection: "auto",
allow_promotion_codes: true,
};
// Always create a new customer or use existing one for authenticated users
if (user) {
logStep("Processing checkout for authenticated user", { userId: user.id,
email: user.email });

// ALWAYS set client_reference_id for reliable identification


sessionConfig.client_reference_id = user.id;

// Add metadata with user_id as requested


sessionConfig.metadata = {
userId: user.id,
userEmail: user.email,
paymentType: paymentType,
priceId: priceId // Store priceId in metadata as fallback
};

// Initialize admin client to check/create Stripe customer


const supabaseAdmin = createClient(
supabaseUrl,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || '',
{ auth: { persistSession: false } }
);

// Check if user already has a Stripe customer ID


const { data: userData, error: userDataError } = await supabaseAdmin
.from("user_profiles")
.select("stripe_customer_id, email")
.eq("id", user.id)
.maybeSingle();

let stripeCustomerId = null;

if (userDataError) {
logStep("Error fetching user data", userDataError);
} else if (userData?.stripe_customer_id) {
// Use existing Stripe customer ID if available
stripeCustomerId = userData.stripe_customer_id;
logStep("Using existing Stripe customer ID", { customerId: stripeCustomerId
});
sessionConfig.customer = stripeCustomerId;
} else {
// Create a new customer in Stripe
const email = userData?.email || user.email;

try {
const customerData = {
email,
metadata: {
user_id: user.id
}
};

logStep("Creating new Stripe customer", customerData);


const customer = await stripe.customers.create(customerData);
stripeCustomerId = customer.id;

// Store the new customer ID in user profile


const { error: updateError } = await supabaseAdmin
.from("user_profiles")
.update({ stripe_customer_id: stripeCustomerId })
.eq("id", user.id);

if (updateError) {
logStep("Error storing customer ID in profile", updateError);
} else {
logStep("Stored new customer ID in user profile", { customerId:
stripeCustomerId });
}

// Use the new customer ID in the checkout session


sessionConfig.customer = stripeCustomerId;
} catch (customerError) {
logStep("Error creating Stripe customer", customerError);
// Fall back to email if customer creation fails
sessionConfig.customer_email = email;
}
}

// Save intent to database


try {
const { error: insertError } = await supabase.from('payments').insert({
user_id: user.id,
amount: null, // Will be updated after payment
payment_status: 'pending',
payment_type: paymentType,
price_id: priceId,
created_at: new Date().toISOString(),
});

if (insertError) {
logStep("Error creating payment record", insertError);
// Continue anyway - non-critical
}
} catch (dbError) {
logStep("Error creating payment record", dbError);
// Continue anyway - non-critical
}
} else {
// For guest checkouts with no authentication
logStep("Proceeding with guest checkout (no userId provided)");

// For guest checkouts, still add metadata for tracking purposes


sessionConfig.metadata = {
guestCheckout: "true",
paymentType: paymentType,
priceId: priceId // Store priceId in metadata as fallback
};

// Use email directly for guest checkouts if provided


if (requestData.email) {
sessionConfig.customer_email = requestData.email;
}
}

// Create checkout session


logStep("Creating checkout session with config", sessionConfig);
const session = await stripe.checkout.sessions.create(sessionConfig);
// Extract relevant data for response
const responseData = {
sessionId: session.id,
url: session.url,
status: session.status,
customerEmail: session.customer_email,
customerId: typeof session.customer === 'string' ? session.customer : null,
clientReferenceId: session.client_reference_id
};

logStep("Checkout session created", responseData);

return new Response(


JSON.stringify(responseData),
{
headers: { ...corsHeaders, "Content-Type": "application/json" },
status: 200,
}
);
} catch (error: any) {
console.error("Error in create-checkout:", error);

const errorMessage = error instanceof Error ? error.message : "Unknown error


occurred";

return new Response(


JSON.stringify({ error: errorMessage }),
{
headers: { ...corsHeaders, "Content-Type": "application/json" },
status: 500,
}
);
}
});

You might also like