From f53af7c466d4001e1f8e29f7327f5aef8cb5ca91 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:20:11 -0500 Subject: [PATCH] [AC-1863] Send `initiationPath` on organization or user signup (#7747) * Sent initiation path for organization and user signups * Rename organizationQueryParameter > organizationTypeQueryParameter * Jared's feedback * Split PM & SM initiation path --- .../register-form/register-form.component.ts | 1 - ...ts-manager-trial-billing-step.component.ts | 183 ------------- ...-manager-trial-free-stepper.component.html | 6 +- ...ts-manager-trial-free-stepper.component.ts | 12 +- ...-manager-trial-paid-stepper.component.html | 17 +- ...ts-manager-trial-paid-stepper.component.ts | 30 ++- .../secrets-manager-trial.component.html | 7 +- .../secrets-manager-trial.component.ts | 6 +- .../trial-initiation.component.html | 18 +- .../trial-initiation.component.ts | 29 ++- .../trial-initiation.module.ts | 6 +- .../trial-initiation/billing.component.html | 78 ------ .../trial-initiation/billing.component.ts | 34 --- .../trial-billing-step.component.html} | 18 +- .../trial-billing-step.component.ts | 242 ++++++++++++++++++ .../organization-plans.component.ts | 1 + .../request/organization-create.request.ts | 3 +- .../organization-billing.service.ts | 2 + .../services/organization-billing.service.ts | 15 +- .../models/request/reference-event.request.ts | 8 + 20 files changed, 366 insertions(+), 350 deletions(-) delete mode 100644 apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.ts delete mode 100644 apps/web/src/app/billing/accounts/trial-initiation/billing.component.html delete mode 100644 apps/web/src/app/billing/accounts/trial-initiation/billing.component.ts rename apps/web/src/app/{auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.html => billing/accounts/trial-initiation/trial-billing-step.component.html} (78%) create mode 100644 apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts diff --git a/apps/web/src/app/auth/register-form/register-form.component.ts b/apps/web/src/app/auth/register-form/register-form.component.ts index 47c6b36f87..6c1b3122c6 100644 --- a/apps/web/src/app/auth/register-form/register-form.component.ts +++ b/apps/web/src/app/auth/register-form/register-form.component.ts @@ -70,7 +70,6 @@ export class RegisterFormComponent extends BaseRegisterComponent { async ngOnInit() { await super.ngOnInit(); this.referenceData = this.referenceDataValue; - if (this.queryParamEmail) { this.formGroup.get("email")?.setValue(this.queryParamEmail); } diff --git a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.ts b/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.ts deleted file mode 100644 index a0854b4900..0000000000 --- a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core"; -import { FormBuilder, Validators } from "@angular/forms"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service"; -import { PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums"; -import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; -import { ProductType } from "@bitwarden/common/enums"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -import { BillingSharedModule, PaymentComponent, TaxInfoComponent } from "../../../billing/shared"; - -export interface OrganizationInfo { - name: string; - email: string; -} - -export interface OrganizationCreatedEvent { - organizationId: string; - planDescription: string; -} - -enum SubscriptionCadence { - Monthly, - Annual, -} - -export enum SubscriptionType { - Teams, - Enterprise, -} - -@Component({ - selector: "app-secrets-manager-trial-billing-step", - templateUrl: "secrets-manager-trial-billing-step.component.html", - imports: [BillingSharedModule], - standalone: true, -}) -export class SecretsManagerTrialBillingStepComponent implements OnInit { - @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; - @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; - @Input() organizationInfo: OrganizationInfo; - @Input() subscriptionType: SubscriptionType; - @Output() steppedBack = new EventEmitter(); - @Output() organizationCreated = new EventEmitter(); - - loading = true; - - annualCadence = SubscriptionCadence.Annual; - monthlyCadence = SubscriptionCadence.Monthly; - - formGroup = this.formBuilder.group({ - cadence: [SubscriptionCadence.Annual, Validators.required], - }); - formPromise: Promise; - - applicablePlans: PlanResponse[]; - annualPlan: PlanResponse; - monthlyPlan: PlanResponse; - - constructor( - private apiService: ApiService, - private i18nService: I18nService, - private formBuilder: FormBuilder, - private messagingService: MessagingService, - private organizationBillingService: OrganizationBillingService, - private platformUtilsService: PlatformUtilsService, - ) {} - - async ngOnInit(): Promise { - const plans = await this.apiService.getPlans(); - this.applicablePlans = plans.data.filter(this.isApplicable); - this.annualPlan = this.findPlanFor(SubscriptionCadence.Annual); - this.monthlyPlan = this.findPlanFor(SubscriptionCadence.Monthly); - this.loading = false; - } - - async submit(): Promise { - this.formPromise = this.createOrganization(); - - const organizationId = await this.formPromise; - const planDescription = this.getPlanDescription(); - - this.platformUtilsService.showToast( - "success", - this.i18nService.t("organizationCreated"), - this.i18nService.t("organizationReadyToGo"), - ); - - this.organizationCreated.emit({ - organizationId, - planDescription, - }); - - this.messagingService.send("organizationCreated", organizationId); - } - - protected changedCountry() { - this.paymentComponent.hideBank = this.taxInfoComponent.taxInfo.country !== "US"; - if ( - this.paymentComponent.hideBank && - this.paymentComponent.method === PaymentMethodType.BankAccount - ) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); - } - } - - protected stepBack() { - this.steppedBack.emit(); - } - - private async createOrganization(): Promise { - const plan = this.findPlanFor(this.formGroup.value.cadence); - const paymentMethod = await this.paymentComponent.createPaymentToken(); - - const response = await this.organizationBillingService.purchaseSubscription({ - organization: { - name: this.organizationInfo.name, - billingEmail: this.organizationInfo.email, - }, - plan: { - type: plan.type, - passwordManagerSeats: 1, - subscribeToSecretsManager: true, - isFromSecretsManagerTrial: true, - secretsManagerSeats: 1, - }, - payment: { - paymentMethod, - billing: { - postalCode: this.taxInfoComponent.taxInfo.postalCode, - country: this.taxInfoComponent.taxInfo.country, - taxId: this.taxInfoComponent.taxInfo.taxId, - addressLine1: this.taxInfoComponent.taxInfo.line1, - addressLine2: this.taxInfoComponent.taxInfo.line2, - city: this.taxInfoComponent.taxInfo.city, - state: this.taxInfoComponent.taxInfo.state, - }, - }, - }); - - return response.id; - } - - private findPlanFor(cadence: SubscriptionCadence) { - switch (this.subscriptionType) { - case SubscriptionType.Teams: - return cadence === SubscriptionCadence.Annual - ? this.applicablePlans.find((plan) => plan.type === PlanType.TeamsAnnually) - : this.applicablePlans.find((plan) => plan.type === PlanType.TeamsMonthly); - case SubscriptionType.Enterprise: - return cadence === SubscriptionCadence.Annual - ? this.applicablePlans.find((plan) => plan.type === PlanType.EnterpriseAnnually) - : this.applicablePlans.find((plan) => plan.type === PlanType.EnterpriseMonthly); - } - } - - private getPlanDescription(): string { - const plan = this.findPlanFor(this.formGroup.value.cadence); - const price = - plan.SecretsManager.basePrice === 0 - ? plan.SecretsManager.seatPrice - : plan.SecretsManager.basePrice; - - switch (this.formGroup.value.cadence) { - case SubscriptionCadence.Annual: - return `${this.i18nService.t("annual")} ($${price}/${this.i18nService.t("yr")})`; - case SubscriptionCadence.Monthly: - return `${this.i18nService.t("monthly")} ($${price}/${this.i18nService.t("monthAbbr")})`; - } - } - - private isApplicable(plan: PlanResponse): boolean { - const hasSecretsManager = !!plan.SecretsManager; - const isTeamsOrEnterprise = - plan.product === ProductType.Teams || plan.product === ProductType.Enterprise; - const notDisabledOrLegacy = !plan.disabled && !plan.legacyYear; - return hasSecretsManager && isTeamsOrEnterprise && notDisabledOrLegacy; - } -} diff --git a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html b/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html index 2956151d04..ed2ed90bab 100644 --- a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html +++ b/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-free-stepper.component.html @@ -5,7 +5,11 @@ [subLabel]="subLabels.createAccount" [addSubLabelSpacing]="true" > - + - + - + >
diff --git a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial.component.ts b/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial.component.ts index 3e2d9589f8..5728f0c751 100644 --- a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial.component.ts +++ b/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial.component.ts @@ -7,7 +7,7 @@ import { Subject, takeUntil } from "rxjs"; templateUrl: "secrets-manager-trial.component.html", }) export class SecretsManagerTrialComponent implements OnInit, OnDestroy { - subscriptionType: string; + organizationTypeQueryParameter: string; private destroy$ = new Subject(); @@ -15,7 +15,7 @@ export class SecretsManagerTrialComponent implements OnInit, OnDestroy { ngOnInit(): void { this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((queryParameters) => { - this.subscriptionType = queryParameters.org; + this.organizationTypeQueryParameter = queryParameters.org; }); } @@ -25,6 +25,6 @@ export class SecretsManagerTrialComponent implements OnInit, OnDestroy { } get freeOrganization() { - return this.subscriptionType === "free"; + return this.organizationTypeQueryParameter === "free"; } } diff --git a/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html b/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html index ce149edd0b..d8e646c8d5 100644 --- a/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html +++ b/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html @@ -97,14 +97,18 @@
- + [organizationInfo]="{ + name: orgInfoFormGroup.get('name').value, + email: orgInfoFormGroup.get('email').value, + type: trialOrganizationType + }" + [subscriptionProduct]="SubscriptionProduct.PasswordManager" + (steppedBack)="previousStep()" + (organizationCreated)="createdOrganization($event)" + > + -
-
-

{{ "billingPlanLabel" | i18n }}

-
- -
-
- -
-

{{ "paymentType" | i18n }}

- - -
- -
- - - -
-
- diff --git a/apps/web/src/app/billing/accounts/trial-initiation/billing.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/billing.component.ts deleted file mode 100644 index 30ed2c9ddd..0000000000 --- a/apps/web/src/app/billing/accounts/trial-initiation/billing.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { FormGroup } from "@angular/forms"; - -import { PlanType } from "@bitwarden/common/billing/enums"; -import { ProductType } from "@bitwarden/common/enums"; - -import { OrganizationPlansComponent } from "../../organizations"; - -@Component({ - selector: "app-billing", - templateUrl: "./billing.component.html", -}) -export class BillingComponent extends OrganizationPlansComponent { - @Input() orgInfoForm: FormGroup; - @Output() previousStep = new EventEmitter(); - - async ngOnInit() { - const additionalSeats = - this.product == ProductType.Families || this.plan === PlanType.TeamsStarter ? 0 : 1; - this.formGroup.patchValue({ - name: this.orgInfoForm.value.name, - billingEmail: this.orgInfoForm.value.email, - additionalSeats: additionalSeats, - plan: this.plan, - product: this.product, - }); - this.isInTrialFlow = true; - await super.ngOnInit(); - } - - stepBack() { - this.previousStep.emit(); - } -} diff --git a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.html b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html similarity index 78% rename from apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.html rename to apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html index ed6daca672..fb7c774d6d 100644 --- a/apps/web/src/app/auth/trial-initiation/secrets-manager/secrets-manager-trial-billing-step.component.html +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html @@ -16,7 +16,7 @@

{{ "billingPlanLabel" | i18n }}

-
+
-
+
diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts new file mode 100644 index 0000000000..0c5f1d706b --- /dev/null +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -0,0 +1,242 @@ +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core"; +import { FormBuilder, Validators } from "@angular/forms"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { + BillingInformation, + OrganizationBillingServiceAbstraction as OrganizationBillingService, + OrganizationInformation, + PaymentInformation, + PlanInformation, +} from "@bitwarden/common/billing/abstractions/organization-billing.service"; +import { PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums"; +import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; +import { ProductType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { BillingSharedModule, PaymentComponent, TaxInfoComponent } from "../../shared"; + +export type TrialOrganizationType = Exclude; + +export interface OrganizationInfo { + name: string; + email: string; + type: TrialOrganizationType; +} + +export interface OrganizationCreatedEvent { + organizationId: string; + planDescription: string; +} + +enum SubscriptionCadence { + Annual, + Monthly, +} + +export enum SubscriptionProduct { + PasswordManager, + SecretsManager, +} + +@Component({ + selector: "app-trial-billing-step", + templateUrl: "trial-billing-step.component.html", + imports: [BillingSharedModule], + standalone: true, +}) +export class TrialBillingStepComponent implements OnInit { + @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; + @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; + @Input() organizationInfo: OrganizationInfo; + @Input() subscriptionProduct: SubscriptionProduct = SubscriptionProduct.PasswordManager; + @Output() steppedBack = new EventEmitter(); + @Output() organizationCreated = new EventEmitter(); + + loading = true; + + annualCadence = SubscriptionCadence.Annual; + monthlyCadence = SubscriptionCadence.Monthly; + + formGroup = this.formBuilder.group({ + cadence: [SubscriptionCadence.Annual, Validators.required], + }); + formPromise: Promise; + + applicablePlans: PlanResponse[]; + annualPlan?: PlanResponse; + monthlyPlan?: PlanResponse; + + constructor( + private apiService: ApiService, + private i18nService: I18nService, + private formBuilder: FormBuilder, + private messagingService: MessagingService, + private organizationBillingService: OrganizationBillingService, + private platformUtilsService: PlatformUtilsService, + ) {} + + async ngOnInit(): Promise { + const plans = await this.apiService.getPlans(); + this.applicablePlans = plans.data.filter(this.isApplicable); + this.annualPlan = this.findPlanFor(SubscriptionCadence.Annual); + this.monthlyPlan = this.findPlanFor(SubscriptionCadence.Monthly); + this.loading = false; + } + + async submit(): Promise { + this.formPromise = this.createOrganization(); + + const organizationId = await this.formPromise; + const planDescription = this.getPlanDescription(); + + this.platformUtilsService.showToast( + "success", + this.i18nService.t("organizationCreated"), + this.i18nService.t("organizationReadyToGo"), + ); + + this.organizationCreated.emit({ + organizationId, + planDescription, + }); + + this.messagingService.send("organizationCreated", organizationId); + } + + protected changedCountry() { + this.paymentComponent.hideBank = this.taxInfoComponent.taxInfo.country !== "US"; + if ( + this.paymentComponent.hideBank && + this.paymentComponent.method === PaymentMethodType.BankAccount + ) { + this.paymentComponent.method = PaymentMethodType.Card; + this.paymentComponent.changeMethod(); + } + } + + protected getPriceFor(cadence: SubscriptionCadence): number { + const plan = this.findPlanFor(cadence); + return this.subscriptionProduct === SubscriptionProduct.PasswordManager + ? plan.PasswordManager.basePrice === 0 + ? plan.PasswordManager.seatPrice + : plan.PasswordManager.basePrice + : plan.SecretsManager.basePrice === 0 + ? plan.SecretsManager.seatPrice + : plan.SecretsManager.basePrice; + } + + protected stepBack() { + this.steppedBack.emit(); + } + + private async createOrganization(): Promise { + const planResponse = this.findPlanFor(this.formGroup.value.cadence); + const paymentMethod = await this.paymentComponent.createPaymentToken(); + + const organization: OrganizationInformation = { + name: this.organizationInfo.name, + billingEmail: this.organizationInfo.email, + initiationPath: + this.subscriptionProduct === SubscriptionProduct.PasswordManager + ? "Password Manager trial from marketing website" + : "Secrets Manager trial from marketing website", + }; + + const plan: PlanInformation = { + type: planResponse.type, + passwordManagerSeats: 1, + }; + + if (this.subscriptionProduct === SubscriptionProduct.SecretsManager) { + plan.subscribeToSecretsManager = true; + plan.isFromSecretsManagerTrial = true; + plan.secretsManagerSeats = 1; + } + + const payment: PaymentInformation = { + paymentMethod, + billing: this.getBillingInformationFromTaxInfoComponent(), + }; + + const response = await this.organizationBillingService.purchaseSubscription({ + organization, + plan, + payment, + }); + + return response.id; + } + + private productTypeToPlanTypeMap: { + [productType in TrialOrganizationType]: { + [cadence in SubscriptionCadence]?: PlanType; + }; + } = { + [ProductType.Enterprise]: { + [SubscriptionCadence.Annual]: PlanType.EnterpriseAnnually, + [SubscriptionCadence.Monthly]: PlanType.EnterpriseMonthly, + }, + [ProductType.Families]: { + [SubscriptionCadence.Annual]: PlanType.FamiliesAnnually, + // No monthly option for Families plan + }, + [ProductType.Teams]: { + [SubscriptionCadence.Annual]: PlanType.TeamsAnnually, + [SubscriptionCadence.Monthly]: PlanType.TeamsMonthly, + }, + [ProductType.TeamsStarter]: { + // No annual option for Teams Starter plan + [SubscriptionCadence.Monthly]: PlanType.TeamsStarter, + }, + }; + + private findPlanFor(cadence: SubscriptionCadence): PlanResponse | null { + const productType = this.organizationInfo.type; + const planType = this.productTypeToPlanTypeMap[productType]?.[cadence]; + return planType ? this.applicablePlans.find((plan) => plan.type === planType) : null; + } + + private getBillingInformationFromTaxInfoComponent(): BillingInformation { + return { + postalCode: this.taxInfoComponent.taxInfo.postalCode, + country: this.taxInfoComponent.taxInfo.country, + taxId: this.taxInfoComponent.taxInfo.taxId, + addressLine1: this.taxInfoComponent.taxInfo.line1, + addressLine2: this.taxInfoComponent.taxInfo.line2, + city: this.taxInfoComponent.taxInfo.city, + state: this.taxInfoComponent.taxInfo.state, + }; + } + + private getPlanDescription(): string { + const plan = this.findPlanFor(this.formGroup.value.cadence); + const price = + this.subscriptionProduct === SubscriptionProduct.PasswordManager + ? plan.PasswordManager.basePrice === 0 + ? plan.PasswordManager.seatPrice + : plan.PasswordManager.basePrice + : plan.SecretsManager.basePrice === 0 + ? plan.SecretsManager.seatPrice + : plan.SecretsManager.basePrice; + + switch (this.formGroup.value.cadence) { + case SubscriptionCadence.Annual: + return `${this.i18nService.t("annual")} ($${price}/${this.i18nService.t("yr")})`; + case SubscriptionCadence.Monthly: + return `${this.i18nService.t("monthly")} ($${price}/${this.i18nService.t("monthAbbr")})`; + } + } + + private isApplicable(plan: PlanResponse): boolean { + const hasCorrectProductType = + plan.product === ProductType.Enterprise || + plan.product === ProductType.Families || + plan.product === ProductType.Teams || + plan.product === ProductType.TeamsStarter; + const notDisabledOrLegacy = !plan.disabled && !plan.legacyYear; + return hasCorrectProductType && notDisabledOrLegacy; + } +} diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts index 44d78ecce2..b7070be1cf 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.ts +++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts @@ -642,6 +642,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { request.collectionName = collectionCt; request.name = this.formGroup.controls.name.value; request.billingEmail = this.formGroup.controls.billingEmail.value; + request.initiationPath = "New organization creation in-product"; request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString); if (this.selectedPlan.type === PlanType.Free) { diff --git a/libs/common/src/admin-console/models/request/organization-create.request.ts b/libs/common/src/admin-console/models/request/organization-create.request.ts index 9c7d620d29..9f0441c434 100644 --- a/libs/common/src/admin-console/models/request/organization-create.request.ts +++ b/libs/common/src/admin-console/models/request/organization-create.request.ts @@ -1,4 +1,5 @@ import { PaymentMethodType, PlanType } from "../../../billing/enums"; +import { InitiationPath } from "../../../models/request/reference-event.request"; import { OrganizationKeysRequest } from "./organization-keys.request"; @@ -23,9 +24,9 @@ export class OrganizationCreateRequest { billingAddressState: string; billingAddressPostalCode: string; billingAddressCountry: string; - useSecretsManager: boolean; additionalSmSeats: number; additionalServiceAccounts: number; isFromSecretsManagerTrial: boolean; + initiationPath: InitiationPath; } diff --git a/libs/common/src/billing/abstractions/organization-billing.service.ts b/libs/common/src/billing/abstractions/organization-billing.service.ts index 8e2bdc2efe..d19724b600 100644 --- a/libs/common/src/billing/abstractions/organization-billing.service.ts +++ b/libs/common/src/billing/abstractions/organization-billing.service.ts @@ -1,10 +1,12 @@ import { OrganizationResponse } from "../../admin-console/models/response/organization.response"; +import { InitiationPath } from "../../models/request/reference-event.request"; import { PaymentMethodType, PlanType } from "../enums"; export type OrganizationInformation = { name: string; billingEmail: string; businessName?: string; + initiationPath?: InitiationPath; }; export type PlanInformation = { diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts index 3e5d67ade6..a9437e288c 100644 --- a/libs/common/src/billing/services/organization-billing.service.ts +++ b/libs/common/src/billing/services/organization-billing.service.ts @@ -76,6 +76,18 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs }; } + private prohibitsAdditionalSeats(planType: PlanType) { + switch (planType) { + case PlanType.Free: + case PlanType.FamiliesAnnually: + case PlanType.FamiliesAnnually2019: + case PlanType.TeamsStarter: + return true; + default: + return false; + } + } + private setOrganizationInformation( request: OrganizationCreateRequest, information: OrganizationInformation, @@ -83,6 +95,7 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs request.name = information.name; request.businessName = information.businessName; request.billingEmail = information.billingEmail; + request.initiationPath = information.initiationPath; } private setOrganizationKeys(request: OrganizationCreateRequest, keys: OrganizationKeys): void { @@ -121,7 +134,7 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs ): void { request.planType = information.type; - if (request.planType === PlanType.Free) { + if (this.prohibitsAdditionalSeats(request.planType)) { request.useSecretsManager = information.subscribeToSecretsManager; request.isFromSecretsManagerTrial = information.isFromSecretsManagerTrial; return; diff --git a/libs/common/src/models/request/reference-event.request.ts b/libs/common/src/models/request/reference-event.request.ts index 73a2532743..7b8f33a237 100644 --- a/libs/common/src/models/request/reference-event.request.ts +++ b/libs/common/src/models/request/reference-event.request.ts @@ -1,6 +1,14 @@ +export type InitiationPath = + | "Registration form" + | "Password Manager trial from marketing website" + | "Secrets Manager trial from marketing website" + | "New organization creation in-product" + | "Upgrade in-product"; + export class ReferenceEventRequest { id: string; session: string; layout: string; flow: string; + initiationPath: InitiationPath; }