mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
feat: auto subscribe to free plan once signup on community version.
This commit is contained in:
@@ -201,4 +201,13 @@ module.exports = {
|
||||
storeId: process.env.LEMONSQUEEZY_STORE_ID,
|
||||
webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
|
||||
},
|
||||
|
||||
/**
|
||||
* Bigcapital (Cloud).
|
||||
* NOTE: DO NOT CHANGE THIS OPTION OR ADD THIS ENV VAR.
|
||||
*/
|
||||
hostedOnBigcapitalCloud: parseBoolean(
|
||||
process.env.HOSTED_ON_BIGCAPITAL_CLOUD,
|
||||
false
|
||||
),
|
||||
};
|
||||
|
||||
@@ -90,6 +90,8 @@ import { SaleReceiptMarkClosedOnMailSentSubcriber } from '@/services/Sales/Recei
|
||||
import { SaleEstimateMarkApprovedOnMailSent } from '@/services/Sales/Estimates/subscribers/SaleEstimateMarkApprovedOnMailSent';
|
||||
import { DeleteCashflowTransactionOnUncategorize } from '@/services/Cashflow/subscribers/DeleteCashflowTransactionOnUncategorize';
|
||||
import { PreventDeleteTransactionOnDelete } from '@/services/Cashflow/subscribers/PreventDeleteTransactionsOnDelete';
|
||||
import { SubscribeFreeOnSignupCommunity } from '@/services/Subscription/events/SubscribeFreeOnSignupCommunity';
|
||||
|
||||
|
||||
export default () => {
|
||||
return new EventPublisher();
|
||||
@@ -219,5 +221,7 @@ export const susbcribers = () => {
|
||||
// Cashflow
|
||||
DeleteCashflowTransactionOnUncategorize,
|
||||
PreventDeleteTransactionOnDelete,
|
||||
|
||||
SubscribeFreeOnSignupCommunity
|
||||
];
|
||||
};
|
||||
|
||||
@@ -96,9 +96,7 @@ export class LemonSqueezyWebhooks {
|
||||
if (webhookEvent === 'subscription_created') {
|
||||
await this.subscriptionService.newSubscribtion(
|
||||
tenantId,
|
||||
'pro-yearly',
|
||||
'year',
|
||||
1
|
||||
'early-adaptor',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,17 @@ export class Subscription {
|
||||
public async newSubscribtion(
|
||||
tenantId: number,
|
||||
planSlug: string,
|
||||
invoiceInterval: string,
|
||||
invoicePeriod: number,
|
||||
subscriptionSlug: string = 'main'
|
||||
) {
|
||||
const tenant = await Tenant.query().findById(tenantId).throwIfNotFound();
|
||||
const plan = await Plan.query().findOne('slug', planSlug).throwIfNotFound();
|
||||
|
||||
const isFree = plan.price === 0;
|
||||
|
||||
// Take the invoice interval and period from the given plan.
|
||||
const invoiceInterval = plan.invoiceInternal;
|
||||
const invoicePeriod = isFree ? Infinity : plan.invoicePeriod;
|
||||
|
||||
const subscription = await tenant
|
||||
.$relatedQuery('subscriptions')
|
||||
.modify('subscriptionBySlug', subscriptionSlug)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import moment from 'moment';
|
||||
import moment, { type unitOfTime } from 'moment';
|
||||
|
||||
export default class SubscriptionPeriod {
|
||||
start: Date;
|
||||
end: Date;
|
||||
interval: string;
|
||||
count: number;
|
||||
private start: Date;
|
||||
private end: Date;
|
||||
private interval: string;
|
||||
private count: number;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -12,7 +12,11 @@ export default class SubscriptionPeriod {
|
||||
* @param {number} count -
|
||||
* @param {Date} start -
|
||||
*/
|
||||
constructor(interval: string = 'month', count: number, start?: Date) {
|
||||
constructor(
|
||||
interval: unitOfTime.DurationConstructor = 'month',
|
||||
count: number,
|
||||
start?: Date
|
||||
) {
|
||||
this.interval = interval;
|
||||
this.count = count;
|
||||
this.start = start;
|
||||
@@ -20,7 +24,11 @@ export default class SubscriptionPeriod {
|
||||
if (!start) {
|
||||
this.start = moment().toDate();
|
||||
}
|
||||
this.end = moment(start).add(count, interval).toDate();
|
||||
if (count === Infinity) {
|
||||
this.end = null;
|
||||
} else {
|
||||
this.end = moment(start).add(count, interval).toDate();
|
||||
}
|
||||
}
|
||||
|
||||
getStartDate() {
|
||||
@@ -36,6 +44,6 @@ export default class SubscriptionPeriod {
|
||||
}
|
||||
|
||||
getIntervalCount() {
|
||||
return this.interval;
|
||||
return this.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { IAuthSignedUpEventPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import config from '@/config';
|
||||
import { Subscription } from '../Subscription';
|
||||
import { Inject, Service } from 'typedi';
|
||||
|
||||
@Service()
|
||||
export class SubscribeFreeOnSignupCommunity {
|
||||
@Inject()
|
||||
private subscriptionService: Subscription;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach = (bus) => {
|
||||
bus.subscribe(
|
||||
events.auth.signUp,
|
||||
this.subscribeFreeOnSigupCommunity.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new free subscription once the user signup if the app is self-hosted.
|
||||
* @param {IAuthSignedUpEventPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async subscribeFreeOnSigupCommunity({
|
||||
signupDTO,
|
||||
tenant,
|
||||
user,
|
||||
}: IAuthSignedUpEventPayload) {
|
||||
if (config.hostedOnBigcapitalCloud) return null;
|
||||
|
||||
await this.subscriptionService.newSubscribtion(tenant.id, 'free');
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ export default class Tenant extends BaseModel {
|
||||
static newSubscription(
|
||||
tenantId: number,
|
||||
planId: number,
|
||||
invoiceInterval: string,
|
||||
invoiceInterval: 'month' | 'year',
|
||||
invoicePeriod: number,
|
||||
subscriptionSlug: string
|
||||
) {
|
||||
|
||||
@@ -1,65 +1,25 @@
|
||||
|
||||
exports.seed = (knex) => {
|
||||
// Deletes ALL existing entries
|
||||
return knex('subscription_plans').del()
|
||||
return knex('subscription_plans')
|
||||
.del()
|
||||
.then(() => {
|
||||
// Inserts seed entries
|
||||
return knex('subscription_plans').insert([
|
||||
{
|
||||
name: 'Essentials',
|
||||
slug: 'essentials-monthly',
|
||||
price: 100,
|
||||
name: 'Free',
|
||||
slug: 'free',
|
||||
price: 0,
|
||||
active: true,
|
||||
currency: 'LYD',
|
||||
trial_period: 7,
|
||||
trial_interval: 'days',
|
||||
currency: 'USD',
|
||||
},
|
||||
{
|
||||
name: 'Essentials',
|
||||
slug: 'essentials-yearly',
|
||||
price: 1200,
|
||||
name: 'Early Adaptor',
|
||||
slug: 'early-adaptor',
|
||||
price: 29,
|
||||
active: true,
|
||||
currency: 'LYD',
|
||||
trial_period: 12,
|
||||
trial_interval: 'months',
|
||||
},
|
||||
{
|
||||
name: 'Pro',
|
||||
slug: 'pro-monthly',
|
||||
price: 200,
|
||||
active: true,
|
||||
currency: 'LYD',
|
||||
trial_period: 1,
|
||||
trial_interval: 'months',
|
||||
},
|
||||
{
|
||||
name: 'Pro',
|
||||
slug: 'pro-yearly',
|
||||
price: 500,
|
||||
active: true,
|
||||
currency: 'LYD',
|
||||
currency: 'USD',
|
||||
invoice_period: 12,
|
||||
invoice_interval: 'month',
|
||||
index: 2,
|
||||
},
|
||||
{
|
||||
name: 'Plus',
|
||||
slug: 'plus-monthly',
|
||||
price: 200,
|
||||
active: true,
|
||||
currency: 'LYD',
|
||||
trial_period: 1,
|
||||
trial_interval: 'months',
|
||||
},
|
||||
{
|
||||
name: 'Plus',
|
||||
slug: 'plus-yearly',
|
||||
price: 500,
|
||||
active: true,
|
||||
currency: 'LYD',
|
||||
invoice_period: 12,
|
||||
invoice_interval: 'month',
|
||||
index: 2,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import PaymentViaVoucherDialog from '@/containers/Dialogs/PaymentViaVoucherDialog';
|
||||
|
||||
/**
|
||||
* Setup dialogs.
|
||||
*/
|
||||
export default function SetupDialogs() {
|
||||
return (
|
||||
<div class="setup-dialogs">
|
||||
<PaymentViaVoucherDialog dialogName={'payment-via-voucher'} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
import SetupDialogs from './SetupDialogs';
|
||||
import SetupWizardContent from './SetupWizardContent';
|
||||
|
||||
import withOrganization from '@/containers/Organization/withOrganization';
|
||||
@@ -30,7 +29,6 @@ function SetupRightSection({
|
||||
return (
|
||||
<section className={'setup-page__right-section'}>
|
||||
<SetupWizardContent stepId={setupStepId} stepIndex={setupStepIndex} />
|
||||
<SetupDialogs />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ function SubscriptionPricing({
|
||||
useGetLemonSqueezyCheckout();
|
||||
|
||||
const handleClick = () => {
|
||||
getLemonCheckout({ variantId: '337977' })
|
||||
getLemonCheckout({ variantId: '338516' })
|
||||
.then((res) => {
|
||||
const checkoutUrl = res.data.data.attributes.url;
|
||||
window.LemonSqueezy.Url.Open(checkoutUrl);
|
||||
|
||||
Reference in New Issue
Block a user