billing - settings - new rest api
MODIFY
src/server/rest/v1/router/GlobalRouter.ts
src/server/rest/v1/router/GlobalRouter.ts
@@ -3,6 +3,7 @@ import express, { NextFunction, Request, Response } from 'express';
import AuthRouter from './auth/AuthRouter';
import AuthService from '../service/AuthService';
import BillingRouter from './api/BillingRouter';
+import BillingSettingRouter from './api/BillingSettingRouter';
import ChargingStationRouter from './api/ChargingStationRouter';
import { StatusCodes } from 'http-status-codes';
import SwaggerRouter from './doc/SwaggerRouter';
@@ -43,6 +44,7 @@ export default class GlobalRouter {
new TransactionRouter().buildRoutes(),
new UserRouter().buildRoutes(),
new BillingRouter().buildRoutes(),
+ new BillingSettingRouter().buildRoutes(),
]);
}

ADD
src/server/rest/v1/router/api/BillingSettingRouter.ts
+import { ServerAction, ServerRoute } from '../../../../../types/Server';
+import express, { NextFunction, Request, Response } from 'express';
+
+import BillingSettingService from '../../service/BillingSettingService';
+import RouterUtils from '../RouterUtils';
+
+export default class SettingRouter {
+ private router: express.Router;
+
+ public constructor() {
+ this.router = express.Router();
+ }
+
+ public buildRoutes(): express.Router {
+ this.buildRouteBillingSettings();
+ this.buildRouteBillingSetting();
+ // this.buildRouteCreateBillingSetting();
+ this.buildRouteUpdateBillingSetting();
+ // this.buildRouteDeleteBillingSetting();
+ this.buildRouteCheckBillingSetting();
+ this.buildRouteActivateBillingSetting();
+ return this.router;
+ }
+
+ protected buildRouteBillingSettings(): void {
+ this.router.get(`/${ServerRoute.REST_BILLING_SETTINGS}`, (req: Request, res: Response, next: NextFunction) => {
+ void RouterUtils.handleServerAction(BillingSettingService.handleGetBillingSettings.bind(this), ServerAction.SETTINGS, req, res, next);
+ });
+ }
+
+ protected buildRouteBillingSetting(): void {
+ this.router.get(`/${ServerRoute.REST_BILLING_SETTING}`, (req: Request, res: Response, next: NextFunction) => {
+ void RouterUtils.handleServerAction(BillingSettingService.handleUpdateBillingSetting.bind(this), ServerAction.SETTINGS, req, res, next);
+ });
+ }
+
+ protected buildRouteUpdateBillingSetting(): void {
+ this.router.put(`/${ServerRoute.REST_BILLING_SETTING}`, (req: Request, res: Response, next: NextFunction) => {
+ void RouterUtils.handleServerAction(BillingSettingService.handleUpdateBillingSetting.bind(this), ServerAction.SETTINGS, req, res, next);
+ });
+ }
+
+ protected buildRouteCheckBillingSetting(): void {
+ this.router.post(`/${ServerRoute.REST_BILLING_SETTING_CHECK}`, (req: Request, res: Response, next: NextFunction) => {
+ void RouterUtils.handleServerAction(BillingSettingService.handleCheckBillingSetting.bind(this), ServerAction.SETTINGS, req, res, next);
+ });
+ }
+
+ protected buildRouteActivateBillingSetting(): void {
+ this.router.post(`/${ServerRoute.REST_BILLING_SETTING_CHECK}`, (req: Request, res: Response, next: NextFunction) => {
+ void RouterUtils.handleServerAction(BillingSettingService.handleActivateBillingSetting.bind(this), ServerAction.SETTINGS, req, res, next);
+ });
+ }
+}
+

ADD
src/server/rest/v1/service/BillingSettingService.ts
+import { Action, Entity } from '../../../../types/Authorization';
+import { NextFunction, Request, Response } from 'express';
+
+import AppAuthError from '../../../../exception/AppAuthError';
+import Authorizations from '../../../../authorization/Authorizations';
+import BillingSettingStorage from '../../../../storage/mongodb/BillingSettingStorage';
+import { BillingSettings } from '../../../../types/Setting';
+import Constants from '../../../../utils/Constants';
+import { HTTPAuthError } from '../../../../types/HTTPError';
+import { ServerAction } from '../../../../types/Server';
+import { StatusCodes } from 'http-status-codes';
+
+const MODULE_NAME = 'BillingSettingService';
+
+export default class BillingSettingService {
+
+ // eslint-disable-next-line @typescript-eslint/require-await
+ public static async handleGetBillingSettings(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ res.sendStatus(StatusCodes.NOT_IMPLEMENTED);
+ next();
+ }
+
+ public static async handleGetBillingSetting(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ if (!await Authorizations.canReadSetting(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: req.user,
+ action: Action.READ, entity: Entity.SETTING,
+ module: MODULE_NAME, method: 'handleGetBillingSetting',
+ });
+ }
+ const billingSettings: BillingSettings = await BillingSettingStorage.getBillingSettings(req.user.tenantID, true);
+ res.json(billingSettings);
+ next();
+ }
+
+ public static async handleUpdateBillingSetting(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ if (!await Authorizations.canUpdateSetting(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: req.user,
+ action: Action.UPDATE, entity: Entity.SETTING,
+ module: MODULE_NAME, method: 'handleUpdateBillingSetting',
+ });
+ }
+ res.json(StatusCodes.NOT_IMPLEMENTED);
+ // const billingSettings: BillingSettings = null;
+ // await BillingSettingStorage.saveBillingSettings(req.user.tenantID, billingSettings);
+ // Ok
+ res.json(Constants.REST_RESPONSE_SUCCESS);
+ next();
+ }
+
+ // eslint-disable-next-line @typescript-eslint/require-await
+ public static async handleCheckBillingSetting(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ res.sendStatus(StatusCodes.NOT_IMPLEMENTED);
+ next();
+ }
+
+ // eslint-disable-next-line @typescript-eslint/require-await
+ public static async handleActivateBillingSetting(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ res.sendStatus(StatusCodes.NOT_IMPLEMENTED);
+ next();
+ }
+
+}

