Aller au contenu principal

🛠️ Spécifications techniques internes

Cette documentation s'adresse aux développeurs travaillant sur la plateforme UBSI.

🏗️ Architecture détaillée

Structure des modules

📁 backend/src/
├── 💳 paiement/ # Module principal paiements
├── 🧾 transactions/ # Gestion transactions
├── 🍎 stripe/ # Intégration Stripe
├── 🔑 token/ # Tokens de session
├── 🐰 rabbitmq/ # Consumer RabbitMQ
└── 🗄️ db/ # Configuration base

📁 frontend/src/
├── 📄 pages/ # PaymentPage, TransactionPage
├── 🧱 components/ # PaymentForm, Modals
├── 🔗 hooks/ # usePaymentRecovery
└── 🎨 assets/ # Images, styles

🔄 Flux de paiement complet

📊 Modèle de données

Schéma principal (Drizzle)

// backend/src/db/schema.ts

export const transactions = pgTable('transactions', {
id: serial('id').primaryKey(),
clientId: varchar('client_id', { length: 255 }).notNull(),
montant: decimal('montant', { precision: 10, scale: 2 }).notNull(),
devise: varchar('devise', { length: 3 }).default('EUR'),
typePaiement: typesPaiementEnum('type_paiement').notNull(),
status: statusPaiementEnum('status').default('en_attente'),
methodePaiement: varchar('methode_paiement', { length: 100 }),
orderId: varchar('order_id', { length: 255 }),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});

export const clients = pgTable('clients', {
id: serial('id').primaryKey(),
nom: varchar('nom', { length: 100 }).notNull(),
prenom: varchar('prenom', { length: 100 }),
email: varchar('email', { length: 255 }).notNull(),
telephoneMobile: bigint('telephone_mobile', { mode: 'number' }),
clientFideliseId: varchar('client_fidelise_id', { length: 50 }),
// ... autres champs
});

export const paymentMethods = pgTable('payment_methods', {
id: serial('id').primaryKey(),
clientId: integer('client_id').references(() => clients.id),
type: varchar('type', { length: 50 }).notNull(),
cardNumber: varchar('card_number', { length: 20 }), // Tokenisé
last4: varchar('last4', { length: 4 }),
expirationDate: varchar('expiration_date', { length: 7 }),
// CVV jamais stocké !
});

Enums TypeScript

// backend/src/paiement/enums/status-paiement.enum.ts

export enum StatusPaiement {
EN_ATTENTE = 'en_attente',
ACCEPTE = 'accepte',
REFUSE = 'refuse',
ANNULE = 'annule',
}

export enum TypePaiement {
ACHAT = 'achat',
PRELEVEMENT = 'prelevement',
REMBOURSEMENT = 'remboursement',
}

🔧 Services métier

PaiementService

Responsabilités :

  • Création de paiements complets
  • Validation des règles métier
  • Orchestration des sous-services

Méthodes principales :

// backend/src/paiement/paiement.service..ts

class PaiementService {
// Création paiement principal
async create(
body: PaiementDto
): Promise<{ response: ReponsePaiementDto; returnUrl: string }>;

// Interface publique
async goToPaiement(body: InterfacePaiementDto): Promise<GoToPaiementDto>;

// Remboursements
async refund(body: RefundDto): Promise<any>;

// Moyens de paiement
async getSecurePaymentMethods(
clientId: string
): Promise<PaymentMethodResponseDto[]>;
async deleteSecurePaymentMethod(id: string): Promise<void>;

// Paiements en espèces
async createCashPaymentComplete(body: CreateCashPaymentDto): Promise<any>;
}

StripeService

Responsabilités :

  • Gestion des Payment Intents
  • Simulation de paiements (dev/test)
  • Webhooks Stripe
// backend/src/stripe/stripe.service.ts

class StripeService {
// Digital wallets
async createPaymentIntent(
params: CreatePaymentIntentDto
): Promise<PaymentIntentResponseDto>;

// Simulation paiements
async simulateCardPaymentComplete(
body: SimulateCardPaymentRequestDto
): Promise<SimulateCardPaymentResponseDto>;

// Webhooks
constructWebhookEvent(payload: Buffer, signature: string): Stripe.Event;

// Remboursements
async createRefund(
paymentIntentId: string,
amount?: number
): Promise<Stripe.Refund>;
}

TransactionsService

Responsabilités :

  • CRUD transactions
  • Recherche et filtrage
  • Mise à jour statuts

ClientService

Responsabilités :

  • Gestion des clients
  • Déduplication automatique
  • Validation des données

🎣 Hooks React personnalisés

usePaymentRecovery

Fichier : frontend/src/hooks/usePaymentRecovery.ts

Fonctionnalités :

  • Sauvegarde automatique des sessions (localStorage)
  • Détection des interruptions (beforeunload, visibilitychange)
  • Récupération au retour sur la page
interface PaymentSession {
transactionId: string;
orderId: string;
amount: number;
timestamp: number;
status: 'pending' | 'processing' | 'completed' | 'failed';
}

