/** * BytesBuys.com - Pro Worker V2.16.8 * 1. EMAILS: Optimized Confirmation Email - Removed labels (Recipient, Company, Address, Phone). * 2. EMAILS: Moved Phone Number directly under the Recipient name. * 3. EMAILS: Preserved "Instructions:" label as requested. * 4. UI: Updated version to V2.16.8 in Dispatch Tool. * 5. LOGIC: 100% Locked Metadata & Inventory & Latest GAS URL. */ const GITHUB_OWNER = "Lemon-bh"; const GITHUB_REPO = "my-store"; const GITHUB_PATH = "products.json"; const PICKING_EMAIL = "liuxin642@gmail.com"; const ORDER_SHEET_URL = "https://script.google.com/macros/s/AKfycbxeHScvU1o5biG_BooXXk2qixp92d6oEckt4fd75LNHONcmOHye9ByCxHR4Khz_Gqj8_g/exec"; const SHEET_SECRET = "BytesBuys_2026_Secure"; export default { async fetch(request, env) { const url = new URL(request.url); const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, GET, OPTIONS", "Access-Control-Allow-Headers": "Content-Type" }; if (request.method === "OPTIONS") return new Response(null, { headers: corsHeaders }); if (url.pathname === "/scan") return new Response(SCANNER_HTML, { headers: { "Content-Type": "text/html;charset=UTF-8" } }); if (url.pathname === "/api/dispatch" && request.method === "POST") { try { const { order_id, tracking } = await request.json(); const gasRes = await fetch(ORDER_SHEET_URL, { method: "POST", body: JSON.stringify({ secret: SHEET_SECRET, action: "scan_ship", order_id, tracking }) }); const gasData = await gasRes.json(); if (gasData.status === "success") { await sendShippedResend(gasData, env); return new Response(JSON.stringify({ status: "success" }), { headers: corsHeaders }); } return new Response(JSON.stringify({ status: "error", msg: gasData.msg }), { headers: corsHeaders }); } catch (e) { return new Response(JSON.stringify({ status: "error", msg: e.message }), { headers: corsHeaders }); } } if (url.pathname === "/create-session" && request.method === "POST") { try { const { items, customer } = await request.json(); const ghRes = await fetch(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${GITHUB_PATH}?t=${Date.now()}`, { headers: { "Authorization": `token ${env.GITHUB_TOKEN}`, "User-Agent": "Cloudflare-Worker" } }); const fileData = await ghRes.json(); const products = JSON.parse(decodeURIComponent(escape(atob(fileData.content.replace(/\s/g, ""))))); const params = new URLSearchParams(); params.append("mode", "payment"); params.append("success_url", `https://bytesbuys.com/success.html?oid=${customer.order_id}&em=${encodeURIComponent(customer.email)}`); params.append("cancel_url", "https://bytesbuys.com/cancel.html"); params.append("metadata[order_id]", customer.order_id); params.append("metadata[customer_name]", customer.name); params.append("metadata[customer_phone]", customer.phone); params.append("metadata[customer_company]", customer.company || "N/A"); params.append("metadata[customer_street]", customer.street); params.append("metadata[customer_street_2]", customer.street_2 || "N/A"); params.append("metadata[customer_suburb]", customer.suburb); params.append("metadata[customer_state]", customer.state); params.append("metadata[customer_postcode]", customer.postcode); params.append("metadata[instructions]", customer.instructions || "None"); params.append("payment_intent_data[description]", `Order Reference: #${customer.order_id}`); items.forEach((item, i) => { const p = products.find(x => String(x.id) === String(item.id)); if (p) { params.append(`line_items[${i}][price_data][currency]`, "aud"); params.append(`line_items[${i}][price_data][unit_amount]`, Math.round(p.price * 100)); params.append(`line_items[${i}][price_data][product_data][name]`, p.name); params.append(`line_items[${i}][price_data][product_data][metadata][loc]`, p.location || "N/A"); params.append(`line_items[${i}][price_data][product_data][metadata][sku]`, p.sku || "N/A"); params.append(`line_items[${i}][quantity]`, item.qty); } }); params.append("shipping_options[0][shipping_rate_data][display_name]", "Standard Delivery"); params.append("shipping_options[0][shipping_rate_data][type]", "fixed_amount"); params.append("shipping_options[0][shipping_rate_data][fixed_amount][amount]", "0"); params.append("shipping_options[0][shipping_rate_data][fixed_amount][currency]", "aud"); const sessionRes = await fetch("https://api.stripe.com/v1/checkout/sessions", { method: "POST", headers: { "Authorization": `Bearer ${env.STRIPE_SECRET_KEY}`, "Content-Type": "application/x-www-form-urlencoded" }, body: params }); const session = await sessionRes.json(); return new Response(JSON.stringify({ url: session.url }), { headers: corsHeaders }); } catch (e) { return new Response(JSON.stringify({ error: e.message }), { status: 500, headers: corsHeaders }); } } if (url.pathname === "/webhook" && request.method === "POST") { try { const body = await request.text(); const event = JSON.parse(body); if (event.type === "checkout.session.completed") { const session = event.data.object; const lineItemsRes = await fetch(`https://api.stripe.com/v1/checkout/sessions/${session.id}/line_items?expand[]=data.price.product`, { headers: { "Authorization": `Bearer ${env.STRIPE_SECRET_KEY}` } }); const lineItems = await lineItemsRes.json(); await updateInventory(lineItems.data, env); await syncOrderToSheet(session, lineItems.data); await sendPickingEmail(session, lineItems.data, env); await sendCustomerConfirmEmail(session, lineItems.data, env); } return new Response("OK", { status: 200 }); } catch (e) { return new Response("Webhook Error", { status: 500 }); } } return new Response(JSON.stringify({ status: "Active" }), { status: 200 }); } }; /** 🥇 数据同步逻辑 (BB_Orders) */ async function syncOrderToSheet(session, items) { const itemSummary = items.map(i => `${i.description} [LOC:${i.price.product.metadata?.loc || "N/A"}][SKU:${i.price.product.metadata?.sku || "N/A"}] (x${i.quantity})`).join(", "); const payload = { secret: SHEET_SECRET, action: "sync_order", order_id: session.metadata?.order_id, name: session.metadata?.customer_name, email: session.customer_details?.email, phone: session.metadata?.customer_phone, company: session.metadata?.customer_company, street: session.metadata?.customer_street, street_2: session.metadata?.customer_street_2 || "N/A", suburb: session.metadata?.customer_suburb, state: session.metadata?.customer_state, postcode: session.metadata?.customer_postcode, amount: (session.amount_total / 100).toFixed(2), items: itemSummary, instructions: session.metadata?.instructions || "None" }; await fetch(ORDER_SHEET_URL, { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(payload) }); } /** 🥇 库存扣减逻辑 */ async function updateInventory(paidItems, env) { const url = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${GITHUB_PATH}`; const res = await fetch(url, { headers: { "Authorization": `token ${env.GITHUB_TOKEN}`, "User-Agent": "Cloudflare-Worker" } }); const data = await res.json(); let products = JSON.parse(decodeURIComponent(escape(atob(data.content.replace(/\s/g, ""))))); paidItems.forEach(item => { const target = products.find(p => p.name === item.description); if (target) target.stock = Math.max(0, target.stock - item.quantity); }); await fetch(url, { method: "PUT", headers: { "Authorization": `token ${env.GITHUB_TOKEN}`, "Content-Type": "application/json", "User-Agent": "Cloudflare-Worker" }, body: JSON.stringify({ message: "Stock Update", content: btoa(unescape(encodeURIComponent(JSON.stringify(products, null, 2)))), sha: data.sha }) }); } async function sendPickingEmail(session, items, env) { const tableRows = items.map(x => `${x.price.product.metadata?.loc || "N/A"}${x.description}${x.price.product.metadata?.sku || "N/A"}${x.quantity}`).join(''); const htmlBody = `

📦 Picking List - #${session.metadata?.order_id}

${tableRows}
LocItemSKUQty

Recipient: ${session.metadata?.customer_name}
Address: ${session.metadata?.customer_street}, ${session.metadata?.customer_suburb} ${session.metadata?.customer_postcode}

`; await fetch("https://api.resend.com/emails", { method: "POST", headers: { "Authorization": `Bearer ${env.RESEND_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ from: "BytesBuys.com ", to: [PICKING_EMAIL], subject: `[Picking] Order #${session.metadata?.order_id}`, html: htmlBody }) }); } async function sendShippedResend(info, env) { const ausPostUrl = `https://auspost.com.au/mypost/track/#/details/${info.tracking}`; const htmlBody = `

Order Shipped! 📦

Hi ${info.name},

Great news! Your order #${info.order_id} has been dispatched and is on its way via Australia Post.

Tracking Number:
${info.tracking} (Click the number to track your parcel)

Thank you for shopping with BytesBuys.com!

`; await fetch("https://api.resend.com/emails", { method: "POST", headers: { "Authorization": `Bearer ${env.RESEND_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ from: "BytesBuys.com ", to: [info.email], subject: `Your BytesBuys Order #${info.order_id} is on its way!`, html: htmlBody }) }); } async function sendCustomerConfirmEmail(session, items, env) { const buyerEmail = session.customer_details?.email; if (!buyerEmail) return; const tableRows = items.map(x => `${x.description}${x.quantity}$${(x.amount_total / 100).toFixed(2)}`).join(''); const companyValue = (session.metadata?.customer_company && session.metadata?.customer_company !== "N/A") ? `${session.metadata.customer_company}
` : ""; const address2Value = (session.metadata?.customer_street_2 && session.metadata?.customer_street_2 !== "N/A") ? `${session.metadata.customer_street_2}
` : ""; const htmlBody = `

Order confirmed!

Hi ${session.metadata?.customer_name}, thanks for shopping with BytesBuys.com! Order received and currently processing. We will arrange delivery as soon as possible.

${tableRows}
Item DescriptionQtyPrice
Total Paid: $${(session.amount_total / 100).toFixed(2)} AUD
Shipping Details: ${session.metadata?.customer_name}
${session.metadata?.customer_phone}
${companyValue} ${session.metadata?.customer_street}
${address2Value} ${session.metadata?.customer_suburb.toUpperCase()}, ${session.metadata?.customer_state} ${session.metadata?.customer_postcode}

Instructions: ${session.metadata?.instructions || "None"}
⚠️ Friendly Reminder: 1. Please check your delivery address carefully. If you find any mistakes, please reply to this email immediately.
2. We suggest avoiding PO Boxes as delivery may not be possible.
3. Please note that we cannot be held responsible for delivery issues resulting from incorrect address details provided.
`; await fetch("https://api.resend.com/emails", { method: "POST", headers: { "Authorization": `Bearer ${env.RESEND_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ from: "BytesBuys.com ", to: [buyerEmail], subject: `BytesBuys.com Order Confirmation - #${session.metadata?.order_id} - Thanks for shopping!`, html: htmlBody }) }); } const SCANNER_HTML = `

📦 BytesBuys Dispatch Tool V2.16.8

`;