Order Confirmation + Receipt
Send beautiful order confirmation emails with PDF receipts attached. The classic e-commerce use case that FormaMail was built for.
The Problem
After a customer places an order, you need to:
- Send a confirmation email immediately
- Include order details in the email body
- Attach a printable PDF receipt
- Make both look professional and consistent
Traditional approach requires multiple services and significant code.
The Solution
One API call sends the email with a dynamically generated PDF receipt. Provide the data once in the API request, and FormaMail handles the rest.
Prerequisites
- FormaMail account with API key
- Order confirmation email template
- PDF receipt template
Quick Example
// Send order confirmation with PDF receipt
await fetch('https://api.formamail.com/api/emails/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
templateId: "order-confirmation",
to: [{ email: order.customerEmail, name: order.customerName }],
variables: {
orderNumber: order.id,
orderDate: new Date().toLocaleDateString(),
customerName: order.customerName,
shippingAddress: order.shippingAddress,
items: order.items,
subtotal: order.subtotal,
shipping: order.shippingCost,
tax: order.tax,
total: order.total,
estimatedDelivery: order.estimatedDelivery
},
attachments: [{
attachmentTemplateId: "order-receipt-pdf",
filename: `receipt-${order.id}.pdf`
}]
})
});Step-by-Step Guide
Create Your Email Template
Design an email that includes:
- Order confirmation header
- Order number and date
- List of items purchased
- Shipping address
- Order total
- Estimated delivery date
- Support contact info
Suggested variables:
| Variable | Type | Example |
|---|---|---|
orderNumber | string | ”ORD-2024-12345” |
orderDate | string | ”November 27, 2024” |
customerName | string | ”John Doe” |
items | array | [{name, quantity, price}] |
subtotal | number | 89.97 |
shipping | number | 5.99 |
tax | number | 7.20 |
total | number | 103.16 |
shippingAddress | object | {street, city, state, zip} |
estimatedDelivery | string | ”Dec 3-5, 2024” |
Create Your PDF Receipt Template
The PDF receipt should be printer-friendly and include:
- Your company logo and info
- Receipt/invoice number
- Complete order details
- Payment method (last 4 digits only)
- Return policy
- Barcode or QR code (optional)
Design your PDF template with the variables it needs. Each template has its own variable definitions.
Integrate with Your Checkout Flow
Add the FormaMail API call after successful payment:
// In your checkout completion handler
async function handleOrderComplete(order, paymentResult) {
// 1. Save order to database
await saveOrder(order);
// 2. Send confirmation email with receipt
try {
const emailResponse = await fetch('https://api.formamail.com/api/emails/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
templateId: "order-confirmation",
to: [{
email: order.customer.email,
name: order.customer.name
}],
variables: formatOrderVariables(order),
attachments: [{
attachmentTemplateId: "order-receipt-pdf",
filename: `receipt-${order.id}.pdf`
}]
})
});
const result = await emailResponse.json();
// 3. Store email ID for reference
await updateOrder(order.id, {
confirmationEmailId: result.emailId
});
} catch (error) {
// Log error but don't fail the order
console.error('Failed to send confirmation email:', error);
// Queue for retry
await queueEmailRetry(order.id);
}
return { success: true, orderId: order.id };
}
function formatOrderVariables(order) {
return {
orderNumber: order.id,
orderDate: new Date(order.createdAt).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}),
customerName: order.customer.name,
customerEmail: order.customer.email,
items: order.items.map(item => ({
name: item.productName,
quantity: item.quantity,
unitPrice: item.price.toFixed(2),
total: (item.quantity * item.price).toFixed(2),
imageUrl: item.imageUrl
})),
subtotal: order.subtotal.toFixed(2),
shipping: order.shipping.toFixed(2),
tax: order.tax.toFixed(2),
total: order.total.toFixed(2),
shippingAddress: {
name: order.shippingAddress.name,
street: order.shippingAddress.street,
city: order.shippingAddress.city,
state: order.shippingAddress.state,
zip: order.shippingAddress.zip,
country: order.shippingAddress.country
},
paymentMethod: `****${order.payment.lastFour}`,
estimatedDelivery: order.estimatedDelivery
};
}Full Code Examples
Express.js E-commerce Backend
const express = require('express');
const router = express.Router();
router.post('/checkout/complete', async (req, res) => {
const { cartId, paymentIntentId, shippingAddress } = req.body;
try {
// Verify payment
const payment = await stripe.paymentIntents.retrieve(paymentIntentId);
if (payment.status !== 'succeeded') {
return res.status(400).json({ error: 'Payment not completed' });
}
// Get cart and create order
const cart = await Cart.findById(cartId).populate('items.product');
const order = await Order.create({
customer: req.user._id,
items: cart.items.map(item => ({
product: item.product._id,
productName: item.product.name,
quantity: item.quantity,
price: item.product.price,
imageUrl: item.product.imageUrl
})),
shippingAddress,
payment: {
intentId: paymentIntentId,
lastFour: payment.payment_method_details.card.last4
},
subtotal: cart.subtotal,
shipping: cart.shippingCost,
tax: cart.tax,
total: cart.total,
estimatedDelivery: calculateDeliveryDate()
});
// Send confirmation email
const emailResult = await fetch('https://api.formamail.com/api/emails/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
templateId: "order-confirmation",
to: [{
email: req.user.email,
name: req.user.name
}],
variables: {
orderNumber: order._id.toString(),
orderDate: new Date().toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric',
year: 'numeric'
}),
customerName: req.user.name,
items: order.items.map(item => ({
name: item.productName,
quantity: item.quantity,
unitPrice: item.price.toFixed(2),
total: (item.quantity * item.price).toFixed(2)
})),
subtotal: order.subtotal.toFixed(2),
shipping: order.shipping.toFixed(2),
tax: order.tax.toFixed(2),
total: order.total.toFixed(2),
shippingAddress: {
name: shippingAddress.name,
street: shippingAddress.line1,
city: shippingAddress.city,
state: shippingAddress.state,
zip: shippingAddress.postal_code
},
paymentMethod: `Visa ending in ${payment.payment_method_details.card.last4}`,
estimatedDelivery: order.estimatedDelivery
},
attachments: [{
attachmentTemplateId: "order-receipt-pdf",
filename: `receipt-${order._id}.pdf`
}]
})
});
// Clear cart
await Cart.findByIdAndDelete(cartId);
res.json({
success: true,
orderId: order._id,
message: 'Order placed successfully'
});
} catch (error) {
console.error('Checkout error:', error);
res.status(500).json({ error: 'Failed to complete order' });
}
});
function calculateDeliveryDate() {
const delivery = new Date();
delivery.setDate(delivery.getDate() + 5); // 5-day shipping
return delivery.toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric'
});
}Shopify Webhook Handler
// Handle Shopify order creation webhook
app.post('/webhooks/shopify/order-created', async (req, res) => {
const order = req.body;
// Verify webhook signature
if (!verifyShopifyWebhook(req)) {
return res.status(401).send('Invalid signature');
}
// Send confirmation via FormaMail
await fetch('https://api.formamail.com/api/emails/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
templateId: "shopify-order-confirmation",
to: [{
email: order.email,
name: `${order.customer.first_name} ${order.customer.last_name}`
}],
variables: {
orderNumber: order.name, // e.g., "#1001"
orderDate: new Date(order.created_at).toLocaleDateString(),
customerName: order.customer.first_name,
items: order.line_items.map(item => ({
name: item.title,
variant: item.variant_title,
quantity: item.quantity,
price: item.price
})),
subtotal: order.subtotal_price,
shipping: order.total_shipping_price_set.shop_money.amount,
tax: order.total_tax,
total: order.total_price,
shippingAddress: order.shipping_address
},
attachments: [{
attachmentTemplateId: "order-receipt-pdf",
filename: `receipt-${order.name.replace('#', '')}.pdf`
}]
})
});
res.status(200).send('OK');
});Template Tips
Show Item Images
Include product images in your email:
{{#each items}}
<div style="display: flex; margin-bottom: 16px;">
<img src="{{imageUrl}}" width="80" height="80" alt="{{name}}" />
<div>
<strong>{{name}}</strong>
<p>Qty: {{quantity}} × ${{unitPrice}}</p>
</div>
</div>
{{/each}}Conditional Content
Show different messages based on order value:
{{#if freeShipping}}
<p style="color: green;">You qualified for FREE shipping!</p>
{{else}}
<p>Shipping: ${{shipping}}</p>
{{/if}}Multiple Addresses
Support gift orders with separate billing/shipping:
variables: {
shippingAddress: order.shippingAddress,
billingAddress: order.billingAddress,
isGift: order.shippingAddress.email !== order.billingAddress.email,
giftMessage: order.giftMessage
}