2025-04-21 22:29:26 -03:00
|
|
|
// BotServer/packages/saas.gbapp/service/MainService.ts
|
2024-09-01 18:21:34 -03:00
|
|
|
import { GBOnlineSubscription } from '../model/MainModel.js';
|
|
|
|
import { GBMinInstance, GBLog } from 'botlib';
|
|
|
|
import { CollectionUtil } from 'pragmatismo-io-framework';
|
|
|
|
import urlJoin from 'url-join';
|
2025-03-29 11:03:46 -03:00
|
|
|
import { GBOService } from './GBOService.js';
|
2025-03-29 20:27:22 -03:00
|
|
|
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
|
2025-04-18 22:20:33 -03:00
|
|
|
import Stripe from 'stripe';
|
2025-04-21 22:29:26 -03:00
|
|
|
import { GBUtil } from '../../../src/util.js';
|
2024-09-01 18:21:34 -03:00
|
|
|
|
|
|
|
export class MainService {
|
2025-04-21 22:29:26 -03:00
|
|
|
private gboService: GBOService;
|
2025-04-18 22:20:33 -03:00
|
|
|
private stripe: Stripe;
|
2025-04-21 22:29:26 -03:00
|
|
|
private readonly PAYMENT_CHECK_INTERVAL = 5000; // 5 seconds
|
|
|
|
private readonly PAYMENT_CHECK_TIMEOUT = 300000; // 5 minutes timeout
|
2025-04-18 22:20:33 -03:00
|
|
|
|
|
|
|
constructor() {
|
2025-04-21 22:29:26 -03:00
|
|
|
this.gboService = new GBOService();
|
2025-04-18 22:20:33 -03:00
|
|
|
this.stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
|
|
|
|
}
|
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
public async startSubscriptionProcess(
|
2024-09-01 18:21:34 -03:00
|
|
|
min: GBMinInstance,
|
|
|
|
name: string,
|
|
|
|
email: string,
|
|
|
|
mobile: string,
|
|
|
|
botName: string,
|
|
|
|
templateName: string,
|
2025-04-21 22:29:26 -03:00
|
|
|
planId: string
|
2025-04-18 22:20:33 -03:00
|
|
|
) {
|
2025-04-21 22:29:26 -03:00
|
|
|
// Create initial subscription record
|
|
|
|
const subscription = await GBOnlineSubscription.create({
|
|
|
|
instanceId: min.instance.instanceId,
|
|
|
|
customerName: name,
|
|
|
|
customerEmail: email,
|
|
|
|
customerMobile: mobile,
|
|
|
|
botName: botName,
|
|
|
|
planId: planId,
|
|
|
|
status: planId === 'free' ? 'active' : 'pending_payment',
|
|
|
|
createdAt: new Date(),
|
|
|
|
activatedAt: planId === 'free' ? new Date() : null
|
|
|
|
});
|
2025-04-18 22:20:33 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
if (planId === 'free') {
|
|
|
|
return await this.createBotResources(min, subscription, templateName);
|
|
|
|
} else {
|
|
|
|
const priceId = this.getPriceIdForPlan(planId);
|
|
|
|
const session = await this.stripe.checkout.sessions.create({
|
|
|
|
payment_method_types: ['card'],
|
|
|
|
line_items: [{
|
|
|
|
price: priceId,
|
|
|
|
quantity: 1,
|
|
|
|
}],
|
2025-04-24 01:18:30 -03:00
|
|
|
success_url: urlJoin(process.env.BOT_URL, min.botId, 'paymentSuccess?session_id={CHECKOUT_SESSION_ID}'),
|
2025-04-21 22:29:26 -03:00
|
|
|
mode: 'subscription',
|
|
|
|
metadata: {
|
|
|
|
subscriptionId: subscription.subscriptionId.toString(),
|
|
|
|
botName: botName
|
|
|
|
}
|
|
|
|
});
|
2025-04-18 22:20:33 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
await subscription.update({
|
|
|
|
stripeSessionId: session.id
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
paymentUrl: session.url,
|
|
|
|
subscriptionId: subscription.subscriptionId,
|
|
|
|
nextStep: 'Please complete the payment in the new window. I will check for completion automatically.'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2025-04-18 22:20:33 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
public async waitForPaymentCompletion(
|
|
|
|
min: GBMinInstance,
|
|
|
|
subscriptionId: number,
|
|
|
|
templateName: string
|
|
|
|
): Promise<any> {
|
|
|
|
const startTime = Date.now();
|
|
|
|
|
|
|
|
while ((Date.now() - startTime) < this.PAYMENT_CHECK_TIMEOUT) {
|
|
|
|
const subscription = await GBOnlineSubscription.findOne({
|
|
|
|
where: { subscriptionId }
|
|
|
|
});
|
2025-04-18 22:20:33 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
if (!subscription) {
|
|
|
|
throw new Error('Subscription not found');
|
|
|
|
}
|
2025-04-18 22:20:33 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
if (subscription.stripeSessionId) {
|
|
|
|
const session = await this.stripe.checkout.sessions.retrieve(
|
|
|
|
subscription.stripeSessionId,
|
|
|
|
{ expand: ['payment_intent'] }
|
|
|
|
);
|
|
|
|
|
|
|
|
if (session.payment_status === 'paid') {
|
|
|
|
await subscription.update({
|
|
|
|
status: 'active',
|
|
|
|
activatedAt: new Date(),
|
|
|
|
stripePaymentIntentId: (session.payment_intent as any)?.id
|
|
|
|
});
|
|
|
|
|
|
|
|
return await this.createBotResources(min, subscription, templateName);
|
|
|
|
}
|
|
|
|
|
2025-04-24 01:18:30 -03:00
|
|
|
if (session.status === 'expired') {
|
2025-04-21 22:29:26 -03:00
|
|
|
throw new Error('Payment failed or session expired. Please try again.');
|
|
|
|
}
|
2025-04-18 22:20:33 -03:00
|
|
|
}
|
2025-04-21 22:29:26 -03:00
|
|
|
|
|
|
|
await GBUtil.sleep(this.PAYMENT_CHECK_INTERVAL);
|
2025-04-18 22:20:33 -03:00
|
|
|
}
|
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
throw new Error('Payment processing timed out. Please check your payment and try again.');
|
|
|
|
}
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
private getPriceIdForPlan(planId: string): string {
|
|
|
|
const priceIds = {
|
|
|
|
personal: process.env.STRIPE_PERSONAL_PLAN_PRICE_ID,
|
|
|
|
professional: process.env.STRIPE_PROFESSIONAL_PLAN_PRICE_ID
|
|
|
|
};
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
if (!priceIds[planId]) {
|
|
|
|
throw new Error(`No price ID configured for plan: ${planId}`);
|
|
|
|
}
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
return priceIds[planId];
|
|
|
|
}
|
|
|
|
|
|
|
|
private async createBotResources(
|
|
|
|
min: GBMinInstance,
|
|
|
|
subscription: GBOnlineSubscription,
|
|
|
|
templateName: string
|
|
|
|
) {
|
2025-04-18 22:20:33 -03:00
|
|
|
GBLog.info('Deploying a blank bot to storage...');
|
2025-04-21 22:29:26 -03:00
|
|
|
const instance = await min.deployService.deployBlankBot(
|
|
|
|
subscription.botName,
|
|
|
|
subscription.customerMobile,
|
|
|
|
subscription.customerEmail
|
|
|
|
);
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
await subscription.update({
|
|
|
|
instanceId: instance.instanceId
|
|
|
|
});
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-18 22:20:33 -03:00
|
|
|
let token =
|
|
|
|
GBConfigService.get('GB_MODE') === 'legacy' ?
|
|
|
|
await (min.adminService.acquireElevatedToken as any)(min.instance.instanceId, true) :
|
|
|
|
null;
|
2024-09-01 18:21:34 -03:00
|
|
|
|
|
|
|
let siteId = process.env.STORAGE_SITE_ID;
|
|
|
|
let libraryId = process.env.STORAGE_LIBRARY;
|
|
|
|
|
2025-04-18 22:20:33 -03:00
|
|
|
GBLog.info('Creating .gbai folder ...');
|
2025-04-21 22:29:26 -03:00
|
|
|
let item = await this.gboService.createRootFolder(
|
|
|
|
token,
|
|
|
|
`${subscription.botName}.gbai`,
|
|
|
|
siteId,
|
|
|
|
libraryId
|
|
|
|
);
|
2025-03-29 20:27:22 -03:00
|
|
|
|
2025-04-18 22:20:33 -03:00
|
|
|
GBLog.info('Copying Templates...');
|
2025-04-21 22:29:26 -03:00
|
|
|
await this.gboService.copyTemplates(min, item, templateName, 'gbkb', subscription.botName);
|
|
|
|
await this.gboService.copyTemplates(min, item, templateName, 'gbot', subscription.botName);
|
|
|
|
await this.gboService.copyTemplates(min, item, templateName, 'gbtheme', subscription.botName);
|
|
|
|
await this.gboService.copyTemplates(min, item, templateName, 'gbdata', subscription.botName);
|
|
|
|
await this.gboService.copyTemplates(min, item, templateName, 'gbdialog', subscription.botName);
|
|
|
|
await this.gboService.copyTemplates(min, item, templateName, 'gbdrive', subscription.botName);
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-18 22:20:33 -03:00
|
|
|
GBLog.info('Configuring .gbot...');
|
2025-04-21 22:29:26 -03:00
|
|
|
await min.core['setConfig'](min, instance.botId, "Can Publish", subscription.customerMobile + ";");
|
|
|
|
await min.core['setConfig'](min, instance.botId, "Admin Notify E-mail", subscription.customerEmail);
|
2025-04-18 22:20:33 -03:00
|
|
|
await min.core['setConfig'](min, instance.botId, 'WebDav Username', instance.botId);
|
|
|
|
await min.core['setConfig'](min, instance.botId, 'WebDav Secret', instance.adminPass);
|
2024-09-01 18:21:34 -03:00
|
|
|
|
2025-04-21 22:29:26 -03:00
|
|
|
return {
|
|
|
|
success: true,
|
|
|
|
botUrl: urlJoin(process.env.BOT_URL, subscription.botName)
|
|
|
|
};
|
2024-09-01 18:21:34 -03:00
|
|
|
}
|
2025-04-21 22:29:26 -03:00
|
|
|
}
|