mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
implement emails for erorr states
This commit is contained in:
@@ -23,7 +23,7 @@ export default class Hostname extends DatabaseProperty {
|
||||
if (Hostname.isValid(value)) {
|
||||
this._route = value;
|
||||
} else {
|
||||
throw new BadDataException('Hostname is not in valid format.');
|
||||
throw new BadDataException('Hostname '+value+' is not in valid format.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ enum EmailTemplateType {
|
||||
StatusPageOwnerResourceCreated = 'StatusPageOwnerResourceCreated.hbs',
|
||||
StatusPageOwnerAdded = 'StatusPageOwnerAdded.hbs',
|
||||
StatusPageOwnerAnnouncementPosted = 'StatusPageOwnerAnnouncementPosted.hbs',
|
||||
SimpleMessage = 'SimpleMessage.hbs',
|
||||
}
|
||||
|
||||
export default EmailTemplateType;
|
||||
|
||||
@@ -531,7 +531,21 @@ export class BillingService {
|
||||
|
||||
await this.stripe.invoices.finalizeInvoice(invoice.id!);
|
||||
|
||||
await this.payInvoice(customerId, invoice.id!);
|
||||
try {
|
||||
await this.payInvoice(customerId, invoice.id!);
|
||||
} catch (err) {
|
||||
// mark invoice as failed and do not collect payment.
|
||||
await this.voidInvoice(invoice.id!);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public static async voidInvoice(invoiceId: string): Promise<Stripe.Invoice> {
|
||||
const invoice = await this.stripe.invoices.voidInvoice(
|
||||
invoiceId
|
||||
);
|
||||
|
||||
return invoice;
|
||||
}
|
||||
|
||||
public static async payInvoice(
|
||||
|
||||
@@ -28,6 +28,8 @@ export default class NotificationService {
|
||||
autoRechargeSmsOrCallByBalanceInUSD: true,
|
||||
autoRechargeSmsOrCallWhenCurrentBalanceFallsInUSD: true,
|
||||
paymentProviderCustomerId: true,
|
||||
name: true,
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners: true
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@@ -56,6 +58,19 @@ export default class NotificationService {
|
||||
project.paymentProviderCustomerId!
|
||||
))
|
||||
) {
|
||||
if (!project.failedCallAndSMSBalanceChargeNotificationSentToOwners) {
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners:
|
||||
true,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
await ProjectService.sendEmailToProjectOwners(project.id!, "ACTION REQUIRED: SMS and Call Recharge Failed for project - " + (project.name || ''), `We have tried recharged your SMS and Call balance for project - ${project.name || ''} and failed. We could not find a payment method for the project. Please add a payment method in project settings.`);
|
||||
}
|
||||
throw new BadDataException(
|
||||
'No payment methods found for the project. Please add a payment method in project settings to continue.'
|
||||
);
|
||||
@@ -74,7 +89,7 @@ export default class NotificationService {
|
||||
// recharge balance
|
||||
const updatedAmount: number = Math.floor(
|
||||
(project.smsOrCallCurrentBalanceInUSDCents || 0) +
|
||||
autoRechargeSmsOrCallByBalanceInUSD * 100
|
||||
autoRechargeSmsOrCallByBalanceInUSD * 100
|
||||
);
|
||||
|
||||
// If the recharge is succcessful, then update the project balance.
|
||||
@@ -88,6 +103,9 @@ export default class NotificationService {
|
||||
data: {
|
||||
smsOrCallCurrentBalanceInUSDCents:
|
||||
updatedAmount,
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners: false, // reset this flag
|
||||
lowCallAndSMSBalanceNotificationSentToOwners: false, // reset this flag
|
||||
notEnabledSmsNotificationSentToOwners: false
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
@@ -95,12 +113,24 @@ export default class NotificationService {
|
||||
},
|
||||
});
|
||||
|
||||
await ProjectService.sendEmailToProjectOwners(project.id!, "SMS and Call Recharge Successful for project - " + (project.name || ''), `We have successfully recharged your SMS and Call balance for project - ${project.name || ''} by ${autoRechargeSmsOrCallByBalanceInUSD} USD. Your current balance is ${updatedAmount / 100} USD.`);
|
||||
|
||||
project.smsOrCallCurrentBalanceInUSDCents =
|
||||
updatedAmount;
|
||||
|
||||
// TODO: Send an email on successful recharge.
|
||||
|
||||
} catch (err) {
|
||||
// TODO: if the recharge fails, then send email to the user.
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners:
|
||||
true,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
await ProjectService.sendEmailToProjectOwners(project.id!, "ACTION REQUIRED: SMS and Call Recharge Failed for project - " + (project.name || ''), `We have tried recharged your SMS and Call balance for project - ${project.name || ''} and failed. Please make sure your payment method is upto date and has sufficient balance. You can add new payment methods in project settings.`);
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ import AccessTokenService from './AccessTokenService';
|
||||
import SubscriptionStatus from 'Common/Types/Billing/SubscriptionStatus';
|
||||
import User from 'Model/Models/User';
|
||||
import NotificationService from './NotificationService';
|
||||
import MailService from './MailService';
|
||||
import logger from '../Utils/Logger';
|
||||
import Email from 'Common/Types/Email';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
@@ -185,7 +189,7 @@ export class Service extends DatabaseService<Model> {
|
||||
plan,
|
||||
project.paymentProviderSubscriptionSeats as number,
|
||||
plan.getYearlyPlanId() ===
|
||||
updateBy.data.paymentProviderPlanId,
|
||||
updateBy.data.paymentProviderPlanId,
|
||||
project.trialEndsAt
|
||||
);
|
||||
|
||||
@@ -724,10 +728,37 @@ export class Service extends DatabaseService<Model> {
|
||||
plan === PlanSelect.Free
|
||||
? false
|
||||
: SubscriptionPlan.isUnpaid(
|
||||
project.paymentProviderSubscriptionStatus ||
|
||||
SubscriptionStatus.Active
|
||||
),
|
||||
project.paymentProviderSubscriptionStatus ||
|
||||
SubscriptionStatus.Active
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
public async sendEmailToProjectOwners(projectId: ObjectID, subject: string, message: string): Promise<void> {
|
||||
const owners: Array<User> = await this.getOwners(projectId);
|
||||
|
||||
if (owners.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const emails: Array<Email> = owners.map((owner: User) => {
|
||||
return owner.email!
|
||||
});
|
||||
|
||||
for(const email of emails) {
|
||||
MailService.sendMail({
|
||||
toEmail: email,
|
||||
templateType: EmailTemplateType.SimpleMessage,
|
||||
vars: {
|
||||
subject: subject,
|
||||
message: message,
|
||||
},
|
||||
subject: subject,
|
||||
}).catch((err: Error) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
export default new Service();
|
||||
|
||||
@@ -636,4 +636,66 @@ export default class Model extends TenantModel {
|
||||
type: ColumnType.Boolean,
|
||||
})
|
||||
public enableAutoRechargeSmsOrCallBalance?: boolean = undefined;
|
||||
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: 'Low Call and SMS Balance Notification Sent to Owners',
|
||||
description:
|
||||
'Low Call and SMS Balance Notification Sent to Owners',
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
default: false,
|
||||
type: ColumnType.Boolean,
|
||||
})
|
||||
public lowCallAndSMSBalanceNotificationSentToOwners?: boolean = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: 'Failed Call and SMS Balance Charge Notification Sent to Owners',
|
||||
description:
|
||||
'Failed Call and SMS Balance Charge Notification Sent to Owners',
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
default: false,
|
||||
type: ColumnType.Boolean,
|
||||
})
|
||||
public failedCallAndSMSBalanceChargeNotificationSentToOwners?: boolean = undefined;
|
||||
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: 'Failed Call and SMS Balance Charge Notification Sent to Owners',
|
||||
description:
|
||||
'Failed Call and SMS Balance Charge Notification Sent to Owners',
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
default: false,
|
||||
type: ColumnType.Boolean,
|
||||
})
|
||||
public notEnabledSmsNotificationSentToOwners?: boolean = undefined;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ export default class SmsService {
|
||||
select: {
|
||||
smsOrCallCurrentBalanceInUSDCents: true,
|
||||
enableSmsNotifications: true,
|
||||
lowCallAndSMSBalanceNotificationSentToOwners: true,
|
||||
name: true,
|
||||
notEnabledSmsNotificationSentToOwners: true
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@@ -82,12 +85,26 @@ export default class SmsService {
|
||||
if (!project.enableSmsNotifications) {
|
||||
smsLog.status = SmsStatus.Error;
|
||||
smsLog.statusMessage = `SMS notifications are not enabled for this project. Please enable SMS notifications in project settings.`;
|
||||
|
||||
await SmsLogService.create({
|
||||
data: smsLog,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
if (!project.notEnabledSmsNotificationSentToOwners) {
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
notEnabledSmsNotificationSentToOwners:
|
||||
true,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
await ProjectService.sendEmailToProjectOwners(project.id!, "SMS notifications not enabled for " + (project.name || ''), `We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/> <br/> This SMS was not sent because SMS notifications are not enabled for this project. Please enable SMS notifications in project settings.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,6 +124,20 @@ export default class SmsService {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!project.lowCallAndSMSBalanceNotificationSentToOwners) {
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
lowCallAndSMSBalanceNotificationSentToOwners:
|
||||
true,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
await ProjectService.sendEmailToProjectOwners(project.id!, "Low SMS and Call Balance for " + (project.name || ''), `We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/>This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${project.smsOrCallCurrentBalanceInUSDCents || 0} USD cents. Required balance to send this SMS should is ${SMSDefaultCostInCents} USD cents. Please enable auto recharge or recharge manually.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,6 +153,19 @@ export default class SmsService {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
if (!project.lowCallAndSMSBalanceNotificationSentToOwners) {
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
lowCallAndSMSBalanceNotificationSentToOwners:
|
||||
true,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
await ProjectService.sendEmailToProjectOwners(project.id!, "Low SMS and Call Balance for " + (project.name || ''), `We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/> <br/> This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${project.smsOrCallCurrentBalanceInUSDCents} cents. Required balance is ${SMSDefaultCostInCents} cents to send this SMS. Please enable auto recharge or recharge manually.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -144,13 +188,14 @@ export default class SmsService {
|
||||
|
||||
project.smsOrCallCurrentBalanceInUSDCents = Math.floor(
|
||||
project.smsOrCallCurrentBalanceInUSDCents! -
|
||||
SMSDefaultCostInCents
|
||||
SMSDefaultCostInCents
|
||||
);
|
||||
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
smsOrCallCurrentBalanceInUSDCents:
|
||||
project.smsOrCallCurrentBalanceInUSDCents,
|
||||
notEnabledSmsNotificationSentToOwners: false, // reset this flag
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
|
||||
11
Notification/Templates/SimpleMessage.hbs
Normal file
11
Notification/Templates/SimpleMessage.hbs
Normal file
@@ -0,0 +1,11 @@
|
||||
{{> Start this}}
|
||||
|
||||
{{> Logo this}}
|
||||
|
||||
{{> EmailTitle title=subject }}
|
||||
|
||||
{{> InfoBlock info=message}}
|
||||
|
||||
{{> Footer this}}
|
||||
|
||||
{{> End}}
|
||||
Reference in New Issue
Block a user