diff --git a/packages/twenty-front/src/modules/billing/hooks/useGetWorkflowNodeExecutionUsage.ts b/packages/twenty-front/src/modules/billing/hooks/useGetWorkflowNodeExecutionUsage.ts index f52a6ebcf..9fe194ec3 100644 --- a/packages/twenty-front/src/modules/billing/hooks/useGetWorkflowNodeExecutionUsage.ts +++ b/packages/twenty-front/src/modules/billing/hooks/useGetWorkflowNodeExecutionUsage.ts @@ -19,7 +19,7 @@ export const useGetWorkflowNodeExecutionUsage = () => { return { usageQuantity: 0, freeUsageQuantity: 0, - includedFreeQuantity: 0, + includedFreeQuantity: 10000, paidUsageQuantity: 0, unitPriceCents: 0, totalCostCents: 0, diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index e066cfd21..343450095 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -106,7 +106,9 @@ export const SettingsBilling = () => { ]} > - + {hasNotCanceledCurrentSubscription && ( + + )}
0) { + await this.billingSubscriptionItemRepository.delete({ + billingSubscriptionId: subscriptionId, + stripeSubscriptionItemId: In(deletedSubscriptionItemIds), + }); + } + + await this.billingSubscriptionItemRepository.upsert( + transformStripeSubscriptionEventToDatabaseSubscriptionItem( + subscriptionId, + event.data, + ), + { + conflictPaths: ['stripeSubscriptionItemId'], + skipUpdateIfNoValuesChanged: true, + }, + ); + } } diff --git a/packages/twenty-server/src/engine/core-modules/billing/webhooks/utils/__tests__/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util.spec.ts b/packages/twenty-server/src/engine/core-modules/billing/webhooks/utils/__tests__/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util.spec.ts new file mode 100644 index 000000000..75c0863bb --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/webhooks/utils/__tests__/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util.spec.ts @@ -0,0 +1,33 @@ +import { + mockStripeSubscriptionUpdatedEventWithDeletedItem, + mockStripeSubscriptionUpdatedEventWithoutUpdatedItem, + mockStripeSubscriptionUpdatedEventWithUpdatedItemOnly, +} from 'src/engine/core-modules/billing/webhooks/__mocks__/stripe-subscription-updated-events'; +import { getDeletedStripeSubscriptionItemIdsFromStripeSubscriptionEvent } from 'src/engine/core-modules/billing/webhooks/utils/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util'; + +describe('getDeletedStripeSubscriptionItemIdsFromStripeSubscriptionEvent', () => { + it('should return an empty array if subscription items are not updated', () => { + const result = + getDeletedStripeSubscriptionItemIdsFromStripeSubscriptionEvent( + mockStripeSubscriptionUpdatedEventWithoutUpdatedItem, + ); + + expect(result).toEqual([]); + }); + it('should return an empty array if subscription items are updated but not deleted', () => { + const result = + getDeletedStripeSubscriptionItemIdsFromStripeSubscriptionEvent( + mockStripeSubscriptionUpdatedEventWithUpdatedItemOnly, + ); + + expect(result).toEqual([]); + }); + it('should return subscription item ids if subscription items are deleted', () => { + const result = + getDeletedStripeSubscriptionItemIdsFromStripeSubscriptionEvent( + mockStripeSubscriptionUpdatedEventWithDeletedItem, + ); + + expect(result).toEqual(['deleted_item_id']); + }); +}); diff --git a/packages/twenty-server/src/engine/core-modules/billing/webhooks/utils/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util.ts b/packages/twenty-server/src/engine/core-modules/billing/webhooks/utils/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util.ts new file mode 100644 index 000000000..ff16a25f1 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/billing/webhooks/utils/get-deleted-stripe-subscription-item-ids-from-stripe-subscription-event.util.ts @@ -0,0 +1,29 @@ +/* @license Enterprise */ + +import Stripe from 'stripe'; +import { isDefined } from 'twenty-shared/utils'; + +export const getDeletedStripeSubscriptionItemIdsFromStripeSubscriptionEvent = ( + event: + | Stripe.CustomerSubscriptionUpdatedEvent + | Stripe.CustomerSubscriptionCreatedEvent + | Stripe.CustomerSubscriptionDeletedEvent, +): string[] => { + const hasUpdatedSubscriptionItems = isDefined( + event.data.previous_attributes?.items?.data, + ); + + if (!hasUpdatedSubscriptionItems) { + return []; + } + + const subscriptionItemIds = + event.data.object.items.data.map((item) => item.id) ?? []; + + const deletedSubscriptionItemIds = + event.data.previous_attributes?.items?.data + .filter((item) => !subscriptionItemIds.includes(item.id)) + .map((item) => item.id) ?? []; + + return deletedSubscriptionItemIds; +};