ADD
src/storage/mongodb/BillingSettingStorage.ts
+import { BillingSettings, BillingSettingsType, SettingDB } from '../../types/Setting';
+
+import BackendError from '../../exception/BackendError';
+import Constants from '../../utils/Constants';
+import Cypher from '../../utils/Cypher';
+import SettingStorage from './SettingStorage';
+import TenantComponents from '../../types/TenantComponents';
+
+const MODULE_NAME = 'BillingSettingStorage';
+export default class BillingSettingStorage {
+
+ public static async getBillingSettings(tenantID: string, encryptSensitiveData = false): Promise<BillingSettings> {
+ const billingSettings = {
+ identifier: TenantComponents.BILLING,
+ } as BillingSettings;
+ const settings = await SettingStorage.getSettings(tenantID, { identifier: TenantComponents.BILLING }, Constants.DB_PARAMS_SINGLE_RECORD);
+ // Encrypt Sensitive Data
+ if (encryptSensitiveData) {
+ for (const setting of settings.result) {
+ // Hash sensitive data before being sent to the front end
+ Cypher.hashSensitiveDataInJSON(setting);
+ }
+ }
+ // TODO - To be clarified - Here we only consider the first one
+ if (settings && settings.count > 0 && settings.result[0].content) {
+ const config = settings.result[0].content;
+ billingSettings.id = settings.result[0].id;
+ billingSettings.sensitiveData = settings.result[0].sensitiveData;
+ billingSettings.backupSensitiveData = settings.result[0].backupSensitiveData;
+ // Billing Common Properties
+ if (config.billing) {
+ const { isTransactionBillingActivated, immediateBillingAllowed, periodicBillingAllowed, taxID, usersLastSynchronizedOn } = config.billing;
+ billingSettings.billing = {
+ isTransactionBillingActivated,
+ immediateBillingAllowed,
+ periodicBillingAllowed,
+ usersLastSynchronizedOn,
+ taxID,
+ };
+ }
+ // Billing Concrete Implementation Properties
+ if (config.stripe) {
+ billingSettings.type = BillingSettingsType.STRIPE;
+ const { url, publicKey, secretKey } = config.stripe;
+ billingSettings.stripe = {
+ url,
+ publicKey,
+ secretKey,
+ };
+ }
+ return billingSettings;
+ }
+ }
+
+ public static async saveBillingSettings(tenantID: string, billingSettingsToSave: BillingSettings): Promise<string> {
+ // Load previous settings
+ const settings = await SettingStorage.getBillingSettings(tenantID);
+ // Build internal structure
+ const settingsToSave = {
+ id: billingSettingsToSave.id,
+ identifier: billingSettingsToSave.identifier,
+ sensitiveData: billingSettingsToSave.sensitiveData,
+ backupSensitiveData: billingSettingsToSave.backupSensitiveData,
+ lastChangedOn: new Date(),
+ content: {
+ billing: billingSettingsToSave.billing,
+ stripe: billingSettingsToSave.stripe
+ },
+ } as SettingDB;
+ // Preserve Data that the DASHBOARD is not aware off
+ settingsToSave.content.billing.isTransactionBillingActivated = settings.billing.isTransactionBillingActivated;
+ // Populates implementation-specific data
+ if (settings.type === BillingSettingsType.STRIPE) {
+ if (!billingSettingsToSave.stripe.secretKey) {
+ throw new BackendError({
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME,
+ method: 'saveBillingSettings',
+ message: 'One or several mandatory fields are missing'
+ });
+ }
+ settingsToSave.content.stripe = billingSettingsToSave.stripe;
+ }
+ // Save
+ return SettingStorage.saveSettings(tenantID, settingsToSave);
+ }
+}

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -505,4 +505,11 @@ export enum ServerRoute {
REST_BILLING_PAYMENT_METHOD_SETUP = 'users/:userID/payment-methods/setup',
REST_BILLING_PAYMENT_METHOD_ATTACH = 'users/:userID/payment-methods/:paymentMethodID/attach',
REST_BILLING_PAYMENT_METHOD_DETACH = 'users/:userID/payment-methods/:paymentMethodID/detach',
+
+ // BILLING SETTING URLs
+ REST_BILLING_SETTINGS = 'billing-settings',
+ REST_BILLING_SETTING = 'billing-settings/:id',
+ REST_BILLING_SETTING_CHECK = 'billing-settings/:id/check',
+ REST_BILLING_SETTING_ACTIVATE = 'billing-settings/:id/activate',
+ REST_BILLING_SETTING_CHECK_CONNECTION = 'billing-settings/:id/checkConnection',
}

billing - settings - new rest api