bde148f286
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
126 lines
3.9 KiB
TypeScript
126 lines
3.9 KiB
TypeScript
import {
|
|
GetCheckoutSession,
|
|
UpdateCheckoutSessionStatus,
|
|
UpsertWorkspaceSubscription,
|
|
UpsertPaidWorkspacePlan,
|
|
GetSubscriptionData,
|
|
getSubscriptionState,
|
|
GetWorkspaceSubscription
|
|
} from '@/modules/gatekeeper/domain/billing'
|
|
import {
|
|
CheckoutSessionNotFoundError,
|
|
WorkspaceAlreadyPaidError,
|
|
WorkspacePlanNotFoundError
|
|
} from '@/modules/gatekeeper/errors/billing'
|
|
import { throwUncoveredError } from '@speckle/shared'
|
|
import { EventBusEmit } from '@/modules/shared/services/eventBus'
|
|
import { GetWorkspacePlan } from '@speckle/shared/authz'
|
|
import { GatekeeperEvents } from '@/modules/gatekeeperCore/domain/events'
|
|
|
|
export const completeCheckoutSessionFactory =
|
|
({
|
|
getCheckoutSession,
|
|
updateCheckoutSessionStatus,
|
|
upsertWorkspaceSubscription,
|
|
upsertPaidWorkspacePlan,
|
|
getWorkspacePlan,
|
|
getWorkspaceSubscription,
|
|
getSubscriptionData,
|
|
emitEvent
|
|
}: {
|
|
getCheckoutSession: GetCheckoutSession
|
|
updateCheckoutSessionStatus: UpdateCheckoutSessionStatus
|
|
upsertWorkspaceSubscription: UpsertWorkspaceSubscription
|
|
getWorkspacePlan: GetWorkspacePlan
|
|
getWorkspaceSubscription: GetWorkspaceSubscription
|
|
upsertPaidWorkspacePlan: UpsertPaidWorkspacePlan
|
|
getSubscriptionData: GetSubscriptionData
|
|
emitEvent: EventBusEmit
|
|
}) =>
|
|
/**
|
|
* Complete a paid checkout session
|
|
*/
|
|
async ({
|
|
sessionId,
|
|
subscriptionId
|
|
}: {
|
|
sessionId: string
|
|
subscriptionId: string
|
|
}): Promise<void> => {
|
|
const checkoutSession = await getCheckoutSession({ sessionId })
|
|
if (!checkoutSession) throw new CheckoutSessionNotFoundError()
|
|
|
|
const previousWorkspacePlan = await getWorkspacePlan({
|
|
workspaceId: checkoutSession.workspaceId
|
|
})
|
|
if (!previousWorkspacePlan) throw new WorkspacePlanNotFoundError()
|
|
|
|
// on states like cancellations, there is a subscription
|
|
const previousSubscription = await getWorkspaceSubscription({
|
|
workspaceId: checkoutSession.workspaceId
|
|
})
|
|
|
|
switch (checkoutSession.paymentStatus) {
|
|
case 'paid':
|
|
// if the session is already paid, we do not need to provision anything
|
|
throw new WorkspaceAlreadyPaidError()
|
|
case 'unpaid':
|
|
break
|
|
default:
|
|
throwUncoveredError(checkoutSession.paymentStatus)
|
|
}
|
|
// TODO: make sure, the subscription data price plan matches the checkout session workspacePlan
|
|
|
|
await updateCheckoutSessionStatus({ sessionId, paymentStatus: 'paid' })
|
|
|
|
// a plan determines the workspace feature set
|
|
const workspacePlan = {
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
workspaceId: checkoutSession.workspaceId,
|
|
name: checkoutSession.workspacePlan,
|
|
status: 'valid'
|
|
} as const
|
|
await upsertPaidWorkspacePlan({
|
|
workspacePlan
|
|
})
|
|
const subscriptionData = await getSubscriptionData({
|
|
subscriptionId
|
|
})
|
|
const currentBillingCycleEnd = subscriptionData.currentPeriodEnd
|
|
|
|
const workspaceSubscription = {
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
currentBillingCycleEnd,
|
|
workspaceId: checkoutSession.workspaceId,
|
|
billingInterval: checkoutSession.billingInterval,
|
|
currency: checkoutSession.currency,
|
|
updateIntent: null,
|
|
subscriptionData
|
|
}
|
|
await upsertWorkspaceSubscription({
|
|
workspaceSubscription
|
|
})
|
|
await emitEvent({
|
|
eventName: GatekeeperEvents.WorkspacePlanUpdated,
|
|
payload: {
|
|
userId: checkoutSession.userId,
|
|
workspacePlan,
|
|
previousWorkspacePlan
|
|
}
|
|
})
|
|
await emitEvent({
|
|
eventName: GatekeeperEvents.WorkspaceSubscriptionUpdated,
|
|
payload: {
|
|
userId: checkoutSession.userId,
|
|
workspacePlan,
|
|
previousWorkspacePlan,
|
|
subscription: getSubscriptionState(workspaceSubscription),
|
|
previousSubscription: previousSubscription
|
|
? getSubscriptionState(previousSubscription)
|
|
: null
|
|
}
|
|
})
|
|
}
|