RecipesOrder Confirmation + Receipt

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:

  1. Send a confirmation email immediately
  2. Include order details in the email body
  3. Attach a printable PDF receipt
  4. 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:

VariableTypeExample
orderNumberstring”ORD-2024-12345”
orderDatestring”November 27, 2024”
customerNamestring”John Doe”
itemsarray[{name, quantity, price}]
subtotalnumber89.97
shippingnumber5.99
taxnumber7.20
totalnumber103.16
shippingAddressobject{street, city, state, zip}
estimatedDeliverystring”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
}

Next Steps