| |
| import os |
| import base64 |
| import uuid |
| from email.utils import formataddr |
| from email.message import EmailMessage |
| from google.oauth2.credentials import Credentials |
| from googleapiclient.discovery import build |
| from google.auth.transport.requests import Request |
| from datetime import datetime |
|
|
| class GmailLogic: |
| def __init__(self): |
| self.client_id = os.getenv("GMAIL_CLIENT_ID") |
| self.client_secret = os.getenv("GMAIL_CLIENT_SECRET") |
| self.refresh_token = os.getenv("GMAIL_REFRESH_TOKEN") |
| self.user_email = os.getenv("GMAIL_USER") |
| self.display_name = "Celeste Store" |
|
|
| def get_service(self): |
| creds = Credentials( |
| None, |
| refresh_token=self.refresh_token, |
| token_uri="https://oauth2.googleapis.com/token", |
| client_id=self.client_id, |
| client_secret=self.client_secret, |
| ) |
| if not creds.valid: |
| creds.refresh(Request()) |
| return build('gmail', 'v1', credentials=creds) |
|
|
| def generate_random_order_id(self): |
| random_suffix = uuid.uuid4().hex[:4].upper() |
| return f"CS-2026-{random_suffix}" |
|
|
| async def send_order_email( |
| self, |
| to_email: str, |
| subject: str, |
| customer_name: str, |
| order_id: str, |
| status: str, |
| products: list, |
| total_price: str, |
| from_name: str = "Celeste Store" |
| ): |
| try: |
| service = self.get_service() |
| date_now = datetime.now().strftime("%d/%m/%Y") |
| |
| display_status = status if status else "Đang chờ duyệt" |
| |
| product_names = ", ".join([p.get('name') for p in products]) |
|
|
| html_content = f""" |
| <!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8"> |
| <style> |
| body{{margin:0;padding:0;background:#f3f4f6;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial;color:#333}} |
| .container{{max-width:560px;margin:20px auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 6px 20px rgba(0,0,0,.05)}} |
| .header{{text-align:center;padding:35px 20px;border-bottom:1px solid #eee}} |
| .header h1{{margin:0;font-size:24px;color:#111}} |
| .content{{padding:30px}} |
| .order-box{{background:#f9fafb;border-radius:10px;padding:20px;border:1px solid #eee;margin-bottom:25px}} |
| table{{width:100%;border-collapse:collapse;font-size:14px}} |
| td{{padding:10px 0;border-bottom:1px solid #eee}} |
| .label{{color:#666}} |
| .value{{text-align:right;font-weight:500}} |
| .status-pending{{color:#f59e0b;font-weight:600}} |
| .total{{font-size:18px;font-weight:700;color:#e91e63;border:none}} |
| .footer{{text-align:center;background:#f3f4f6;padding:22px;font-size:13px;color:#888;border-top:1px solid #eee}} |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <div class="header"> |
| <h1>ĐƠN HÀNG ĐÃ ĐẶT</h1> |
| <p>Cảm ơn bạn đã tin tưởng shop ♡</p> |
| </div> |
| <div class="content"> |
| <p>Chào <strong>{customer_name}</strong>,</p> |
| <p>Chúng mình đã nhận được yêu cầu đặt hàng của bạn. Đơn hàng sẽ sớm được xử lý!</p> |
| <div class="order-box"> |
| <table> |
| <tr><td class="label">Mã đơn hàng</td><td class="value"><strong>#{order_id}</strong></td></tr> |
| <tr><td class="label">Ngày</td><td class="value">{date_now}</td></tr> |
| <tr><td class="label">Sản phẩm</td><td class="value">{product_names}</td></tr> |
| <tr><td class="label">Trạng thái</td><td class="value status-pending">{display_status}</td></tr> |
| <tr><td class="label">Tổng tiền</td><td class="value total">{total_price}</td></tr> |
| </table> |
| </div> |
| <p style="text-align:center; color:#666; font-size:14px;">Shop đang kiểm tra thanh toán, thông tin tài khoản sẽ được gửi đến bạn sớm nhất qua email này.</p> |
| </div> |
| <div class="footer">Celeste Store • Trà Vinh • Việt Nam<br>© 2026 Celeste Store</div> |
| </div> |
| </body> |
| </html> |
| """ |
| |
| message = EmailMessage() |
| message['To'] = to_email |
| message['From'] = formataddr((from_name, self.user_email)) |
| message['Subject'] = subject |
| message.add_alternative(html_content, subtype='html') |
| |
| raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode() |
| service.users().messages().send(userId="me", body={'raw': raw_message}).execute() |
| return {"ok": True, "order_id": order_id} |
| except Exception as e: |
| return {"ok": False, "error": str(e)} |
|
|
| async def send_complete_email( |
| self, |
| to_email: str, |
| customer_name: str, |
| order_id: str, |
| product_name: str, |
| price: str, |
| tk: str, |
| mk: str |
| ): |
| try: |
| service = self.get_service() |
| date_now = datetime.now().strftime("%d/%m/%Y") |
| |
| html_content = f""" |
| <!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8"> |
| <style> |
| body{{margin:0;padding:0;background:#f3f4f6;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial;color:#333}} |
| .container{{max-width:560px;margin:20px auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 6px 20px rgba(0,0,0,.05)}} |
| .header{{text-align:center;padding:35px 20px;border-bottom:1px solid #eee}} |
| .header h1{{margin:0;font-size:24px;color:#111}} |
| .content{{padding:30px}} |
| .order-box{{background:#f9fafb;border-radius:10px;padding:20px;border:1px solid #eee;margin-bottom:25px}} |
| table{{width:100%;border-collapse:collapse;font-size:14px}} |
| td{{padding:10px 0;border-bottom:1px solid #eee}} |
| .label{{color:#666}} |
| .value{{text-align:right;font-weight:500}} |
| .status-done{{color:#10b981;font-weight:600}} |
| .total{{font-size:18px;font-weight:700;color:#e91e63;border:none}} |
| .account-box{{background:#f5f6fa;border:1px solid #e5e7eb;border-radius:10px;padding:18px;margin-top:10px}} |
| .account-title{{font-size:14px;color:#666;font-weight:600;margin-bottom:10px}} |
| .footer{{text-align:center;background:#f3f4f6;padding:22px;font-size:13px;color:#888;border-top:1px solid #eee}} |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <div class="header"> |
| <h1>ĐƠN HÀNG HOÀN THÀNH</h1> |
| <p>Cảm ơn bạn đã mua hàng tại shop ♡</p> |
| </div> |
| <div class="content"> |
| <p>Chào <strong>{customer_name}</strong>,</p> |
| <p>Đơn hàng của bạn đã được xử lý thành công. Thông tin chi tiết bên dưới:</p> |
| <div class="order-box"> |
| <table> |
| <tr><td class="label">Mã đơn hàng</td><td class="value"><strong>#{order_id}</strong></td></tr> |
| <tr><td class="label">Ngày</td><td class="value">{date_now}</td></tr> |
| <tr><td class="label">Sản phẩm</td><td class="value">{product_name}</td></tr> |
| <tr><td class="label">Trạng thái</td><td class="value status-done">Hoàn Thành</td></tr> |
| <tr><td class="label">Tổng tiền</td><td class="value total">{price}</td></tr> |
| </table> |
| </div> |
| <div class="account-box"> |
| <div class="account-title">Thông tin tài khoản</div> |
| <table style="width:100%;"> |
| <tr><td style="width:90px;color:#777;">Tài khoản</td><td style="font-weight:600;">{tk}</td></tr> |
| <tr><td style="color:#777;">Mật khẩu</td><td style="font-weight:600;">{mk}</td></tr> |
| </table> |
| <div style="margin-top:14px;padding:10px;background:#fff;border:1px dashed #ddd;border-radius:6px;font-family:monospace;font-size:13px;word-break:break-all"> |
| {tk}:{mk} |
| </div> |
| </div> |
| <p style="font-size:13px;color:#777;margin-top:15px;">Lưu ý:<br>1. KHÔNG đăng nhập vào iCloud<br>2. KHÔNG thêm SĐT<br>3. KHÔNG đổi mật khẩu</p> |
| </div> |
| <div class="footer">Celeste Store • Trà Vinh • Việt Nam<br>© 2026 Celeste Store</div> |
| </div> |
| </body> |
| </html> |
| """ |
| |
| message = EmailMessage() |
| message['To'] = to_email |
| message['From'] = formataddr((self.display_name, self.user_email)) |
| message['Subject'] = f"Đơn hàng #{order_id} đã hoàn thành" |
| message.add_alternative(html_content, subtype='html') |
| |
| raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode() |
| service.users().messages().send(userId="me", body={'raw': raw_message}).execute() |
| return {"ok": True} |
| except Exception as e: |
| return {"ok": False, "error": str(e)} |