Add error management to the Billing Package (#10343)
Solves https://github.com/twentyhq/core-team-issues/issues/403 **TLDR:** Enhance error management in Billing and when a customer is updated it updates automatically the Stripecustomer id in the entitlements. - Add Billing exceptions to filter. - Add onUpdate for billing customer and entitlement. - Remember to run the migrations with is BILLING_ENABLED set to true. **In order to test (a simple test case)** - Ensure that the environment variables for Sentry and Billing are set, ensuring that SENTRY_ENVIRONMENT=staging - Run the server, the worker and the stripe cli - Do a database reset with IS_BILLING_ENABLED set to true - Go to stripe in test mode and update a random price description, this causes an exception because you are trying to write a price of. a product that doesn't exists in the database - You should see an error in Sentry: 
This commit is contained in:
committed by
GitHub
parent
cc0d892de0
commit
1b64f87d75
@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddOnUpdateInEntitlements1739981257356
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddOnUpdateInEntitlements1739981257356';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."billingEntitlement" DROP CONSTRAINT "FK_766a1918aa3dbe0d67d3df62356"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."billingEntitlement" ADD CONSTRAINT "FK_766a1918aa3dbe0d67d3df62356" FOREIGN KEY ("stripeCustomerId") REFERENCES "core"."billingCustomer"("stripeCustomerId") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."billingEntitlement" DROP CONSTRAINT "FK_766a1918aa3dbe0d67d3df62356"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."billingEntitlement" ADD CONSTRAINT "FK_766a1918aa3dbe0d67d3df62356" FOREIGN KEY ("stripeCustomerId") REFERENCES "core"."billingCustomer"("stripeCustomerId") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -47,23 +47,34 @@ export class BillingController {
|
||||
@Res() res: Response,
|
||||
) {
|
||||
if (!req.rawBody) {
|
||||
res.status(400).end();
|
||||
|
||||
return;
|
||||
throw new BillingException(
|
||||
'Missing request body',
|
||||
BillingExceptionCode.BILLING_MISSING_REQUEST_BODY,
|
||||
);
|
||||
}
|
||||
const event = this.stripeWebhookService.constructEventFromPayload(
|
||||
signature,
|
||||
req.rawBody,
|
||||
);
|
||||
|
||||
try {
|
||||
const event = this.stripeWebhookService.constructEventFromPayload(
|
||||
signature,
|
||||
req.rawBody,
|
||||
);
|
||||
const result = await this.handleStripeEvent(event);
|
||||
|
||||
res.status(200).send(result).end();
|
||||
} catch (error) {
|
||||
if (error instanceof BillingException) {
|
||||
res.status(404).end();
|
||||
if (
|
||||
error instanceof BillingException ||
|
||||
error instanceof Stripe.errors.StripeError
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : JSON.stringify(error);
|
||||
|
||||
throw new BillingException(
|
||||
errorMessage,
|
||||
BillingExceptionCode.BILLING_UNHANDLED_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,4 +16,7 @@ export enum BillingExceptionCode {
|
||||
BILLING_SUBSCRIPTION_EVENT_WORKSPACE_NOT_FOUND = 'BILLING_SUBSCRIPTION_EVENT_WORKSPACE_NOT_FOUND',
|
||||
BILLING_ACTIVE_SUBSCRIPTION_NOT_FOUND = 'BILLING_ACTIVE_SUBSCRIPTION_NOT_FOUND',
|
||||
BILLING_METER_EVENT_FAILED = 'BILLING_METER_EVENT_FAILED',
|
||||
BILLING_MISSING_REQUEST_BODY = 'BILLING_MISSING_REQUEST_BODY',
|
||||
BILLING_UNHANDLED_ERROR = 'BILLING_UNHANDLED_ERROR',
|
||||
BILLING_STRIPE_ERROR = 'BILLING_STRIPE_ERROR',
|
||||
}
|
||||
|
||||
@ -54,6 +54,7 @@ export class BillingEntitlement {
|
||||
(billingCustomer) => billingCustomer.billingEntitlements,
|
||||
{
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
},
|
||||
)
|
||||
@JoinColumn({
|
||||
|
||||
@ -17,22 +17,41 @@ export class BillingRestApiExceptionFilter implements ExceptionFilter {
|
||||
private readonly httpExceptionHandlerService: HttpExceptionHandlerService,
|
||||
) {}
|
||||
|
||||
catch(exception: BillingException, host: ArgumentsHost) {
|
||||
catch(
|
||||
exception: BillingException | Stripe.errors.StripeError,
|
||||
host: ArgumentsHost,
|
||||
) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
if (exception instanceof Stripe.errors.StripeError) {
|
||||
return this.httpExceptionHandlerService.handleError(
|
||||
{
|
||||
code: BillingExceptionCode.BILLING_STRIPE_ERROR,
|
||||
message: exception.message,
|
||||
name: 'StripeError',
|
||||
},
|
||||
response,
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
switch (exception.code) {
|
||||
case BillingExceptionCode.BILLING_CUSTOMER_NOT_FOUND:
|
||||
case BillingExceptionCode.BILLING_ACTIVE_SUBSCRIPTION_NOT_FOUND:
|
||||
case BillingExceptionCode.BILLING_SUBSCRIPTION_EVENT_WORKSPACE_NOT_FOUND:
|
||||
case BillingExceptionCode.BILLING_PRODUCT_NOT_FOUND:
|
||||
case BillingExceptionCode.BILLING_PLAN_NOT_FOUND:
|
||||
return this.httpExceptionHandlerService.handleError(
|
||||
exception,
|
||||
response,
|
||||
404,
|
||||
);
|
||||
case BillingExceptionCode.BILLING_PRODUCT_NOT_FOUND:
|
||||
case BillingExceptionCode.BILLING_METER_EVENT_FAILED:
|
||||
return this.httpExceptionHandlerService.handleError(
|
||||
exception,
|
||||
response,
|
||||
404,
|
||||
400,
|
||||
);
|
||||
default:
|
||||
return this.httpExceptionHandlerService.handleError(
|
||||
|
||||
Reference in New Issue
Block a user