Bot Jualan dengan Generate Invoice Otomatis

Cara buat bot jualan WhatsApp dengan invoice otomatis. Auto generate, kirim PDF, track payment. Tutorial lengkap!

Bot Jualan dengan Generate Invoice Otomatis
Bot Jualan dengan Generate Invoice Otomatis

Bikin invoice manual = Capek dan error!

Biarkan bot generate invoice otomatis setiap ada order. Lebih rapi, profesional, dan efisien!


Fitur Invoice Otomatis

✅ Auto generate saat order confirmed
✅ Nomor invoice sequential
✅ Detail lengkap (item, qty, harga)
✅ Kirim via WA (text/PDF)
✅ Track status pembayaran
✅ Reminder jatuh tempo

Format Invoice WhatsApp

Invoice Text (Simple):

📄 INVOICE

━━━━━━━━━━━━━━━━━━━━━━━━━
INVOICE #INV-2026-0001
Tanggal: 20 Januari 2026
━━━━━━━━━━━━━━━━━━━━━━━━━

Kepada:
[NAMA CUSTOMER]
[ALAMAT]
[NO HP]

━━━━━━━━━━━━━━━━━━━━━━━━━
DETAIL PESANAN
━━━━━━━━━━━━━━━━━━━━━━━━━
Produk A (Size M)
  1 x Rp 150.000      Rp 150.000

Produk B (Hitam)
  2 x Rp 75.000       Rp 150.000

━━━━━━━━━━━━━━━━━━━━━━━━━
Subtotal:             Rp 300.000
Ongkir (JNE REG):     Rp 15.000
Diskon (PROMO10):    -Rp 30.000
━━━━━━━━━━━━━━━━━━━━━━━━━
TOTAL:                Rp 285.000
━━━━━━━━━━━━━━━━━━━━━━━━━

💳 PEMBAYARAN:
Bank: BCA
No. Rek: 1234567890
A.n: [NAMA TOKO]

⏰ Batas bayar: 22 Jan 2026 (1x24 jam)

Setelah transfer, kirim bukti ke sini!

Terima kasih 🙏
━━━━━━━━━━━━━━━━━━━━━━━━━
[NAMA TOKO]
WA: 08123456789
IG: @namatoko

Invoice dengan Status:

📄 INVOICE #INV-2026-0001

Status: ⏳ BELUM DIBAYAR

Total: Rp 285.000
Jatuh tempo: 22 Jan 2026

💳 Transfer ke:
BCA 1234567890 a.n. [NAMA]

Sudah bayar? Kirim bukti transfer!

Invoice Lunas:

✅ INVOICE LUNAS!

Invoice: #INV-2026-0001
Total: Rp 285.000
Dibayar: 21 Jan 2026 14:30

Pesanan sedang diproses!
Estimasi kirim: Hari ini

Terima kasih! 🎉

Implementasi Generate Invoice

Invoice Number Generator:

javascript

async function generateInvoiceNumber() {
    const year = new Date().getFullYear();
    const month = String(new Date().getMonth() + 1).padStart(2, '0');
    
    // Get last invoice number this month
    const lastInvoice = await db.invoices.findOne({
        invoiceNumber: { $regex: `^INV-${year}${month}` }
    }, { sort: { invoiceNumber: -1 } });
    
    let sequence = 1;
    if (lastInvoice) {
        const lastSeq = parseInt(lastInvoice.invoiceNumber.split('-')[2]);
        sequence = lastSeq + 1;
    }
    
    return `INV-${year}${month}-${String(sequence).padStart(4, '0')}`;
    // Output: INV-202601-0001
}

Create Invoice:

javascript

async function createInvoice(order) {
    const invoiceNumber = await generateInvoiceNumber();
    
    const invoice = {
        invoiceNumber,
        orderId: order.id,
        customerId: order.customerId,
        customerName: order.customerName,
        customerAddress: order.address,
        customerPhone: order.phone,
        items: order.items.map(item => ({
            name: item.name,
            variant: item.variant,
            quantity: item.quantity,
            unitPrice: item.price,
            subtotal: item.quantity * item.price
        })),
        subtotal: order.subtotal,
        shipping: order.shipping,
        discount: order.discount,
        total: order.total,
        status: 'unpaid',
        createdAt: new Date(),
        dueDate: addDays(new Date(), 1), // 1x24 jam
        paidAt: null
    };
    
    await db.invoices.insert(invoice);
    return invoice;
}

Format Invoice Message:

javascript

function formatInvoiceMessage(invoice) {
    const itemLines = invoice.items.map(item => 
        `${item.name} ${item.variant ? `(${item.variant})` : ''}
  ${item.quantity} x Rp ${item.unitPrice.toLocaleString()}      Rp ${item.subtotal.toLocaleString()}`
    ).join('\n\n');
    
    return `📄 INVOICE

━━━━━━━━━━━━━━━━━━━━━━━━━
INVOICE #${invoice.invoiceNumber}
Tanggal: ${formatDate(invoice.createdAt)}
━━━━━━━━━━━━━━━━━━━━━━━━━

Kepada:
${invoice.customerName}
${invoice.customerAddress}
${invoice.customerPhone}

━━━━━━━━━━━━━━━━━━━━━━━━━
DETAIL PESANAN
━━━━━━━━━━━━━━━━━━━━━━━━━
${itemLines}

━━━━━━━━━━━━━━━━━━━━━━━━━
Subtotal:             Rp ${invoice.subtotal.toLocaleString()}
Ongkir:               Rp ${invoice.shipping.toLocaleString()}
${invoice.discount > 0 ? `Diskon:              -Rp ${invoice.discount.toLocaleString()}` : ''}
━━━━━━━━━━━━━━━━━━━━━━━━━
TOTAL:                Rp ${invoice.total.toLocaleString()}
━━━━━━━━━━━━━━━━━━━━━━━━━

💳 PEMBAYARAN:
Bank: BCA
No. Rek: 1234567890
A.n: ${STORE_NAME}

⏰ Batas bayar: ${formatDate(invoice.dueDate)}

Setelah transfer, kirim bukti ke sini!`;
}

