Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add entitlement table and sso stripe feature #8608

Merged
merged 6 commits into from
Nov 22, 2024
Merged

Conversation

anamarn
Copy link
Contributor

@anamarn anamarn commented Nov 20, 2024

TLDR

Added Billing Entitlement table, based on stripe customer.ActiveEntitlements webhook event. In this table it has a key value pair with each key being the stripe feature lookup key and the value a boolean. We use this table in order to see if SSO or other feaures are enabled by workspace.

In order to test: twenty-server

Billing:

  • Set IS_BILLING_ENABLED to true
  • Add your BILLING_STRIPE_SECRET and BILLING_STRIPE_API_KEY
  • Add your BILLING_STRIPE_BASE_PLAN_PRODUCT_ID (use the one in testMode > Base Plan)

Auth:

  • Set AUTH_SSO_ENABLED to true
  • Set your ACCESS_TOKEN_SECRET, LOGIN_TOKEN_SECRET, REFRESH_TOKEN_SECRET and FILE_TOKEN_SECRET
  • Set IS_SSO_ENABLED feature flag to true

Stripe Webhook:

Migration:

  • npx nx typeorm -- migration:run -d src/database/typeorm/core/core.datasource.ts

In order to test: twenty site

  • Buy a subscription (you can use the card 4242...42 with expiration date later in the future)
  • Go to SSO and create an OICD subscription
  • Change the value in the entitlement table in order to put it in false
  • An error should occur saying that the current workspace has no entitlement

Considerations

The data from the Entitlement table is updated based on the stripe webhook responses, and we use the customerActiveEntitlemet response to update the info on the table, however this event doesnt have the metadata containing the workspaceId. Because we cannot control at wich order the webhook send events, we force a server error if the entitlements are updated before the BillingSubscription. Stripe resends the event based on a exponential backoff (for more info see https://docs.stripe.com/webhooks#retries ) because we are in test mode Stripe retries three times over a few hours. So if the BillingEntitlement is not updated it is completely normal and it will be updated when stripe resends the event.

event.data,
);
} catch (error) {
res.status(500).end();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch (BillingCustomerNotFoundException) { 404)
catch(error) { 500 }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for catch(error) { 500 } just catch (BillingCustomerNotFoundException) { 404)

} catch (error) {
res.status(500).end();

return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be useless

@@ -3,7 +3,7 @@ import { ArgsType, Field } from '@nestjs/graphql';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
import Stripe from 'stripe';

import { SubscriptionInterval } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/subcription-interval.enum';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefix with billing
billing-subscription-interval.enum

@@ -2,7 +2,7 @@ import { ArgsType, Field } from '@nestjs/graphql';

import { IsNotEmpty, IsString } from 'class-validator';

import { AvailableProduct } from 'src/engine/core-modules/billing/interfaces/available-product.interface';
import { AvailableProduct } from 'src/engine/core-modules/billing/enums/available-product.enum';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

CUSTOMER_SUBSCRIPTION_UPDATED = 'customer.subscription.updated',
CUSTOMER_SUBSCRIPTION_DELETED = 'customer.subscription.deleted',
SETUP_INTENT_SUCCEEDED = 'setup_intent.succeeded',
CUSTOMER_ACTIVE_ENTITLEMENT = 'entitlements.active_entitlement_summary.updated',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CUSTOMER_ACTIVE_ENTITLEMENT_UDPATED

@@ -148,6 +97,23 @@ export class BillingSubscriptionService {
}
}

async getWorkspaceEntitlementByKey(
workspaceId: string,
lookupKey: FeatureStripeLookupKey,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key: BillingEntitlementKey

value: true,
});

if (!entitlement?.value) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split to be fully explicit
if (entitlement === undefined) { return false; }
return entitlement.value

@@ -0,0 +1,3 @@
export enum FeatureStripeLookupKey {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment below
SSO

const workspaceId = billingSubscription.workspaceId;
const stripeCustomerId = data.object.customer;

const currentEntitlements = data.object.entitlements.data.map(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentEntitlement ==> activeEntitlement everywhere
currentEntitlements => activeEntitlementKeys

@FelixMalfait
Copy link
Member

Great work!

@FelixMalfait FelixMalfait merged commit 35f2d7a into main Nov 22, 2024
22 of 23 checks passed
@FelixMalfait FelixMalfait deleted the feat/function-billing branch November 22, 2024 14:32
Copy link

Thanks @anamarn for your contribution!
This marks your 11th PR on the repo. You're top 3% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants