stripe webhooks. removed payout cron. webhook for when amount is deposited into bank. More communication about payout timelines
This commit is contained in:
@@ -5,6 +5,87 @@ const logger = require("../utils/logger");
|
||||
const { Op } = require("sequelize");
|
||||
|
||||
class PayoutService {
|
||||
/**
|
||||
* Attempt to process payout for a single rental immediately after completion.
|
||||
* Checks if owner's Stripe account has payouts enabled before attempting.
|
||||
* @param {string} rentalId - The rental ID to process
|
||||
* @returns {Object} - { attempted, success, reason, transferId, amount }
|
||||
*/
|
||||
static async triggerPayoutOnCompletion(rentalId) {
|
||||
try {
|
||||
const rental = await Rental.findByPk(rentalId, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: "owner",
|
||||
attributes: ["id", "email", "firstName", "lastName", "stripeConnectedAccountId", "stripePayoutsEnabled"],
|
||||
},
|
||||
{ model: Item, as: "item" },
|
||||
],
|
||||
});
|
||||
|
||||
if (!rental) {
|
||||
logger.warn("Rental not found for payout trigger", { rentalId });
|
||||
return { attempted: false, success: false, reason: "rental_not_found" };
|
||||
}
|
||||
|
||||
// Check eligibility conditions
|
||||
if (rental.paymentStatus !== "paid") {
|
||||
logger.info("Payout skipped: payment not paid", { rentalId, paymentStatus: rental.paymentStatus });
|
||||
return { attempted: false, success: false, reason: "payment_not_paid" };
|
||||
}
|
||||
|
||||
if (rental.payoutStatus !== "pending") {
|
||||
logger.info("Payout skipped: payout not pending", { rentalId, payoutStatus: rental.payoutStatus });
|
||||
return { attempted: false, success: false, reason: "payout_not_pending" };
|
||||
}
|
||||
|
||||
if (!rental.owner?.stripeConnectedAccountId) {
|
||||
logger.info("Payout skipped: owner has no Stripe account", { rentalId, ownerId: rental.ownerId });
|
||||
return { attempted: false, success: false, reason: "no_stripe_account" };
|
||||
}
|
||||
|
||||
// Check if owner has payouts enabled (onboarding complete)
|
||||
if (!rental.owner.stripePayoutsEnabled) {
|
||||
logger.info("Payout deferred: owner payouts not enabled, will process when onboarding completes", {
|
||||
rentalId,
|
||||
ownerId: rental.ownerId,
|
||||
});
|
||||
return { attempted: false, success: false, reason: "payouts_not_enabled" };
|
||||
}
|
||||
|
||||
// Attempt the payout
|
||||
const result = await this.processRentalPayout(rental);
|
||||
|
||||
logger.info("Payout triggered successfully on completion", {
|
||||
rentalId,
|
||||
transferId: result.transferId,
|
||||
amount: result.amount,
|
||||
});
|
||||
|
||||
return {
|
||||
attempted: true,
|
||||
success: true,
|
||||
transferId: result.transferId,
|
||||
amount: result.amount,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error("Error triggering payout on completion", {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
rentalId,
|
||||
});
|
||||
|
||||
// Payout marked as failed by processRentalPayout, will be retried by daily retry job
|
||||
return {
|
||||
attempted: true,
|
||||
success: false,
|
||||
reason: "payout_failed",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static async getEligiblePayouts() {
|
||||
try {
|
||||
const eligibleRentals = await Rental.findAll({
|
||||
@@ -21,6 +102,7 @@ class PayoutService {
|
||||
stripeConnectedAccountId: {
|
||||
[Op.not]: null,
|
||||
},
|
||||
stripePayoutsEnabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -167,6 +249,7 @@ class PayoutService {
|
||||
stripeConnectedAccountId: {
|
||||
[Op.not]: null,
|
||||
},
|
||||
stripePayoutsEnabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user