billing - check access control on statr transaction
MODIFY
src/integration/billing/BillingIntegration.ts
src/integration/billing/BillingIntegration.ts
@@ -5,6 +5,7 @@ import User, { UserStatus } from '../../types/User';
import BackendError from '../../exception/BackendError';
import { BillingSettings } from '../../types/Setting';
import BillingStorage from '../../storage/mongodb/BillingStorage';
+import ChargingStation from '../../types/ChargingStation';
import Constants from '../../utils/Constants';
import { DataResult } from '../../types/DataResult';
import { Decimal } from 'decimal.js';
@@ -228,7 +229,7 @@ export default abstract class BillingIntegration {
}
}
- public checkStartTransaction(transaction: Transaction): void {
+ public checkStartTransaction(transaction: Transaction, chargingStation: ChargingStation): void {
// Check User
if (!transaction.userID || !transaction.user) {
throw new BackendError({
@@ -238,7 +239,7 @@ export default abstract class BillingIntegration {
action: ServerAction.BILLING_TRANSACTION
});
}
- // Check Billing Data (only in Live Mode)
+ // Check Billing Data
if (!transaction.user?.billingData?.customerID) {
throw new BackendError({
message: 'User has no billing data or no customer ID',
@@ -247,18 +248,23 @@ export default abstract class BillingIntegration {
action: ServerAction.BILLING_TRANSACTION
});
}
+ if (!chargingStation) {
+ throw new BackendError({
+ message: 'The charging station is mandatory to start a transaction',
+ module: MODULE_NAME,
+ method: 'checkStartTransaction',
+ action: ServerAction.BILLING_TRANSACTION
+ });
}
-
- private async _getUsersWithNoBillingData(): Promise<User[]> {
- const newUsers = await UserStorage.getUsers(this.tenant,
- {
- statuses: [UserStatus.ACTIVE],
- notSynchronizedBillingData: true
- }, Constants.DB_PARAMS_MAX_LIMIT);
- if (newUsers.count > 0) {
- return newUsers.result;
+ // Check for the Site Area
+ if (!chargingStation.siteArea) {
+ throw new BackendError({
+ message: 'The site area is mandatory to start a transaction',
+ module: MODULE_NAME,
+ method: 'checkStartTransaction',
+ action: ServerAction.BILLING_TRANSACTION
+ });
}
- return [];
}
private async _synchronizeUser(user: User, forceMode = false): Promise<BillingUser> {
@@ -498,7 +504,7 @@ export default abstract class BillingIntegration {
abstract resetConnectionSettings() : Promise<BillingSettings>;
- abstract startTransaction(transaction: Transaction): Promise<BillingDataTransactionStart>;
+ abstract startTransaction(transaction: Transaction, chargingStation: ChargingStation): Promise<BillingDataTransactionStart>;
abstract updateTransaction(transaction: Transaction): Promise<BillingDataTransactionUpdate>;

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -13,6 +13,7 @@ import BackendError from '../../../exception/BackendError';
import BillingIntegration from '../BillingIntegration';
import { BillingSettings } from '../../../types/Setting';
import BillingStorage from '../../../storage/mongodb/BillingStorage';
+import ChargingStation from '../../../types/ChargingStation';
import Constants from '../../../utils/Constants';
import Cypher from '../../../utils/Cypher';
import DatabaseUtils from '../../../storage/mongodb/DatabaseUtils';
@@ -781,30 +782,29 @@ export default class StripeBillingIntegration extends BillingIntegration {
}
}
- public async startTransaction(transaction: Transaction): Promise<BillingDataTransactionStart> {
+ public async startTransaction(transaction: Transaction, chargingStation: ChargingStation): Promise<BillingDataTransactionStart> {
if (!this.settings.billing.isTransactionBillingActivated) {
return {
// Keeps track whether the billing was activated or not on start transaction
withBillingActive: false
};
}
- // User with free access are not billed
- if (transaction.user?.freeAccess) {
+ // Check Transaction
+ this.checkStartTransaction(transaction, chargingStation);
+ // Check Free Access
+ if (transaction.user.freeAccess) {
return {
- // Do not bill internal users
withBillingActive: false
};
}
- if (!transaction.chargeBox?.siteArea?.accessControl) {
+ // Check Access Control
+ if (!chargingStation.siteArea.accessControl) {
return {
- // Do not bill sessions where the Access Control is OFF
withBillingActive: false
};
}
// Check Stripe
await this.checkConnection();
- // Check Transaction
- this.checkStartTransaction(transaction);
// Check Start Transaction Prerequisites
const customerID: string = transaction.user?.billingData?.customerID;
// Check whether the customer exists or not

MODIFY
src/scheduler/tasks/BillPendingTransactionTask.ts
src/scheduler/tasks/BillPendingTransactionTask.ts
@@ -110,7 +110,7 @@ export default class BillPendingTransactionTask extends TenantSchedulerTask {
transaction.stop.extraInactivityComputed = true;
transaction.stop.extraInactivitySecs = 0;
// Billing - This starts the billing async task - the BillingStatus will remain PENDING for a while!
- await OCPPUtils.processTransactionBilling(tenant, transaction, TransactionAction.END);
+ await OCPPUtils.processTransactionBilling(tenant, transaction, transaction.chargeBox, TransactionAction.END);
// Save
await TransactionStorage.saveTransaction(tenant, transaction);
await Logging.logInfo({

MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -217,7 +217,7 @@ export default class OCPPService {
// Pricing
await OCPPUtils.processTransactionPricing(tenant, transaction, chargingStation, consumption, TransactionAction.UPDATE);
// Billing
- await OCPPUtils.processTransactionBilling(tenant, transaction, TransactionAction.UPDATE);
+ await OCPPUtils.processTransactionBilling(tenant, transaction, chargingStation, TransactionAction.UPDATE);
}
// Save
await ConsumptionStorage.saveConsumption(tenant, consumption);
@@ -364,7 +364,7 @@ export default class OCPPService {
// Enrich
this.enrichStartTransaction(tenant, startTransaction, chargingStation);
// Create Transaction
- const newTransaction = await this.createTransaction(tenant, chargingStation, startTransaction);
+ const newTransaction = await this.createTransaction(tenant, startTransaction);
// Check User
const { user, tag } = await Authorizations.isAuthorizedToStartTransaction(
tenant, chargingStation, startTransaction.tagID, newTransaction, ServerAction.OCPP_START_TRANSACTION, Action.START_TRANSACTION);
@@ -382,7 +382,7 @@ export default class OCPPService {
const firstConsumption = await OCPPUtils.createFirstConsumption(tenant, chargingStation, newTransaction);
await OCPPUtils.processTransactionPricing(tenant, newTransaction, chargingStation, firstConsumption, TransactionAction.START);
// Billing
- await OCPPUtils.processTransactionBilling(tenant, newTransaction, TransactionAction.START);
+ await OCPPUtils.processTransactionBilling(tenant, newTransaction, chargingStation, TransactionAction.START);
// Roaming
await OCPPUtils.processTransactionRoaming(tenant, newTransaction, chargingStation, chargingStation.siteArea, tag, TransactionAction.START);
// Save it
@@ -489,7 +489,7 @@ export default class OCPPService {
// Update Transaction with Stop Transaction and Stop MeterValues
OCPPUtils.updateTransactionWithStopTransaction(transaction, chargingStation, stopTransaction, user, alternateUser, tagID, isSoftStop);
// Bill
- await OCPPUtils.processTransactionBilling(tenant, transaction, TransactionAction.STOP);
+ await OCPPUtils.processTransactionBilling(tenant, transaction, chargingStation, TransactionAction.STOP);
// Roaming
await OCPPUtils.processTransactionRoaming(tenant, transaction, chargingStation, chargingStation.siteArea, transaction.tag, TransactionAction.STOP);
// Save the transaction
@@ -796,7 +796,7 @@ export default class OCPPService {
const transactionUpdated = await this.checkAndComputeTransactionExtraInactivityFromStatusNotification(
tenant, chargingStation, lastTransaction, connector, statusNotification);
// Billing: Trigger the asynchronous billing task
- const billingDataUpdated = await this.checkAndBillTransaction(tenant, lastTransaction);
+ const billingDataUpdated = await this.checkAndBillTransaction(tenant, lastTransaction, chargingStation);
// OCPI: Post the CDR
const ocpiUpdated = await this.checkAndSendOCPITransactionCdr(
tenant, lastTransaction, chargingStation, lastTransaction.tag);
@@ -893,13 +893,13 @@ export default class OCPPService {
return extraInactivityUpdated;
}
- private async checkAndBillTransaction(tenant: Tenant, transaction: Transaction): Promise<boolean> {
+ private async checkAndBillTransaction(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation): Promise<boolean> {
let transactionUpdated = false;
// Make sure the Extra Inactivity is already known
if (transaction.stop?.extraInactivityComputed) {
transactionUpdated = true;
// Billing - Start the asynchronous billing flow
- await OCPPUtils.processTransactionBilling(tenant, transaction, TransactionAction.END);
+ await OCPPUtils.processTransactionBilling(tenant, transaction, chargingStation, TransactionAction.END);
}
return transactionUpdated;
}
@@ -1743,12 +1743,11 @@ export default class OCPPService {
}
}
- private async createTransaction(tenant: Tenant, chargingStation: ChargingStation, startTransaction: OCPPStartTransactionRequestExtended): Promise<Transaction> {
+ private async createTransaction(tenant: Tenant, startTransaction: OCPPStartTransactionRequestExtended): Promise<Transaction> {
return {
id: await TransactionStorage.findAvailableID(tenant),
issuer: true,
chargeBoxID: startTransaction.chargeBoxID,
- chargeBox: chargingStation, // TODO - Clarify if this is allowed!!!!
tagID: startTransaction.idTag,
timezone: startTransaction.timezone,
userID: startTransaction.userID,

MODIFY
src/server/ocpp/utils/OCPPUtils.ts
src/server/ocpp/utils/OCPPUtils.ts
@@ -300,7 +300,7 @@ export default class OCPPUtils {
}
}
- public static async processTransactionBilling(tenant: Tenant, transaction: Transaction, action: TransactionAction): Promise<void> {
+ public static async processTransactionBilling(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, action: TransactionAction): Promise<void> {
if (!transaction.user || !transaction.user.issuer) {
return;
}
@@ -311,7 +311,7 @@ export default class OCPPUtils {
case TransactionAction.START:
try {
// Delegate
- const billingDataTransactionStart: BillingDataTransactionStart = await billingImpl.startTransaction(transaction);
+ const billingDataTransactionStart: BillingDataTransactionStart = await billingImpl.startTransaction(transaction, chargingStation);
// Update
transaction.billingData = {
withBillingActive: billingDataTransactionStart.withBillingActive,
@@ -387,7 +387,7 @@ export default class OCPPUtils {
transaction.billingData.lastUpdate = new Date();
}
} catch (error) {
- const message = `Billing - stopTransaction failed - transaction ID '${transaction.id}'`;
+ const message = `Billing - endTransaction failed - transaction ID '${transaction.id}'`;
await Logging.logError({
...LoggingHelper.getTransactionProperties(transaction),
tenantID: tenant.id,

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
@@ -598,7 +598,7 @@ export default class BillingTestHelper {
transaction = await TransactionStorage.getTransaction(tenant, transactionId, { withUser: true, withChargingStation: true });
transaction.stop.extraInactivityComputed = true;
transaction.stop.extraInactivitySecs = 0;
- await OCPPUtils.processTransactionBilling(tenant, transaction, TransactionAction.END);
+ await OCPPUtils.processTransactionBilling(tenant, transaction, transaction.chargeBox, TransactionAction.END);
} else {
// #end
const stopTransactionResponse = await this.chargingStationContext.stopTransaction(transactionId, tagId, meterStop, stopDate.toDate());

billing - check access control on statr transaction