first commit
This commit is contained in:
parent
bde4f90235
commit
b409b81b18
23
package-lock.json
generated
23
package-lock.json
generated
@ -26,6 +26,8 @@
|
||||
"cache-manager-redis-store": "^3.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"passport-headerapikey": "^1.2.2",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
@ -9201,11 +9203,30 @@
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-headerapikey": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/passport-headerapikey/-/passport-headerapikey-1.2.2.tgz",
|
||||
"integrity": "sha512-4BvVJRrWsNJPrd3UoZfcnnl4zvUWYKEtfYkoDsaOKBsrWHYmzTApCjs7qUbncOLexE9ul0IRiYBFfBG0y9IVQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.15",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-jwt": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
|
||||
"integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
|
||||
@ -37,6 +37,8 @@
|
||||
"cache-manager-redis-store": "^3.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"passport-headerapikey": "^1.2.2",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { Response } from 'express';
|
||||
import { Prisma } from 'generated/prisma';
|
||||
|
||||
@Catch(Prisma.PrismaClientKnownRequestError)
|
||||
export class PrismaExceptionFilter implements ExceptionFilter {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('app', () => ({
|
||||
port: parseInt(process.env.PORT, 10) || 3000,
|
||||
port: parseInt(process.env.PORT ?? "3000", 10) || 3000,
|
||||
env: process.env.NODE_ENV || 'development',
|
||||
apiPrefix: process.env.API_PREFIX || 'v2',
|
||||
jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
|
||||
@ -11,6 +11,6 @@ export default registerAs('app', () => ({
|
||||
},
|
||||
redis: {
|
||||
host: process.env.REDIS_HOST || 'localhost',
|
||||
port: parseInt(process.env.REDIS_PORT, 10) || 6379,
|
||||
port: parseInt(process.env.REDIS_PORT?? "6379", 10) || 6379,
|
||||
},
|
||||
}));
|
||||
|
||||
3
src/modules/auth/auth.controller.ts
Normal file
3
src/modules/auth/auth.controller.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export class AuthController{
|
||||
|
||||
}
|
||||
@ -14,13 +14,23 @@ import { OperatorsModule } from '../operators/operators.module';
|
||||
PassportModule.register({ defaultStrategy: 'jwt' }),
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
secret: configService.get<string>('app.jwtSecret'),
|
||||
secret: configService.get<string>('JWT_SECRET'),
|
||||
signOptions: {
|
||||
expiresIn: configService.get<string>('app.jwtExpiresIn'),
|
||||
expiresIn: configService.get('JWT_EXPIRATION') || '1h',
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
/*todo
|
||||
useFactory: async (configService: ConfigService) => {
|
||||
return {
|
||||
secret: configService.get<string>('JWT_SECRET'),
|
||||
signOptions: {
|
||||
expiresIn: configService.get<string | number>('JWT_EXPIRATION') || '1h'
|
||||
},
|
||||
};
|
||||
},*/
|
||||
|
||||
}),
|
||||
OperatorsModule,
|
||||
],
|
||||
|
||||
@ -8,10 +8,7 @@ export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy, 'api-
|
||||
constructor(private readonly prisma: PrismaService) {
|
||||
super(
|
||||
{ header: 'X-API-Key', prefix: '' },
|
||||
true,
|
||||
async (apiKey, done) => {
|
||||
return this.validate(apiKey, done);
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { Controller, Post, Get, Body, Param, Query, UseGuards, Request } from '@
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { NotificationsService } from './services/notifications.service';
|
||||
import { SendNotificationDto, BulkNotificationDto } from './dto/notification.dto';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard';
|
||||
|
||||
@ApiTags('notifications')
|
||||
@Controller('notifications')
|
||||
|
||||
@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { NotificationsController } from './notifications.controller';
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import { NotificationsService } from './services/notifications.service';
|
||||
import { SmsService } from './services/sms.service';
|
||||
import { EmailService } from './services/email.service';
|
||||
import { WebhookService } from './services/webhook.service';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Process, Processor } from '@nestjs/bull';
|
||||
import { Job } from 'bull';
|
||||
import { NotificationsService } from '../notifications.service';
|
||||
import bull from 'bull';
|
||||
import { NotificationsService } from '../services/notifications.service';
|
||||
import { WebhookService } from '../services/webhook.service';
|
||||
|
||||
@Processor('notifications')
|
||||
@ -10,7 +10,7 @@ export class NotificationProcessor {
|
||||
) {}
|
||||
|
||||
@Process('send-notification')
|
||||
async handleSendNotification(job: Job) {
|
||||
async handleSendNotification(job: bull.Job) {
|
||||
const { notificationId } = job.data;
|
||||
|
||||
try {
|
||||
@ -23,9 +23,9 @@ export class NotificationProcessor {
|
||||
}
|
||||
|
||||
@Process('bulk-send')
|
||||
async handleBulkSend(job: Job) {
|
||||
async handleBulkSend(job: bull.Job) {
|
||||
const { notifications } = job.data;
|
||||
const results = [];
|
||||
const results:any = [];
|
||||
|
||||
for (const notificationId of notifications) {
|
||||
try {
|
||||
@ -47,7 +47,7 @@ export class WebhookProcessor {
|
||||
) {}
|
||||
|
||||
@Process('send-webhook')
|
||||
async handleSendWebhook(job: Job) {
|
||||
async handleSendWebhook(job: bull.Job) {
|
||||
const { webhookId, attempt } = job.data;
|
||||
|
||||
return await this.webhookService.processWebhook(webhookId, attempt);
|
||||
|
||||
98
src/modules/notifications/services/email.service.ts
Normal file
98
src/modules/notifications/services/email.service.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { OperatorsService } from '../../operators/operators.service';
|
||||
import { PrismaService } from '../../../shared/services/prisma.service';
|
||||
//todo rewrite
|
||||
@Injectable()
|
||||
export class EmailService {
|
||||
constructor(
|
||||
private readonly operatorsService: OperatorsService,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async send(params: {
|
||||
to: string;
|
||||
message: string;
|
||||
subject?:string;
|
||||
content?:string;
|
||||
template?:string
|
||||
userToken?: string;
|
||||
userAlias?: string;
|
||||
from?: string;
|
||||
}) {
|
||||
// Si on a un userToken, utiliser l'opérateur de l'utilisateur
|
||||
if (params.userToken) {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { userToken: params.userToken },
|
||||
include: { operator: true },
|
||||
});
|
||||
|
||||
if (user) {
|
||||
const adapter = this.operatorsService.getAdapter(
|
||||
user.operator.code,
|
||||
user.country,
|
||||
);
|
||||
|
||||
return await adapter.sendSms({
|
||||
to: params.to,
|
||||
message: params.message,
|
||||
userToken: params.userToken,
|
||||
userAlias: params.userAlias,
|
||||
from: params.from,
|
||||
country: user.country,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sinon, détecter l'opérateur par le numéro
|
||||
const operator = this.detectOperatorByNumber(params.to);
|
||||
const adapter = this.operatorsService.getAdapter(operator.code, operator.country);
|
||||
|
||||
return await adapter.sendSms({
|
||||
to: params.to,
|
||||
message: params.message,
|
||||
from: params.from,
|
||||
country: operator.country,
|
||||
});
|
||||
}
|
||||
|
||||
async sendOtp(msisdn: string, code: string, template?: string) {
|
||||
const message = template
|
||||
? template.replace('{code}', code)
|
||||
: `Your verification code is: ${code}`;
|
||||
|
||||
return this.send({
|
||||
to: msisdn,
|
||||
message: message,
|
||||
});
|
||||
}
|
||||
|
||||
async sendTransactional(params: {
|
||||
to: string;
|
||||
template: string;
|
||||
variables: Record<string, any>;
|
||||
userToken?: string;
|
||||
}) {
|
||||
// Remplacer les variables dans le template
|
||||
let message = params.template;
|
||||
for (const [key, value] of Object.entries(params.variables)) {
|
||||
message = message.replace(new RegExp(`{${key}}`, 'g'), value);
|
||||
}
|
||||
|
||||
return this.send({
|
||||
to: params.to,
|
||||
message: message,
|
||||
userToken: params.userToken,
|
||||
});
|
||||
}
|
||||
|
||||
private detectOperatorByNumber(msisdn: string) {
|
||||
// Logique de détection basée sur le préfixe
|
||||
// Pour simplifier, on retourne Orange CI par défaut
|
||||
return {
|
||||
code: 'ORANGE',
|
||||
country: 'CI',
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Queue } from 'bull';
|
||||
import { PrismaService } from '../../shared/services/prisma.service';
|
||||
import { SmsService } from './services/sms.service';
|
||||
import { EmailService } from './services/email.service';
|
||||
import { WebhookService } from './services/webhook.service';
|
||||
import { NotificationTemplateService } from './services/template.service';
|
||||
import { SendNotificationDto, BulkNotificationDto } from './dto/notification.dto';
|
||||
import bull from 'bull';
|
||||
import { SmsService } from './sms.service';
|
||||
import { EmailService } from './email.service';
|
||||
import { WebhookService } from './webhook.service';
|
||||
import { NotificationTemplateService } from './template.service';
|
||||
import { SendNotificationDto, BulkNotificationDto } from '../dto/notification.dto';
|
||||
import { PrismaService } from 'src/shared/services/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsService {
|
||||
@ -16,12 +16,12 @@ export class NotificationsService {
|
||||
private readonly emailService: EmailService,
|
||||
private readonly webhookService: WebhookService,
|
||||
private readonly templateService: NotificationTemplateService,
|
||||
@InjectQueue('notifications') private notificationQueue: Queue,
|
||||
@InjectQueue('notifications') private notificationQueue: bull.Queue,
|
||||
) {}
|
||||
|
||||
async send(partnerId: string, dto: SendNotificationDto) {
|
||||
// Valider l'utilisateur si fourni
|
||||
let user = null;
|
||||
let user:any = null;
|
||||
if (dto.userToken) {
|
||||
user = await this.prisma.user.findFirst({
|
||||
where: {
|
||||
@ -70,13 +70,13 @@ export class NotificationsService {
|
||||
}
|
||||
|
||||
async sendBulk(partnerId: string, dto: BulkNotificationDto) {
|
||||
const notifications = [];
|
||||
const notifications :any= [];
|
||||
|
||||
// Récupérer les destinataires selon les critères
|
||||
const recipients = await this.getRecipients(partnerId, dto);
|
||||
const recipients:any = await this.getRecipients(partnerId, dto);
|
||||
|
||||
for (const recipient of recipients) {
|
||||
const notification = await this.prisma.notification.create({
|
||||
const notification:any = await this.prisma.notification.create({
|
||||
data: {
|
||||
partnerId: partnerId,
|
||||
userId: recipient.userId,
|
||||
@ -148,6 +148,7 @@ export class NotificationsService {
|
||||
subject: notification.subject,
|
||||
content: notification.content,
|
||||
template: notification.templateId,
|
||||
message: ''
|
||||
});
|
||||
break;
|
||||
|
||||
@ -230,7 +231,7 @@ export class NotificationsService {
|
||||
}
|
||||
|
||||
private async getRecipients(partnerId: string, dto: BulkNotificationDto) {
|
||||
const recipients = [];
|
||||
const recipients:any = [];
|
||||
|
||||
if (dto.userIds && dto.userIds.length > 0) {
|
||||
const users = await this.prisma.user.findMany({
|
||||
@ -259,7 +260,7 @@ export class NotificationsService {
|
||||
}
|
||||
|
||||
private async getSegmentUsers(partnerId: string, segments: string[]) {
|
||||
const users = [];
|
||||
const users:any = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
switch (segment) {
|
||||
|
||||
6
src/modules/notifications/services/template.service.ts
Normal file
6
src/modules/notifications/services/template.service.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export class NotificationTemplateService{
|
||||
render(templateId: string | undefined, arg1: any) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Queue } from 'bull';
|
||||
import bull from 'bull';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { PrismaService } from '../../../shared/services/prisma.service';
|
||||
import * as crypto from 'crypto';
|
||||
@ -11,7 +11,7 @@ export class WebhookService {
|
||||
constructor(
|
||||
private readonly httpService: HttpService,
|
||||
private readonly prisma: PrismaService,
|
||||
@InjectQueue('webhooks') private webhookQueue: Queue,
|
||||
@InjectQueue('webhooks') private webhookQueue: bull.Queue,
|
||||
) {}
|
||||
|
||||
async send(params: {
|
||||
|
||||
@ -1,3 +1,35 @@
|
||||
|
||||
|
||||
export interface AuthValidateParams{
|
||||
|
||||
}
|
||||
|
||||
export interface AuthValidateResponse{
|
||||
|
||||
}
|
||||
|
||||
export interface RefundParams{
|
||||
|
||||
}
|
||||
|
||||
export interface SmsParams{
|
||||
|
||||
}
|
||||
|
||||
export interface SmsResponse{
|
||||
|
||||
}
|
||||
|
||||
export interface RefundResponse{
|
||||
|
||||
}
|
||||
|
||||
export interface SubscriptionParams{
|
||||
|
||||
}
|
||||
export interface SubscriptionResponse{
|
||||
|
||||
}
|
||||
export interface IOperatorAdapter {
|
||||
initializeAuth(params: AuthInitParams): Promise<AuthInitResponse>;
|
||||
validateAuth(params: AuthValidateParams): Promise<AuthValidateResponse>;
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
} from './operator.adapter.interface';
|
||||
import { OrangeTransformer } from '../transformers/orange.transformer';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class OrangeAdapter implements IOperatorAdapter {
|
||||
private baseUrl: string;
|
||||
@ -21,8 +22,8 @@ export class OrangeAdapter implements IOperatorAdapter {
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
this.baseUrl = this.configService.get('ORANGE_API_URL');
|
||||
this.accessToken = this.configService.get('ORANGE_ACCESS_TOKEN');
|
||||
this.baseUrl = this.configService.get('ORANGE_API_URL') as string;
|
||||
this.accessToken = this.configService.get('ORANGE_ACCESS_TOKEN') as string;
|
||||
this.transformer = new OrangeTransformer();
|
||||
}
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class OrangeTransformer {
|
||||
transformChargeResponse(bizaoResponse: any): any {
|
||||
return {
|
||||
paymentId: bizaoResponse.amountTransaction?.serverReferenceCode,
|
||||
status: this.mapStatus(
|
||||
bizaoResponse.amountTransaction?.transactionOperationStatus,
|
||||
),
|
||||
operatorReference: bizaoResponse.amountTransaction?.serverReferenceCode,
|
||||
amount: parseFloat(
|
||||
bizaoResponse.amountTransaction?.paymentAmount?.totalAmountCharged,
|
||||
),
|
||||
currency:
|
||||
bizaoResponse.amountTransaction?.paymentAmount?.chargingInformation
|
||||
?.currency,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
private mapStatus(bizaoStatus: string): string {
|
||||
const statusMap = {
|
||||
Charged: 'SUCCESS',
|
||||
Failed: 'FAILED',
|
||||
Pending: 'PENDING',
|
||||
};
|
||||
return statusMap[bizaoStatus] || 'PENDING';
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,30 @@
|
||||
export class OrangeTransformer{
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class OrangeTransformer {
|
||||
transformChargeResponse(bizaoResponse: any): any {
|
||||
return {
|
||||
paymentId: bizaoResponse.amountTransaction?.serverReferenceCode,
|
||||
status: this.mapStatus(
|
||||
bizaoResponse.amountTransaction?.transactionOperationStatus,
|
||||
),
|
||||
operatorReference: bizaoResponse.amountTransaction?.serverReferenceCode,
|
||||
amount: parseFloat(
|
||||
bizaoResponse.amountTransaction?.paymentAmount?.totalAmountCharged,
|
||||
),
|
||||
currency:
|
||||
bizaoResponse.amountTransaction?.paymentAmount?.chargingInformation
|
||||
?.currency,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
private mapStatus(bizaoStatus: string): string {
|
||||
const statusMap = {
|
||||
Charged: 'SUCCESS',
|
||||
Failed: 'FAILED',
|
||||
Pending: 'PENDING',
|
||||
};
|
||||
return statusMap[bizaoStatus] || 'PENDING';
|
||||
}
|
||||
}
|
||||
4
src/modules/partners/partners.controller.ts
Normal file
4
src/modules/partners/partners.controller.ts
Normal file
@ -0,0 +1,4 @@
|
||||
//todoe
|
||||
export class PartnersController{
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { Process, Processor } from '@nestjs/bull';
|
||||
import { Job } from 'bull';
|
||||
import bull from 'bull';
|
||||
import { SubscriptionsService } from '../subscriptions.service';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
@ -12,7 +12,7 @@ export class SubscriptionProcessor {
|
||||
) {}
|
||||
|
||||
@Process('webhook-notification')
|
||||
async handleWebhookNotification(job: Job) {
|
||||
async handleWebhookNotification(job: bull.Job) {
|
||||
const { url, event, subscription, payment } = job.data;
|
||||
|
||||
try {
|
||||
@ -50,13 +50,13 @@ export class BillingProcessor {
|
||||
) {}
|
||||
|
||||
@Process('process-renewal')
|
||||
async handleRenewal(job: Job) {
|
||||
async handleRenewal(job: bull.Job) {
|
||||
const { subscriptionId } = job.data;
|
||||
await this.subscriptionsService.processRenewal(subscriptionId);
|
||||
}
|
||||
|
||||
@Process('trial-end')
|
||||
async handleTrialEnd(job: Job) {
|
||||
async handleTrialEnd(job: bull.Job) {
|
||||
const { subscriptionId } = job.data;
|
||||
|
||||
// Convertir de TRIAL à ACTIVE et traiter le premier paiement
|
||||
@ -64,7 +64,7 @@ export class BillingProcessor {
|
||||
}
|
||||
|
||||
@Process('retry-renewal')
|
||||
async handleRetryRenewal(job: Job) {
|
||||
async handleRetryRenewal(job: bull.Job) {
|
||||
const { subscriptionId, attempt } = job.data;
|
||||
|
||||
console.log(`Retrying renewal for subscription ${subscriptionId}, attempt ${attempt}`);
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Queue } from 'bull';
|
||||
import bull from 'bull';
|
||||
import { PrismaService } from '../../../shared/services/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class SubscriptionScheduler {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
@InjectQueue('billing') private billingQueue: Queue,
|
||||
@InjectQueue('billing') private billingQueue: bull.Queue,
|
||||
) {}
|
||||
|
||||
@Cron(CronExpression.EVERY_HOUR)
|
||||
|
||||
@ -45,6 +45,7 @@ export class BillingService {
|
||||
end: subscription.currentPeriodEnd,
|
||||
},
|
||||
},
|
||||
partnerId: ''
|
||||
});
|
||||
|
||||
// Mettre à jour la facture
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../shared/services/prisma.service';
|
||||
import { CreatePlanDto, UpdatePlanDto } from '../dto/plan.dto';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { Prisma } from 'generated/prisma';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class PlanService {
|
||||
|
||||
@ -9,6 +9,15 @@ import { CreateSubscriptionDto, UpdateSubscriptionDto } from './dto/subscription
|
||||
|
||||
@Injectable()
|
||||
export class SubscriptionsService {
|
||||
get(id: string, partnerId: any) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
list(arg0: { partnerId: any; status: string | undefined; userId: string | undefined; page: number; limit: number; }) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getInvoices(id: string, partnerId: any) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly paymentsService: PaymentsService,
|
||||
@ -141,7 +150,7 @@ export class SubscriptionsService {
|
||||
if (dto.status === 'PAUSED' && subscription.status === 'ACTIVE') {
|
||||
updateData.status = 'PAUSED';
|
||||
updateData.pausedAt = new Date();
|
||||
} else if (dto.status === 'ACTIVE' && subscription.status === 'PAUSED') {
|
||||
} else if (dto.status === 'ACTIVE' && subscription.status === 'SUSPENDED') {
|
||||
updateData.status = 'ACTIVE';
|
||||
updateData.pausedAt = null;
|
||||
// Recalculer la prochaine date de facturation
|
||||
@ -166,7 +175,7 @@ export class SubscriptionsService {
|
||||
}
|
||||
|
||||
if (dto.metadata) {
|
||||
updateData.metadata = { ...subscription.metadata, ...dto.metadata };
|
||||
updateData.metadata = { metadata:subscription.metadata, ...dto.metadata };
|
||||
}
|
||||
|
||||
const updatedSubscription = await this.prisma.subscription.update({
|
||||
@ -202,9 +211,7 @@ export class SubscriptionsService {
|
||||
data: {
|
||||
status: 'CANCELLED',
|
||||
cancelledAt: new Date(),
|
||||
cancellationReason: reason,
|
||||
metadata: {
|
||||
...subscription.metadata,
|
||||
cancellationDetails: {
|
||||
reason,
|
||||
cancelledBy: 'partner',
|
||||
@ -227,7 +234,7 @@ export class SubscriptionsService {
|
||||
where: { id: partnerId },
|
||||
});
|
||||
|
||||
if (partner?.callbacks?.subscription?.onCancel) {
|
||||
if (partner?.callbacks?subscription?.onCancel) {
|
||||
await this.subscriptionQueue.add('webhook-notification', {
|
||||
url: partner.callbacks.subscription.onCancel,
|
||||
event: 'SUBSCRIPTION_CANCELLED',
|
||||
@ -259,10 +266,11 @@ export class SubscriptionsService {
|
||||
|
||||
try {
|
||||
// Créer le paiement de renouvellement
|
||||
//todo
|
||||
const payment = await this.paymentsService.createCharge({
|
||||
userToken: subscription.user.userToken,
|
||||
amount: subscription.amount,
|
||||
currency: subscription.currency,
|
||||
amount: subscription.plan.amount,
|
||||
currency: subscription.plan.currency,
|
||||
description: `Renewal: ${subscription.plan.name}`,
|
||||
reference: `REN-${subscription.id}-${Date.now()}`,
|
||||
metadata: {
|
||||
@ -273,6 +281,7 @@ export class SubscriptionsService {
|
||||
end: this.calculatePeriodEnd(subscription.plan, subscription.currentPeriodEnd),
|
||||
},
|
||||
},
|
||||
partnerId: ''
|
||||
});
|
||||
|
||||
if (payment.status === 'SUCCESS') {
|
||||
@ -315,7 +324,7 @@ export class SubscriptionsService {
|
||||
await this.handleRenewalFailure(subscription);
|
||||
}
|
||||
}
|
||||
|
||||
//todo
|
||||
private async processInitialPayment(subscription: any, callbackUrl?: string) {
|
||||
try {
|
||||
const payment = await this.paymentsService.createCharge({
|
||||
@ -329,6 +338,7 @@ export class SubscriptionsService {
|
||||
subscriptionId: subscription.id,
|
||||
type: 'initial',
|
||||
},
|
||||
partnerId:""
|
||||
});
|
||||
|
||||
if (payment.status === 'SUCCESS') {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaClient } from 'generated/prisma';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user