mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
183fe0a93c
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
153 lines
4.5 KiB
TypeScript
153 lines
4.5 KiB
TypeScript
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.39.3";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers":
|
|
"authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
|
|
};
|
|
|
|
// Called after SetupIntent succeeds to save enrollment
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") {
|
|
return new Response(null, { headers: corsHeaders });
|
|
}
|
|
|
|
try {
|
|
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
|
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
|
|
|
const authHeader = req.headers.get("Authorization");
|
|
if (!authHeader) {
|
|
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
status: 401,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
const token = authHeader.replace("Bearer ", "");
|
|
const {
|
|
data: { user },
|
|
error: authError,
|
|
} = await supabase.auth.getUser(token);
|
|
if (authError || !user) {
|
|
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
status: 401,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
const body = await req.json();
|
|
const {
|
|
setup_intent_id,
|
|
association_id,
|
|
owner_id,
|
|
unit_id,
|
|
customer_id,
|
|
payment_method_type,
|
|
} = body;
|
|
|
|
if (!setup_intent_id || !association_id || !customer_id) {
|
|
return new Response(
|
|
JSON.stringify({ error: "Missing required fields" }),
|
|
{
|
|
status: 400,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
// Get Stripe mapping to retrieve the payment method from SetupIntent
|
|
const { data: mapping } = await supabase
|
|
.from("stripe_account_mappings")
|
|
.select("stripe_secret_key")
|
|
.eq("association_id", association_id)
|
|
.eq("is_active", true)
|
|
.maybeSingle();
|
|
|
|
if (!mapping?.stripe_secret_key) {
|
|
return new Response(
|
|
JSON.stringify({ error: "No Stripe configuration found" }),
|
|
{
|
|
status: 400,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
// Retrieve SetupIntent to get payment_method
|
|
const siRes = await fetch(
|
|
`https://api.stripe.com/v1/setup_intents/${setup_intent_id}`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${mapping.stripe_secret_key}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
const siData = await siRes.json();
|
|
if (!siRes.ok || siData.status !== "succeeded") {
|
|
return new Response(
|
|
JSON.stringify({
|
|
error: "SetupIntent not successful",
|
|
status: siData.status,
|
|
}),
|
|
{
|
|
status: 400,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
const paymentMethodId = siData.payment_method;
|
|
|
|
// Deactivate any existing enrollment for this user+association
|
|
await supabase
|
|
.from("autopay_enrollments")
|
|
.update({ is_active: false })
|
|
.eq("association_id", association_id)
|
|
.eq("enrolled_by", user.id)
|
|
.eq("is_active", true);
|
|
|
|
// Insert new enrollment
|
|
const { data: enrollment, error: insertError } = await supabase
|
|
.from("autopay_enrollments")
|
|
.insert({
|
|
association_id,
|
|
owner_id: owner_id || null,
|
|
unit_id: unit_id || null,
|
|
stripe_customer_id: customer_id,
|
|
stripe_payment_method_id: paymentMethodId,
|
|
payment_method_type: payment_method_type || "card",
|
|
is_active: true,
|
|
enrolled_by: user.id,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (insertError) {
|
|
console.error("Insert error:", insertError);
|
|
return new Response(
|
|
JSON.stringify({ error: "Failed to save enrollment" }),
|
|
{
|
|
status: 500,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
return new Response(JSON.stringify({ success: true, enrollment }), {
|
|
status: 200,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
});
|
|
} catch (err: unknown) {
|
|
console.error("Error in confirm-autopay:", err);
|
|
const message =
|
|
err instanceof Error ? err.message : "Internal server error";
|
|
return new Response(JSON.stringify({ error: message }), {
|
|
status: 500,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
});
|