Bot Jualan dengan Generate Invoice Otomatis
Cara buat bot jualan WhatsApp dengan invoice otomatis. Auto generate, kirim PDF, track payment. Tutorial lengkap!
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 tempoFormat 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: @namatokoInvoice 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!
| Manual | Auto Invoice |
|---|---|
| Ketik ulang | Auto generate |
| Salah hitung | Akurat |
| Tidak konsisten | Standar format |
| Susah track | Easy tracking |
Look professional with auto invoices!