I wrote a lambda function in using Python where a customer makes an order and the receipt will be generated in form of PDF in S3
This is the code in my lambda function
import json
import boto3
from datetime import datetime, timedelta
import uuid
from io import BytesIO
import base64
For PDF generation, we'll use a simpler approach with HTML to PDF
Since ReportLab requires additional layers in Lambda
dynamodb = boto3.resource('dynamodb')
s3 = boto3.client('s3')
lambda_client = boto3.client('lambda')
orders_table = dynamodb.Table('TechHub-Orders')
customers_table = dynamodb.Table('TechHub-Customers')
def lambda_handler(event, context):
"""
Generate PDF invoice for order
"""
try:
order_id = event.get('order_id')
if not order_id:
return {
'statusCode': 400,
'body': json.dumps({'error': 'Order ID is required'})
}
# Get order details
order_response = orders_table.get_item(Key={'order_id': order_id})
if 'Item' not in order_response:
return {
'statusCode': 404,
'body': json.dumps({'error': 'Order not found'})
}
order = order_response['Item']
# Get customer details
customer_response = customers_table.get_item(Key={'customer_id': order['customer_id']})
customer = customer_response.get('Item', {})
# Generate HTML invoice
html_content = generate_invoice_html(order, customer)
# Convert HTML to PDF (using weasyprint approach for Lambda)
pdf_content = html_to_pdf_simple(html_content)
# Save to S3
s3_key = f"invoices/{order_id}/{order_id}_invoice.pdf"
bucket_name = 'techhub-do5' # Replace with your bucket name
s3.put_object(
Bucket=bucket_name,
Key=s3_key,
Body=pdf_content,
ContentType='application/pdf'
)
# Generate pre-signed URL
pdf_url = s3.generate_presigned_url(
'get_object',
Params={'Bucket': bucket_name, 'Key': s3_key},
ExpiresIn=86400 # 24 hours
)
# Trigger notification
send_invoice_notification(order, customer, pdf_url)
return {
'statusCode': 200,
'body': json.dumps({
'invoice_url': pdf_url,
'message': 'PDF Invoice generated successfully',
'order_id': order_id
})
}
except Exception as e:
print(f"Invoice generation error: {str(e)}")
import traceback
traceback.print_exc()
return {
'statusCode': 500,
'body': json.dumps({'error': 'Failed to generate PDF invoice', 'details': str(e)})
}
def generate_invoice_html(order, customer):
"""
Generate professional HTML invoice
"""
# Calculate totals
subtotal = float(order.get('total_amount', 0))
tax_rate = 0.075 # 7.5% VAT in Nigeria
tax_amount = subtotal * tax_rate
total_amount = subtotal + tax_amount
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Invoice - {order['order_id'][:8].upper()}</title>
<style>
body {{
font-family: 'Arial', sans-serif;
margin: 0;
padding: 20px;
color: #333;
line-height: 1.6;
}}
.header {{
background: linear-gradient(135deg, #1a5490, #2d71b8);
color: white;
padding: 30px;
text-align: center;
margin-bottom: 30px;
}}
.company-name {{
font-size: 28px;
font-weight: bold;
margin-bottom: 5px;
}}
.company-tagline {{
font-size: 14px;
opacity: 0.9;
}}
.invoice-title {{
font-size: 24px;
font-weight: bold;
text-align: center;
margin: 20px 0;
color: #1a5490;
}}
.invoice-details {{
display: flex;
justify-content: space-between;
margin-bottom: 30px;
}}
.invoice-info, .customer-info {{
width: 45%;
}}
.invoice-info h3, .customer-info h3 {{
color: #1a5490;
border-bottom: 2px solid #e0e0e0;
padding-bottom: 5px;
}}
.items-table {{
width: 100%;
border-collapse: collapse;
margin: 20px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}}
.items-table th {{
background: #1a5490;
color: white;
padding: 15px;
text-align: left;
}}
.items-table td {{
padding: 12px 15px;
border-bottom: 1px solid #e0e0e0;
}}
.items-table tr:nth-child(even) {{
background-color: #f9f9f9;
}}
.totals {{
float: right;
width: 300px;
margin-top: 20px;
}}
.totals table {{
width: 100%;
border-collapse: collapse;
}}
.totals td {{
padding: 8px 15px;
border-bottom: 1px solid #e0e0e0;
}}
.totals .total-row {{
background: #1a5490;
color: white;
font-weight: bold;
font-size: 16px;
}}
.footer {{
margin-top: 50px;
padding: 20px;
background: #f8f9fa;
border-radius: 5px;
text-align: center;
}}
.payment-terms {{
margin: 30px 0;
padding: 20px;
background: #e8f4fd;
border-left: 4px solid #1a5490;
}}
.currency {{
font-weight: bold;
}}
</style>
</head>
<body>
<div class="header">
<div class="company-name">TECHHUB NIGERIA</div>
<div class="company-tagline">Technology Solutions & Services</div>
<div style="margin-top: 10px; font-size: 12px;">
Lagos, Nigeria | Email: |
</div>
</div>
<div class="invoice-title">INVOICE</div>
<div class="invoice-details">
<div class="invoice-info">
<h3>Invoice Details</h3>
<p><strong>Invoice Number:</strong> {order['order_id'][:8].upper()}</p>
<p><strong>Date:</strong> {datetime.utcnow().strftime('%B %d, %Y')}</p>
<p><strong>Due Date:</strong> {(datetime.utcnow() + timedelta(days=30)).strftime('%B %d, %Y')}</p>
<p><strong>Order ID:</strong> {order['order_id']}</p>
</div>
<div class="customer-info">
<h3>Bill To</h3>
<p><strong>{customer.get('name', 'N/A')}</strong></p>
<p>Email: {customer.get('email', 'N/A')}</p>
<p>Phone: {customer.get('phone', 'N/A')}</p>
<p>Customer ID: {customer.get('customer_id', 'N/A')}</p>
</div>
</div>
<table class="items-table">
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
"""
# Add items
for item in order.get('items', []):
html_content += f"""
<tr>
<td>{item.get('product_name', 'N/A')}</td>
<td>{item.get('quantity', 0)}</td>
<td class="currency">₦{float(item.get('unit_price', 0)):,.2f}</td>
<td class="currency">₦{float(item.get('total_price', 0)):,.2f}</td>
</tr>
"""
html_content += f"""
</tbody>
</table>
<div class="totals">
<table>
<tr>
<td>Subtotal:</td>
<td class="currency">₦{subtotal:,.2f}</td>
</tr>
<tr>
<td>VAT (7.5%):</td>
<td class="currency">₦{tax_amount:,.2f}</td>
</tr>
<tr class="total-row">
<td>Total Amount:</td>
<td class="currency">₦{total_amount:,.2f}</td>
</tr>
</table>
</div>
<div style="clear: both;"></div>
<div class="payment-terms">
<h3>Payment Terms & Information</h3>
<p><strong>Payment Due:</strong> Net 30 days from invoice date</p>
<p><strong>Bank Transfer Details:</strong></p>
<p>Account Name: TechHub Nigeria Limited<br>
Account Number: 8<br>
Bank: Access Bank Nigeria<br>
Sort Code: </p>
</div>
<div class="footer">
<p><strong>Thank you for choosing TechHub Nigeria!</strong></p>
<p>For support or inquiries, contact us at suppog</p>
<p style="font-size: 11px; color: #666;">
This is an automatically generated invoice. Please retain this document for your records.
</p>
</div>
</body>
</html>
"""
return html_content
def html_to_pdf_simple(html_content):
"""
Convert HTML to PDF using a simple approach
For production, you'd want to use a proper PDF generation service
"""
# This is a simplified approach - for production use weasyprint or similar
# For now, we'll save as HTML and let the client handle PDF conversion
# or use a PDF generation service
try:
# For demo purposes, we'll use pdfkit if available in Lambda layer
# Otherwise, save as HTML with PDF-like styling
import pdfkit
pdf_content = pdfkit.from_string(html_content, False)
return pdf_content
except ImportError:
# Fallback: return HTML content for now
# In production, add a PDF generation layer
print("PDF generation library not available, returning HTML")
return html_content.encode('utf-8')
def send_invoice_notification(order, customer, pdf_url):
"""
Send invoice notification to customer
"""
try:
# Trigger notification Lambda
payload = {
'type': 'invoice_ready',
'customer_email': customer.get('email'),
'customer_phone': customer.get('phone'),
'data': {
'order_id': order['order_id'],
'invoice_url': pdf_url,
'total_amount': float(order.get('total_amount', 0))
}
def send_invoice_notification(email, phone, data):
"""
Send invoice ready notification with PDF attachment
"""
if email:
try:
# Email with invoice link
html_body = f"""
<html>
<body>
<h2>Your TechHub Nigeria Invoice is Ready!</h2>
<p>Dear Valued Customer,</p>
<p>Your invoice for Order #{data.get('order_id', '')[:8].upper()} is now ready.</p>
<p><strong>Total Amount:</strong> ₦{data.get('total_amount', 0):,.2f}</p>
<p>
<a href="{data.get('invoice_url', '')}"
style="background-color: #1a5490; color: white; padding: 10px 20px;
text-decoration: none; border-radius: 5px;">
Download Invoice PDF
</a>
</p>
<p>Payment is due within 30 days. Thank you for your business!</p>
<p>Best regards,<br>TechHub Nigeria Team</p>
</body>
</html>
"""
ses.send_email(
Source=', # Replace with your verified email
Destination={'ToAddresses': [email]},
Message={
'Subject': {'Data': f'Invoice Ready - Order #{data.get("order_id", "")[:8].upper()}'},
'Body': {
'Html': {'Data': html_body},
'Text': {'Data': f'Your invoice is ready! Download: {data.get("invoice_url", "")}'}
}
}
)
except Exception as e:
print(f"Email send error: {e}")
if phone:
try:
sns.publish(
PhoneNumber=phone,
Message=f'TechHub: Invoice ready for Order #{data.get("order_id", "")[:8]}. Total: ₦{data.get("total_amount", 0):,.2f}. Check your email for PDF.'
)
except Exception as e:
print(f"SMS send error: {e}")
}
lambda_client.invoke(
FunctionName='techhub-notifications',
InvocationType='Event',
Payload=json.dumps(payload)
)
except Exception as e:
print(f"Notification error: {str(e)}")