payment confirmation for renter after rental request approval, first listing celebration email, removed burstprotection for google places autocomplete, renamed email templates
This commit is contained in:
@@ -225,16 +225,37 @@ router.post("/", authenticateToken, requireVerifiedEmail, async (req, res) => {
|
||||
{
|
||||
model: User,
|
||||
as: "owner",
|
||||
attributes: ["id", "username", "firstName", "lastName"],
|
||||
attributes: ["id", "username", "firstName", "lastName", "email", "stripeConnectedAccountId"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Check if this is the owner's first listing
|
||||
const ownerItemCount = await Item.count({
|
||||
where: { ownerId: req.user.id }
|
||||
});
|
||||
|
||||
// If first listing, send celebration email
|
||||
if (ownerItemCount === 1) {
|
||||
try {
|
||||
const emailService = require("../services/emailService");
|
||||
await emailService.sendFirstListingCelebrationEmail(
|
||||
itemWithOwner.owner,
|
||||
itemWithOwner
|
||||
);
|
||||
console.log(`First listing celebration email sent to owner ${req.user.id}`);
|
||||
} catch (emailError) {
|
||||
// Log but don't fail the item creation
|
||||
console.error('Failed to send first listing celebration email:', emailError.message);
|
||||
}
|
||||
}
|
||||
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Item created", {
|
||||
itemId: item.id,
|
||||
ownerId: req.user.id,
|
||||
itemName: req.body.name
|
||||
itemName: req.body.name,
|
||||
isFirstListing: ownerItemCount === 1
|
||||
});
|
||||
|
||||
res.status(201).json(itemWithOwner);
|
||||
|
||||
@@ -69,7 +69,6 @@ const handleServiceError = (error, res, req) => {
|
||||
router.post(
|
||||
"/places/autocomplete",
|
||||
authenticateToken,
|
||||
rateLimiter.burstProtection,
|
||||
rateLimiter.placesAutocomplete,
|
||||
validateInput,
|
||||
async (req, res) => {
|
||||
@@ -150,7 +149,6 @@ router.post(
|
||||
router.post(
|
||||
"/geocode",
|
||||
authenticateToken,
|
||||
rateLimiter.burstProtection,
|
||||
rateLimiter.geocoding,
|
||||
validateInput,
|
||||
async (req, res) => {
|
||||
|
||||
@@ -404,6 +404,9 @@ router.put("/:id/status", authenticateToken, async (req, res) => {
|
||||
status: "confirmed",
|
||||
paymentStatus: "paid",
|
||||
stripePaymentIntentId: paymentResult.paymentIntentId,
|
||||
paymentMethodBrand: paymentResult.paymentMethod?.brand || null,
|
||||
paymentMethodLast4: paymentResult.paymentMethod?.last4 || null,
|
||||
chargedAt: paymentResult.chargedAt || new Date(),
|
||||
});
|
||||
|
||||
const updatedRental = await Rental.findByPk(rental.id, {
|
||||
@@ -423,7 +426,58 @@ router.put("/:id/status", authenticateToken, async (req, res) => {
|
||||
});
|
||||
|
||||
// Send confirmation emails
|
||||
await emailService.sendRentalConfirmationEmails(updatedRental);
|
||||
// Send approval confirmation to owner with Stripe reminder
|
||||
try {
|
||||
await emailService.sendRentalApprovalConfirmationEmail(updatedRental);
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Rental approval confirmation sent to owner", {
|
||||
rentalId: updatedRental.id,
|
||||
ownerId: updatedRental.ownerId,
|
||||
});
|
||||
} catch (emailError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error("Failed to send rental approval confirmation email to owner", {
|
||||
error: emailError.message,
|
||||
rentalId: updatedRental.id,
|
||||
ownerId: updatedRental.ownerId,
|
||||
});
|
||||
}
|
||||
|
||||
// Send rental confirmation to renter with payment receipt
|
||||
try {
|
||||
const renter = await User.findByPk(updatedRental.renterId, {
|
||||
attributes: ["email", "firstName"],
|
||||
});
|
||||
if (renter) {
|
||||
const renterNotification = {
|
||||
type: "rental_confirmed",
|
||||
title: "Rental Confirmed",
|
||||
message: `Your rental of "${updatedRental.item.name}" has been confirmed.`,
|
||||
rentalId: updatedRental.id,
|
||||
userId: updatedRental.renterId,
|
||||
metadata: { rentalStart: updatedRental.startDateTime },
|
||||
};
|
||||
await emailService.sendRentalConfirmation(
|
||||
renter.email,
|
||||
renterNotification,
|
||||
updatedRental,
|
||||
renter.firstName,
|
||||
true // isRenter = true to show payment receipt
|
||||
);
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Rental confirmation sent to renter", {
|
||||
rentalId: updatedRental.id,
|
||||
renterId: updatedRental.renterId,
|
||||
});
|
||||
}
|
||||
} catch (emailError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error("Failed to send rental confirmation email to renter", {
|
||||
error: emailError.message,
|
||||
rentalId: updatedRental.id,
|
||||
renterId: updatedRental.renterId,
|
||||
});
|
||||
}
|
||||
|
||||
res.json(updatedRental);
|
||||
return;
|
||||
@@ -464,7 +518,58 @@ router.put("/:id/status", authenticateToken, async (req, res) => {
|
||||
});
|
||||
|
||||
// Send confirmation emails
|
||||
await emailService.sendRentalConfirmationEmails(updatedRental);
|
||||
// Send approval confirmation to owner (for free rentals, no Stripe reminder shown)
|
||||
try {
|
||||
await emailService.sendRentalApprovalConfirmationEmail(updatedRental);
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Rental approval confirmation sent to owner", {
|
||||
rentalId: updatedRental.id,
|
||||
ownerId: updatedRental.ownerId,
|
||||
});
|
||||
} catch (emailError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error("Failed to send rental approval confirmation email to owner", {
|
||||
error: emailError.message,
|
||||
rentalId: updatedRental.id,
|
||||
ownerId: updatedRental.ownerId,
|
||||
});
|
||||
}
|
||||
|
||||
// Send rental confirmation to renter
|
||||
try {
|
||||
const renter = await User.findByPk(updatedRental.renterId, {
|
||||
attributes: ["email", "firstName"],
|
||||
});
|
||||
if (renter) {
|
||||
const renterNotification = {
|
||||
type: "rental_confirmed",
|
||||
title: "Rental Confirmed",
|
||||
message: `Your rental of "${updatedRental.item.name}" has been confirmed.`,
|
||||
rentalId: updatedRental.id,
|
||||
userId: updatedRental.renterId,
|
||||
metadata: { rentalStart: updatedRental.startDateTime },
|
||||
};
|
||||
await emailService.sendRentalConfirmation(
|
||||
renter.email,
|
||||
renterNotification,
|
||||
updatedRental,
|
||||
renter.firstName,
|
||||
true // isRenter = true (for free rentals, shows "no payment required")
|
||||
);
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Rental confirmation sent to renter", {
|
||||
rentalId: updatedRental.id,
|
||||
renterId: updatedRental.renterId,
|
||||
});
|
||||
}
|
||||
} catch (emailError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error("Failed to send rental confirmation email to renter", {
|
||||
error: emailError.message,
|
||||
rentalId: updatedRental.id,
|
||||
renterId: updatedRental.renterId,
|
||||
});
|
||||
}
|
||||
|
||||
res.json(updatedRental);
|
||||
return;
|
||||
@@ -958,6 +1063,40 @@ router.post("/:id/mark-return", authenticateToken, async (req, res) => {
|
||||
actualReturnDateTime: actualReturnDateTime || rental.endDateTime,
|
||||
notes: notes || null,
|
||||
});
|
||||
|
||||
// Fetch full rental details with associations for email
|
||||
const rentalWithDetails = await Rental.findByPk(rentalId, {
|
||||
include: [
|
||||
{ model: Item, as: "item" },
|
||||
{
|
||||
model: User,
|
||||
as: "owner",
|
||||
attributes: ["id", "username", "firstName", "lastName"],
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: "renter",
|
||||
attributes: ["id", "username", "firstName", "lastName"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Send completion emails to both renter and owner
|
||||
try {
|
||||
await emailService.sendRentalCompletionEmails(rentalWithDetails);
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Rental completion emails sent", {
|
||||
rentalId,
|
||||
ownerId: rental.ownerId,
|
||||
renterId: rental.renterId,
|
||||
});
|
||||
} catch (emailError) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.error("Failed to send rental completion emails", {
|
||||
error: emailError.message,
|
||||
rentalId,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "damaged":
|
||||
|
||||
Reference in New Issue
Block a user