🛠️ 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.