File size: 7,234 Bytes
6e13570
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4c6b1e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e13570
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4c6b1e8
6e13570
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
const nodemailer = require("nodemailer");
const { env } = require("../config/env");
const logger = require("./logger");

let transporter = null;

function getTransporter() {
  if (transporter) return transporter;
  transporter = nodemailer.createTransport({
    host: env.smtp.host,
    port: env.smtp.port,
    secure: env.smtp.secure, // true for 465, false for other ports (STARTTLS)
    auth: env.smtp.user ? { user: env.smtp.user, pass: env.smtp.pass } : undefined,
    requireTLS: !env.smtp.secure, // enforce STARTTLS when not already on a TLS port
  });
  return transporter;
}

async function sendMail({ to, subject, html, text }) {
  try {
    const info = await getTransporter().sendMail({
      from: env.emailFrom,
      to,
      subject,
      html,
      text,
    });
    return info;
  } catch (err) {
    // Email failures must never crash a request or leak SMTP credentials.
    logger.error("Failed to send email", { subject, error: err.message });
    return null;
  }
}

function baseLayout(bodyHtml) {
  return `

  <div style="font-family:Arial,Helvetica,sans-serif;max-width:600px;margin:0 auto;color:#1a1a1a;">

    <div style="padding:24px 0;border-bottom:2px solid #eee;">

      <h2 style="margin:0;color:#111;">Aynwerd Academy</h2>

    </div>

    <div style="padding:24px 0;">${bodyHtml}</div>

    <div style="padding:16px 0;border-top:1px solid #eee;font-size:12px;color:#777;">

      This is an automated message regarding your order. Never share your password or

      full payment reference by email. If you didn't make this request, contact

      support immediately.

    </div>

  </div>`;
}

async function sendOrderPlacedEmail({ to, name, order, courseTitle, bankDetails, payoneerDetails }) {
  const methodBlock =
    order.paymentMethod === "bank_wire"
      ? `<h3>Bank wire transfer instructions</h3>

         <table style="border-collapse:collapse;width:100%;font-size:14px;">

           <tr><td style="padding:4px 0;color:#555;">Bank name</td><td><strong>${bankDetails.name}</strong></td></tr>

           <tr><td style="padding:4px 0;color:#555;">Account name</td><td><strong>${bankDetails.accountName}</strong></td></tr>

           <tr><td style="padding:4px 0;color:#555;">Account number</td><td><strong>${bankDetails.accountNumber}</strong></td></tr>

           <tr><td style="padding:4px 0;color:#555;">Routing number</td><td><strong>${bankDetails.routingNumber}</strong></td></tr>

           <tr><td style="padding:4px 0;color:#555;">SWIFT/BIC</td><td><strong>${bankDetails.swiftBic}</strong></td></tr>

           <tr><td style="padding:4px 0;color:#555;">Bank address</td><td><strong>${bankDetails.address}</strong></td></tr>

         </table>

         <p>Please use order number <strong>${order.orderNumber}</strong> as your payment reference.</p>`
      : `<h3>Payoneer payment instructions</h3>

         <p>Send payment to Payoneer account <strong>${payoneerDetails.accountEmail}</strong>

         or use your Payoneer app's "Request a Payment" link: <a href="${payoneerDetails.paymentUrl}">${payoneerDetails.paymentUrl}</a>.</p>

         <p>Please use order number <strong>${order.orderNumber}</strong> as your payment note.</p>`;

  const html = baseLayout(`

    <p>Hi ${name},</p>

    <p>Thanks for enrolling in <strong>${courseTitle}</strong>. Your order has been created and is

    awaiting payment${order.status === "awaiting_confirmation" ? " confirmation" : ""}.</p>

    <p><strong>Order number:</strong> ${order.orderNumber}<br/>

       <strong>Amount due:</strong> ${order.currency} ${order.amount.toFixed(2)}<br/>

       <strong>Payment method:</strong> ${order.paymentMethod === "bank_wire" ? "Bank wire transfer" : "Payoneer"}</p>

    ${methodBlock}

    <p>Once our team confirms your payment, you'll receive a separate email and full course access

    will be unlocked automatically on your account.</p>

  `);

  return sendMail({
    to,
    subject: `Your Aynwerd order ${order.orderNumber} β€” payment instructions`,
    html,
    text: `Order ${order.orderNumber} created for ${courseTitle}. Amount due: ${order.currency} ${order.amount.toFixed(2)}.`,
  });
}

