start transaction - sync user if necessary or inconsistent!
MODIFY
src/integration/billing/BillingIntegration.ts
src/integration/billing/BillingIntegration.ts
@@ -438,18 +438,19 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
action: ServerAction.BILLING_TRANSACTION
});
}
- if (!transaction.user.billingData) {
+ // Check for Billing data
+ if (!transaction.user.billingData || !transaction.user.billingData.customerID) {
throw new BackendError({
- message: 'User has no Billing Data',
+ message: 'Transaction user does not include the expected billing data',
source: Constants.CENTRAL_SERVER,
module: MODULE_NAME,
- method: 'checkStopTransaction',
+ method: 'stopTransaction',
action: ServerAction.BILLING_TRANSACTION
});
}
}
- public checkStartTransaction(transaction: Transaction): void {
+ public checkStartTransaction(transaction: Transaction, strictMode: boolean): void {
// Check User
if (!transaction.userID || !transaction.user) {
throw new BackendError({
@@ -460,11 +461,11 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
action: ServerAction.BILLING_TRANSACTION
});
}
- // Get User
- const billingUser = transaction.user;
- if (!billingUser.billingData || !billingUser.billingData.customerID) {
+ if (strictMode) { // TODO - temporary solution - to be removed
+ // Check for Billing data
+ if (!transaction.user.billingData || !transaction.user.billingData.customerID) {
throw new BackendError({
- message: 'Transaction user has no billing method or no customer in Stripe',
+ message: 'Transaction user does not include the expected billing data',
source: Constants.CENTRAL_SERVER,
module: MODULE_NAME,
method: 'startTransaction',
@@ -472,6 +473,7 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
});
}
}
+ }
private async checkAndGetBillingUser(user: User): Promise<BillingUser> {
// const billingUser = await this.getUserByEmail(user.email);

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -617,19 +617,22 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
// Check Stripe
await this.checkConnection();
// Check Transaction
- this.checkStartTransaction(transaction);
- // Checks
- // const customer = await this.getCustomerByUserID(transaction.user.id);
- const customer = await this.getStripeCustomer(transaction.user);
- if (!customer || customer.id !== transaction.user.billingData.customerID) {
- throw new BackendError({
- message: 'Stripe customer ID of the transaction user is invalid',
- source: Constants.CENTRAL_SERVER,
- module: MODULE_NAME,
- method: 'startTransaction',
- action: ServerAction.BILLING_TRANSACTION
- });
+ this.checkStartTransaction(transaction, false);
+ // Let's create the STRIPE customer (if necessary)
+ let customer = null;
+ const customerID = transaction.user?.billingData?.customerID;
+ if (customerID) {
+ customer = await this.getStripeCustomer(customerID);
}
+ // Check for the customer
+ if (!customer) {
+ // Let's create the STRIPE customer and update the billingData
+ const billingUser: BillingUser = await this.updateUser(transaction.user);
+ await UserStorage.saveUserBillingData(this.tenantID, billingUser.userID, billingUser.billingData);
+ transaction.user.billingData = billingUser.billingData;
+ }
+ // Let's check again!
+ this.checkStartTransaction(transaction, true /* strict mode */);
return {
cancelTransaction: false
};
@@ -830,15 +833,13 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
return Math.round(transaction.stop.roundedPrice * 100);
}
+ // eslint-disable-next-line @typescript-eslint/require-await
public async checkIfUserCanBeCreated(user: User): Promise<boolean> {
- // Check
- return this.checkIfUserCanBeUpdated(user);
+ return true;
}
// eslint-disable-next-line @typescript-eslint/require-await
public async checkIfUserCanBeUpdated(user: User): Promise<boolean> {
- // Check connection
- // await this.checkConnection();
return true;
}
@@ -902,7 +903,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
public async createUser(user: User): Promise<BillingUser> {
// Check
- const success = await this.checkIfUserCanBeUpdated(user);
+ const success = await this.checkIfUserCanBeCreated(user);
if (!success) {
throw new BackendError({
source: Constants.CENTRAL_SERVER,
@@ -912,7 +913,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
message: 'Cannot create the user'
});
}
- return this.modifyUser(user);
+ return this.saveStripeCustomer(user);
}
public async updateUser(user: User): Promise<BillingUser> {
@@ -927,7 +928,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
message: 'Cannot update the user'
});
}
- return this.modifyUser(user);
+ return this.saveStripeCustomer(user);
}
public async deleteUser(user: User): Promise<void> {
@@ -945,34 +946,37 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
private async getStripeCustomer(userOrCustomerID: User | string): Promise<Stripe.Customer> {
await this.checkConnection();
// Get customer
-
let customerID ;
if (typeof userOrCustomerID === 'string') {
customerID = userOrCustomerID;
} else {
customerID = userOrCustomerID?.billingData?.customerID;
}
-
if (customerID) {
try {
const customer: Stripe.Customer = await this.stripe.customers.retrieve(customerID) as Stripe.Customer;
return customer;
} catch (error) {
- throw new BackendError({
- source: Constants.CENTRAL_SERVER,
- module: MODULE_NAME, method: 'getStripeCustomer',
+ void Logging.logError({
+ tenantID: this.tenantID,
action: ServerAction.BILLING,
- message: `Stripe Inconsistency: ${error.message as string}`,
- detailedMessages: { error: error.message, stack: error.stack }
- });
+ module: MODULE_NAME, method: 'getStripeCustomer',
+ message: `Stripe operation failed - ${error?.message as string}`
+ });
+ // throw new BackendError({
+ // source: Constants.CENTRAL_SERVER,
+ // module: MODULE_NAME, method: 'getStripeCustomer',
+ // action: ServerAction.BILLING,
+ // message: `Stripe Inconsistency: ${error.message as string}`,
+ // detailedMessages: { error: error.message, stack: error.stack }
+ // });
}
}
-
// No Customer in STRIPE DB so far!
return null;
}
- private async modifyUser(user: User): Promise<BillingUser> {
+ private async saveStripeCustomer(user: User): Promise<BillingUser> {
await this.checkConnection();
const fullName = Utils.buildUserFullName(user, false, false);
const locale = Utils.getLanguageFromLocale(user.locale).toLocaleLowerCase();

start transaction - sync user if necessary or inconsistent!