export function usePaymentRecovery() {
const savePaymentSession = (session: PaymentSession) => {
// Sauvegarde localStorage avec logs debug
};

const getInterruptedPayment = (): PaymentSession | null => {
// Récupération + validation âge (30min max)
};

const clearPaymentSession = () => {
// Nettoyage fin de session
};
}

Intégration dans PaymentForm

// frontend/src/components/PaymentForm.tsx

const { savePaymentSession, clearPaymentSession } = usePaymentRecovery();

// ✅ AVANT la redirection
const data = await res.json();
console.log('🟢 Paiement enregistré :', data);

// 💾 Sauvegarde critique
savePaymentSession({
transactionId: data.transactionId.toString(),
orderId: paymentPayload.orderId,
amount: paymentPayload.amount,
timestamp: Date.now(),
status: 'processing',
});

// 🔄 Redirection APRÈS sauvegarde
window.location.href = data.returnUrl;

🔄 Consumer RabbitMQ

PaiementConsumer

Fichier : backend/src/paiement/paiement.consumer.ts

Queue écoutée : paiement.expedition-commande

@RabbitSubscribe({
queue: 'paiement.expedition-commande',
queueOptions: { durable: true }
})
async handleExpeditionMessage(message: any, context: RmqContext) {
this.logger.log(`📦 Message reçu pour expédition: ${JSON.stringify(message)}`);

try {
// Traitement métier de l'expédition
// Mise à jour statuts, notifications, etc.

const channel = context.getChannelRef();
channel.ack(context.getMessage());
} catch (error) {
this.logger.error('Erreur traitement expédition:', error);
// Stratégie de retry ou DLQ
}
}

Publication d'événements

// Dans PaiementService après succès
await this.rabbitmqService.publish('paiement.expedition-commande', {
transactionId,
orderId: body.orderId,
status: 'accepte',
clientId: responseClientId,
timestamp: new Date().toISOString(),
});

🧪 Tests et debugging

Logs structurés

// Exemple de logs dans les services
this.logger.log('🚀 Début création paiement');
this.logger.debug(
`📝 Détails: montant=${body.montant}€, méthode=${body.moyenPaiement.type}`
);
this.logger.error('❌ Erreur lors du traitement:', error);

Variables d'environnement clés

# Backend (.env)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
DATABASE_URL=postgresql://...
RABBITMQ_URL=amqp://localhost:5672

# Frontend (.env)
VITE_API_HOST=http://localhost:3000
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_...

Panel de test intégré

Fichier : frontend/src/components/PaymentTestPanel.tsx

Fonctionnalités de debug :

  • Simulation fermeture de page
  • Inspection du localStorage
  • Simulation réseau lent
  • Reset des sessions

🔐 Sécurité et bonnes pratiques

Validation des DTOs

// Exemple de DTO avec validation
export class PaiementDto {
@IsNotEmpty()
@ValidateNested()
client: ClientPaiementDto;

@IsNumber()
@Min(0.01)
@Max(999999.99)
montant: number;

@IsEnum(StatusPaiement)
status: StatusPaiement;
}

Sanitization des cartes

// Dans PaiementService.getSecurePaymentMethods()
return results.map((pm) => ({
id: pm.id,
type: pm.type,
details: {
last4: pm.last4, // ✅ Seulement les 4 derniers
expirationDate: pm.expirationDate,
cardHolderName: pm.cardHolderName,
// ❌ CVV JAMAIS retourné
// ❌ Numéro complet JAMAIS retourné
},
}));

Protection contre les doublons

// Validation unicité transaction
const existingTransaction = await this.transactionsService.findByOrderId(
body.orderId
);
if (existingTransaction) {
throw new BadRequestException('Transaction déjà existante pour cet orderId');
}

📋 TODO et améliorations futures

Fonctionnalités prévues

  • Webhooks sortants pour e-commerce (notification temps réel)
  • Multi-tenant : support de plusieurs marques/enseignes
  • Paiements récurrents : abonnements et prélèvements
  • Gestion des devises : conversion automatique
  • Analytics : dashboard avec métriques temps réel
  • API GraphQL : alternative à REST pour queries complexes

Optimisations techniques

  • Cache Redis pour les sessions et tokens
  • Queue prioritaires RabbitMQ selon types de paiement
  • Monitoring : Prometheus + Grafana
  • Tests E2E : Playwright pour parcours complets
  • CD/CI : Pipeline automatisé staging → production
  • Rate limiting : protection contre abus API

Sécurité avancée

  • Authentification : JWT + refresh tokens pour API privée
  • Audit trail : logs immuables de toutes les opérations
  • Chiffrement : données sensibles au niveau base
  • PCI-DSS : certification complète pour cartes
  • Fraud detection : ML pour détection anomalies
  • 2FA : authentification forte pour administration

Cette documentation est maintenue par l'équipe de développement UBSI. Pour toute question technique, voir le README du projet ou contacter l'équipe via les issues GitHub.