async function sendPaymentConfirmedEmail({ to, name, order, courseTitle }) {
  const html = baseLayout(`

    <p>Hi ${name},</p>

    <p>Great news β€” we've confirmed your payment for <strong>${courseTitle}</strong>.</p>

    <p><strong>Order number:</strong> ${order.orderNumber}<br/>

       <strong>Amount paid:</strong> ${order.currency} ${order.amount.toFixed(2)}<br/>

       <strong>Confirmed on:</strong> ${new Date(order.confirmedAt).toUTCString()}</p>

    <p>Full access to the course has been unlocked on your account. Log in any time to get started.</p>

  `);

  return sendMail({
    to,
    subject: `Payment confirmed β€” order ${order.orderNumber}`,
    html,
    text: `Payment confirmed for order ${order.orderNumber}. Course access unlocked.`,
  });
}

async function sendVoucherRedeemedEmail({ to, name, order, courseTitle, voucherCode }) {
  const html = baseLayout(`
    <p>Hi ${name},</p>
    <p>Your voucher <strong>${voucherCode}</strong> has been redeemed for <strong>${courseTitle}</strong> β€”
    no payment was required.</p>
    <p><strong>Order number:</strong> ${order.orderNumber}</p>
    <p>Full access to the course has been unlocked on your account. Log in any time to get started.</p>
  `);

  return sendMail({
    to,
    subject: `Voucher redeemed β€” ${courseTitle} unlocked`,
    html,
    text: `Voucher ${voucherCode} redeemed for ${courseTitle}. Course access unlocked. Order ${order.orderNumber}.`,
  });
}

async function sendPaymentRejectedEmail({ to, name, order, courseTitle, reason }) {
  const html = baseLayout(`

    <p>Hi ${name},</p>

    <p>We were unable to confirm your payment for <strong>${courseTitle}</strong> (order

    <strong>${order.orderNumber}</strong>).</p>

    ${reason ? `<p><strong>Reason:</strong> ${reason}</p>` : ""}

    <p>Please reply to this email or contact support so we can help resolve this.</p>

  `);

  return sendMail({
    to,
    subject: `Action needed on order ${order.orderNumber}`,
    html,
    text: `We could not confirm payment for order ${order.orderNumber}.${reason ? ` Reason: ${reason}` : ""}`,
  });
}

async function sendAdminNewOrderAlert({ order, courseTitle, customerEmail }) {
  if (!env.adminNotifyEmail) return null;
  const html = baseLayout(`

    <p>New order awaiting confirmation.</p>

    <p><strong>Order:</strong> ${order.orderNumber}<br/>

       <strong>Customer:</strong> ${customerEmail}<br/>

       <strong>Course:</strong> ${courseTitle}<br/>

       <strong>Amount:</strong> ${order.currency} ${order.amount.toFixed(2)}<br/>

       <strong>Method:</strong> ${order.paymentMethod}</p>

    <p>Review and confirm it from the admin dashboard.</p>

  `);
  return sendMail({
    to: env.adminNotifyEmail,
    subject: `New order ${order.orderNumber} awaiting confirmation`,
    html,
  });
}

module.exports = {
  sendOrderPlacedEmail,
  sendPaymentConfirmedEmail,
  sendVoucherRedeemedEmail,
  sendPaymentRejectedEmail,
  sendAdminNewOrderAlert,
};