From dca1d2c370509980dc75e13cf3ca8ccc73f182e0 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Fri, 27 Oct 2023 17:14:58 +0100 Subject: [PATCH] add call and sms cost --- Common/Types/Call/CallRequest.ts | 9 +++++ .../oneuptime/templates/notification.yaml | 2 +- HelmChart/Public/oneuptime/values.yaml | 4 +- Notification/Config.ts | 12 ++++++ Notification/Services/CallService.ts | 37 +++++++++++-------- Notification/Services/SmsService.ts | 34 +++++++++-------- config.example.env | 3 ++ docker-compose.base.yml | 2 + 8 files changed, 70 insertions(+), 33 deletions(-) diff --git a/Common/Types/Call/CallRequest.ts b/Common/Types/Call/CallRequest.ts index 4f99e3cd99..ba06089da6 100644 --- a/Common/Types/Call/CallRequest.ts +++ b/Common/Types/Call/CallRequest.ts @@ -28,3 +28,12 @@ export interface CallRequestMessage { export default interface CallRequest extends CallRequestMessage { to: Phone; } + +export const isHighRiskPhoneNumber: Function = (phoneNumber: Phone): boolean => { + // Pakistan + if (phoneNumber.toString().startsWith('+92')) { + return true; + } + + return false; +}; diff --git a/HelmChart/Public/oneuptime/templates/notification.yaml b/HelmChart/Public/oneuptime/templates/notification.yaml index 8efa2930b3..943a25074c 100644 --- a/HelmChart/Public/oneuptime/templates/notification.yaml +++ b/HelmChart/Public/oneuptime/templates/notification.yaml @@ -1,5 +1,5 @@ # OneUptime notification Deployment -{{- $notificationEnv := dict "PORT" $.Values.port.notification "SMS_DEFAULT_COST_IN_CENTS" $.Values.billing.smsDefaultValueInCents "CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE" $.Values.billing.callDefaultValueInCentsPerMinute "INTERNAL_SMTP_EMAIL" $.Values.internalSmtp.email "INTERNAL_SMTP_PASSWORD" "internal_smtp_password" -}} +{{- $notificationEnv := dict "PORT" $.Values.port.notification "SMS_HIGH_RISK_COST_IN_CENTS" $.Values.billing.smsHighRiskValueInCents "CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE" $.Values.billing.callHighRiskValueInCentsPerMinute "SMS_DEFAULT_COST_IN_CENTS" $.Values.billing.smsDefaultValueInCents "CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE" $.Values.billing.callDefaultValueInCentsPerMinute "INTERNAL_SMTP_EMAIL" $.Values.internalSmtp.email "INTERNAL_SMTP_PASSWORD" "internal_smtp_password" -}} {{- $notificationDeploymentArgs :=dict "IsServer" true "ServiceName" "notification" "Port" $.Values.port.notification "Release" $.Release "Values" $.Values "Env" $notificationEnv -}} {{- include "oneuptime.deployment" $notificationDeploymentArgs }} --- diff --git a/HelmChart/Public/oneuptime/values.yaml b/HelmChart/Public/oneuptime/values.yaml index f90635d752..d3c683311a 100644 --- a/HelmChart/Public/oneuptime/values.yaml +++ b/HelmChart/Public/oneuptime/values.yaml @@ -87,7 +87,9 @@ billing: publicKey: privateKey: smsDefaultValueInCents: - callDefaultValueInCentsPerMinute: + callDefaultValueInCentsPerMinute: + smsHighRiskValueInCents: + callHighRiskValueInCentsPerMinute: subscriptionPlan: basic: diff --git a/Notification/Config.ts b/Notification/Config.ts index 5ef4e09154..a1c67e1ccf 100644 --- a/Notification/Config.ts +++ b/Notification/Config.ts @@ -185,6 +185,18 @@ export const SMSDefaultCostInCents: number = process.env[ ? parseInt(process.env['SMS_DEFAULT_COST_IN_CENTS']) : 0; +export const SMSHighRiskCostInCents: number = process.env[ + 'SMS_HIGH_RISK_COST_IN_CENTS' +] + ? parseInt(process.env['SMS_HIGH_RISK_COST_IN_CENTS']) + : 0; + +export const CallHighRiskCostInCentsPerMinute: number = process.env[ + 'CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE' +] + ? parseInt(process.env['CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE']) + : 0; + export const CallDefaultCostInCentsPerMinute: number = process.env[ 'CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE' ] diff --git a/Notification/Services/CallService.ts b/Notification/Services/CallService.ts index eedcca134d..39401e149f 100644 --- a/Notification/Services/CallService.ts +++ b/Notification/Services/CallService.ts @@ -2,13 +2,18 @@ import ObjectID from 'Common/Types/ObjectID'; import Phone from 'Common/Types/Phone'; import { CallDefaultCostInCentsPerMinute, + CallHighRiskCostInCentsPerMinute, TwilioConfig, getTwilioConfig, } from '../Config'; import Twilio from 'twilio'; import CallLog from 'Model/Models/CallLog'; import CallStatus from 'Common/Types/Call/CallStatus'; -import CallRequest, { GatherInput, Say } from 'Common/Types/Call/CallRequest'; +import CallRequest, { + GatherInput, + Say, + isHighRiskPhoneNumber, +} from 'Common/Types/Call/CallRequest'; import { IsBillingEnabled } from 'CommonServer/EnvironmentConfig'; import CallLogService from 'CommonServer/Services/CallLogService'; import ProjectService from 'CommonServer/Services/ProjectService'; @@ -33,6 +38,15 @@ export default class CallService { userOnCallLogTimelineId?: ObjectID | undefined; // user notification log timeline id } ): Promise { + let callCost: number = 0; + + if (IsBillingEnabled) { + callCost = CallDefaultCostInCentsPerMinute / 100; + if (isHighRiskPhoneNumber(callRequest.to)) { + callCost = CallHighRiskCostInCentsPerMinute / 100; + } + } + const twilioConfig: TwilioConfig | null = await getTwilioConfig(); if (!twilioConfig) { @@ -165,9 +179,7 @@ export default class CallService { `We tried to make a call to ${callRequest.to.toString()}. This call was not made because project does not have enough balance to make calls. Current balance is ${ (project.smsOrCallCurrentBalanceInUSDCents || 0) / 100 - } USD. Required balance to send this SMS should is ${ - CallDefaultCostInCentsPerMinute / 100 - } USD. Please enable auto recharge or recharge manually.` + } USD. Required balance to send this SMS should is ${callCost} USD. Please enable auto recharge or recharge manually.` ); } return; @@ -175,14 +187,12 @@ export default class CallService { if ( project.smsOrCallCurrentBalanceInUSDCents < - CallDefaultCostInCentsPerMinute + callCost * 100 ) { callLog.status = CallStatus.LowBalance; callLog.statusMessage = `Project does not have enough balance to make this call. Current balance is ${ project.smsOrCallCurrentBalanceInUSDCents / 100 - } USD. Required balance is ${ - CallDefaultCostInCentsPerMinute / 100 - } USD to make this call.`; + } USD. Required balance is ${callCost} USD to make this call.`; logger.error(callLog.statusMessage); await CallLogService.create({ data: callLog, @@ -207,9 +217,7 @@ export default class CallService { (project.name || ''), `We tried to make a call to ${callRequest.to.toString()}. This call was not made because project does not have enough balance to make a call. Current balance is ${ project.smsOrCallCurrentBalanceInUSDCents / 100 - } USD. Required balance is ${ - CallDefaultCostInCentsPerMinute / 100 - } USD to make this call. Please enable auto recharge or recharge manually.` + } USD. Required balance is ${callCost} USD to make this call. Please enable auto recharge or recharge manually.` ); } return; @@ -230,18 +238,17 @@ export default class CallService { logger.info('Call Request sent successfully.'); logger.info(callLog.statusMessage); if (IsBillingEnabled && project) { - callLog.callCostInUSDCents = CallDefaultCostInCentsPerMinute; + callLog.callCostInUSDCents = callCost * 100; if (twillioCall && parseInt(twillioCall.duration) > 60) { callLog.callCostInUSDCents = Math.ceil( Math.ceil(parseInt(twillioCall.duration) / 60) * - CallDefaultCostInCentsPerMinute + (callCost * 100) ); } project.smsOrCallCurrentBalanceInUSDCents = Math.floor( - project.smsOrCallCurrentBalanceInUSDCents! - - CallDefaultCostInCentsPerMinute + project.smsOrCallCurrentBalanceInUSDCents! - callCost * 100 ); await ProjectService.updateOneById({ diff --git a/Notification/Services/SmsService.ts b/Notification/Services/SmsService.ts index 178402ad95..9bdc6920cf 100644 --- a/Notification/Services/SmsService.ts +++ b/Notification/Services/SmsService.ts @@ -2,6 +2,7 @@ import ObjectID from 'Common/Types/ObjectID'; import Phone from 'Common/Types/Phone'; import { SMSDefaultCostInCents, + SMSHighRiskCostInCents, TwilioConfig, getTwilioConfig, } from '../Config'; @@ -18,6 +19,7 @@ import logger from 'CommonServer/Utils/Logger'; import UserOnCallLogTimelineService from 'CommonServer/Services/UserOnCallLogTimelineService'; import UserNotificationStatus from 'Common/Types/UserNotification/UserNotificationStatus'; import BadDataException from 'Common/Types/Exception/BadDataException'; +import { isHighRiskPhoneNumber } from 'Common/Types/Call/CallRequest'; export default class SmsService { public static async sendSms( @@ -30,6 +32,16 @@ export default class SmsService { userOnCallLogTimelineId?: ObjectID | undefined; } ): Promise { + let smsCost: number = 0; + + if (IsBillingEnabled) { + smsCost = SMSDefaultCostInCents / 100; + + if (isHighRiskPhoneNumber(to)) { + smsCost = SMSHighRiskCostInCents / 100; + } + } + const twilioConfig: TwilioConfig | null = await getTwilioConfig(); if (!twilioConfig) { @@ -161,24 +173,17 @@ export default class SmsService { `We tried to send an SMS to ${to.toString()} with message:

${message}
This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${ (project.smsOrCallCurrentBalanceInUSDCents || 0) / 100 - } USD cents. Required balance to send this SMS should is ${ - SMSDefaultCostInCents / 100 - } USD. Please enable auto recharge or recharge manually.` + } USD cents. Required balance to send this SMS should is ${smsCost} USD. Please enable auto recharge or recharge manually.` ); } return; } - if ( - project.smsOrCallCurrentBalanceInUSDCents < - SMSDefaultCostInCents - ) { + if (project.smsOrCallCurrentBalanceInUSDCents < smsCost * 100) { smsLog.status = SmsStatus.LowBalance; smsLog.statusMessage = `Project does not have enough balance to send SMS. Current balance is ${ project.smsOrCallCurrentBalanceInUSDCents / 100 - } USD. Required balance is ${ - SMSDefaultCostInCents / 100 - } USD to send this SMS.`; + } USD. Required balance is ${smsCost} USD to send this SMS.`; logger.error(smsLog.statusMessage); await SmsLogService.create({ data: smsLog, @@ -203,9 +208,7 @@ export default class SmsService { (project.name || ''), `We tried to send an SMS to ${to.toString()} with message:

${message}

This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${ project.smsOrCallCurrentBalanceInUSDCents / 100 - } USD. Required balance is ${ - SMSDefaultCostInCents / 100 - } USD to send this SMS. Please enable auto recharge or recharge manually.` + } USD. Required balance is ${smsCost} USD to send this SMS. Please enable auto recharge or recharge manually.` ); } return; @@ -229,11 +232,10 @@ export default class SmsService { logger.info(smsLog.statusMessage); if (IsBillingEnabled && project) { - smsLog.smsCostInUSDCents = SMSDefaultCostInCents; + smsLog.smsCostInUSDCents = smsCost * 100; project.smsOrCallCurrentBalanceInUSDCents = Math.floor( - project.smsOrCallCurrentBalanceInUSDCents! - - SMSDefaultCostInCents + project.smsOrCallCurrentBalanceInUSDCents! - smsCost * 100 ); await ProjectService.updateOneById({ diff --git a/config.example.env b/config.example.env index 40a9c9ea75..0f8766576e 100644 --- a/config.example.env +++ b/config.example.env @@ -177,6 +177,9 @@ METERED_PLAN_ACTIVE_MONITORING=priceMonthlyId,1,active-monitor,month SMS_DEFAULT_COST_IN_CENTS= CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE= +SMS_HIGH_RISK_COST_IN_CENTS= +CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE= + # IS BILLING ENABLED for this installer. BILLING_ENABLED=false # Public and private key for billing provider, usually stripe. diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 7fa4cd2df2..713c759d6a 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -172,6 +172,8 @@ services: PORT: ${NOTIFICATION_PORT} SMS_DEFAULT_COST_IN_CENTS: ${SMS_DEFAULT_COST_IN_CENTS} CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE: ${CALL_DEFAULT_COST_IN_CENTS_PER_MINUTE} + SMS_HIGH_RISK_COST_IN_CENTS: ${SMS_HIGH_RISK_COST_IN_CENTS} + CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE: ${CALL_HIGH_RISK_COST_IN_CENTS_PER_MINUTE} INTERNAL_SMTP_EMAIL: ${INTERNAL_SMTP_EMAIL} INTERNAL_SMTP_PASSWORD: ${INTERNAL_SMTP_PASSWORD} depends_on: