oneuptime/CommonServer/API/BillingInvoiceAPI.ts

177 lines
6.9 KiB
TypeScript
Raw Normal View History

2022-11-24 15:10:21 +00:00
import BadDataException from 'Common/Types/Exception/BadDataException';
import { JSONObject } from 'Common/Types/JSON';
2022-11-25 14:57:58 +00:00
import Permission, { UserPermission } from 'Common/Types/Permission';
2022-11-24 15:10:21 +00:00
import BillingInvoice from 'Model/Models/BillingInvoice';
import Project from 'Model/Models/Project';
2023-09-11 10:29:36 +00:00
import { IsBillingEnabled } from '../EnvironmentConfig';
2022-11-24 15:10:21 +00:00
import UserMiddleware from '../Middleware/UserAuthorization';
import BillingInvoiceService, {
Service as BillingInvoiceServiceType,
} from '../Services/BillingInvoiceService';
import BillingService, { Invoice } from '../Services/BillingService';
import ProjectService from '../Services/ProjectService';
import {
ExpressRequest,
ExpressResponse,
NextFunction,
} from '../Utils/Express';
import Response from '../Utils/Response';
import BaseAPI from './BaseAPI';
2023-05-11 11:58:49 +00:00
import SubscriptionStatus from 'Common/Types/Billing/SubscriptionStatus';
import BaseModel from 'Common/Models/BaseModel';
2022-11-24 15:10:21 +00:00
export default class UserAPI extends BaseAPI<
BillingInvoice,
BillingInvoiceServiceType
> {
public constructor() {
super(BillingInvoice, BillingInvoiceService);
this.router.post(
2022-12-20 07:13:36 +00:00
`${new this.entityType().getCrudApiPath()?.toString()}/pay`,
2022-11-24 15:10:21 +00:00
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction
) => {
try {
if (!IsBillingEnabled) {
throw new BadDataException(
2023-07-30 21:29:50 +00:00
'Billing is not enabled for this server'
2022-11-24 15:10:21 +00:00
);
}
if (req.body['projectId']) {
throw new BadDataException(
'projectId is required in request body'
);
}
2022-11-25 14:57:58 +00:00
const userPermissions: Array<UserPermission> = (
2022-11-24 15:13:21 +00:00
await this.getPermissionsForTenant(req)
2022-11-25 14:57:58 +00:00
).filter((permission: UserPermission) => {
2022-11-24 15:10:21 +00:00
return (
2022-11-24 15:13:21 +00:00
permission.permission.toString() ===
2023-08-04 11:38:11 +00:00
Permission.ProjectOwner.toString() ||
2022-11-24 15:13:21 +00:00
permission.permission.toString() ===
2023-08-04 11:38:11 +00:00
Permission.CanEditInvoices.toString()
2022-11-24 15:10:21 +00:00
);
});
if (userPermissions.length === 0) {
throw new BadDataException(
`You need ${Permission.ProjectOwner} or ${Permission.CanEditInvoices} permission to pay invoices.`
);
}
const project: Project | null =
await ProjectService.findOneById({
id: this.getTenantId(req)!,
props: {
isRoot: true,
},
select: {
_id: true,
paymentProviderCustomerId: true,
2022-11-24 15:13:21 +00:00
paymentProviderSubscriptionId: true,
2023-08-04 11:11:48 +00:00
paymentProviderMeteredSubscriptionId: true,
2022-11-24 15:10:21 +00:00
},
});
if (!project) {
throw new BadDataException('Project not found');
}
if (!project.paymentProviderCustomerId) {
throw new BadDataException(
'Payment Provider customer not found'
);
}
if (!project.paymentProviderSubscriptionId) {
throw new BadDataException(
'Payment Provider subscription not found'
);
}
const body: JSONObject = req.body;
2022-11-24 15:13:21 +00:00
const item: BillingInvoice =
BaseModel.fromJSON<BillingInvoice>(
2022-11-24 15:13:21 +00:00
body['data'] as JSONObject,
this.entityType
) as BillingInvoice;
2022-11-24 15:10:21 +00:00
if (!item.paymentProviderInvoiceId) {
2022-11-24 15:13:21 +00:00
throw new BadDataException('Invoice ID not found');
2022-11-24 15:10:21 +00:00
}
if (!item.paymentProviderCustomerId) {
2022-11-24 15:13:21 +00:00
throw new BadDataException('Customer ID not found');
2022-11-24 15:10:21 +00:00
}
2022-11-24 15:13:21 +00:00
const invoice: Invoice = await BillingService.payInvoice(
item.paymentProviderCustomerId!,
item.paymentProviderInvoiceId!
);
2022-11-24 15:10:21 +00:00
2022-11-24 15:13:21 +00:00
// save updated status.
2022-11-24 15:10:21 +00:00
await this.service.updateOneBy({
query: {
2022-11-24 15:13:21 +00:00
paymentProviderInvoiceId: invoice.id!,
2022-11-24 15:10:21 +00:00
},
props: {
isRoot: true,
2022-11-24 15:13:21 +00:00
ignoreHooks: true,
2022-11-24 15:10:21 +00:00
},
data: {
2022-11-24 15:13:21 +00:00
status: invoice.status,
},
});
2022-11-24 15:10:21 +00:00
2022-11-24 15:13:21 +00:00
// refresh subscription status.
2023-05-11 11:58:49 +00:00
const subscriptionState: SubscriptionStatus =
2022-11-24 15:13:21 +00:00
await BillingService.getSubscriptionStatus(
project.paymentProviderSubscriptionId as string
);
2022-11-24 15:10:21 +00:00
2023-08-04 11:11:48 +00:00
const meteredSubscriptionState: SubscriptionStatus =
await BillingService.getSubscriptionStatus(
2023-08-04 11:38:11 +00:00
project.paymentProviderMeteredSubscriptionId as string
2023-08-04 11:11:48 +00:00
);
2023-10-08 14:03:50 +00:00
// if subscription is cancelled, create a new subscription and update project.
if (
meteredSubscriptionState ===
SubscriptionStatus.Canceled ||
subscriptionState === SubscriptionStatus.Canceled
) {
await ProjectService.reactiveSubscription(project.id!);
}
2022-11-24 15:10:21 +00:00
await ProjectService.updateOneById({
id: project.id!,
data: {
2022-11-24 15:13:21 +00:00
paymentProviderSubscriptionStatus:
subscriptionState,
2023-08-04 11:38:11 +00:00
paymentProviderMeteredSubscriptionStatus:
meteredSubscriptionState,
2022-11-24 15:10:21 +00:00
},
props: {
isRoot: true,
2022-11-24 15:13:21 +00:00
ignoreHooks: true,
},
2022-11-24 15:10:21 +00:00
});
return Response.sendEmptyResponse(req, res);
} catch (err) {
next(err);
}
}
);
}
}