From 67d155759e8216fd96b7165ffa3ab156a6136971 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 14 Jul 2024 14:19:04 +0200 Subject: [PATCH] feat: backend the new monthly susbcription plans --- .../Subscription/LemonSqueezyWebhooks.ts | 25 +---- ..._lemon_variant_id_to_subscription_plans.js | 11 +++ ...4101229_seed_monthly_subscription_plans.js | 96 +++++++++++++++++++ .../src/constants/subscriptionModels.tsx | 62 +++++++++--- 4 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 packages/server/src/system/migrations/20240714101006_add_lemon_variant_id_to_subscription_plans.js create mode 100644 packages/server/src/system/migrations/20240714101229_seed_monthly_subscription_plans.js diff --git a/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts b/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts index 6a7c3966d..0be4dac35 100644 --- a/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts +++ b/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts @@ -1,4 +1,3 @@ -import { getPrice } from '@lemonsqueezy/lemonsqueezy.js'; import config from '@/config'; import { Inject, Service } from 'typedi'; import { @@ -10,7 +9,6 @@ import { } from './utils'; import { Plan } from '@/system/models'; import { Subscription } from './Subscription'; -import { isEmpty } from 'lodash'; @Service() export class LemonSqueezyWebhooks { @@ -18,7 +16,7 @@ export class LemonSqueezyWebhooks { private subscriptionService: Subscription; /** - * handle the LemonSqueezy webhooks. + * Handles the Lemon Squeezy webhooks. * @param {string} rawBody * @param {string} signature * @returns {Promise} @@ -74,7 +72,7 @@ export class LemonSqueezyWebhooks { const variantId = attributes.variant_id as string; // We assume that the Plan table is up to date. - const plan = await Plan.query().findOne('slug', 'early-adaptor'); + const plan = await Plan.query().findOne('lemonVariantId', variantId); if (!plan) { throw new Error(`Plan with variantId ${variantId} not found.`); @@ -82,26 +80,9 @@ export class LemonSqueezyWebhooks { // Update the subscription in the database. const priceId = attributes.first_subscription_item.price_id; - // Get the price data from Lemon Squeezy. - const priceData = await getPrice(priceId); - - if (priceData.error) { - throw new Error( - `Failed to get the price data for the subscription ${eventBody.data.id}.` - ); - } - const isUsageBased = - attributes.first_subscription_item.is_usage_based; - const price = isUsageBased - ? priceData.data?.data.attributes.unit_price_decimal - : priceData.data?.data.attributes.unit_price; - // Create a new subscription of the tenant. if (webhookEvent === 'subscription_created') { - await this.subscriptionService.newSubscribtion( - tenantId, - 'early-adaptor' - ); + await this.subscriptionService.newSubscribtion(tenantId, plan.slug); } } } else if (webhookEvent.startsWith('order_')) { diff --git a/packages/server/src/system/migrations/20240714101006_add_lemon_variant_id_to_subscription_plans.js b/packages/server/src/system/migrations/20240714101006_add_lemon_variant_id_to_subscription_plans.js new file mode 100644 index 000000000..eeee3581f --- /dev/null +++ b/packages/server/src/system/migrations/20240714101006_add_lemon_variant_id_to_subscription_plans.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.table('subscription_plans', (table) => { + table.string('lemon_variant_id').nullable().index(); + }); +}; + +exports.down = (knex) => { + return knex.schema.table('subscription_plans', (table) => { + table.dropColumn('lemon_variant_id'); + }); +}; diff --git a/packages/server/src/system/migrations/20240714101229_seed_monthly_subscription_plans.js b/packages/server/src/system/migrations/20240714101229_seed_monthly_subscription_plans.js new file mode 100644 index 000000000..7f1e506ac --- /dev/null +++ b/packages/server/src/system/migrations/20240714101229_seed_monthly_subscription_plans.js @@ -0,0 +1,96 @@ +exports.up = function (knex) { + return knex('subscription_plans').insert([ + // Capital Basic + { + name: 'Capital Basic (Monthly)', + slug: 'capital-basic-monthly', + price: 10, + active: true, + currency: 'USD', + invoice_period: 1, + invoice_interval: 'month', + lemon_variant_id: '446152', + // lemon_variant_id: '450016', + }, + { + name: 'Capital Basic (Annually)', + slug: 'capital-basic-annually', + price: 90, + active: true, + currency: 'USD', + invoice_period: 1, + invoice_interval: 'year', + lemon_variant_id: '446153', + // lemon_variant_id: '450018', + }, + + // # Capital Essential + { + name: 'Capital Essential (Monthly)', + slug: 'capital-essential-monthly', + price: 20, + active: true, + currency: 'USD', + invoice_period: 1, + invoice_interval: 'month', + lemon_variant_id: '446155', + // lemon_variant_id: '450028', + }, + { + name: 'Capital Essential (Annually)', + slug: 'capital-essential-annually', + price: 180, + active: true, + invoice_period: 1, + invoice_interval: 'year', + lemon_variant_id: '446156', + // lemon_variant_id: '450029', + }, + + // # Capital Plus + { + name: 'Capital Plus (Monthly)', + slug: 'capital-plus-monthly', + price: 25, + active: true, + invoice_period: 1, + invoice_interval: 'month', + lemon_variant_id: '446165', + // lemon_variant_id: '450031', + }, + { + name: 'Capital Plus (Annually)', + slug: 'capital-plus-annually', + price: 228, + active: true, + invoice_period: 1, + invoice_interval: 'year', + lemon_variant_id: '446164', + // lemon_variant_id: '450032', + }, + + // # Capital Big + { + name: 'Capital Big (Monthly)', + slug: 'capital-big-monthly', + price: 40, + active: true, + invoice_period: 1, + invoice_interval: 'month', + lemon_variant_id: '446167', + // lemon_variant_id: '450024', + }, + { + name: 'Capital Big (Annually)', + slug: 'capital-big-annually', + price: 360, + active: true, + invoice_period: 1, + invoice_interval: 'year', + lemon_variant_id: '446168', + // lemon_variant_id: '450025', + }, + ]); +}; + +exports.down = function (knex) {}; diff --git a/packages/webapp/src/constants/subscriptionModels.tsx b/packages/webapp/src/constants/subscriptionModels.tsx index ab16d07ed..e1a6ba35c 100644 --- a/packages/webapp/src/constants/subscriptionModels.tsx +++ b/packages/webapp/src/constants/subscriptionModels.tsx @@ -33,8 +33,15 @@ export const SubscriptionPlans = [ { text: 'Track GST and VAT' }, { text: 'Connect Banks for Automatic Importing' }, { text: 'Chart of Accounts' }, - { text: 'Manual Journals' }, - { text: 'Basic Financial Reports & Insights' }, + { + text: 'Manual Journals', + hintLabel: 'Manual Journals', + hint: 'Write manual journals entries for financial transactions not automatically captured by the system to adjust financial statements.', + }, + { + text: 'Basic Financial Reports & Insights', + hint: 'Balance sheet, profit & loss statement, cashflow statement, general ledger, journal sheet, A/P aging summary, A/R aging summary', + }, { text: 'Unlimited User Seats' }, ], monthlyPrice: '$10', @@ -42,7 +49,9 @@ export const SubscriptionPlans = [ annuallyPrice: '$7.5', annuallyPriceLabel: 'Per month', monthlyVariantId: '446152', + // monthlyVariantId: '450016', annuallyVariantId: '446153', + // annuallyVariantId: '450018', }, { name: 'Capital Essential', @@ -51,9 +60,21 @@ export const SubscriptionPlans = [ features: [ { text: 'All Capital Basic features' }, { text: 'Purchase Invoices' }, - { text: 'Multi Currency Transactions' }, - { text: 'Transactions Locking' }, - { text: 'Inventory Tracking' }, + { + text: 'Multi Currency Transactions', + hintLabel: 'Multi Currency', + hint: 'Pay and get paid and do manual journals in any currency with real time exchange rates conversions.', + }, + { + text: 'Transactions Locking', + hintLabel: 'Transactions Locking', + hint: 'Transaction Locking freezes transactions to prevent any additions, modifications, or deletions of transactions recorded during the specified date.', + }, + { + text: 'Inventory Tracking', + hintLabel: 'Inventory Tracking', + hint: 'Track goods in the stock, cost of goods, and get notifications when quantity is low.', + }, { text: 'Smart Financial Reports' }, { text: 'Advanced Inventory Reports' }, ], @@ -61,8 +82,10 @@ export const SubscriptionPlans = [ monthlyPriceLabel: 'Per month', annuallyPrice: '$15', annuallyPriceLabel: 'Per month', - monthlyVariantId: '446165', - annuallyVariantId: '446164', + // monthlyVariantId: '450028', + monthlyVariantId: '446155', + // annuallyVariantId: '450029', + annuallyVariantId: '446156', }, { name: 'Capital Plus', @@ -72,15 +95,20 @@ export const SubscriptionPlans = [ { text: 'All Capital Essential features' }, { text: 'Custom User Roles Access' }, { text: 'Vendor Credits' }, - { text: 'Budgeting' }, - { text: 'Analysis Tracking Tags' }, + { + text: 'Budgeting', + hint: 'Create multiple budgets and compare targets with actuals to understand how your business is performing.', + }, + { text: 'Analysis Cost Center' }, ], monthlyPrice: '$25', monthlyPriceLabel: 'Per month', - annuallyPrice: '$18', + annuallyPrice: '$19', annuallyPriceLabel: 'Per month', featured: true, + // monthlyVariantId: '450031', monthlyVariantId: '446165', + // annuallyVariantId: '450032', annuallyVariantId: '446164', }, { @@ -89,14 +117,24 @@ export const SubscriptionPlans = [ description: 'Good for businesses have multiple branches.', features: [ { text: 'All Capital Plus features' }, - { text: 'Multiple Branches' }, - { text: 'Multiple Warehouses' }, + { + text: 'Multiple Branches', + hintLabel: '', + hint: 'Track the organization transactions and accounts in multiple branches.', + }, + { + text: 'Multiple Warehouses', + hintLabel: 'Multiple Warehouses', + hint: 'Track the organization inventory in multiple warehouses and transfer goods between them.', + }, ], monthlyPrice: '$40', monthlyPriceLabel: 'Per month', annuallyPrice: '$30', annuallyPriceLabel: 'Per month', + // monthlyVariantId: '450024', monthlyVariantId: '446167', + // annuallyVariantId: '450025', annuallyVariantId: '446168', }, ] as SubscriptionPlan[];