From d9f3426903fe8497e2df1d2d3eb329e136137d07 Mon Sep 17 00:00:00 2001 From: red-sakai Date: Sat, 7 Feb 2026 22:21:07 +0800 Subject: [PATCH] feat: add payment method and GCash receipt handling to order processing --- frontend/app/admin/page.tsx | 43 ++++++++++- frontend/app/api/admin/orders/route.ts | 2 +- frontend/app/page.tsx | 102 ++++++++++++++++++++++++- frontend/supabase/schema.sql | 3 + 4 files changed, 145 insertions(+), 5 deletions(-) diff --git a/frontend/app/admin/page.tsx b/frontend/app/admin/page.tsx index e0619e2..c761ac7 100644 --- a/frontend/app/admin/page.tsx +++ b/frontend/app/admin/page.tsx @@ -11,6 +11,9 @@ type OrderItem = { email: string; phone: string; address: string; + paymentMethod: string; + gcashReference: string; + gcashReceiptUrl: string; item: string; size: string; quantity: number; @@ -74,6 +77,9 @@ export default function AdminPage() { email: string; phone: string; address: string; + payment_method: string; + gcash_reference: string; + gcash_receipt_url: string; item_name: string; size: string; quantity: number; @@ -91,6 +97,9 @@ export default function AdminPage() { email: order.email, phone: order.phone, address: order.address, + paymentMethod: order.payment_method, + gcashReference: order.gcash_reference, + gcashReceiptUrl: order.gcash_receipt_url, item: order.item_name, size: order.size, quantity: order.quantity, @@ -134,6 +143,9 @@ export default function AdminPage() { order.phone, order.item, order.size, + order.paymentMethod, + order.gcashReference, + order.gcashReceiptUrl, order.status, ].some((field) => field.toLowerCase().includes(normalized)); const matchesStatus = @@ -229,6 +241,9 @@ export default function AdminPage() { "Email", "Phone", "Address", + "Payment Method", + "GCash Reference", + "GCash Receipt", "Item", "Size", "Qty", @@ -244,6 +259,9 @@ export default function AdminPage() { order.email, order.phone, order.address, + order.paymentMethod, + order.gcashReference, + order.gcashReceiptUrl, order.item, order.size, String(order.quantity), @@ -491,12 +509,13 @@ export default function AdminPage() {
- +
+ @@ -528,6 +547,28 @@ export default function AdminPage() { {order.address} +
Order Customer AddressPayment Item Qty Price +
+ {order.paymentMethod} +
+
+ {order.gcashReference} +
+ {order.gcashReceiptUrl ? ( + + View Receipt + + ) : ( +
+ Missing receipt +
+ )} +
{order.item} diff --git a/frontend/app/api/admin/orders/route.ts b/frontend/app/api/admin/orders/route.ts index 6f3b00c..651b33c 100644 --- a/frontend/app/api/admin/orders/route.ts +++ b/frontend/app/api/admin/orders/route.ts @@ -37,7 +37,7 @@ export async function GET(request: NextRequest) { const { data, error } = await supabaseAdmin .from("orders") .select( - "id,full_name,email,phone,address,item_name,size,quantity,unit_price,line_total,status,created_at" + "id,full_name,email,phone,address,payment_method,gcash_reference,gcash_receipt_url,item_name,size,quantity,unit_price,line_total,status,created_at" ) .order("created_at", { ascending: false }); diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index a80d4dd..0f367a1 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -22,6 +22,9 @@ type CartItem = { quantity: number; }; +const GCASH_BUCKET = "gcash-receipts"; +const MAX_RECEIPT_SIZE = 5 * 1024 * 1024; + export default function Home() { const [merchItems, setMerchItems] = useState([]); const [merchLoading, setMerchLoading] = useState(true); @@ -196,19 +199,71 @@ export default function Home() { const email = String(formData.get("email") ?? "").trim(); const phone = String(formData.get("contactNumber") ?? "").trim(); const address = String(formData.get("address") ?? "").trim(); + const paymentMethod = String(formData.get("paymentMethod") ?? "gcash"); + const gcashReference = String( + formData.get("gcashReference") ?? "" + ).trim(); + const receiptFile = formData.get("gcashReceipt"); if (!fullName || !email || !phone || !address) { setSubmitError("Please complete all contact fields."); return; } + if (paymentMethod !== "gcash") { + setSubmitError("Select GCash as your payment method."); + return; + } + + if (!gcashReference) { + setSubmitError("Enter your GCash reference number."); + return; + } + + if (!(receiptFile instanceof File) || receiptFile.size === 0) { + setSubmitError("Upload your GCash receipt screenshot."); + return; + } + + if (!receiptFile.type.startsWith("image/")) { + setSubmitError("Receipt screenshot must be an image file."); + return; + } + + if (receiptFile.size > MAX_RECEIPT_SIZE) { + setSubmitError("Receipt image must be 5MB or smaller."); + return; + } + setSubmitting(true); + const fileExtension = + receiptFile.name.split(".").pop()?.toLowerCase() || "jpg"; + const uploadPath = `gcash/${crypto.randomUUID()}.${fileExtension}`; + const { error: uploadError } = await supabase.storage + .from(GCASH_BUCKET) + .upload(uploadPath, receiptFile, { + contentType: receiptFile.type, + }); + + if (uploadError) { + setSubmitError("Unable to upload receipt. Please try again."); + setSubmitting(false); + return; + } + + const receiptUrl = supabase.storage + .from(GCASH_BUCKET) + .getPublicUrl(uploadPath).data.publicUrl; + const orderRows = cartItems.map((item) => ({ full_name: fullName, email, phone, address, + payment_method: "gcash", + gcash_reference: gcashReference, + gcash_receipt_url: receiptUrl, item_id: item.itemId, item_name: item.name, size: item.size, @@ -229,7 +284,7 @@ export default function Home() { setCartItems([]); setQuantities((prev) => prev.map(() => 0)); form.reset(); - setSubmitSuccess("Order received! We'll email you with next steps."); + setSubmitSuccess("Order received! We'll verify your GCash receipt."); setSubmitting(false); }; return ( @@ -538,8 +593,8 @@ export default function Home() { Place Your Order

- Fill up the form below to reserve your merch. We will follow up - with payment details and size confirmation. + Fill up the form below and attach your GCash receipt to confirm + your merch order.

@@ -594,6 +649,47 @@ export default function Home() { /> +
+

+ Payment Method +

+ +

+ Upload your GCash receipt before confirming the order. +

+
+ + + + + {submitError ? (

{submitError} diff --git a/frontend/supabase/schema.sql b/frontend/supabase/schema.sql index f1cbe95..3357510 100644 --- a/frontend/supabase/schema.sql +++ b/frontend/supabase/schema.sql @@ -21,6 +21,9 @@ create table if not exists public.orders ( email text not null, phone text not null, address text not null, + payment_method text not null default 'gcash', + gcash_reference text not null, + gcash_receipt_url text not null, item_id uuid references public.merch_items(id), item_name text not null, size text not null,