Browse Source

Merge pull request #126 from vernu/improve-reminder-emails

improve checkout reminder email
dev
Israel Abebe 6 months ago
committed by GitHub
parent
commit
183dbeb29a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 43
      api/src/billing/abandoned-checkout.service.ts

43
api/src/billing/abandoned-checkout.service.ts

@ -13,7 +13,8 @@ import { BillingService } from './billing.service'
interface EmailConfig { interface EmailConfig {
template: string template: string
subject: string subject: string
hoursAfterExpiry: number
minutesBeforeExpiry?: number // Send X minutes before expiry
minutesAfterExpiry?: number // Send X minutes after expiry
emailType: emailType:
| 'first_reminder' | 'first_reminder'
| 'second_reminder' | 'second_reminder'
@ -30,9 +31,10 @@ export class AbandonedCheckoutService {
{ {
template: 'abandoned-checkout-10-minutes', template: 'abandoned-checkout-10-minutes',
subject: '⏰ Your textbee pro upgrade is waiting!', subject: '⏰ Your textbee pro upgrade is waiting!',
hoursAfterExpiry: -0.167, // 10 minutes before expiry (-10/60 hours)
minutesBeforeExpiry: 15,
emailType: 'first_reminder', emailType: 'first_reminder',
}, },
] ]
constructor( constructor(
@ -62,42 +64,41 @@ export class AbandonedCheckoutService {
* Send reminder emails for a specific email configuration * Send reminder emails for a specific email configuration
*/ */
private async sendReminderEmails(emailConfig: EmailConfig) { private async sendReminderEmails(emailConfig: EmailConfig) {
const now = new Date()
let windowStart: Date, windowEnd: Date let windowStart: Date, windowEnd: Date
let query: any let query: any
if (emailConfig.hoursAfterExpiry < 0) {
// Before expiry: find sessions that will expire soon
const targetTime = new Date()
targetTime.setHours(
targetTime.getHours() + Math.abs(emailConfig.hoursAfterExpiry),
)
windowStart = new Date(targetTime.getTime() - 60 * 60 * 1000) // 1 hour window
windowEnd = new Date(targetTime.getTime() + 60 * 60 * 1000)
if (emailConfig.minutesBeforeExpiry !== undefined) {
// BEFORE EXPIRY: Find sessions that will expire in X minutes
const targetExpiryTime = new Date(now.getTime() + emailConfig.minutesBeforeExpiry * 60 * 1000)
windowStart = new Date(targetExpiryTime.getTime() - 10 * 60 * 1000)
windowEnd = new Date(targetExpiryTime.getTime() + 10 * 60 * 1000)
query = { query = {
expiresAt: { expiresAt: {
$gte: windowStart, $gte: windowStart,
$lte: windowEnd, $lte: windowEnd,
$gt: new Date(), // Only send to sessions that haven't expired yet
}, },
'abandonedEmails.emailType': { $ne: emailConfig.emailType },
'abandonedEmails.emailType': { $ne: emailConfig.emailType }, // Don't send duplicate emails
} }
} else {
// After expiry: find sessions that expired the specified time ago
const targetTime = new Date()
targetTime.setHours(targetTime.getHours() - emailConfig.hoursAfterExpiry)
windowStart = new Date(targetTime.getTime() - 60 * 60 * 1000)
windowEnd = new Date(targetTime.getTime() + 60 * 60 * 1000)
} else if (emailConfig.minutesAfterExpiry !== undefined) {
// AFTER EXPIRY: Find sessions that expired X minutes ago
const targetExpiryTime = new Date(now.getTime() - emailConfig.minutesAfterExpiry * 60 * 1000)
windowStart = new Date(targetExpiryTime.getTime() - 10 * 60 * 1000)
windowEnd = new Date(targetExpiryTime.getTime() + 10 * 60 * 1000)
query = { query = {
expiresAt: { expiresAt: {
$gte: windowStart, $gte: windowStart,
$lte: windowEnd, $lte: windowEnd,
}, },
'abandonedEmails.emailType': { $ne: emailConfig.emailType },
'abandonedEmails.emailType': { $ne: emailConfig.emailType }, // Don't send duplicate emails
} }
} else {
this.logger.error(`Invalid email config: must specify either minutesBeforeExpiry or minutesAfterExpiry`)
return
} }
const abandonedSessions = await this.checkoutSessionModel const abandonedSessions = await this.checkoutSessionModel

Loading…
Cancel
Save