// Cloudflare Billing Manager - Fixed Stripe PK Validation // Global API Key Auth + Account Auto-Discovery + Billing + Payment Methods const CLOUDFLARE_API = "https://api.cloudflare.com/client/v4"; export default { async fetch(request, env, ctx) { const url = new URL(request.url); const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, X-CF-Email, X-CF-Key", }; if (request.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { if (url.pathname === "/" && request.method === "GET") { return new Response(getDashboardHTML(), { headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders }, }); } const cfEmail = request.headers.get("X-CF-Email"); const cfKey = request.headers.get("X-CF-Key"); if (!cfEmail || !cfKey) { return Response.json( { ok: false, error: "Missing X-CF-Email or X-CF-Key header" }, { status: 401, headers: corsHeaders } ); } const authHeaders = { "X-Auth-Email": cfEmail, "X-Auth-Key": cfKey, "Content-Type": "application/json", }; const accountId = await resolveAccountId(authHeaders); if (url.pathname === "/api/accounts" && request.method === "GET") { const data = await cfFetch(authHeaders, "/accounts?per_page=50"); return Response.json({ data: data.result || [] }, { headers: corsHeaders }); } if (url.pathname === "/api/billing" && request.method === "GET") { const billing = await cfFetch( authHeaders, `/accounts/${accountId}/billing/history?per_page=50` ); return Response.json( { source: "api", accountId, data: billing.result || [] }, { headers: corsHeaders } ); } if (url.pathname === "/api/profile" && request.method === "GET") { const profile = await cfFetch( authHeaders, `/accounts/${accountId}/billing/profile` ); return Response.json( { ok: true, data: profile.result }, { headers: corsHeaders } ); } if (url.pathname === "/api/profile" && request.method === "PUT") { const body = await request.json(); const res = await fetch( `${CLOUDFLARE_API}/accounts/${accountId}/billing/profile`, { method: "PUT", headers: authHeaders, body: JSON.stringify(body), } ); const json = await res.json(); if (!res.ok || json.success === false) { return Response.json( { ok: false, error: "Profile update failed", detail: json }, { status: res.status, headers: corsHeaders } ); } return Response.json({ ok: true, result: json.result }, { headers: corsHeaders }); } if (url.pathname === "/api/payment-methods" && request.method === "GET") { const pms = await cfFetch( authHeaders, `/accounts/${accountId}/payment-methods` ); return Response.json( { ok: true, accountId, data: pms.result || [] }, { headers: corsHeaders } ); } if ( url.pathname.startsWith("/api/payment-methods/") && url.pathname.endsWith("/set-as-default") && request.method === "POST" ) { const pmId = url.pathname.split("/")[3]; const res = await fetch( `${CLOUDFLARE_API}/accounts/${accountId}/payment-methods/${pmId}/set-as-default`, { method: "POST", headers: authHeaders } ); if (!res.ok && res.status !== 204) { const errText = await res.text(); return Response.json( { ok: false, error: `set-as-default failed: ${res.status}`, detail: errText }, { status: res.status, headers: corsHeaders } ); } return Response.json( { ok: true, action: "set-as-default", pmId }, { headers: corsHeaders } ); } if (url.pathname === "/api/payment-methods/events" && request.method === "POST") { const body = await request.json(); const res = await fetch( `${CLOUDFLARE_API}/accounts/${accountId}/payment-methods/events`, { method: "POST", headers: authHeaders, body: JSON.stringify({ operation_id: crypto.randomUUID(), action: body.action || "backup_added", payment_method_id: body.payment_method_id, }), } ); return Response.json( { ok: true, status: res.status }, { headers: corsHeaders } ); } if (url.pathname === "/api/payment-intent" && request.method === "POST") { const intent = await cfFetch( authHeaders, `/accounts/${accountId}/billing/profile/payment-method`, "POST" ); return Response.json( { ok: true, accountId, client_secret: intent.result?.client_secret, intent_type: intent.result?.intent_type, }, { headers: corsHeaders } ); } if (url.pathname === "/api/payment-method/attach" && request.method === "POST") { const body = await request.json(); if (!body.pmId) { return Response.json( { ok: false, error: "Missing pmId" }, { status: 400, headers: corsHeaders } ); } const profileRes = await fetch( `${CLOUDFLARE_API}/accounts/${accountId}/billing/profile`, { method: "PUT", headers: authHeaders, body: JSON.stringify({ account_type: body.account_type || "personal", first_name: body.first_name || "", last_name: body.last_name || "", address: body.address || "", address2: body.address2 || "", city: body.city || "", country: body.country || "ID", state: body.state || "", zipcode: body.zipcode || "", payment_gateway: "stripe", payment_nonce: body.pmId, }), } ); const profileJson = await profileRes.json(); const defRes = await fetch( `${CLOUDFLARE_API}/accounts/${accountId}/payment-methods/${body.pmId}/set-as-default`, { method: "POST", headers: authHeaders } ); await fetch( `${CLOUDFLARE_API}/accounts/${accountId}/payment-methods/events`, { method: "POST", headers: authHeaders, body: JSON.stringify({ operation_id: crypto.randomUUID(), action: "backup_added", payment_method_id: body.pmId, }), } ); return Response.json( { ok: true, profile: profileJson.result, setAsDefault: defRes.ok || defRes.status === 204, }, { headers: corsHeaders } ); } return new Response("Not Found", { status: 404, headers: corsHeaders }); } catch (err) { return Response.json( { ok: false, error: err.message, code: err.code || "UNKNOWN", detail: err.detail || null, }, { status: 500, headers: corsHeaders } ); } }, }; async function resolveAccountId(authHeaders) { const accounts = await cfFetch(authHeaders, "/accounts?per_page=50"); if (!accounts.result?.length) { const e = new Error("No accounts accessible with this token/key"); e.code = "NO_ACCOUNT"; throw e; } return accounts.result[0].id; } async function cfFetch(headers, path, method = "GET", body = null) { const opts = { method, headers }; if (body) opts.body = JSON.stringify(body); const res = await fetch(`${CLOUDFLARE_API}${path}`, opts); const text = await res.text(); if (res.status === 204) return { success: true, result: null }; let json; try { json = JSON.parse(text); } catch { const e = new Error(`Non-JSON response (${res.status})`); e.code = `CF_${res.status}`; e.detail = text.slice(0, 500); throw e; } if (!res.ok || json.success === false) { const e = new Error(`Cloudflare API error (${res.status})`); e.code = `CF_${res.status}`; e.detail = JSON.stringify(json.errors || json); throw e; } return json; } function getDashboardHTML() { return ` CF Billing Manager

Cloudflare Billing Manager

Auth (Global API Key)

Isi email + key, lalu muat data

Total Invoice

-

Total Amount

-

Account ID

-

Payment Methods

Belum dimuat. Klik "Load Payment Methods".

Attach Existing PM to Profile

Add New Card (via Stripe Setup Intent)

Stripe Publishable Key belum dikonfigurasi. Form card disabled.
Belum ada data.
`; }