billing - BILLING_SYNC_USER ON/OFF
MODIFY
src/integration/billing/BillingIntegration.ts
src/integration/billing/BillingIntegration.ts
@@ -49,6 +49,7 @@ export default abstract class BillingIntegration {
inSuccess: 0,
inError: 0
};
+ if (FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USERS)) {
// Sync e-Mobility Users with no billing data
const users: User[] = await this._getUsersWithNoBillingData();
if (users.length > 0) {
@@ -60,8 +61,6 @@ export default abstract class BillingIntegration {
module: MODULE_NAME, method: 'synchronizeUsers',
message: `${users.length} new user(s) are going to be synchronized`
});
- // Check LIVE MODE
- if (FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USERS)) {
for (const user of users) {
// Synchronize user
if (await this.synchronizeUser(user)) {
@@ -76,7 +75,7 @@ export default abstract class BillingIntegration {
source: Constants.CENTRAL_SERVER,
action: ServerAction.BILLING_SYNCHRONIZE_USERS,
module: MODULE_NAME, method: 'synchronizeUsers',
- message: 'Live Mode is OFF - operation has been aborted'
+ message: 'Feature is switched OFF - operation has been aborted'
});
}
}
@@ -98,6 +97,7 @@ export default abstract class BillingIntegration {
public async synchronizeUser(user: User): Promise<BillingUser> {
let billingUser: BillingUser = null;
+ if (FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USER)) {
try {
billingUser = await this._synchronizeUser(user);
await Logging.logInfo({
@@ -120,6 +120,15 @@ export default abstract class BillingIntegration {
detailedMessages: { error: error.message, stack: error.stack }
});
}
+ } else {
+ await Logging.logWarning({
+ tenantID: this.tenantID,
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.BILLING_SYNCHRONIZE_USER,
+ module: MODULE_NAME, method: 'synchronizeUser',
+ message: 'Feature is switched OFF - operation has been aborted'
+ });
+ }
return billingUser;
}
@@ -176,10 +185,9 @@ export default abstract class BillingIntegration {
// Specific use-case - Trying to REPAIR inconsistencies
// e.g.: CustomerID is set, but the corresponding data does not exist anymore on the STRIPE side
// ----------------------------------------------------------------------------------------------
- let exists;
try {
- exists = await this.getUser(user);
- if (!exists) {
+ billingUser = await this.getUser(user);
+ if (!billingUser) {
billingUser = await this.createUser(user);
} else {
billingUser = await this.updateUser(user);

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -147,10 +147,8 @@ export default class StripeBillingIntegration extends BillingIntegration {
public async isUserSynchronized(user: User): Promise<boolean> {
// Check Stripe
await this.checkConnection();
- if (!user.billingData) {
- // Make sure the billing data has been provided
+ // Make sure to get fresh data
user = await UserStorage.getUser(this.tenantID, user.id);
- }
const customerID: string = user?.billingData?.customerID;
// returns true when the customerID is properly set!
return !!customerID;
@@ -165,10 +163,14 @@ export default class StripeBillingIntegration extends BillingIntegration {
}
// Retrieve the STRIPE customer (if any)
const customerID: string = user.billingData?.customerID;
+ if (customerID) {
const customer = await this.getStripeCustomer(customerID);
// Return the corresponding Billing User
return this.convertToBillingUser(customer, user);
}
+ // customerID is not set - do not throw exceptions in that case
+ return null;
+ }
public async getTaxes(): Promise<BillingTax[]> {
await this.checkConnection();
@@ -1130,6 +1132,7 @@ export default class StripeBillingIntegration extends BillingIntegration {
const customerID = user.billingData?.customerID;
const customer = await this.getStripeCustomer(customerID);
if (customer && customer.id) {
+ // TODO - ro be clarified - is this allowed when the user has some invoices
await this.stripe.customers.del(
customer.id
);

MODIFY
src/server/rest/v1/service/AuthService.ts
src/server/rest/v1/service/AuthService.ts
@@ -515,7 +515,7 @@ export default class AuthService {
const billingImpl = await BillingFactory.getBillingImpl(tenantID);
if (billingImpl) {
try {
- await billingImpl.createUser(user);
+ await billingImpl.synchronizeUser(user);
await Logging.logInfo({
tenantID: tenantID,
module: MODULE_NAME, method: 'handleVerifyEmail',

MODIFY
src/server/rest/v1/service/UserService.ts
src/server/rest/v1/service/UserService.ts
@@ -466,7 +466,7 @@ export default class UserService {
const billingImpl = await BillingFactory.getBillingImpl(req.user.tenantID);
if (billingImpl) {
try {
- await billingImpl.updateUser(user);
+ await billingImpl.synchronizeUser(user);
} catch (error) {
await Logging.logError({
tenantID: req.user.tenantID,
@@ -1034,7 +1034,7 @@ export default class UserService {
if (billingImpl) {
try {
const user = await UserStorage.getUser(req.user.tenantID, newUser.id);
- await billingImpl.createUser(user);
+ await billingImpl.synchronizeUser(user);
await Logging.logInfo({
tenantID: req.user.tenantID,
action: action,

MODIFY
src/utils/FeatureToggles.ts
src/utils/FeatureToggles.ts
// Declare here the list of features
export enum Feature {
BILLING_SYNC_USERS,
+ BILLING_SYNC_USER,
BILLING_CHECK_USER_BILLING_DATA,
BILLING_CHECK_CUSTOMER_ID,
BILLING_CHECK_USER_DEFAULT_PAYMENT_METHOD,
@@ -12,6 +13,7 @@ export default class FeatureToggles {
// Comment out the features that you want to switch OFF
static activeFeatures: Feature[] = [
// Feature.BILLING_SYNC_USERS, //Let's avoid polluting the stripe accounts for now!
+ // Feature.BILLING_SYNC_USER,
// Feature.BILLING_CHECK_USER_BILLING_DATA,
// Feature.BILLING_CHECK_CUSTOMER_ID,
// Feature.BILLING_CHECK_USER_DEFAULT_PAYMENT_METHOD,

MODIFY
test/api/BillingStripeTestData.ts
test/api/BillingStripeTestData.ts
import { BillingInvoice, BillingInvoiceItem, BillingInvoiceStatus, BillingOperationResult, BillingUser, BillingUserData } from '../../src/types/Billing';
-import { BillingSettings, BillingSettingsType, SettingDB, StripeBillingSetting } from '../../src/types/Setting';
+import { BillingSettings, BillingSettingsType, SettingDB } from '../../src/types/Setting';
+import FeatureToggles, { Feature } from '../../src/utils/FeatureToggles';
import chai, { assert, expect } from 'chai';
import BillingStorage from '../../src/storage/mongodb/BillingStorage';
@@ -64,6 +65,9 @@ export default class StripeIntegrationTestData {
// The tests requires some settings to be forced
this.billingImpl = await this.setBillingSystemValidCredentials(immediateBilling);
this.billingUser = await this.billingImpl.getUser(this.dynamicUser);
+ if (!this.billingUser && !FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USER)) {
+ this.billingUser = await this.billingImpl.forceSynchronizeUser(this.dynamicUser);
+ }
assert(this.billingUser, 'Billing user should not be null');
}

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
import { BillingInvoiceStatus, BillingUser } from '../../src/types/Billing';
import { BillingSettings, BillingSettingsType, SettingDB } from '../../src/types/Setting';
+import FeatureToggles, { Feature } from '../../src/utils/FeatureToggles';
import chai, { assert, expect } from 'chai';
import CentralServerService from './client/CentralServerService';
@@ -266,6 +267,9 @@ describe('Billing Service', function() {
testData.createdUsers.push(fakeUser);
// Let's check that the corresponding billing user exists as well (a Customer in the STRIPE DB)
let billingUser = await testData.billingImpl.getUser(fakeUser);
+ if (!billingUser && !FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USER)) {
+ billingUser = await testData.billingImpl.forceSynchronizeUser(fakeUser);
+ }
expect(billingUser).to.be.not.null;
// Let's update the new user
fakeUser.firstName = 'Test';
@@ -276,6 +280,9 @@ describe('Billing Service', function() {
fakeUser,
false
);
+ if (!FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USER)) {
+ billingUser = await testData.billingImpl.forceSynchronizeUser(fakeUser);
+ }
// Let's check that the corresponding billing user was updated as well
billingUser = await testData.billingImpl.getUser(fakeUser);
expect(billingUser.name).to.be.eq(fakeUser.firstName + ' ' + fakeUser.name);
@@ -512,7 +519,11 @@ describe('Billing Service', function() {
);
testData.createdUsers.push(fakeUser);
testData.billingImpl = await testData.setBillingSystemValidCredentials();
+ if (FeatureToggles.isFeatureActive(Feature.BILLING_SYNC_USER)) {
await testData.userService.billingApi.synchronizeUser({ id: fakeUser.id });
+ } else {
+ await testData.userService.billingApi.forceSynchronizeUser(fakeUser);
+ }
const userExists = await testData.billingImpl.isUserSynchronized(fakeUser);
expect(userExists).to.be.true;
});

billing - BILLING_SYNC_USER ON/OFF