814 lines
33 KiB
TypeScript
814 lines
33 KiB
TypeScript
import { io } from "../../index";
|
|
import {
|
|
IPlanSuscriptionDocument,
|
|
MP_SUBS_STATUS,
|
|
PlanSuscriptionsAdapterMongoose,
|
|
} from "./PlanSubscriptions.Adapter.Mongoose";
|
|
import UsersManager from "../Users/Users";
|
|
import {
|
|
CancellPlanSuscriptionParams,
|
|
CheckFeatureParams,
|
|
CreateMPPreApprovalParams,
|
|
DeletePlanSuscriptionByUserParams,
|
|
FindPlanSuscripcionsParams,
|
|
GetSubscriptionInitPointParams,
|
|
GetSuscriptionInitPointResponse,
|
|
IPlanSuscriptionsManager,
|
|
ISubscriptionInfo,
|
|
MP_PRE_APPROVAL_STATUS,
|
|
MPPreApprovalResponse,
|
|
ToFreePlanParams,
|
|
ToMpParams,
|
|
} from "./PlanSubscriptions.interface";
|
|
import axios from "axios";
|
|
import PlansList from "../../Models/Plans/Plans";
|
|
import dayjs from "dayjs";
|
|
import "dayjs/locale/es";
|
|
import { isNull } from "../../helpers/IsNull";
|
|
import { PlanFeatures } from "../Plans/Plans.interface";
|
|
import { NotificationsManager } from "../Notifications/Notifications";
|
|
import WapServerList from "../WapServer/WapServer";
|
|
import CompaniesList from "../Companies/Companies";
|
|
import { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";
|
|
import { calculateProratedUpgradeAmount } from "./PlanSubscriptions.pricing";
|
|
|
|
dayjs.locale("es");
|
|
|
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
|
|
class PlanSuscriptionsManager implements IPlanSuscriptionsManager {
|
|
planSuscriptions: PlanSuscriptionsAdapterMongoose;
|
|
|
|
constructor() {
|
|
this.planSuscriptions = new PlanSuscriptionsAdapterMongoose();
|
|
}
|
|
|
|
private shouldDowngradeExpiredSubscription(subscription: IPlanSuscriptionDocument): boolean {
|
|
return subscription.mpStatus === MP_SUBS_STATUS.AUTHORIZED || subscription.mpStatus === MP_SUBS_STATUS.CANCELLED;
|
|
}
|
|
|
|
private buildMercadoPagoBackUrl(returnStatus: "success" | "failure" | "pending"): string {
|
|
const backUrl = process.env.MP_BACK_URL || "";
|
|
if (!backUrl) return backUrl;
|
|
|
|
try {
|
|
const url = new URL(backUrl);
|
|
url.searchParams.set("mp_return", returnStatus);
|
|
return url.toString();
|
|
} catch {
|
|
const separator = backUrl.includes("?") ? "&" : "?";
|
|
return `${backUrl}${separator}mp_return=${returnStatus}`;
|
|
}
|
|
}
|
|
|
|
public async getInitPoint(
|
|
data: GetSubscriptionInitPointParams
|
|
): Promise<GetSuscriptionInitPointResponse> {
|
|
const sessionUser = await UsersManager.users.findOne({
|
|
_id: data.sessionUser,
|
|
});
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
const subscription = await this.planSuscriptions.findOne({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
if (!subscription) {
|
|
throw new Error("No se ha encontrado una suscripción activa");
|
|
}
|
|
|
|
const plan = await PlansList.plans.findOne({
|
|
_id: String(subscription.planId),
|
|
});
|
|
|
|
if (!plan) {
|
|
throw new Error("No se ha encontrado el plan");
|
|
}
|
|
|
|
if (plan.price == 0) {
|
|
throw new Error("El plan gratuito no requiere el pago de una suscripción");
|
|
}
|
|
|
|
if (!subscription.mpPreferenceId) {
|
|
throw new Error("No se ha encontrado una suscripción activa");
|
|
}
|
|
|
|
if (subscription.mpStatus !== MP_SUBS_STATUS.PENDING) {
|
|
throw new Error("No se ha encontrado una suscripción pendiente de pago");
|
|
}
|
|
|
|
if (!subscription.mpInitPoint) {
|
|
throw new Error("No se ha encontrado una suscripción pendiente de pago");
|
|
}
|
|
|
|
return {
|
|
init_point: subscription.mpInitPoint,
|
|
};
|
|
}
|
|
|
|
public async deletePlanSuscriptionByUser(
|
|
data: DeletePlanSuscriptionByUserParams
|
|
): Promise<void> {
|
|
await this.planSuscriptions.delete({ sessionUser: data.sessionUser });
|
|
}
|
|
|
|
public async create(data: CreateMPPreApprovalParams): Promise<MPPreApprovalResponse> {
|
|
const sessionUser = await UsersManager.users.findOne({
|
|
_id: data.sessionUser,
|
|
});
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
const plan = await PlansList.plans.findOne({ _id: data.planId });
|
|
|
|
if (!plan) {
|
|
throw new Error("El plan no existe");
|
|
}
|
|
|
|
if (!plan.active) {
|
|
throw new Error("El plan no esta activo");
|
|
}
|
|
|
|
let subscription = await this.planSuscriptions.findOne({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
let isNewSubscription = false;
|
|
|
|
if (!subscription) {
|
|
const subscriptionStartDate = dayjs(new Date());
|
|
|
|
if (plan.price === 0) {
|
|
isNewSubscription = true;
|
|
subscription = await this.planSuscriptions.create({
|
|
userId: data.sessionUser,
|
|
planId: data.planId,
|
|
mpPlanId: data.planId,
|
|
startDate: subscriptionStartDate.toDate(),
|
|
endDate: subscriptionStartDate.clone().add(10, "years").toDate(),
|
|
autoRenew: false,
|
|
mpStatus: "",
|
|
mpInitPoint: "/landing/dashboard",
|
|
});
|
|
} else if (plan.price > 0) {
|
|
|
|
|
|
isNewSubscription = true;
|
|
subscription = await this.toMP({
|
|
planId: data.planId,
|
|
sessionUser: data.sessionUser,
|
|
months: data.months || 1,
|
|
});
|
|
}
|
|
} else {
|
|
const actualPlan = await PlansList.plans.findOne({
|
|
_id: subscription.planId,
|
|
});
|
|
if (!actualPlan) {
|
|
throw new Error("El plan no existe");
|
|
}
|
|
isNewSubscription = true;
|
|
|
|
const dashboardUrl = "/landing/dashboard";
|
|
|
|
if (plan.price === 0) {
|
|
if (actualPlan.id !== data.planId) {
|
|
await this.toFreePlan({ sessionUser: data.sessionUser });
|
|
subscription = await this.planSuscriptions.findOne({ sessionUser: data.sessionUser });
|
|
}
|
|
|
|
if (subscription) {
|
|
const subscriptionStartDate = dayjs(new Date());
|
|
subscription.startDate = subscriptionStartDate.toDate();
|
|
subscription.endDate = subscriptionStartDate.clone().add(10, "years").toDate();
|
|
subscription.autoRenew = false;
|
|
subscription.mpStatus = "";
|
|
subscription.mpInitPoint = dashboardUrl || "";
|
|
subscription.downgradedFromPlanId = undefined;
|
|
subscription.downgradedFromPlanName = undefined;
|
|
subscription.downgradedFromPlanCode = undefined;
|
|
subscription.downgradedAt = undefined;
|
|
subscription.downgradeReason = undefined;
|
|
await subscription.save();
|
|
}
|
|
} else {
|
|
// Actualizamos el plan si actualmente tiene el plan gratuito.
|
|
if (actualPlan.id !== data.planId && actualPlan.price === 0) {
|
|
//realizar la suscripcion...
|
|
subscription = await this.toMP({
|
|
planId: data.planId,
|
|
sessionUser: data.sessionUser,
|
|
months: data.months || 1,
|
|
});
|
|
} else {
|
|
const isActivePaidSubscription =
|
|
actualPlan.price > 0 &&
|
|
(subscription.mpStatus === MP_SUBS_STATUS.AUTHORIZED ||
|
|
subscription.mpStatus === MP_SUBS_STATUS.CANCELLED) &&
|
|
dayjs(subscription.endDate).isAfter(new Date());
|
|
|
|
if (isActivePaidSubscription && actualPlan.id !== data.planId) {
|
|
if (plan.price <= actualPlan.price) {
|
|
throw new Error("No se puede cambiar a un plan igual o inferior desde esta ruta de actualización.");
|
|
}
|
|
|
|
subscription = await this.toMP({
|
|
planId: data.planId,
|
|
sessionUser: data.sessionUser,
|
|
months: data.months || 1,
|
|
});
|
|
} else {
|
|
if (subscription.mpStatus != MP_SUBS_STATUS.CANCELLED && actualPlan.id !== data.planId) {
|
|
throw new Error(
|
|
"No se puede cambiar el plan, ya que el usuario ya tiene una suscripción activa. Para cambiarlo primero debes cancelar tu plan actual."
|
|
);
|
|
}
|
|
|
|
subscription = await this.toMP({
|
|
planId: data.planId,
|
|
sessionUser: data.sessionUser,
|
|
months: data.months || 1,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!subscription) {
|
|
throw new Error("No fue posible crear la suscripción");
|
|
}
|
|
|
|
return {
|
|
init_point: isNewSubscription
|
|
? subscription.mpInitPoint
|
|
: isNull<string>(process.env.MP_BACK_URL, ""),
|
|
status: MP_PRE_APPROVAL_STATUS.READY_TO_START,
|
|
};
|
|
}
|
|
|
|
public async toMP(data: ToMpParams): Promise<IPlanSuscriptionDocument> {
|
|
const sessionUser = await UsersManager.users.findOne({
|
|
_id: data.sessionUser,
|
|
});
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
const plan = await PlansList.plans.findOne({ _id: data.planId });
|
|
|
|
if (!plan) {
|
|
throw new Error("El plan no existe");
|
|
}
|
|
|
|
try {
|
|
const preferenceDate = dayjs(new Date());
|
|
|
|
let discount = 0;
|
|
if (data.months === 3) discount = plan.discount3Months || 0;
|
|
else if (data.months === 6) discount = plan.discount6Months || 0;
|
|
else if (data.months === 12) discount = plan.discount12Months || 0;
|
|
|
|
const totalPrice = plan.price * data.months;
|
|
const finalPrice = totalPrice - (totalPrice * (discount / 100));
|
|
|
|
const currentSubscription = await this.planSuscriptions.findOne({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
const currentPlan = currentSubscription
|
|
? await PlansList.plans.findOne({ _id: String(currentSubscription.planId) })
|
|
: null;
|
|
|
|
const isUpgradePayment =
|
|
currentSubscription &&
|
|
currentPlan &&
|
|
String(currentSubscription.planId) !== String(data.planId) &&
|
|
currentPlan.price > 0 &&
|
|
plan.price > currentPlan.price &&
|
|
(currentSubscription.mpStatus === MP_SUBS_STATUS.AUTHORIZED ||
|
|
currentSubscription.mpStatus === MP_SUBS_STATUS.CANCELLED) &&
|
|
dayjs(currentSubscription.endDate).isAfter(preferenceDate);
|
|
|
|
const remainingDays = isUpgradePayment
|
|
? Math.max(0, Math.ceil((new Date(currentSubscription.endDate).getTime() - preferenceDate.toDate().getTime()) / DAY_MS))
|
|
: 0;
|
|
const proratedAmount = isUpgradePayment
|
|
? calculateProratedUpgradeAmount(currentPlan.price, plan.price, remainingDays)
|
|
: 0;
|
|
|
|
if (isUpgradePayment && remainingDays <= 0) {
|
|
throw new Error("No se puede actualizar el plan porque la suscripción actual no tiene días restantes.");
|
|
}
|
|
|
|
if (
|
|
currentSubscription?.pendingPaymentPreferenceId &&
|
|
currentSubscription.pendingPaymentInitPoint
|
|
) {
|
|
if (String(currentSubscription.pendingPaymentPlanId || currentSubscription.planId) === String(data.planId)) {
|
|
return currentSubscription;
|
|
}
|
|
|
|
throw new Error("Ya tenés un pago pendiente para tu plan. Verificalo o esperá a que se actualice antes de intentar otro cambio.");
|
|
}
|
|
|
|
if (
|
|
currentSubscription &&
|
|
String(currentSubscription.planId) === String(data.planId) &&
|
|
(currentSubscription.mpStatus === MP_SUBS_STATUS.AUTHORIZED ||
|
|
currentSubscription.mpStatus === MP_SUBS_STATUS.CANCELLED) &&
|
|
currentSubscription.pendingPaymentPreferenceId &&
|
|
currentSubscription.pendingPaymentInitPoint
|
|
) {
|
|
return currentSubscription;
|
|
}
|
|
|
|
const response = await axios.post(
|
|
"https://api.mercadopago.com/checkout/preferences",
|
|
{
|
|
items: [
|
|
{
|
|
title: isUpgradePayment
|
|
? "TurnosXpress.com.ar - Upgrade al plan " + plan.name
|
|
: "TurnosXpress.com.ar - Plan " + plan.name + " (" + data.months + " meses)",
|
|
quantity: 1,
|
|
currency_id: "ARS",
|
|
unit_price: isUpgradePayment ? proratedAmount : finalPrice
|
|
}
|
|
],
|
|
back_urls: {
|
|
success: this.buildMercadoPagoBackUrl("success"),
|
|
failure: this.buildMercadoPagoBackUrl("failure"),
|
|
pending: this.buildMercadoPagoBackUrl("pending")
|
|
},
|
|
auto_return: "approved",
|
|
payer: {
|
|
email: sessionUser.mpPayerEmail || ""
|
|
},
|
|
external_reference: String(sessionUser._id),
|
|
notification_url: process.env.MP_WEBHOOK_URL
|
|
},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${process.env.MP_ACCESS_TOKEN}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
}
|
|
);
|
|
|
|
const subscriptionData = response.data;
|
|
|
|
if (isUpgradePayment && currentSubscription && currentPlan) {
|
|
currentSubscription.pendingPaymentInitPoint = subscriptionData.init_point;
|
|
currentSubscription.pendingPaymentPreferenceId = subscriptionData.id;
|
|
currentSubscription.pendingPaymentBillingMonths = undefined;
|
|
currentSubscription.pendingPaymentType = "upgrade";
|
|
currentSubscription.pendingPaymentPlanId = data.planId;
|
|
currentSubscription.pendingPaymentCurrentPlanPrice = currentPlan.price;
|
|
currentSubscription.pendingPaymentRequestedPlanPrice = plan.price;
|
|
currentSubscription.pendingPaymentProratedAmount = proratedAmount;
|
|
currentSubscription.pendingPaymentRemainingDays = remainingDays;
|
|
currentSubscription.pendingPaymentPeriodEndDate = currentSubscription.endDate;
|
|
currentSubscription.lastPaymentStatus = undefined;
|
|
currentSubscription.lastPaymentPreferenceId = undefined;
|
|
currentSubscription.lastPaymentAt = undefined;
|
|
currentSubscription.lastPaymentType = undefined;
|
|
currentSubscription.downgradedFromPlanId = undefined;
|
|
currentSubscription.downgradedFromPlanName = undefined;
|
|
currentSubscription.downgradedFromPlanCode = undefined;
|
|
currentSubscription.downgradedAt = undefined;
|
|
currentSubscription.downgradeReason = undefined;
|
|
currentSubscription.mpInitPoint = subscriptionData.init_point;
|
|
await currentSubscription.save();
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.sessionUser),
|
|
subject: "Se ha creado una actualización de suscripción",
|
|
message: `Se ha creado una actualización al plan ${plan.name}.`,
|
|
type: NotificationType.BILLING,
|
|
code: String(currentSubscription._id)
|
|
});
|
|
|
|
return currentSubscription;
|
|
}
|
|
|
|
if (
|
|
currentSubscription &&
|
|
String(currentSubscription.planId) === String(data.planId) &&
|
|
(currentSubscription.mpStatus === MP_SUBS_STATUS.AUTHORIZED ||
|
|
currentSubscription.mpStatus === MP_SUBS_STATUS.CANCELLED)
|
|
) {
|
|
currentSubscription.pendingPaymentInitPoint = subscriptionData.init_point;
|
|
currentSubscription.pendingPaymentPreferenceId = subscriptionData.id;
|
|
currentSubscription.pendingPaymentBillingMonths = data.months;
|
|
currentSubscription.pendingPaymentType = "extension";
|
|
currentSubscription.lastPaymentStatus = undefined;
|
|
currentSubscription.lastPaymentPreferenceId = undefined;
|
|
currentSubscription.lastPaymentAt = undefined;
|
|
currentSubscription.lastPaymentType = undefined;
|
|
currentSubscription.downgradedFromPlanId = undefined;
|
|
currentSubscription.downgradedFromPlanName = undefined;
|
|
currentSubscription.downgradedFromPlanCode = undefined;
|
|
currentSubscription.downgradedAt = undefined;
|
|
currentSubscription.downgradeReason = undefined;
|
|
currentSubscription.mpInitPoint = subscriptionData.init_point;
|
|
await currentSubscription.save();
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.sessionUser),
|
|
subject: "Se ha creado una nueva extensión de suscripción",
|
|
message: `Se ha creado una nueva extensión para el plan ${plan.name}.`,
|
|
type: NotificationType.BILLING,
|
|
code: String(currentSubscription._id)
|
|
});
|
|
|
|
return currentSubscription;
|
|
}
|
|
|
|
await this.planSuscriptions.delete({ sessionUser: data.sessionUser });
|
|
|
|
const subscription = await this.planSuscriptions.create({
|
|
userId: data.sessionUser,
|
|
planId: data.planId,
|
|
mpPlanId: data.planId,
|
|
startDate: preferenceDate.toDate(),
|
|
endDate: preferenceDate.toDate(),
|
|
isActive: false,
|
|
autoRenew: false,
|
|
mpPayerEmail: sessionUser.mpPayerEmail,
|
|
mpStatus: MP_SUBS_STATUS.PENDING,
|
|
mpDateCreated: new Date(),
|
|
mpInitPoint: subscriptionData.init_point,
|
|
mpPreferenceId: subscriptionData.id,
|
|
billingMonths: data.months,
|
|
pendingPaymentInitPoint: "",
|
|
pendingPaymentPreferenceId: "",
|
|
pendingPaymentBillingMonths: undefined,
|
|
pendingPaymentType: undefined,
|
|
lastPaymentStatus: undefined,
|
|
lastPaymentPreferenceId: undefined,
|
|
lastPaymentAt: undefined,
|
|
lastPaymentType: undefined,
|
|
downgradedFromPlanId: undefined,
|
|
downgradedFromPlanName: undefined,
|
|
downgradedFromPlanCode: undefined,
|
|
downgradedAt: undefined,
|
|
downgradeReason: undefined,
|
|
});
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.sessionUser),
|
|
subject: "Se ha creado una nueva suscripción",
|
|
message: `Se ha creado una nueva suscripción para el plan ${plan.name}.`,
|
|
type: NotificationType.BILLING,
|
|
code: String(subscription._id)
|
|
});
|
|
|
|
return subscription;
|
|
} catch (error) {
|
|
console.log("ha ocurrido un error:", error);
|
|
throw new Error("No fue posible crear la suscripción en el proveedor de pagos");
|
|
}
|
|
}
|
|
|
|
public async cancell(data: CancellPlanSuscriptionParams): Promise<void> {
|
|
const sessionUser = await UsersManager.users.findOne({
|
|
_id: data.sessionUser,
|
|
});
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
const subscription = await this.planSuscriptions.findOne({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
if (!subscription) {
|
|
throw new Error("No se ha encontrado una suscripción activa");
|
|
}
|
|
|
|
if (subscription.mpStatus === MP_SUBS_STATUS.CANCELLED) {
|
|
throw new Error("La suscripción ya se encuentraba cancelada");
|
|
}
|
|
|
|
try {
|
|
if (subscription.mpPreferenceId) {
|
|
// Preferences don't need to be explicitly cancelled in MercadoPago
|
|
// We just cancel it locally if not paid, or change to free plan if paid.
|
|
}
|
|
|
|
const dateNow = new Date();
|
|
const dateEnd = new Date(subscription.endDate);
|
|
|
|
if (dateNow > dateEnd) {
|
|
await this.toFreePlan({
|
|
sessionUser: data.sessionUser,
|
|
downgradeReason: "expired",
|
|
});
|
|
} else {
|
|
subscription.mpStatus = MP_SUBS_STATUS.CANCELLED;
|
|
subscription.mpInitPoint = "";
|
|
subscription.mpPayerEmail = "";
|
|
subscription.mpPayerId = -1;
|
|
subscription.pendingPaymentInitPoint = "";
|
|
subscription.pendingPaymentPreferenceId = "";
|
|
subscription.pendingPaymentBillingMonths = undefined;
|
|
subscription.pendingPaymentType = undefined;
|
|
subscription.pendingPaymentPlanId = undefined;
|
|
subscription.pendingPaymentCurrentPlanPrice = undefined;
|
|
subscription.pendingPaymentRequestedPlanPrice = undefined;
|
|
subscription.pendingPaymentProratedAmount = undefined;
|
|
subscription.pendingPaymentRemainingDays = undefined;
|
|
subscription.pendingPaymentPeriodEndDate = undefined;
|
|
await subscription.save();
|
|
}
|
|
} catch (error) {
|
|
console.log("ha ocurrido un error:", error);
|
|
throw new Error("No fue posible crear la suscripción en el proveedor de pagos");
|
|
}
|
|
}
|
|
|
|
public async toFreePlan(data: ToFreePlanParams): Promise<void> {
|
|
console.log("intentando aplicar el free plan...");
|
|
|
|
const sessionUser = await UsersManager.users.findOne({
|
|
_id: data.sessionUser,
|
|
});
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
const subscription = await this.planSuscriptions.findOne({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
if (!subscription) {
|
|
throw new Error("No se ha encontrado una suscripción activa");
|
|
}
|
|
|
|
const freePlan = await PlansList.plans.findOne({ price: 0 });
|
|
if (!freePlan || !freePlan._id) {
|
|
throw new Error("No se ha encontrado el plan gratuito");
|
|
}
|
|
|
|
if (String(subscription.planId) == String(freePlan._id)) {
|
|
//console.log("El usuario ya tiene el plan gratuito");
|
|
return;
|
|
}
|
|
|
|
const company = await CompaniesList.companies.findOne({
|
|
ownerId: data.sessionUser,
|
|
});
|
|
|
|
if (company) {
|
|
//Intentamos eliminar el bot de el usuario.
|
|
try {
|
|
//console.log("intentando borrar el bot.");
|
|
await WapServerList.deleteBot({
|
|
companyId: String(company._id),
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
} catch (error) {
|
|
console.log("ha ocurrido un error al intentar eliminar el bot:", error);
|
|
}
|
|
}
|
|
|
|
//console.log("guardando el plan gratuito...");
|
|
//try {
|
|
const downgradedFromPlan = data.downgradeReason === "expired"
|
|
? await PlansList.plans.findOne({ _id: String(subscription.planId) })
|
|
: null;
|
|
|
|
console.log("cambiando al free plan:", freePlan.id);
|
|
subscription.planId = freePlan.id;
|
|
subscription.mpStatus = "";
|
|
subscription.isActive = true;
|
|
subscription.autoRenew = false;
|
|
subscription.mpInitPoint = "";
|
|
subscription.mpPreferenceId = "";
|
|
subscription.billingMonths = undefined;
|
|
subscription.pendingPaymentInitPoint = "";
|
|
subscription.pendingPaymentPreferenceId = "";
|
|
subscription.pendingPaymentBillingMonths = undefined;
|
|
subscription.pendingPaymentType = undefined;
|
|
subscription.pendingPaymentPlanId = undefined;
|
|
subscription.pendingPaymentCurrentPlanPrice = undefined;
|
|
subscription.pendingPaymentRequestedPlanPrice = undefined;
|
|
subscription.pendingPaymentProratedAmount = undefined;
|
|
subscription.pendingPaymentRemainingDays = undefined;
|
|
subscription.pendingPaymentPeriodEndDate = undefined;
|
|
subscription.startDate = new Date();
|
|
subscription.endDate = dayjs(new Date()).add(10, "years").toDate();
|
|
if (data.downgradeReason === "expired" && downgradedFromPlan) {
|
|
subscription.downgradedFromPlanId = String(downgradedFromPlan.id || downgradedFromPlan._id);
|
|
subscription.downgradedFromPlanName = downgradedFromPlan.name;
|
|
subscription.downgradedFromPlanCode = downgradedFromPlan.code;
|
|
subscription.downgradedAt = new Date();
|
|
subscription.downgradeReason = "expired";
|
|
} else {
|
|
subscription.downgradedFromPlanId = undefined;
|
|
subscription.downgradedFromPlanName = undefined;
|
|
subscription.downgradedFromPlanCode = undefined;
|
|
subscription.downgradedAt = undefined;
|
|
subscription.downgradeReason = undefined;
|
|
}
|
|
|
|
await subscription.save();
|
|
// console.log("plan gratuito guardado:", freePlan.id);
|
|
//} catch (error) {
|
|
// console.log("no se pudo guardar el plan gratuito.");
|
|
//}
|
|
|
|
await NotificationsManager.sendSystemNotification({
|
|
userId: String(data.sessionUser),
|
|
subject: "Bienvenido al plan gratuito",
|
|
message: `El plan gratuito ha sido asignado a su usuario. Cuando lo desee puede actualizar su plan para acceder más funciones.`,
|
|
type: NotificationType.BILLING,
|
|
code: String(subscription._id)
|
|
});
|
|
|
|
const susInfo: ISubscriptionInfo = {
|
|
id: subscription.id,
|
|
plan: freePlan,
|
|
startDate: subscription.startDate,
|
|
endDate: subscription.endDate,
|
|
isActive: subscription.isActive,
|
|
autoRenew: subscription.autoRenew,
|
|
mpStatus: subscription.mpStatus,
|
|
mpDateCreated: subscription.mpDateCreated,
|
|
lastPaymentStatus: subscription.lastPaymentStatus,
|
|
lastPaymentPreferenceId: subscription.lastPaymentPreferenceId,
|
|
lastPaymentAt: subscription.lastPaymentAt,
|
|
lastPaymentType: subscription.lastPaymentType,
|
|
downgradedFromPlanId: subscription.downgradedFromPlanId,
|
|
downgradedFromPlanName: subscription.downgradedFromPlanName,
|
|
downgradedFromPlanCode: subscription.downgradedFromPlanCode,
|
|
downgradedAt: subscription.downgradedAt,
|
|
downgradeReason: subscription.downgradeReason,
|
|
};
|
|
|
|
io.to(`user:${String(subscription.userId)}`).emit("suscription_status_updated", susInfo);
|
|
}
|
|
|
|
public async findOne(
|
|
data: FindPlanSuscripcionsParams
|
|
): Promise<IPlanSuscriptionDocument | null> {
|
|
const subscription = await this.planSuscriptions.findOne(data);
|
|
|
|
//console.log("findOne", subscription);
|
|
|
|
if (!subscription) {
|
|
return null;
|
|
}
|
|
|
|
const dateNow = new Date();
|
|
const dateEnd = new Date(subscription.endDate);
|
|
|
|
if (dateNow > dateEnd && this.shouldDowngradeExpiredSubscription(subscription)) {
|
|
await this.toFreePlan({
|
|
sessionUser: data.sessionUser,
|
|
downgradeReason: "expired",
|
|
});
|
|
}
|
|
|
|
return await this.planSuscriptions.findOne(data);
|
|
}
|
|
|
|
public async getSubscriptionByUser(
|
|
data: FindPlanSuscripcionsParams
|
|
): Promise<ISubscriptionInfo | null> {
|
|
const subscription = await this.planSuscriptions.findOne(data);
|
|
|
|
if (!subscription) {
|
|
return null;
|
|
}
|
|
|
|
const dateNow = new Date();
|
|
const dateEnd = new Date(subscription.endDate);
|
|
|
|
if (dateNow > dateEnd && this.shouldDowngradeExpiredSubscription(subscription)) {
|
|
await this.toFreePlan({
|
|
sessionUser: data.sessionUser,
|
|
downgradeReason: "expired",
|
|
});
|
|
return this.getSubscriptionByUser(data);
|
|
}
|
|
|
|
let plan = await PlansList.findOne({
|
|
_id: subscription.planId,
|
|
});
|
|
|
|
const isPendingPaidSubscription = subscription.mpStatus === MP_SUBS_STATUS.PENDING && plan.price > 0;
|
|
|
|
if (!isPendingPaidSubscription && (!subscription.isActive || subscription.mpStatus === MP_SUBS_STATUS.PENDING)) {
|
|
const freePlan = await PlansList.findOne({ price: 0 });
|
|
if (freePlan) {
|
|
plan = freePlan;
|
|
}
|
|
}
|
|
|
|
if (!plan) {
|
|
throw new Error("No se ha encontrado el plan");
|
|
}
|
|
|
|
return {
|
|
id: subscription.id,
|
|
plan: plan,
|
|
startDate: subscription.startDate,
|
|
endDate: subscription.endDate,
|
|
isActive: subscription.isActive,
|
|
autoRenew: subscription.autoRenew,
|
|
mpStatus: subscription.mpStatus,
|
|
mpDateCreated: subscription.mpDateCreated,
|
|
pendingPaymentInitPoint: subscription.pendingPaymentInitPoint,
|
|
pendingPaymentPreferenceId: subscription.pendingPaymentPreferenceId,
|
|
pendingPaymentType: subscription.pendingPaymentType,
|
|
lastPaymentStatus: subscription.lastPaymentStatus,
|
|
lastPaymentPreferenceId: subscription.lastPaymentPreferenceId,
|
|
lastPaymentAt: subscription.lastPaymentAt,
|
|
lastPaymentType: subscription.lastPaymentType,
|
|
downgradedFromPlanId: subscription.downgradedFromPlanId,
|
|
downgradedFromPlanName: subscription.downgradedFromPlanName,
|
|
downgradedFromPlanCode: subscription.downgradedFromPlanCode,
|
|
downgradedAt: subscription.downgradedAt,
|
|
downgradeReason: subscription.downgradeReason,
|
|
};
|
|
}
|
|
|
|
public async checkFeature(data: CheckFeatureParams): Promise<boolean> {
|
|
const subscriptionInfo = await this.getSubscriptionByUser({
|
|
sessionUser: data.userId,
|
|
});
|
|
if (!subscriptionInfo) {
|
|
throw new Error("No se encontro una suscripcion activa");
|
|
}
|
|
const plan = subscriptionInfo.plan;
|
|
|
|
if (!subscriptionInfo.isActive) {
|
|
return false;
|
|
}
|
|
|
|
if (!plan) {
|
|
throw new Error("No se encontro el plan de la suscripcion");
|
|
}
|
|
|
|
switch (data.feature) {
|
|
case PlanFeatures.WAP_NOTIFICATIONS:
|
|
if (plan.wapNotifications) {
|
|
return true;
|
|
}
|
|
break;
|
|
case PlanFeatures.MAIL_NOTIFICATIONS:
|
|
if (plan.mailNotifications) {
|
|
return true;
|
|
}
|
|
break;
|
|
case PlanFeatures.SMS_NOTIFICATIONS:
|
|
if (plan.smsNotifications) {
|
|
return true;
|
|
}
|
|
break;
|
|
case PlanFeatures.HAS_BOT:
|
|
if (plan.bot) {
|
|
return true;
|
|
}
|
|
break;
|
|
case PlanFeatures.PAYMENTS:
|
|
if (plan.payments) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public async isSubscriptionActive(data: FindPlanSuscripcionsParams): Promise<boolean> {
|
|
const subscriptionInfo = await this.getSubscriptionByUser(data);
|
|
if (!subscriptionInfo) {
|
|
throw new Error("No se encontro una suscripcion activa");
|
|
}
|
|
return subscriptionInfo.isActive && subscriptionInfo.mpStatus !== MP_SUBS_STATUS.PENDING;
|
|
}
|
|
}
|
|
|
|
const PlanSubscriptionsList = new PlanSuscriptionsManager();
|
|
|
|
export default PlanSubscriptionsList;
|