firts commit

This commit is contained in:
Mamadou Khoussa [028918 DSI/DAC/DIF/DS] 2025-10-22 03:03:37 +00:00
parent b409b81b18
commit df7ae8dbeb
12 changed files with 91 additions and 38 deletions

View File

@ -1,3 +1,10 @@
import { Controller, Get } from "@nestjs/common";
//todo
@Controller()
export class AuthController{ export class AuthController{
@Get()
getHello(): string {
return 'Hello World!';
}
} }

View File

@ -84,10 +84,11 @@ export class NotificationsService {
channel: dto.channel, channel: dto.channel,
recipient: recipient.contact, recipient: recipient.contact,
subject: dto.subject, subject: dto.subject,
content: await this.templateService.render(dto.templateId, { content:'{}',
...recipient.data, //content: await this.templateService.render(dto.templateId, {
...dto.variables, // ...recipient.data,
}), // ...dto.variables,
//}),
templateId: dto.templateId, templateId: dto.templateId,
status: 'PENDING', status: 'PENDING',
batchId: dto.batchId, batchId: dto.batchId,
@ -138,16 +139,16 @@ export class NotificationsService {
message: notification.content, message: notification.content,
userToken: notification.user?.userToken, userToken: notification.user?.userToken,
userAlias: notification.user?.userAlias, userAlias: notification.user?.userAlias,
from: notification.metadata?.from, //from: notification.metadata?.from,
}); });
break; break;
case 'EMAIL': case 'EMAIL':
result = await this.emailService.send({ result = await this.emailService.send({
to: notification.recipient, to: notification.recipient,
subject: notification.subject, // subject: notification.subject,
content: notification.content, content: notification.content,
template: notification.templateId, //template: notification.templateId,
message: '' message: ''
}); });
break; break;
@ -244,8 +245,8 @@ export class NotificationsService {
for (const user of users) { for (const user of users) {
recipients.push({ recipients.push({
userId: user.id, userId: user.id,
contact: dto.channel === 'SMS' ? user.msisdn : user.email, contact: dto.channel === 'SMS' ? user.msisdn : user.msisdn,
data: { name: user.name, msisdn: user.msisdn }, data: { name: user.msisdn, msisdn: user.msisdn },
}); });
} }
} }
@ -278,7 +279,7 @@ export class NotificationsService {
users.push(...activeUsers.map(u => ({ users.push(...activeUsers.map(u => ({
userId: u.id, userId: u.id,
contact: u.msisdn, contact: u.msisdn,
data: { name: u.name, msisdn: u.msisdn }, data: { name: u.msisdn, msisdn: u.msisdn },
}))); })));
break; break;

View File

@ -63,7 +63,7 @@ export class WebhookService {
try { try {
const signature = this.generateSignature( const signature = this.generateSignature(
webhook.payload, webhook.payload,
webhook.partner?.secretKey, webhook.partner?.secretKey as string,
); );
const response = await firstValueFrom( const response = await firstValueFrom(
@ -84,8 +84,8 @@ export class WebhookService {
data: { data: {
status: 'SUCCESS', status: 'SUCCESS',
response: response.data, response: response.data,
responseCode: response.status, //responseCode: response.status,
deliveredAt: new Date(), //deliveredAt: new Date(),
attempts: attempt, attempts: attempt,
}, },
}); });
@ -96,7 +96,7 @@ export class WebhookService {
where: { id: webhookId }, where: { id: webhookId },
data: { data: {
status: attempt >= 3 ? 'FAILED' : 'RETRYING', status: attempt >= 3 ? 'FAILED' : 'RETRYING',
lastError: error.message, //lastError: error.message,
attempts: attempt, attempts: attempt,
lastAttempt: new Date(), lastAttempt: new Date(),
}, },

View File

@ -1,5 +1,11 @@
//todo //todo
import { Controller, Get } from "@nestjs/common";
@Controller()
export class OperatorsController{ export class OperatorsController{
@Get()
getHello(): string {
return 'Hello World!';
}
} }

View File

@ -4,6 +4,7 @@ import {
IsOptional, IsOptional,
IsObject, IsObject,
MinLength, MinLength,
IsUrl,
} from 'class-validator'; } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
@ -73,4 +74,16 @@ export class UpdateCallbacksDto {
onSuccess?: string; onSuccess?: string;
onFailure?: string; onFailure?: string;
}; };
@IsOptional()
@IsUrl()
success?: string;
@IsOptional()
@IsUrl()
cancel?: string;
@IsOptional()
@IsUrl()
webhook?: string;
} }

View File

