condition check lambda
This commit is contained in:
@@ -1,258 +0,0 @@
|
||||
const cron = require("node-cron");
|
||||
const {
|
||||
Rental,
|
||||
User,
|
||||
Item,
|
||||
ConditionCheck,
|
||||
} = require("../models");
|
||||
const { Op } = require("sequelize");
|
||||
const emailServices = require("../services/email");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
const reminderSchedule = "0 * * * *"; // Run every hour
|
||||
|
||||
class ConditionCheckReminderJob {
|
||||
static startScheduledReminders() {
|
||||
console.log("Starting automated condition check reminder job...");
|
||||
|
||||
const reminderJob = cron.schedule(
|
||||
reminderSchedule,
|
||||
async () => {
|
||||
try {
|
||||
await this.sendConditionCheckReminders();
|
||||
} catch (error) {
|
||||
logger.error("Error in scheduled condition check reminders", {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
scheduled: false,
|
||||
timezone: "America/New_York",
|
||||
}
|
||||
);
|
||||
|
||||
// Start the job
|
||||
reminderJob.start();
|
||||
|
||||
console.log("Condition check reminder job scheduled:");
|
||||
console.log("- Reminders every hour: " + reminderSchedule);
|
||||
|
||||
return {
|
||||
reminderJob,
|
||||
|
||||
stop() {
|
||||
reminderJob.stop();
|
||||
console.log("Condition check reminder job stopped");
|
||||
},
|
||||
|
||||
getStatus() {
|
||||
return {
|
||||
reminderJobRunning: reminderJob.getStatus() === "scheduled",
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Send reminders for upcoming condition check windows
|
||||
static async sendConditionCheckReminders() {
|
||||
try {
|
||||
const now = new Date();
|
||||
const reminderWindow = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24 hours ahead
|
||||
|
||||
// Find rentals with upcoming condition check windows
|
||||
const rentals = await Rental.findAll({
|
||||
where: {
|
||||
status: {
|
||||
[Op.in]: ["confirmed", "active", "completed"],
|
||||
},
|
||||
},
|
||||
include: [
|
||||
{ model: User, as: "owner" },
|
||||
{ model: User, as: "renter" },
|
||||
{ model: Item, as: "item" },
|
||||
],
|
||||
});
|
||||
|
||||
for (const rental of rentals) {
|
||||
await this.checkAndSendConditionReminders(rental, now, reminderWindow);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Processed ${rentals.length} rentals for condition check reminders`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending condition check reminders:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check specific rental for reminder needs
|
||||
static async checkAndSendConditionReminders(rental, now, reminderWindow) {
|
||||
const rentalStart = new Date(rental.startDateTime);
|
||||
const rentalEnd = new Date(rental.endDateTime);
|
||||
|
||||
// Pre-rental owner check (24 hours before rental start)
|
||||
const preRentalWindow = new Date(
|
||||
rentalStart.getTime() - 24 * 60 * 60 * 1000
|
||||
);
|
||||
if (now <= preRentalWindow && preRentalWindow <= reminderWindow) {
|
||||
const existingCheck = await ConditionCheck.findOne({
|
||||
where: {
|
||||
rentalId: rental.id,
|
||||
checkType: "pre_rental_owner",
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingCheck) {
|
||||
await this.sendPreRentalOwnerReminder(rental);
|
||||
}
|
||||
}
|
||||
|
||||
// Rental start renter check (within 24 hours of rental start)
|
||||
if (now <= rentalStart && rentalStart <= reminderWindow) {
|
||||
const existingCheck = await ConditionCheck.findOne({
|
||||
where: {
|
||||
rentalId: rental.id,
|
||||
checkType: "rental_start_renter",
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingCheck) {
|
||||
await this.sendRentalStartRenterReminder(rental);
|
||||
}
|
||||
}
|
||||
|
||||
// Rental end renter check (within 24 hours of rental end)
|
||||
if (now <= rentalEnd && rentalEnd <= reminderWindow) {
|
||||
const existingCheck = await ConditionCheck.findOne({
|
||||
where: {
|
||||
rentalId: rental.id,
|
||||
checkType: "rental_end_renter",
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingCheck) {
|
||||
await this.sendRentalEndRenterReminder(rental);
|
||||
}
|
||||
}
|
||||
|
||||
// Post-rental owner check (24 hours after rental end)
|
||||
const postRentalWindow = new Date(
|
||||
rentalEnd.getTime() + 24 * 60 * 60 * 1000
|
||||
);
|
||||
if (now <= postRentalWindow && postRentalWindow <= reminderWindow) {
|
||||
const existingCheck = await ConditionCheck.findOne({
|
||||
where: {
|
||||
rentalId: rental.id,
|
||||
checkType: "post_rental_owner",
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingCheck) {
|
||||
await this.sendPostRentalOwnerReminder(rental);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Individual email senders
|
||||
static async sendPreRentalOwnerReminder(rental) {
|
||||
const notificationData = {
|
||||
type: "condition_check_reminder",
|
||||
subtype: "pre_rental_owner",
|
||||
title: "Condition Check Reminder",
|
||||
message: `Please take photos of "${rental.item.name}" before the rental begins tomorrow.`,
|
||||
rentalId: rental.id,
|
||||
userId: rental.ownerId,
|
||||
metadata: {
|
||||
checkType: "pre_rental_owner",
|
||||
deadline: new Date(rental.startDateTime).toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
await emailServices.rentalReminder.sendConditionCheckReminder(
|
||||
rental.owner.email,
|
||||
notificationData,
|
||||
rental
|
||||
);
|
||||
|
||||
console.log(`Pre-rental owner reminder sent for rental ${rental.id}`);
|
||||
}
|
||||
|
||||
static async sendRentalStartRenterReminder(rental) {
|
||||
const notificationData = {
|
||||
type: "condition_check_reminder",
|
||||
subtype: "rental_start_renter",
|
||||
title: "Condition Check Reminder",
|
||||
message: `Please take photos when you receive "${rental.item.name}" to document its condition.`,
|
||||
rentalId: rental.id,
|
||||
userId: rental.renterId,
|
||||
metadata: {
|
||||
checkType: "rental_start_renter",
|
||||
deadline: new Date(
|
||||
rental.startDateTime.getTime() + 24 * 60 * 60 * 1000
|
||||
).toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
await emailServices.rentalReminder.sendConditionCheckReminder(
|
||||
rental.renter.email,
|
||||
notificationData,
|
||||
rental
|
||||
);
|
||||
|
||||
console.log(`Rental start renter reminder sent for rental ${rental.id}`);
|
||||
}
|
||||
|
||||
static async sendRentalEndRenterReminder(rental) {
|
||||
const notificationData = {
|
||||
type: "condition_check_reminder",
|
||||
subtype: "rental_end_renter",
|
||||
title: "Condition Check Reminder",
|
||||
message: `Please take photos when returning "${rental.item.name}" to document its condition.`,
|
||||
rentalId: rental.id,
|
||||
userId: rental.renterId,
|
||||
metadata: {
|
||||
checkType: "rental_end_renter",
|
||||
deadline: new Date(
|
||||
rental.endDateTime.getTime() + 24 * 60 * 60 * 1000
|
||||
).toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
await emailServices.rentalReminder.sendConditionCheckReminder(
|
||||
rental.renter.email,
|
||||
notificationData,
|
||||
rental
|
||||
);
|
||||
|
||||
console.log(`Rental end renter reminder sent for rental ${rental.id}`);
|
||||
}
|
||||
|
||||
static async sendPostRentalOwnerReminder(rental) {
|
||||
const notificationData = {
|
||||
type: "condition_check_reminder",
|
||||
subtype: "post_rental_owner",
|
||||
title: "Condition Check Reminder",
|
||||
message: `Please take photos and mark the return status for "${rental.item.name}".`,
|
||||
rentalId: rental.id,
|
||||
userId: rental.ownerId,
|
||||
metadata: {
|
||||
checkType: "post_rental_owner",
|
||||
deadline: new Date(
|
||||
rental.endDateTime.getTime() + 48 * 60 * 60 * 1000
|
||||
).toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
await emailServices.rentalReminder.sendConditionCheckReminder(
|
||||
rental.owner.email,
|
||||
notificationData,
|
||||
rental
|
||||
);
|
||||
|
||||
console.log(`Post-rental owner reminder sent for rental ${rental.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConditionCheckReminderJob;
|
||||
886
backend/package-lock.json
generated
886
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.940.0",
|
||||
"@aws-sdk/client-scheduler": "^3.896.0",
|
||||
"@aws-sdk/client-ses": "^3.896.0",
|
||||
"@aws-sdk/credential-providers": "^3.901.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.940.0",
|
||||
|
||||
@@ -14,6 +14,7 @@ const DamageAssessmentService = require("../services/damageAssessmentService");
|
||||
const StripeWebhookService = require("../services/stripeWebhookService");
|
||||
const StripeService = require("../services/stripeService");
|
||||
const emailServices = require("../services/email");
|
||||
const EventBridgeSchedulerService = require("../services/eventBridgeSchedulerService");
|
||||
const logger = require("../utils/logger");
|
||||
const { PaymentError } = require("../utils/stripeErrors");
|
||||
const { validateS3Keys } = require("../utils/s3KeyValidator");
|
||||
@@ -553,6 +554,23 @@ router.put("/:id/status", authenticateToken, async (req, res) => {
|
||||
],
|
||||
});
|
||||
|
||||
// Create condition check reminder schedules
|
||||
try {
|
||||
await EventBridgeSchedulerService.createConditionCheckSchedules(
|
||||
updatedRental
|
||||
);
|
||||
} catch (schedulerError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error(
|
||||
"Failed to create condition check schedules",
|
||||
{
|
||||
error: schedulerError.message,
|
||||
rentalId: updatedRental.id,
|
||||
}
|
||||
);
|
||||
// Don't fail the confirmation - schedules are non-critical
|
||||
}
|
||||
|
||||
// Send confirmation emails
|
||||
// Send approval confirmation to owner with Stripe reminder
|
||||
try {
|
||||
@@ -707,6 +725,23 @@ router.put("/:id/status", authenticateToken, async (req, res) => {
|
||||
],
|
||||
});
|
||||
|
||||
// Create condition check reminder schedules
|
||||
try {
|
||||
await EventBridgeSchedulerService.createConditionCheckSchedules(
|
||||
updatedRental
|
||||
);
|
||||
} catch (schedulerError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error(
|
||||
"Failed to create condition check schedules",
|
||||
{
|
||||
error: schedulerError.message,
|
||||
rentalId: updatedRental.id,
|
||||
}
|
||||
);
|
||||
// Don't fail the confirmation - schedules are non-critical
|
||||
}
|
||||
|
||||
// Send confirmation emails
|
||||
// Send approval confirmation to owner (for free rentals, no Stripe reminder shown)
|
||||
try {
|
||||
@@ -1842,6 +1877,21 @@ router.post(
|
||||
paymentMethodLast4,
|
||||
});
|
||||
|
||||
// Create condition check reminder schedules
|
||||
try {
|
||||
await EventBridgeSchedulerService.createConditionCheckSchedules(rental);
|
||||
} catch (schedulerError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error(
|
||||
"Failed to create condition check schedules",
|
||||
{
|
||||
error: schedulerError.message,
|
||||
rentalId: rental.id,
|
||||
}
|
||||
);
|
||||
// Don't fail the confirmation - schedules are non-critical
|
||||
}
|
||||
|
||||
// Send confirmation emails
|
||||
try {
|
||||
await emailServices.rentalFlow.sendRentalApprovalConfirmationEmail(
|
||||
|
||||
@@ -33,7 +33,6 @@ const uploadRoutes = require("./routes/upload");
|
||||
const healthRoutes = require("./routes/health");
|
||||
|
||||
const PayoutProcessor = require("./jobs/payoutProcessor");
|
||||
const ConditionCheckReminderJob = require("./jobs/conditionCheckReminder");
|
||||
const emailServices = require("./services/email");
|
||||
const s3Service = require("./services/s3Service");
|
||||
|
||||
@@ -233,11 +232,6 @@ sequelize
|
||||
const payoutJobs = PayoutProcessor.startScheduledPayouts();
|
||||
logger.info("Payout processor started");
|
||||
|
||||
// Start the condition check reminder job
|
||||
const conditionCheckJobs =
|
||||
ConditionCheckReminderJob.startScheduledReminders();
|
||||
logger.info("Condition check reminder job started");
|
||||
|
||||
server.listen(PORT, () => {
|
||||
logger.info(`Server is running on port ${PORT}`, {
|
||||
port: PORT,
|
||||
|
||||
292
backend/services/eventBridgeSchedulerService.js
Normal file
292
backend/services/eventBridgeSchedulerService.js
Normal file
@@ -0,0 +1,292 @@
|
||||
const {
|
||||
SchedulerClient,
|
||||
CreateScheduleCommand,
|
||||
DeleteScheduleCommand,
|
||||
} = require("@aws-sdk/client-scheduler");
|
||||
const { getAWSConfig } = require("../config/aws");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
/**
|
||||
* Service for managing EventBridge Scheduler schedules for condition check reminders.
|
||||
*
|
||||
* Creates one-time schedules when a rental is confirmed:
|
||||
* - pre_rental_owner: 24 hours before rental start
|
||||
* - rental_start_renter: At rental start
|
||||
* - rental_end_renter: At rental end
|
||||
* - post_rental_owner: 24 hours after rental end
|
||||
*/
|
||||
class EventBridgeSchedulerService {
|
||||
constructor() {
|
||||
if (EventBridgeSchedulerService.instance) {
|
||||
return EventBridgeSchedulerService.instance;
|
||||
}
|
||||
|
||||
this.client = null;
|
||||
this.initialized = false;
|
||||
// Only enable when explicitly set - allows deploying code without activating the feature
|
||||
this.conditionCheckRemindersEnabled =
|
||||
process.env.CONDITION_CHECK_REMINDERS_ENABLED === "true";
|
||||
|
||||
EventBridgeSchedulerService.instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the EventBridge Scheduler client.
|
||||
*/
|
||||
async initialize() {
|
||||
if (this.initialized) return;
|
||||
|
||||
try {
|
||||
const awsConfig = getAWSConfig();
|
||||
this.client = new SchedulerClient(awsConfig);
|
||||
this.initialized = true;
|
||||
logger.info("EventBridge Scheduler Service initialized");
|
||||
} catch (error) {
|
||||
logger.error("Failed to initialize EventBridge Scheduler Service", {
|
||||
error,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration from environment.
|
||||
*/
|
||||
getConfig() {
|
||||
return {
|
||||
groupName:
|
||||
process.env.SCHEDULER_GROUP_NAME || "condition-check-reminders",
|
||||
lambdaArn: process.env.LAMBDA_CONDITION_CHECK_ARN,
|
||||
schedulerRoleArn: process.env.SCHEDULER_ROLE_ARN,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create schedule name for a rental and check type.
|
||||
*/
|
||||
getScheduleName(rentalId, checkType) {
|
||||
return `rental-${rentalId}-${checkType}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate schedule times for all 4 check types.
|
||||
*/
|
||||
getScheduleTimes(rental) {
|
||||
const startDate = new Date(rental.startDateTime);
|
||||
const endDate = new Date(rental.endDateTime);
|
||||
|
||||
return {
|
||||
pre_rental_owner: new Date(startDate.getTime() - 24 * 60 * 60 * 1000),
|
||||
rental_start_renter: startDate,
|
||||
rental_end_renter: endDate,
|
||||
post_rental_owner: new Date(endDate.getTime() + 24 * 60 * 60 * 1000),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single EventBridge schedule.
|
||||
*/
|
||||
async createSchedule(name, scheduleTime, payload) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const config = this.getConfig();
|
||||
|
||||
if (!config.lambdaArn) {
|
||||
logger.warn("Lambda ARN not configured, skipping schedule creation", {
|
||||
name,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't create schedules for times in the past
|
||||
if (scheduleTime <= new Date()) {
|
||||
logger.info("Schedule time is in the past, skipping", {
|
||||
name,
|
||||
scheduleTime,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const command = new CreateScheduleCommand({
|
||||
Name: name,
|
||||
GroupName: config.groupName,
|
||||
ScheduleExpression: `at(${scheduleTime.toISOString().replace(".000Z", "")})`,
|
||||
ScheduleExpressionTimezone: "UTC",
|
||||
FlexibleTimeWindow: {
|
||||
Mode: "OFF",
|
||||
},
|
||||
Target: {
|
||||
Arn: config.lambdaArn,
|
||||
RoleArn: config.schedulerRoleArn,
|
||||
Input: JSON.stringify({
|
||||
...payload,
|
||||
scheduleName: name,
|
||||
}),
|
||||
},
|
||||
ActionAfterCompletion: "DELETE", // Auto-delete after execution
|
||||
});
|
||||
|
||||
const result = await this.client.send(command);
|
||||
|
||||
logger.info("Created EventBridge schedule", {
|
||||
name,
|
||||
scheduleTime,
|
||||
scheduleArn: result.ScheduleArn,
|
||||
});
|
||||
|
||||
return result.ScheduleArn;
|
||||
} catch (error) {
|
||||
// If schedule already exists, that's okay
|
||||
if (error.name === "ConflictException") {
|
||||
logger.info("Schedule already exists", { name });
|
||||
return name;
|
||||
}
|
||||
|
||||
logger.error("Failed to create EventBridge schedule", {
|
||||
name,
|
||||
error: error.message,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single EventBridge schedule.
|
||||
*/
|
||||
async deleteSchedule(name) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const config = this.getConfig();
|
||||
|
||||
try {
|
||||
const command = new DeleteScheduleCommand({
|
||||
Name: name,
|
||||
GroupName: config.groupName,
|
||||
});
|
||||
|
||||
await this.client.send(command);
|
||||
|
||||
logger.info("Deleted EventBridge schedule", { name });
|
||||
return true;
|
||||
} catch (error) {
|
||||
// If schedule doesn't exist, that's okay
|
||||
if (error.name === "ResourceNotFoundException") {
|
||||
logger.info("Schedule not found (already deleted)", { name });
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.error("Failed to delete EventBridge schedule", {
|
||||
name,
|
||||
error: error.message,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all 4 condition check schedules for a rental.
|
||||
* Call this after a rental is confirmed.
|
||||
*/
|
||||
async createConditionCheckSchedules(rental) {
|
||||
if (!this.conditionCheckRemindersEnabled) {
|
||||
logger.debug(
|
||||
"EventBridge Scheduler disabled, skipping schedule creation"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("Creating condition check schedules for rental", {
|
||||
rentalId: rental.id,
|
||||
});
|
||||
|
||||
const scheduleTimes = this.getScheduleTimes(rental);
|
||||
let schedulesCreated = 0;
|
||||
|
||||
const checkTypes = [
|
||||
"pre_rental_owner",
|
||||
"rental_start_renter",
|
||||
"rental_end_renter",
|
||||
"post_rental_owner",
|
||||
];
|
||||
|
||||
for (const checkType of checkTypes) {
|
||||
const name = this.getScheduleName(rental.id, checkType);
|
||||
const scheduleTime = scheduleTimes[checkType];
|
||||
|
||||
try {
|
||||
const arn = await this.createSchedule(name, scheduleTime, {
|
||||
rentalId: rental.id,
|
||||
checkType,
|
||||
scheduledFor: scheduleTime.toISOString(),
|
||||
});
|
||||
|
||||
if (arn) {
|
||||
schedulesCreated++;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to create schedule, continuing with others", {
|
||||
rentalId: rental.id,
|
||||
checkType,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Finished creating condition check schedules", {
|
||||
rentalId: rental.id,
|
||||
schedulesCreated,
|
||||
});
|
||||
|
||||
return schedulesCreated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all condition check schedules for a rental.
|
||||
* Call this when a rental is cancelled.
|
||||
*/
|
||||
async deleteConditionCheckSchedules(rental) {
|
||||
if (!this.conditionCheckRemindersEnabled) {
|
||||
logger.debug(
|
||||
"EventBridge Scheduler disabled, skipping schedule deletion"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Deleting condition check schedules for rental", {
|
||||
rentalId: rental.id,
|
||||
});
|
||||
|
||||
const checkTypes = [
|
||||
"pre_rental_owner",
|
||||
"rental_start_renter",
|
||||
"rental_end_renter",
|
||||
"post_rental_owner",
|
||||
];
|
||||
|
||||
for (const checkType of checkTypes) {
|
||||
const name = this.getScheduleName(rental.id, checkType);
|
||||
|
||||
try {
|
||||
await this.deleteSchedule(name);
|
||||
} catch (error) {
|
||||
logger.error("Failed to delete schedule, continuing with others", {
|
||||
rentalId: rental.id,
|
||||
checkType,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Finished deleting condition check schedules", {
|
||||
rentalId: rental.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
module.exports = new EventBridgeSchedulerService();
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Rental } = require("../models");
|
||||
const StripeService = require("./stripeService");
|
||||
const EventBridgeSchedulerService = require("./eventBridgeSchedulerService");
|
||||
const { isActive } = require("../utils/rentalStatus");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
@@ -187,6 +188,17 @@ class RefundService {
|
||||
payoutStatus: "pending",
|
||||
});
|
||||
|
||||
// Delete condition check schedules since rental is cancelled
|
||||
try {
|
||||
await EventBridgeSchedulerService.deleteConditionCheckSchedules(updatedRental);
|
||||
} catch (schedulerError) {
|
||||
logger.error("Failed to delete condition check schedules", {
|
||||
error: schedulerError.message,
|
||||
rentalId: updatedRental.id,
|
||||
});
|
||||
// Don't fail the cancellation - schedule cleanup is non-critical
|
||||
}
|
||||
|
||||
return {
|
||||
rental: updatedRental,
|
||||
refund: {
|
||||
|
||||
Reference in New Issue
Block a user