You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
13 KiB
307 lines
13 KiB
"""
|
|
Notification service for Plutus payment processing application.
|
|
|
|
Handles email notifications for payment failures and success reports.
|
|
Integrates with existing emailclass.py for SMTP functionality.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import List, Dict, Any, Optional
|
|
from emailclass import SendEmail
|
|
import json
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class NotificationService:
|
|
def __init__(self):
|
|
self.email_client = SendEmail()
|
|
|
|
def send_payment_failure_notification(
|
|
self,
|
|
payment_data: Dict[str, Any],
|
|
recipient_email: str = "alan.woodman@interphone.com.au"
|
|
) -> bool:
|
|
"""
|
|
Send email notification for a failed payment.
|
|
|
|
Args:
|
|
payment_data: Dictionary containing payment information
|
|
recipient_email: Email address to send notification to
|
|
|
|
Returns:
|
|
bool: True if email sent successfully
|
|
"""
|
|
try:
|
|
# Extract payment information
|
|
splynx_id = payment_data.get('splynx_id', 'Unknown')
|
|
payment_id = payment_data.get('payment_id', 'Unknown')
|
|
amount = payment_data.get('amount', 0.0)
|
|
error = payment_data.get('error', 'Unknown error')
|
|
payment_method = payment_data.get('payment_method', 'Unknown')
|
|
customer_name = payment_data.get('customer_name', 'Unknown Customer')
|
|
|
|
# Configure email
|
|
self.email_client.receiver = recipient_email
|
|
self.email_client.subject = f"Payment Failure - Customer {splynx_id} - ${amount:.2f}"
|
|
self.email_client.message_type = "html"
|
|
|
|
# Create HTML email content
|
|
html_content = self._create_failure_email_html(
|
|
payment_data, splynx_id, payment_id, amount, error,
|
|
payment_method, customer_name
|
|
)
|
|
self.email_client.message_body_html = html_content
|
|
|
|
# Send email
|
|
result = self.email_client.send()
|
|
|
|
if result:
|
|
logger.info(f"Payment failure email sent successfully for payment {payment_id}")
|
|
else:
|
|
logger.error(f"Failed to send payment failure email for payment {payment_id}")
|
|
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error sending payment failure notification: {e}")
|
|
return False
|
|
|
|
def send_batch_summary_email(
|
|
self,
|
|
batch_summary: Dict[str, Any],
|
|
recipient_email: str = "alan.woodman@interphone.com.au"
|
|
) -> bool:
|
|
"""
|
|
Send email summary for batch payment processing.
|
|
|
|
Args:
|
|
batch_summary: Dictionary containing batch processing summary
|
|
recipient_email: Email address to send summary to
|
|
|
|
Returns:
|
|
bool: True if email sent successfully
|
|
"""
|
|
try:
|
|
batch_id = batch_summary.get('batch_id', 'Unknown')
|
|
total_processed = batch_summary.get('total_processed', 0)
|
|
successful_count = batch_summary.get('successful_count', 0)
|
|
failed_count = batch_summary.get('failed_count', 0)
|
|
total_amount = batch_summary.get('total_amount', 0.0)
|
|
|
|
# Configure email
|
|
self.email_client.receiver = recipient_email
|
|
self.email_client.subject = f"Batch Payment Summary - Batch #{batch_id} - {successful_count}/{total_processed} Successful"
|
|
self.email_client.message_type = "html"
|
|
|
|
# Create HTML email content
|
|
html_content = self._create_batch_summary_html(batch_summary)
|
|
self.email_client.message_body_html = html_content
|
|
|
|
# Send email
|
|
result = self.email_client.send()
|
|
|
|
if result:
|
|
logger.info(f"Batch summary email sent successfully for batch {batch_id}")
|
|
else:
|
|
logger.error(f"Failed to send batch summary email for batch {batch_id}")
|
|
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error sending batch summary email: {e}")
|
|
return False
|
|
|
|
def _create_failure_email_html(
|
|
self,
|
|
payment_data: Dict[str, Any],
|
|
splynx_id: str,
|
|
payment_id: str,
|
|
amount: float,
|
|
error: str,
|
|
payment_method: str,
|
|
customer_name: str
|
|
) -> str:
|
|
"""Create HTML content for payment failure email."""
|
|
|
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
return f"""
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
|
|
.header {{ background-color: #dc3545; color: white; padding: 20px; text-align: center; }}
|
|
.content {{ padding: 20px; }}
|
|
.info-table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
|
|
.info-table th, .info-table td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }}
|
|
.info-table th {{ background-color: #f8f9fa; font-weight: bold; }}
|
|
.error-box {{ background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px; padding: 15px; margin: 20px 0; }}
|
|
.footer {{ background-color: #f8f9fa; padding: 15px; text-align: center; color: #666; }}
|
|
.alert {{ padding: 10px; margin: 10px 0; border-radius: 4px; }}
|
|
.alert-danger {{ background-color: #f8d7da; border-left: 4px solid #dc3545; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>🚨 Payment Processing Failure</h1>
|
|
<p>Plutus Payment System Alert</p>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<div class="alert alert-danger">
|
|
<strong>Payment Failed:</strong> A payment processing attempt has failed and requires attention.
|
|
</div>
|
|
|
|
<h2>Payment Details</h2>
|
|
<table class="info-table">
|
|
<tr><th>Payment ID</th><td>{payment_id}</td></tr>
|
|
<tr><th>Splynx Customer ID</th><td><a href="https://billing.interphone.com.au/admin/customers/view?id={splynx_id}">{splynx_id}</a></td></tr>
|
|
<tr><th>Customer Name</th><td>{customer_name}</td></tr>
|
|
<tr><th>Payment Amount</th><td>${amount:.2f} AUD</td></tr>
|
|
<tr><th>Payment Method</th><td>{payment_method}</td></tr>
|
|
<tr><th>Timestamp</th><td>{timestamp}</td></tr>
|
|
</table>
|
|
|
|
<h2>Error Information</h2>
|
|
<div class="error-box">
|
|
<h3>Error Details:</h3>
|
|
<p>{error}</p>
|
|
</div>
|
|
|
|
<h2>Recommended Actions</h2>
|
|
<ul>
|
|
<li>Review customer payment method in Splynx</li>
|
|
<li>Contact customer about payment failure</li>
|
|
<li>Check if customer needs to update payment details</li>
|
|
<li>Consider creating a support ticket</li>
|
|
</ul>
|
|
|
|
<p><strong>System Links:</strong></p>
|
|
<ul>
|
|
<li><a href="https://billing.interphone.com.au/admin/customers/view?id={splynx_id}">View Customer in Splynx</a></li>
|
|
<li><a href="http://your-plutus-url/single-payment/detail/{payment_id}">View Payment in Plutus</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<p>This email was automatically generated by the Plutus Payment System</p>
|
|
<p>Timestamp: {timestamp}</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
def _create_batch_summary_html(self, batch_summary: Dict[str, Any]) -> str:
|
|
"""Create HTML content for batch summary email."""
|
|
|
|
batch_id = batch_summary.get('batch_id', 'Unknown')
|
|
total_processed = batch_summary.get('total_processed', 0)
|
|
successful_count = batch_summary.get('successful_count', 0)
|
|
failed_count = batch_summary.get('failed_count', 0)
|
|
total_amount = batch_summary.get('total_amount', 0.0)
|
|
success_amount = batch_summary.get('success_amount', 0.0)
|
|
failed_payments = batch_summary.get('failed_payments', [])
|
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
# Determine status color
|
|
if failed_count == 0:
|
|
status_color = "#28a745" # Green
|
|
status_text = "✅ All Successful"
|
|
elif successful_count == 0:
|
|
status_color = "#dc3545" # Red
|
|
status_text = "❌ All Failed"
|
|
else:
|
|
status_color = "#ffc107" # Yellow
|
|
status_text = "⚠️ Partial Success"
|
|
|
|
# Build failed payments table
|
|
failed_payments_html = ""
|
|
if failed_payments:
|
|
failed_payments_html = """
|
|
<h2>Failed Payments</h2>
|
|
<table class="info-table">
|
|
<tr><th>Payment ID</th><th>Customer ID</th><th>Amount</th><th>Error</th></tr>
|
|
"""
|
|
for payment in failed_payments[:10]: # Limit to first 10 failures
|
|
failed_payments_html += f"""
|
|
<tr>
|
|
<td>{payment.get('id', 'N/A')}</td>
|
|
<td><a href="https://billing.interphone.com.au/admin/customers/view?id={payment.get('splynx_id', 'N/A')}">{payment.get('splynx_id', 'N/A')}</a></td>
|
|
<td>${payment.get('amount', 0.0):.2f}</td>
|
|
<td>{payment.get('error', 'Unknown error')[:100]}...</td>
|
|
</tr>
|
|
"""
|
|
failed_payments_html += "</table>"
|
|
|
|
if len(failed_payments) > 10:
|
|
failed_payments_html += f"<p><em>... and {len(failed_payments) - 10} more failed payments</em></p>"
|
|
|
|
return f"""
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
|
|
.header {{ background-color: {status_color}; color: white; padding: 20px; text-align: center; }}
|
|
.content {{ padding: 20px; }}
|
|
.info-table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
|
|
.info-table th, .info-table td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }}
|
|
.info-table th {{ background-color: #f8f9fa; font-weight: bold; }}
|
|
.footer {{ background-color: #f8f9fa; padding: 15px; text-align: center; color: #666; }}
|
|
.stats {{ display: flex; justify-content: space-around; margin: 20px 0; }}
|
|
.stat-box {{ text-align: center; padding: 15px; border-radius: 8px; }}
|
|
.stat-success {{ background-color: #d4edda; }}
|
|
.stat-danger {{ background-color: #f8d7da; }}
|
|
.stat-info {{ background-color: #d1ecf1; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>📊 Batch Payment Summary</h1>
|
|
<h2>Batch #{batch_id}</h2>
|
|
<h3>{status_text}</h3>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<h2>Processing Summary</h2>
|
|
<div class="stats">
|
|
<div class="stat-box stat-info">
|
|
<h3>{total_processed}</h3>
|
|
<p>Total Processed</p>
|
|
</div>
|
|
<div class="stat-box stat-success">
|
|
<h3>{successful_count}</h3>
|
|
<p>Successful</p>
|
|
</div>
|
|
<div class="stat-box stat-danger">
|
|
<h3>{failed_count}</h3>
|
|
<p>Failed</p>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="info-table">
|
|
<tr><th>Metric</th><th>Value</th></tr>
|
|
<tr><td>Batch ID</td><td>#{batch_id}</td></tr>
|
|
<tr><td>Total Amount Processed</td><td>${total_amount:.2f} AUD</td></tr>
|
|
<tr><td>Successful Amount</td><td>${success_amount:.2f} AUD</td></tr>
|
|
<tr><td>Success Rate</td><td>{(successful_count/total_processed*100):.1f}%</td></tr>
|
|
<tr><td>Processing Time</td><td>{timestamp}</td></tr>
|
|
</table>
|
|
|
|
{failed_payments_html}
|
|
|
|
<h2>Actions Required</h2>
|
|
<ul>
|
|
{"<li>✅ No action required - all payments successful</li>" if failed_count == 0 else f"<li>⚠️ Review {failed_count} failed payment(s)</li>"}
|
|
{"<li>📧 Consider contacting customers with failed payments</li>" if failed_count > 0 else ""}
|
|
<li>📊 <a href='http://your-plutus-url/batch/{batch_id}'>View detailed batch report</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<p>This email was automatically generated by the Plutus Payment System</p>
|
|
<p>Batch processed at: {timestamp}</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|