@ -1,4 +1,9 @@
//todoe //todo
import { Controller, Get } from "@nestjs/common";
@Controller()
export class PartnersController{ export class PartnersController{
@Get()
getHello(): string {
return 'Hello World!';
}
} }

View File

@ -7,6 +7,7 @@ import { PrismaService } from '../../shared/services/prisma.service';
import * as bcrypt from 'bcrypt'; import * as bcrypt from 'bcrypt';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import { CreatePartnerDto, UpdateCallbacksDto } from './dto/partner.dto'; import { CreatePartnerDto, UpdateCallbacksDto } from './dto/partner.dto';
import { Prisma } from 'generated/prisma';
@Injectable() @Injectable()
export class PartnersService { export class PartnersService {
@ -65,8 +66,10 @@ export class PartnersService {
const updatedPartner = await this.prisma.partner.update({ const updatedPartner = await this.prisma.partner.update({
where: { id: partnerId }, where: { id: partnerId },
data: { data: {
callbacks: dto, //callbacks: dto as unknown as Prisma.JsonValue,
}, // ou
callbacks: JSON.parse(JSON.stringify(dto)),
},
}); });
return { return {

View File

@ -45,9 +45,11 @@ export class PaymentsService {
// Créer la transaction dans la base // Créer la transaction dans la base
const payment = await this.prisma.payment.create({ const payment = await this.prisma.payment.create({
data: { data: {
partnerId:"",
userId: user.id, userId: user.id,
amount: chargeDto.amount, amount: chargeDto.amount,
currency: chargeDto.currency, currency: chargeDto.currency,
description: chargeDto.description, description: chargeDto.description,
reference: chargeDto.reference || this.generateReference(), reference: chargeDto.reference || this.generateReference(),

View File

@ -73,7 +73,7 @@ export class SubscriptionScheduler {
}, },
data: { data: {
status: 'EXPIRED', status: 'EXPIRED',
expiredAt: new Date(), //expiredAt: new Date(),
}, },
}); });

View File

@ -2,7 +2,6 @@ import { Injectable, BadRequestException, NotFoundException } from '@nestjs/comm
import { PrismaService } from '../../../shared/services/prisma.service'; import { PrismaService } from '../../../shared/services/prisma.service';
import { CreatePlanDto, UpdatePlanDto } from '../dto/plan.dto'; import { CreatePlanDto, UpdatePlanDto } from '../dto/plan.dto';
import { Prisma } from 'generated/prisma'; import { Prisma } from 'generated/prisma';
@Injectable() @Injectable()
export class PlanService { export class PlanService {
@ -248,9 +247,9 @@ export class PlanService {
interval: plan.interval, interval: plan.interval,
intervalCount: plan.intervalCount, intervalCount: plan.intervalCount,
trialDays: plan.trialDays, trialDays: plan.trialDays,
features: plan.features, //features: plan.features,
limits: plan.limits, //limits: plan.limits,
metadata: { ...plan.metadata, duplicatedFrom: plan.id }, metadata: {metadata:plan.metadata, duplicatedFrom: plan.id },
active: false, // Désactivé par défaut active: false, // Désactivé par défaut
}, },
}); });
@ -321,14 +320,14 @@ export class PlanService {
select: { select: {
createdAt: true, createdAt: true,
cancelledAt: true, cancelledAt: true,
expiredAt: true, suspendedAt: true,
}, },
}); });
if (subscriptions.length === 0) return 0; if (subscriptions.length === 0) return 0;
const lifetimes = subscriptions.map(sub => { const lifetimes = subscriptions.map(sub => {
const endDate = sub.cancelledAt || sub.expiredAt || new Date(); const endDate = sub.cancelledAt || sub.suspendedAt || new Date();
return endDate.getTime() - sub.createdAt.getTime(); return endDate.getTime() - sub.createdAt.getTime();
}); });

View File

@ -9,9 +9,11 @@ import { BillingService } from './services/billing.service';
import { PrismaService } from '../../shared/services/prisma.service'; import { PrismaService } from '../../shared/services/prisma.service';
import { PaymentsModule } from '../payments/payments.module'; import { PaymentsModule } from '../payments/payments.module';
import { NotificationsModule } from '../notifications/notifications.module'; import { NotificationsModule } from '../notifications/notifications.module';
import { HttpModule } from '@nestjs/axios';
@Module({ @Module({
imports: [ imports: [
HttpModule,
BullModule.registerQueue({ BullModule.registerQueue({
name: 'subscriptions', name: 'subscriptions',
}), }),

View File

@ -85,9 +85,7 @@ export class SubscriptionsService {
currentPeriodStart, currentPeriodStart,
currentPeriodEnd, currentPeriodEnd,
nextBillingDate, nextBillingDate,
trialEndsAt, trialEndsAt,
amount: plan.amount,
currency: plan.currency,
metadata: { metadata: {
...dto.metadata, ...dto.metadata,
userAlias: user.userAlias, userAlias: user.userAlias,
@ -234,6 +232,18 @@ export class SubscriptionsService {
where: { id: partnerId }, where: { id: partnerId },
}); });
interface PartnerCallbacks {
subscription?: {
onCancel?: string;
onRenew?: string;
onExpire?: string;
};
payment?: {
onSuccess?: string;
onFailure?: string;
};
}
/*
if (partner?.callbacks?subscription?.onCancel) { if (partner?.callbacks?subscription?.onCancel) {
await this.subscriptionQueue.add('webhook-notification', { await this.subscriptionQueue.add('webhook-notification', {
url: partner.callbacks.subscription.onCancel, url: partner.callbacks.subscription.onCancel,
@ -241,6 +251,7 @@ export class SubscriptionsService {
subscription: updatedSubscription, subscription: updatedSubscription,
}); });
} }
*/
return updatedSubscription; return updatedSubscription;
} }
@ -293,29 +304,32 @@ export class SubscriptionsService {
currentPeriodEnd: this.calculatePeriodEnd(subscription.plan, subscription.currentPeriodEnd), currentPeriodEnd: this.calculatePeriodEnd(subscription.plan, subscription.currentPeriodEnd),
nextBillingDate: this.calculatePeriodEnd(subscription.plan, subscription.currentPeriodEnd), nextBillingDate: this.calculatePeriodEnd(subscription.plan, subscription.currentPeriodEnd),
lastPaymentId: payment.id, lastPaymentId: payment.id,
lastPaymentDate: new Date(), // lastPaymentDate: new Date(),
renewalCount: { increment: 1 }, renewalCount: { increment: 1 },
failureCount: 0, // Reset failure count on success failureCount: 0, // Reset failure count on success
}, },
}); });
// Programmer le prochain renouvellement // Programmer le prochain renouvellement
/* todo
const delay = subscription.nextBillingDate.getTime() - Date.now(); const delay = subscription.nextBillingDate.getTime() - Date.now();
await this.billingQueue.add( await this.billingQueue.add(
'process-renewal', 'process-renewal',
{ subscriptionId }, { subscriptionId },
{ delay }, { delay },
); );
*/
// Notifier le succès // Notifier le succès
if (subscription.partner?.callbacks?.subscription?.onRenew) {
/* if (subscription.partner?.callbacks?.subscription?.onRenew) {
await this.subscriptionQueue.add('webhook-notification', { await this.subscriptionQueue.add('webhook-notification', {
url: subscription.partner.callbacks.subscription.onRenew, url: subscription.partner.callbacks.subscription.onRenew,
event: 'SUBSCRIPTION_RENEWED', event: 'SUBSCRIPTION_RENEWED',
subscription: subscription, subscription: subscription,
payment: payment, payment: payment,
}); });
} }*/
} else { } else {
await this.handleRenewalFailure(subscription); await this.handleRenewalFailure(subscription);
} }
@ -342,13 +356,14 @@ export class SubscriptionsService {
}); });
if (payment.status === 'SUCCESS') { if (payment.status === 'SUCCESS') {
//todo
await this.prisma.subscription.update({ await this.prisma.subscription.update({
where: { id: subscription.id }, where: { id: subscription.id },
data: { data: {
status: 'ACTIVE', status: 'ACTIVE',
activatedAt: new Date(), createdAt: new Date(),
lastPaymentId: payment.id, lastPaymentId: payment.id,
lastPaymentDate: new Date(), //lastPaymentDate: new Date(),
}, },
}); });
@ -364,7 +379,7 @@ export class SubscriptionsService {
where: { id: subscription.id }, where: { id: subscription.id },
data: { data: {
status: 'FAILED', status: 'FAILED',
failureReason: payment.failureReason, //todo failureReason: payment.failureReason,
}, },
}); });
} }
@ -373,7 +388,7 @@ export class SubscriptionsService {
where: { id: subscription.id }, where: { id: subscription.id },
data: { data: {
status: 'FAILED', status: 'FAILED',
failureReason: error.message, //failureReason: error.message,
}, },
}); });
throw error; throw error;
@ -392,7 +407,7 @@ export class SubscriptionsService {
status: 'SUSPENDED', status: 'SUSPENDED',
failureCount, failureCount,
suspendedAt: new Date(), suspendedAt: new Date(),
suspensionReason: `Payment failed ${maxRetries} times`, //suspensionReason: `Payment failed ${maxRetries} times`,
}, },
}); });