bp - transfers - USD
MODIFY
.eslintrc.json
.eslintrc.json
},
"overrides": [
{
- "files": ["*.ts", "*.tsx"],
+ "files": ["src/**/*.ts", "*.tsx"],
"rules": {
"@typescript-eslint/explicit-member-accessibility": ["error"]
}

MODIFY
test/api/BillingPlatformTest.ts
test/api/BillingPlatformTest.ts
@@ -8,7 +8,6 @@ import BillingStorage from '../../src/storage/mongodb/BillingStorage';
import { BillingTestConfigHelper } from './StripeTestHelper';
import BillingTestHelper from './BillingTestHelper';
import CompanyFactory from '../factories/CompanyFactory';
-import Constants from '../../src/utils/Constants';
import ContextDefinition from './context/ContextDefinition';
import MongoDBStorage from '../../src/storage/mongodb/MongoDBStorage';
import SiteFactory from '../factories/SiteFactory';
@@ -25,7 +24,8 @@ chai.use(responseHelper);
const billingTestHelper = new BillingTestHelper();
// Conditional test execution function
const describeif = (condition) => condition ? describe : describe.skip;
-const isBillingProperlyConfigured = BillingTestConfigHelper.isBillingProperlyConfigured();
+// ACHTUNG - STRIPE Limitation - We cannot test transfer capabilities with accounts in Europe
+const isBillingProperlyConfigured = BillingTestConfigHelper.isBillingProperlyConfigured('USD');
describeif(isBillingProperlyConfigured)('Billing Platform (utbillingplatform)', () => {
jest.setTimeout(60000);
@@ -205,7 +205,12 @@ describeif(isBillingProperlyConfigured)('Billing Platform (utbillingplatform)',
it('should finalize transfer', async () => {
const billingAccount = await billingTestHelper.getActivatedAccount();
- const transfer: BillingTransfer = { ...BillingTransferFactory.build(), accountID: billingAccount.id, status: BillingTransferStatus.DRAFT };
+ const transfer: BillingTransfer = {
+ ...BillingTransferFactory.build(),
+ currency: billingTestHelper.getCurrentCurrency(),
+ accountID: billingAccount.id,
+ status: BillingTransferStatus.DRAFT
+ };
const transferID = await BillingStorage.saveTransfer(billingTestHelper.getTenant(), transfer);
transfer.id = transferID;
const finalizeResponse = await billingTestHelper.getCurrentUserService().billingApi.finalizeTransfer(transferID);
@@ -215,7 +220,10 @@ describeif(isBillingProperlyConfigured)('Billing Platform (utbillingplatform)',
});
it('should not finalize a draft transfer', async () => {
- const transfer = BillingTransferFactory.build();
+ const transfer = {
+ ...BillingTransferFactory.build(),
+ currency: billingTestHelper.getCurrentCurrency(),
+ };
transfer.status = BillingTransferStatus.FINALIZED;
const transferID = await BillingStorage.saveTransfer(billingTestHelper.getTenant(), transfer);
transfer.id = transferID;
@@ -225,15 +233,19 @@ describeif(isBillingProperlyConfigured)('Billing Platform (utbillingplatform)',
it('should send a transfer', async () => {
const billingAccount = await billingTestHelper.createActivatedAccount();
- const transfer: BillingTransfer = { ...BillingTransferFactory.build(), status: BillingTransferStatus.DRAFT, accountID: billingAccount.id };
- // Only works for bank accounts using the USD currency!!!!
- // await stripeTestHelper.addFundsToBalance(transfer.totalAmount);
+ const transfer: BillingTransfer = {
+ ...BillingTransferFactory.build(),
+ currency: billingTestHelper.getCurrentCurrency(),
+ status: BillingTransferStatus.DRAFT,
+ accountID: billingAccount.id
+ };
transfer.id = await BillingStorage.saveTransfer(billingTestHelper.getTenant(), transfer);
const finalizeResponse = await billingTestHelper.getCurrentUserService().billingApi.finalizeTransfer(transfer.id);
expect(finalizeResponse.status).to.be.eq(StatusCodes.OK);
+ // Only works for bank accounts using the USD currency!!!!
+ await billingTestHelper.addFundsToBalance(transfer.totalAmount);
const sendResponse = await billingTestHelper.getCurrentUserService().billingApi.sendTransfer(transfer.id);
- // Does not yet work as expected - funds cannot be sent because of the balance
- // expect(sendResponse.status).to.be.eq(StatusCodes.OK);
+ expect(sendResponse.status).to.be.eq(StatusCodes.OK);
});
});

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
@@ -12,6 +12,7 @@ import chai, { expect } from 'chai';
import AsyncTaskStorage from '../../src/storage/mongodb/AsyncTaskStorage';
import BillingFacade from '../../src/integration/billing/BillingFacade';
import { BillingPlatformFeeStrategyFactory } from '../factories/BillingFactory';
+import { BillingTestConfigHelper } from './StripeTestHelper';
import CentralServerService from './client/CentralServerService';
import ChargingStationContext from './context/ChargingStationContext';
import Company from '../../src/types/Company';
@@ -45,6 +46,7 @@ chai.use(responseHelper);
export default class BillingTestHelper {
// Tenant: utbilling
private tenantContext: TenantContext;
+ private tenantCurrency = 'EUR';
// Context with Admin permissions
private adminUserContext: User;
private adminUserService: CentralServerService;
@@ -67,6 +69,9 @@ export default class BillingTestHelper {
public async initialize(tenantContext = ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING) : Promise<void> {
this.tenantContext = await ContextProvider.defaultInstance.getTenantContext(tenantContext);
+ if (tenantContext === ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING_PLATFORM) {
+ this.tenantCurrency = 'USD';
+ }
this.adminUserContext = this.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
this.adminUserService = new CentralServerService(
this.tenantContext.getTenant().subdomain,
@@ -87,6 +92,10 @@ export default class BillingTestHelper {
return this.tenantContext.getTenant().id;
}
+ public getCurrentCurrency(): string {
+ return this.tenantCurrency;
+ }
+
public getAdminUserService(): CentralServerService {
return this.adminUserService;
}
@@ -496,7 +505,7 @@ export default class BillingTestHelper {
}
public async setBillingSystemValidCredentials(activateTransactionBilling = true, immediateBillingAllowed = false) : Promise<void> {
- const billingSettings = this.getLocalSettings(immediateBillingAllowed);
+ const billingSettings = BillingTestConfigHelper.getLocalSettings(this.tenantCurrency, immediateBillingAllowed);
// Here we switch ON or OFF the billing of charging sessions
billingSettings.billing.isTransactionBillingActivated = activateTransactionBilling;
// Invoke the generic setting service API to properly persist this information
@@ -510,7 +519,7 @@ export default class BillingTestHelper {
}
public async setBillingSystemInvalidCredentials() : Promise<void> {
- const billingSettings = this.getLocalSettings(false);
+ const billingSettings = BillingTestConfigHelper.getLocalSettings(this.tenantCurrency, false);
const tenant = this.tenantContext?.getTenant();
assert(!!tenant, 'Tenant cannot be null');
billingSettings.stripe.secretKey = await Cypher.encrypt(tenant, 'sk_test_' + 'invalid_credentials');
@@ -520,31 +529,6 @@ export default class BillingTestHelper {
this.billingImpl = billingImpl;
}
- public getLocalSettings(immediateBillingAllowed: boolean): BillingSettings {
- // ---------------------------------------------------------------------
- // ACHTUNG: Our test may need the immediate billing to be switched off!
- // Because we want to check the DRAFT state of the invoice
- // ---------------------------------------------------------------------
- const billingProperties = {
- isTransactionBillingActivated: true, // config.get('billing.isTransactionBillingActivated'),
- immediateBillingAllowed: immediateBillingAllowed, // config.get('billing.immediateBillingAllowed'),
- periodicBillingAllowed: !immediateBillingAllowed, // config.get('billing.periodicBillingAllowed'),
- taxID: config.get('billing.taxID')
- };
- const stripeProperties = {
- url: config.get('stripe.url'),
- publicKey: config.get('stripe.publicKey'),
- secretKey: config.get('stripe.secretKey'),
- };
- const settings: BillingSettings = {
- identifier: TenantComponents.BILLING,
- type: BillingSettingsType.STRIPE,
- billing: billingProperties,
- stripe: stripeProperties,
- };
- return settings;
- }
-
public async saveBillingSettings(billingSettings: BillingSettings) : Promise<void> {
// TODO - rethink that part
const tenantBillingSettings = await this.adminUserService.settingApi.readByIdentifier({ 'Identifier': 'billing' });
@@ -793,14 +777,6 @@ export default class BillingTestHelper {
return response?.data?.result;
}
- public isBillingProperlyConfigured(): boolean {
- const billingSettings = this.getLocalSettings(false);
- // Check that the mandatory settings are properly provided
- return (!!billingSettings.stripe.publicKey
- && !!billingSettings.stripe.secretKey
- && !!billingSettings.stripe.url);
- }
-
public async getLatestDraftInvoice(userId?: string): Promise<BillingInvoice> {
// ACHTUNG: There is no data after running: npm run mochatest:createContext
// In that situation we return 0!
@@ -1025,4 +1001,19 @@ export default class BillingTestHelper {
expect(response.status).to.be.eq(StatusCodes.OK);
expect(response.data.status).to.eq(BillingTransferStatus.FINALIZED);
}
+
+ public async addFundsToBalance(amount: number, stripe_test_token = 'btok_us_verified') : Promise<Stripe.Topup> {
+ // Assign funds to the stripe balance is a prerequisite for testing transfers
+ // c.f.: https://stripe.com/docs/connect/testing#testing-top-ups
+ const stripeInstance = await this.billingImpl.getStripeInstance();
+ const topup = await stripeInstance.topups.create({
+ amount,
+ currency: 'eur',
+ description: 'test-addFundsToBalance',
+ source:stripe_test_token,
+ });
+ expect(topup).to.not.be.null;
+ return topup;
+ }
+
}

MODIFY
test/api/StripeTestHelper.ts
test/api/StripeTestHelper.ts
@@ -27,25 +27,45 @@ chai.use(chaiSubset);
chai.use(responseHelper);
export class BillingTestConfigHelper {
- public static isBillingProperlyConfigured(): boolean {
- const billingSettings = BillingTestConfigHelper.getLocalSettings(false);
+ public static isBillingProperlyConfigured(tenantCurrency: string): boolean {
+ const billingSettings = BillingTestConfigHelper.getLocalSettings(tenantCurrency, false);
// Check that the mandatory settings are properly provided
return (!!billingSettings.stripe.publicKey
&& !!billingSettings.stripe.secretKey
&& !!billingSettings.stripe.url);
}
- public static getLocalSettings(immediateBillingAllowed: boolean): BillingSettings {
+ public static getLocalSettings(tenantCurrency: string, immediateBillingAllowed: boolean): BillingSettings {
+ // ----------------------------------
+ // CONFIGURATION EXAMPLE - in test/local.json
+ // ----------------------------------
+ // "billing": {
+ // "isTransactionBillingActivated": true,
+ // "immediateBillingAllowed": true,
+ // "periodicBillingAllowed": false,
+ // "taxID": ""
+ // },
+ // "stripe": {
+ // "url": "https://dashboard.stripe.com/b/acct_1FFFFFFFFFFF",
+ // "publicKey": "pk_test_511FFFFFFFFFFF",
+ // "secretKey": "sk_test_51K1FFFFFFFFFF"
+ // },
+ // "stripeUSD": {
+ // "url": "https://dashboard.stripe.com/b/acct_1AAAAAAAAAAA",
+ // "publicKey": "pk_test_51AAAAAAAAAAA",
+ // "secretKey": "sk_test_52AAAAAAAAAAA"
+ // },
+ const propertySuffix = (tenantCurrency !== 'EUR') ? tenantCurrency : '';
const billingProperties = {
isTransactionBillingActivated: config.get('billing.isTransactionBillingActivated'),
- immediateBillingAllowed: config.get('billing.immediateBillingAllowed'),
- periodicBillingAllowed: config.get('billing.periodicBillingAllowed'),
+ immediateBillingAllowed: immediateBillingAllowed, // config.get('billing.immediateBillingAllowed'),
+ periodicBillingAllowed: !immediateBillingAllowed, // config.get('billing.periodicBillingAllowed'),
taxID: config.get('billing.taxID')
};
const stripeProperties = {
- url: config.get('stripe.url'),
- publicKey: config.get('stripe.publicKey'),
- secretKey: config.get('stripe.secretKey'),
+ url: config.get(`stripe${propertySuffix}.url`),
+ publicKey: config.get(`stripe${propertySuffix}.publicKey`),
+ secretKey: config.get(`stripe${propertySuffix}.secretKey`),
};
const settings: BillingSettings = {
identifier: TenantComponents.BILLING,
@@ -66,6 +86,7 @@ export class BillingTestConfigHelper {
export default class StripeTestHelper {
// Tenant: utbilling
private tenantContext: TenantContext;
+ private tenantCurrency = 'EUR';
// User Service for action requiring admin permissions (e.g.: set/reset stripe settings)
private adminUserContext: User;
private adminUserService: CentralServerService;
@@ -78,6 +99,9 @@ export default class StripeTestHelper {
public async initialize(tenantContext = ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING): Promise<void> {
this.tenantContext = await ContextProvider.defaultInstance.getTenantContext(tenantContext);
+ if (tenantContext === ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING_PLATFORM) {
+ this.tenantCurrency = 'USD';
+ }
this.adminUserContext = this.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
this.adminUserService = new CentralServerService(
this.tenantContext.getTenant().subdomain,
@@ -110,7 +134,7 @@ export default class StripeTestHelper {
}
public async setBillingSystemValidCredentials(immediateBilling: boolean) : Promise<void> {
- const billingSettings = BillingTestConfigHelper.getLocalSettings(immediateBilling);
+ const billingSettings = BillingTestConfigHelper.getLocalSettings(this.tenantCurrency, immediateBilling);
await this.saveBillingSettings(billingSettings);
billingSettings.stripe.secretKey = await Cypher.encrypt(this.getTenant(), billingSettings.stripe.secretKey);
this.billingImpl = StripeBillingIntegration.getInstance(this.getTenant(), billingSettings);
@@ -118,7 +142,7 @@ export default class StripeTestHelper {
}
public async fakeLiveBillingSettings() : Promise<StripeBillingIntegration> {
- const billingSettings = BillingTestConfigHelper.getLocalSettings(true);
+ const billingSettings = BillingTestConfigHelper.getLocalSettings(this.tenantCurrency, true);
const mode = 'live';
billingSettings.stripe.secretKey = `sk_${mode}_0234567890`;
billingSettings.stripe.publicKey = `pk_${mode}_0234567890`;
@@ -151,20 +175,6 @@ export default class StripeTestHelper {
return source;
}
- public async addFundsToBalance(amount: number, stripe_test_token = 'btok_us_verified') : Promise<Stripe.Topup> {
- // Assign funds to the stripe balance is a prerequisite for testing transfers
- // c.f.: https://stripe.com/docs/connect/testing#testing-top-ups
- const stripeInstance = await this.billingImpl.getStripeInstance();
- const topup = await stripeInstance.topups.create({
- amount,
- currency: 'eur',
- description: 'test-addFundsToBalance',
- source:stripe_test_token,
- });
- expect(topup).to.not.be.null;
- return topup;
- }
-
// Detach the latest assigned source
public async checkDetachPaymentMethod(newSourceId: string) : Promise<void> {
const concreteImplementation : StripeBillingIntegration = this.billingImpl;

MODIFY
test/api/context/ContextDefinition.ts
test/api/context/ContextDefinition.ts
@@ -402,7 +402,7 @@ export default class ContextDefinition {
type: PricingSettingsType.SIMPLE,
simple: {
price: ContextDefinition.DEFAULT_PRICE,
- currency: 'EUR'
+ currency: 'USD'
}
}
},

MODIFY
test/config.js
test/config.js
@@ -290,6 +290,23 @@ const config = convict({
default: ''
},
},
+ stripeUSD: {
+ url: {
+ doc: 'Billing provider dashboard',
+ format: String,
+ default: ''
+ },
+ publicKey: {
+ doc: 'Public key',
+ format: String,
+ default: ''
+ },
+ secretKey: {
+ doc: 'Secret Key',
+ format: String,
+ default: ''
+ },
+ },
smartCharging: {
optimizerUrl: {
doc: 'Smart charging url',

bp - transfers - USD