pdf includes payment link
This commit is contained in:
@@ -24,7 +24,28 @@ function calculateNextRecurringDate(invoiceDate, interval) {
|
|||||||
}
|
}
|
||||||
return d.toISOString().split('T')[0];
|
return d.toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Build HTML block for Stripe Payment Link on PDF invoice.
|
||||||
|
* Returns empty string if no link or invoice is already paid.
|
||||||
|
*/
|
||||||
|
function buildPaymentLinkHtml(invoice) {
|
||||||
|
if (!invoice.stripe_payment_link_url) return '';
|
||||||
|
if (invoice.paid_date || invoice.stripe_payment_status === 'paid') return '';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div style="margin-top: 30px; padding: 16px 20px; border: 2px solid #635bff; border-radius: 8px; text-align: center;">
|
||||||
|
<p style="font-size: 14px; font-weight: bold; color: #635bff; margin: 0 0 8px 0;">
|
||||||
|
Pay Online — Credit Card or ACH
|
||||||
|
</p>
|
||||||
|
<a href="${invoice.stripe_payment_link_url}"
|
||||||
|
style="font-size: 13px; color: #635bff; word-break: break-all;">
|
||||||
|
${invoice.stripe_payment_link_url}
|
||||||
|
</a>
|
||||||
|
<p style="font-size: 11px; color: #888; margin: 8px 0 0 0;">
|
||||||
|
Secure payment powered by Stripe. ACH payments incur lower processing fees.
|
||||||
|
</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
// GET all invoices
|
// GET all invoices
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -745,7 +766,8 @@ router.get('/:id/pdf', async (req, res) => {
|
|||||||
.replace('{{INVOICE_DATE}}', formatDate(invoice.invoice_date))
|
.replace('{{INVOICE_DATE}}', formatDate(invoice.invoice_date))
|
||||||
.replace('{{TERMS}}', invoice.terms)
|
.replace('{{TERMS}}', invoice.terms)
|
||||||
.replace('{{AUTHORIZATION}}', authHTML)
|
.replace('{{AUTHORIZATION}}', authHTML)
|
||||||
.replace('{{ITEMS}}', itemsHTML);
|
.replace('{{ITEMS}}', itemsHTML)
|
||||||
|
.replace('{{PAYMENT_LINK}}', buildPaymentLinkHtml(invoice));
|
||||||
|
|
||||||
const pdf = await generatePdfFromHtml(html);
|
const pdf = await generatePdfFromHtml(html);
|
||||||
|
|
||||||
@@ -808,7 +830,8 @@ router.get('/:id/html', async (req, res) => {
|
|||||||
.replace('{{INVOICE_DATE}}', formatDate(invoice.invoice_date))
|
.replace('{{INVOICE_DATE}}', formatDate(invoice.invoice_date))
|
||||||
.replace('{{TERMS}}', invoice.terms)
|
.replace('{{TERMS}}', invoice.terms)
|
||||||
.replace('{{AUTHORIZATION}}', authHTML)
|
.replace('{{AUTHORIZATION}}', authHTML)
|
||||||
.replace('{{ITEMS}}', itemsHTML);
|
.replace('{{ITEMS}}', itemsHTML)
|
||||||
|
.replace('{{PAYMENT_LINK}}', buildPaymentLinkHtml(invoice));
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'text/html');
|
res.setHeader('Content-Type', 'text/html');
|
||||||
res.send(html);
|
res.send(html);
|
||||||
@@ -862,7 +885,8 @@ router.post('/:id/send-email', async (req, res) => {
|
|||||||
.replace('{{INVOICE_DATE}}', formatDate(invoice.invoice_date))
|
.replace('{{INVOICE_DATE}}', formatDate(invoice.invoice_date))
|
||||||
.replace('{{TERMS}}', invoice.terms)
|
.replace('{{TERMS}}', invoice.terms)
|
||||||
.replace('{{AUTHORIZATION}}', authHTML)
|
.replace('{{AUTHORIZATION}}', authHTML)
|
||||||
.replace('{{ITEMS}}', itemsHTML);
|
.replace('{{ITEMS}}', itemsHTML)
|
||||||
|
.replace('{{PAYMENT_LINK}}', buildPaymentLinkHtml(invoice));
|
||||||
|
|
||||||
const pdfBuffer = await generatePdfFromHtml(html);
|
const pdfBuffer = await generatePdfFromHtml(html);
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,7 @@
|
|||||||
{{ITEMS}}
|
{{ITEMS}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{{PAYMENT_LINK}}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user