Fix: Handle consulting orders properly in handle-order-paid edge function
Critical bug fix: Consulting orders were not being processed after payment because the function checked order_items for consulting products, but consulting orders don't have order_items - they have consulting_slots instead. Changes: - Fetch consulting_slots along with order_items in the query - Check for consulting_slots.length > 0 to detect consulting orders - Update consulting_slots status from 'pending_payment' to 'confirmed' - Create Google Meet events for each consulting slot - Send consulting_scheduled notification This fixes the issue where: - Consulting slots stayed in 'pending_payment' status after payment - No meet links were generated - No access was granted - Schedules didn't show up in admin or member dashboard
This commit is contained in:
@@ -29,7 +29,7 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
||||||
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
||||||
|
|
||||||
// Get full order details with items
|
// Get full order details with items AND consulting slots
|
||||||
const { data: order, error: orderError } = await supabase
|
const { data: order, error: orderError } = await supabase
|
||||||
.from("orders")
|
.from("orders")
|
||||||
.select(`
|
.select(`
|
||||||
@@ -38,6 +38,13 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
order_items (
|
order_items (
|
||||||
product_id,
|
product_id,
|
||||||
product:products (title, type)
|
product:products (title, type)
|
||||||
|
),
|
||||||
|
consulting_slots (
|
||||||
|
id,
|
||||||
|
date,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
status
|
||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
.eq("id", order_id)
|
.eq("id", order_id)
|
||||||
@@ -58,30 +65,33 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
product: { title: string; type: string };
|
product: { title: string; type: string };
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
// Check if this is a consulting order
|
// Check if this is a consulting order by checking consulting_slots
|
||||||
const hasConsulting = orderItems.some(item => item.product.type === "consulting");
|
const consultingSlots = order.consulting_slots as Array<{
|
||||||
|
id: string;
|
||||||
|
date: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
status: string;
|
||||||
|
meet_link?: string;
|
||||||
|
}>;
|
||||||
|
const isConsultingOrder = consultingSlots && consultingSlots.length > 0;
|
||||||
|
|
||||||
if (hasConsulting) {
|
if (isConsultingOrder) {
|
||||||
console.log("[HANDLE-PAID] Consulting order detected, processing slots");
|
console.log("[HANDLE-PAID] Consulting order detected, processing slots");
|
||||||
|
|
||||||
// Update consulting slots status
|
// Update consulting slots status from pending_payment to confirmed
|
||||||
await supabase
|
await supabase
|
||||||
.from("consulting_slots")
|
.from("consulting_slots")
|
||||||
.update({ status: "confirmed" })
|
.update({ status: "confirmed" })
|
||||||
.eq("order_id", order_id);
|
.eq("order_id", order_id)
|
||||||
|
.in("status", ["pending_payment"]);
|
||||||
// Create Google Meet events for each slot
|
|
||||||
const { data: consultingSlots } = await supabase
|
|
||||||
.from("consulting_slots")
|
|
||||||
.select("*")
|
|
||||||
.eq("order_id", order_id);
|
|
||||||
|
|
||||||
if (consultingSlots && consultingSlots.length > 0) {
|
if (consultingSlots && consultingSlots.length > 0) {
|
||||||
for (const slot of consultingSlots) {
|
for (const slot of consultingSlots) {
|
||||||
try {
|
try {
|
||||||
console.log("[HANDLE-PAID] Creating Google Meet for slot:", slot.id);
|
console.log("[HANDLE-PAID] Creating Google Meet for slot:", slot.id);
|
||||||
|
|
||||||
const topic = orderItems.find(i => i.product.type === "consulting")?.product.title || "Konsultasi 1-on-1";
|
const topic = "Konsultasi 1-on-1";
|
||||||
|
|
||||||
const meetResponse = await fetch(
|
const meetResponse = await fetch(
|
||||||
`${supabaseUrl}/functions/v1/create-google-meet-event`,
|
`${supabaseUrl}/functions/v1/create-google-meet-event`,
|
||||||
@@ -115,40 +125,23 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh slots to get meet_link
|
// Send consulting notification with the consultingSlots data
|
||||||
const { data: updatedSlots } = await supabase
|
await sendNotification(supabase, "consulting_scheduled", {
|
||||||
.from("consulting_slots")
|
|
||||||
.select("*")
|
|
||||||
.eq("order_id", order_id);
|
|
||||||
|
|
||||||
const slots = (updatedSlots || []) as Array<{
|
|
||||||
date: string;
|
|
||||||
start_time: string;
|
|
||||||
meet_link?: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
// Send consulting notification
|
|
||||||
await sendNotification(supabase, "consulting_scheduled", userEmail, {
|
|
||||||
nama: userName,
|
nama: userName,
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
order_id: order_id.substring(0, 8),
|
order_id: order_id.substring(0, 8),
|
||||||
tanggal_pesanan: new Date().toLocaleDateString("id-ID"),
|
tanggal_pesanan: new Date().toLocaleDateString("id-ID"),
|
||||||
total: `Rp ${order.total_amount.toLocaleString("id-ID")}`,
|
total: `Rp ${order.total_amount.toLocaleString("id-ID")}`,
|
||||||
metode_pembayaran: order.payment_method || "Unknown",
|
metode_pembayaran: order.payment_method || "Unknown",
|
||||||
tanggal_konsultasi: slots[0]?.date || "",
|
tanggal_konsultasi: consultingSlots[0]?.date || "",
|
||||||
jam_konsultasi: slots.map(s => s.start_time.substring(0, 5)).join(", "),
|
jam_konsultasi: consultingSlots.map(s => s.start_time.substring(0, 5)).join(", "),
|
||||||
link_meet: slots[0]?.meet_link || "Akan dikirim terpisah",
|
link_meet: consultingSlots[0]?.meet_link || "Akan dikirim terpisah",
|
||||||
}, {
|
|
||||||
event: "consulting_scheduled",
|
event: "consulting_scheduled",
|
||||||
order_id,
|
order_id,
|
||||||
user_id: order.user_id,
|
user_id: order.user_id,
|
||||||
user_email: userEmail,
|
|
||||||
user_name: userName,
|
user_name: userName,
|
||||||
total_amount: order.total_amount,
|
slots: consultingSlots,
|
||||||
payment_method: order.payment_method,
|
|
||||||
slots: updatedSlots,
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Regular product order - grant access
|
// Regular product order - grant access
|
||||||
console.log("[HANDLE-PAID] Regular product order, granting access");
|
console.log("[HANDLE-PAID] Regular product order, granting access");
|
||||||
@@ -176,7 +169,7 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
const productTitles = orderItems.map(i => i.product.title);
|
const productTitles = orderItems.map(i => i.product.title);
|
||||||
|
|
||||||
// Send payment success notification
|
// Send payment success notification
|
||||||
await sendNotification(supabase, "payment_success", userEmail, {
|
await sendNotification(supabase, "payment_success", {
|
||||||
nama: userName,
|
nama: userName,
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
order_id: order_id.substring(0, 8),
|
order_id: order_id.substring(0, 8),
|
||||||
@@ -185,26 +178,21 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
metode_pembayaran: order.payment_method || "Unknown",
|
metode_pembayaran: order.payment_method || "Unknown",
|
||||||
produk: productTitles.join(", "),
|
produk: productTitles.join(", "),
|
||||||
link_akses: `${Deno.env.get("SITE_URL") || ""}/access`,
|
link_akses: `${Deno.env.get("SITE_URL") || ""}/access`,
|
||||||
}, {
|
|
||||||
event: "payment_success",
|
event: "payment_success",
|
||||||
order_id,
|
order_id,
|
||||||
user_id: order.user_id,
|
user_id: order.user_id,
|
||||||
user_email: userEmail,
|
|
||||||
user_name: userName,
|
user_name: userName,
|
||||||
total_amount: order.total_amount,
|
|
||||||
payment_method: order.payment_method,
|
|
||||||
products: productTitles,
|
products: productTitles,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send access granted notification
|
// Send access granted notification
|
||||||
await sendNotification(supabase, "access_granted", userEmail, {
|
await sendNotification(supabase, "access_granted", {
|
||||||
nama: userName,
|
nama: userName,
|
||||||
|
email: userEmail,
|
||||||
produk: productTitles.join(", "),
|
produk: productTitles.join(", "),
|
||||||
}, {
|
|
||||||
event: "access_granted",
|
event: "access_granted",
|
||||||
order_id,
|
order_id,
|
||||||
user_id: order.user_id,
|
user_id: order.user_id,
|
||||||
user_email: userEmail,
|
|
||||||
user_name: userName,
|
user_name: userName,
|
||||||
products: productTitles,
|
products: productTitles,
|
||||||
});
|
});
|
||||||
@@ -231,8 +219,7 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
async function sendNotification(
|
async function sendNotification(
|
||||||
supabase: any,
|
supabase: any,
|
||||||
templateKey: string,
|
templateKey: string,
|
||||||
shortcodeData: Record<string, string>,
|
data: Record<string, any>
|
||||||
webhookPayload: Record<string, unknown>
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log("[HANDLE-PAID] Sending notification:", templateKey);
|
console.log("[HANDLE-PAID] Sending notification:", templateKey);
|
||||||
|
|
||||||
@@ -254,7 +241,7 @@ async function sendNotification(
|
|||||||
await fetch(template.webhook_url, {
|
await fetch(template.webhook_url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify(webhookPayload),
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
console.log("[HANDLE-PAID] Webhook sent to:", template.webhook_url);
|
console.log("[HANDLE-PAID] Webhook sent to:", template.webhook_url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -276,10 +263,10 @@ async function sendNotification(
|
|||||||
"Authorization": `Bearer ${Deno.env.get("SUPABASE_ANON_KEY")}`,
|
"Authorization": `Bearer ${Deno.env.get("SUPABASE_ANON_KEY")}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
to: shortcodeData.email,
|
to: data.email,
|
||||||
subject: template.email_subject,
|
subject: template.email_subject,
|
||||||
html: template.email_body_html,
|
html: template.email_body_html,
|
||||||
shortcodeData,
|
shortcodeData: data,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user