/** * XENDIT INDUSTRIAL BILLING ENGINE (X-IBE) * * Engine pembayaran murni tanpa dependensi pihak ketiga. * Menggunakan standar REST API Xendit v2 untuk Invoice & Webhooks. */ const XENDIT_SECRET_KEY = process.env.XENDIT_SECRET_KEY || ""; const XENDIT_ENDPOINT = "https://api.xendit.co/v2/invoices"; export interface XenditInvoiceRequest { external_id: string; amount: number; payer_email: string; description: string; success_redirect_url: string; failure_redirect_url: string; currency: string; items?: Array<{ name: string; quantity: number; price: number; category?: string; }>; } export class XenditEngine { private static getAuthHeader() { // Xendit requires Basic Auth with secret key as username and empty password const auth = Buffer.from(`${XENDIT_SECRET_KEY}:`).toString('base64'); return `Basic ${auth}`; } static async createInvoice(req: XenditInvoiceRequest) { if (!XENDIT_SECRET_KEY) { console.warn("[XENDIT] Secret Key missing. Using Sandbox Mode (Simulated URL)."); return { id: "sim_inv_" + Date.now(), invoice_url: `${req.success_redirect_url}?sim_status=PAID&external_id=${req.external_id}` }; } try { const response = await fetch(XENDIT_ENDPOINT, { method: 'POST', headers: { 'Authorization': this.getAuthHeader(), 'Content-Type': 'application/json' }, body: JSON.stringify({ ...req, should_send_email: true, payment_methods: ["BNI", "BRI", "MANDIRI", "BCA", "PERMATA", "OVO", "DANA", "QRIS"] }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || "Xendit API Error"); } return data; // returns { id, invoice_url, status, etc } } catch (error) { console.error("[XENDIT ERROR]", error); throw error; } } static async getInvoice(invoiceId: string) { try { const response = await fetch(`${XENDIT_ENDPOINT}/${invoiceId}`, { headers: { 'Authorization': this.getAuthHeader() } }); return await response.json(); } catch (error) { console.error("[XENDIT GET ERROR]", error); throw error; } } }