Generate PDF Invoice

javascript

const PDFDocument = require('pdfkit');
const fs = require('fs');

async function generateInvoicePDF(invoice) {
    const doc = new PDFDocument({ margin: 50 });
    const filePath = `/tmp/invoice-${invoice.invoiceNumber}.pdf`;
    
    doc.pipe(fs.createWriteStream(filePath));
    
    // Header
    doc.fontSize(20).text('INVOICE', { align: 'center' });
    doc.fontSize(12).text(`#${invoice.invoiceNumber}`, { align: 'center' });
    doc.moveDown();
    
    // Customer info
    doc.fontSize(10);
    doc.text(`Kepada: ${invoice.customerName}`);
    doc.text(invoice.customerAddress);
    doc.text(invoice.customerPhone);
    doc.moveDown();
    
    // Items table
    doc.fontSize(10).text('Detail Pesanan:', { underline: true });
    doc.moveDown(0.5);
    
    for (const item of invoice.items) {
        doc.text(`${item.name} ${item.variant || ''}`);
        doc.text(`  ${item.quantity} x Rp ${item.unitPrice.toLocaleString()} = Rp ${item.subtotal.toLocaleString()}`);
    }
    
    doc.moveDown();
    
    // Totals
    doc.text(`Subtotal: Rp ${invoice.subtotal.toLocaleString()}`, { align: 'right' });
    doc.text(`Ongkir: Rp ${invoice.shipping.toLocaleString()}`, { align: 'right' });
    if (invoice.discount > 0) {
        doc.text(`Diskon: -Rp ${invoice.discount.toLocaleString()}`, { align: 'right' });
    }
    doc.fontSize(12).text(`TOTAL: Rp ${invoice.total.toLocaleString()}`, { align: 'right' });
    
    doc.moveDown();
    
    // Payment info
    doc.fontSize(10).text('Pembayaran:');
    doc.text('Bank BCA: 1234567890');
    doc.text(`a.n. ${STORE_NAME}`);
    doc.text(`Batas bayar: ${formatDate(invoice.dueDate)}`);
    
    doc.end();
    
    return filePath;
}

// Kirim PDF via WhatsApp
async function sendInvoicePDF(phone, invoice) {
    const pdfPath = await generateInvoicePDF(invoice);
    
    const media = MessageMedia.fromFilePath(pdfPath);
    await client.sendMessage(phone, media, {
        caption: `📄 Invoice #${invoice.invoiceNumber}\nTotal: Rp ${invoice.total.toLocaleString()}`
    });
    
    // Cleanup
    fs.unlinkSync(pdfPath);
}

Payment Tracking

javascript

// Mark invoice as paid
async function markInvoicePaid(invoiceNumber, paymentProof) {
    const invoice = await db.invoices.findOne({ invoiceNumber });
    
    if (!invoice) {
        return { success: false, message: 'Invoice tidak ditemukan' };
    }
    
    if (invoice.status === 'paid') {
        return { success: false, message: 'Invoice sudah lunas' };
    }
    
    await db.invoices.update(invoiceNumber, {
        status: 'paid',
        paidAt: new Date(),
        paymentProof: paymentProof
    });
    
    // Update order status
    await db.orders.update(invoice.orderId, {
        status: 'paid',
        paidAt: new Date()
    });
    
    return { success: true, invoice };
}

// Auto reminder for unpaid invoices
cron.schedule('0 9 * * *', async () => {
    const unpaidInvoices = await db.invoices.find({
        status: 'unpaid',
        dueDate: { $lte: addHours(new Date(), 6) } // Due dalam 6 jam
    });
    
    for (const invoice of unpaidInvoices) {
        await sendPaymentReminder(invoice);
    }
});

Bot Handler

javascript

client.on('message', async msg => {
    const text = msg.body.toUpperCase();
    
    // Cek invoice
    if (text.startsWith('CEK INVOICE ')) {
        const invoiceNum = text.replace('CEK INVOICE ', '');
        const invoice = await db.invoices.findOne({ invoiceNumber: invoiceNum });
        
        if (invoice) {
            await msg.reply(formatInvoiceStatus(invoice));
        } else {
            await msg.reply('Invoice tidak ditemukan.');
        }
    }
    
    // Kirim ulang invoice
    if (text.startsWith('INVOICE ')) {
        const invoiceNum = text.replace('INVOICE ', '');
        const invoice = await db.invoices.findOne({ invoiceNumber: invoiceNum });
        
        if (invoice) {
            await sendInvoicePDF(msg.from, invoice);
        }
    }
});

FAQ

Text atau PDF?

Text untuk simple & quick. PDF untuk profesional & archive. Bisa offer keduanya!

Bagaimana track yang sudah bayar?

Customer kirim bukti TF → Admin verify → Mark as paid → Auto update status.

Perlu nomor invoice sequential?

Recommended untuk tracking dan accounting. Format: INV-YYYYMM-XXXX


Kesimpulan

Auto invoice = Professional & efficient!

ManualAuto Invoice
Ketik ulangAuto generate
Salah hitungAkurat
Tidak konsistenStandar format
Susah trackEasy tracking

Look professional with auto invoices!

Setup Invoice Bot →


Artikel Terkait