emailr_
Todos los artículos
usecase·8 min

Correos de facturas y recibos: qué incluir

billingtransactionalreceipts

Los correos de facturas y recibos cumplen funciones legales, contables y de atención al cliente. Así es como diseñarlos eficazmente.

Recibo vs factura

  • Recibo: confirma el pago recibido
  • Factura: solicita el pago (puede estar prepagada)

Ambos necesitan información similar, pero cumplen objetivos distintos.

Elementos esenciales

Correo de recibo

interface ReceiptEmail {
  customer: {
    name: string;
    email: string;
    billingAddress?: Address;
  };
  transaction: {
    id: string;
    date: Date;
    paymentMethod: string;
    last4?: string;
  };
  items: Array<{
    description: string;
    quantity: number;
    unitPrice: number;
    total: number;
  }>;
  totals: {
    subtotal: number;
    tax: number;
    discount?: number;
    total: number;
    currency: string;
  };
  business: {
    name: string;
    address: string;
    taxId?: string;
  };
}


await sendEmail({
  to: customer.email,
  subject: `Receipt for your purchase - $${totals.total}`,
  template: 'receipt',
  data: receipt,
  attachments: [
    {
      filename: `receipt-${transaction.id}.pdf`,
      content: await generateReceiptPDF(receipt)
    }
  ]
});

Correo de factura

interface InvoiceEmail {
  customer: {
    name: string;
    email: string;
    billingAddress: Address;
  };
  invoice: {
    number: string;
    issueDate: Date;
    dueDate: Date;
    status: 'draft' | 'sent' | 'paid' | 'overdue';
  };
  items: InvoiceItem[];
  totals: {
    subtotal: number;
    tax: number;
    total: number;
    currency: string;
    amountDue: number;
  };
  paymentOptions: PaymentOption[];
}

await sendEmail({
  to: customer.email,
  subject: `Invoice #${invoice.number} - $${totals.amountDue} due ${formatDate(invoice.dueDate)}`,
  template: 'invoice',
  data: invoiceData,
  attachments: [
    {
      filename: `invoice-${invoice.number}.pdf`,
      content: await generateInvoicePDF(invoiceData)
    }
  ]
});

Recordatorios de pago

Próxima fecha de vencimiento

const reminderSequence = [
  { daysBefore: 7, template: 'invoice-reminder-7d' },
  { daysBefore: 3, template: 'invoice-reminder-3d' },
  { daysBefore: 1, template: 'invoice-reminder-1d' },
  { daysAfter: 1, template: 'invoice-overdue-1d' },
  { daysAfter: 7, template: 'invoice-overdue-7d' }
];

async function sendInvoiceReminder(invoice: Invoice, reminder: Reminder) {
  await sendEmail({
    to: invoice.customer.email,
    subject: reminder.daysBefore 
      ? `Invoice #${invoice.number} due in ${reminder.daysBefore} days`
      : `Invoice #${invoice.number} is overdue`,
    template: reminder.template,
    data: {
      invoice,
      payNowUrl: `${baseUrl}/invoices/${invoice.id}/pay`,
      amountDue: invoice.amountDue
    }
  });
}

Mejores prácticas de formato

Visualización de moneda

function formatMoney(amount: number, currency: string, locale: string): string {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency
  }).format(amount);
}

// $1,234.56 (en-US, USD)
// €1.234,56 (de-DE, EUR)
// ¥1,235 (ja-JP, JPY)

Tabla de partidas

<table>
  <thead>
    <tr>
      <th>Description</th>
      <th>Qty</th>
      <th>Unit Price</th>
      <th>Total</th>
    </tr>
  </thead>
  <tbody>
    &#123;&#123;#each items&#125;&#125;
    <tr>
      <td>&#123;&#123;this.description&#125;&#125;</td>
      <td>&#123;&#123;this.quantity&#125;&#125;</td>
      <td>&#123;&#123;formatMoney this.unitPrice&#125;&#125;</td>
      <td>&#123;&#123;formatMoney this.total&#125;&#125;</td>
    </tr>
    &#123;&#123;/each&#125;&#125;
  </tbody>
  <tfoot>
    <tr>
      <td colspan="3">Subtotal</td>
      <td>&#123;&#123;formatMoney totals.subtotal&#125;&#125;</td>
    </tr>
    <tr>
      <td colspan="3">Tax</td>
      <td>&#123;&#123;formatMoney totals.tax&#125;&#125;</td>
    </tr>
    <tr>
      <td colspan="3"><strong>Total</strong></td>
      <td><strong>&#123;&#123;formatMoney totals.total&#125;&#125;</strong></td>
    </tr>
  </tfoot>
</table>

Requisitos legales

Elementos obligatorios

const requiredReceiptElements = {
  business: ['name', 'address', 'taxId'],
  transaction: ['date', 'id', 'paymentMethod'],
  items: ['description', 'quantity', 'price'],
  totals: ['subtotal', 'tax', 'total']
};

// Validate before sending
function validateReceipt(receipt: ReceiptEmail): boolean {
  // Check all required fields present
  for (const [section, fields] of Object.entries(requiredReceiptElements)) {
    for (const field of fields) {
      if (!receipt[section]?.[field]) {
        throw new Error(`Missing required field: ${section}.${field}`);
      }
    }
  }
  return true;
}

Mejores prácticas

  1. Adjunta el PDF - Los clientes necesitan registros imprimibles
  2. Totales claros - Desglosa subtotal, impuestos, descuentos
  3. Información de pago - Muestra cómo pagaron o cómo pagar
  4. Detalles del negocio - Nombre legal, dirección, ID fiscal
  5. Identificadores únicos - Números de factura/recibo para referencia
  6. Adaptado a móviles - Tablas que funcionen en pantallas pequeñas

Los correos de facturas y recibos son documentos legales. Si los haces bien, reducirás las preguntas contables y generarás confianza.

e_

Escrito por el equipo de emailr

Construyendo infraestructura de email para desarrolladores

¿Listo para empezar a enviar?

Obtén tu clave API y envía tu primer email en menos de 5 minutos. No se requiere tarjeta de crédito.