Aller au contenu

PCI SAQ A

Logmis est dans le périmètre PCI DSS SAQ A (Self-Assessment Questionnaire A) : le système ne traite, ne stocke ni ne transmet de données de porteurs de cartes. Toutes les opérations de carte sont déléguées au PSP.

Le domaine ne connaît que le port PaymentGatewayPort :

apps/api/src/modules/payment/domain/ports/
interface PaymentGatewayPort {
authorize(req: AuthorizeRequest): Promise<Result<AuthorizeResponse, PaymentError>>;
capture(req: CaptureRequest): Promise<Result<CaptureResponse, PaymentError>>;
void(req: VoidRequest): Promise<Result<VoidResponse, PaymentError>>;
refund(req: RefundRequest): Promise<Result<RefundResponse, PaymentError>>;
}

Les implémentations (adapters) sont interchangeables :

  • InMemoryPaymentGateway — seul adapter V1, staging opt-in uniquement
  • Stripe adapter — résiduel A (T9 réel à livrer)
  • Adyen/Mollie — futurs adapters possibles

La garde assert-test-only-gateway s’assure que InMemoryPaymentGateway ne peut pas être utilisée en production :

// Si NODE_ENV === 'production' et gateway === InMemory → throw au démarrage
assertTestOnlyGateway(gatewayAdapter);

En production, si aucun adapter réel n’est configuré, l’API refuse de démarrer.

La fitness function lint:payment-pci scanne le code source pour toute occurrence de :

  • pan (Primary Account Number)
  • cvv, cvc, cvv2
  • track (données de piste)
  • card_number

Une occurrence dans le code source = build CI bloqué.

Les références de paiement PSP sont des tokens opaques fournis par le PSP (ex: Stripe PaymentIntent ID pi_...). Logmis ne génère ni ne stocke de numéros de carte.

La fonction makeSettlementRef construit la référence côté Logmis :

  • Détecte et rejette tout pattern ressemblant à un PAN (séquences de 12 à 19 chiffres contigus vérifiant Luhn)
  • La détection porte sur les séquences contiguës uniquement — ne rejette pas les UUIDs qui contiendraient par coïncidence une séquence numérique (faux positif corrigé en Programme G)

Pour les tests manuels en staging :

  • Toutes les opérations authorize/capture/void/refund réussissent immédiatement
  • Aucune intégration réseau
  • Activé via variable d’environnement PAYMENT_GATEWAY=memory (uniquement si NODE_ENV !== 'production')

Logmis n’expose pas de webhook endpoint reçu depuis un PSP (SAQ A ne le permet pas). Les événements PSP (refund async, dispute) devront être réconciliés manuellement en V1, ou via un webhook côté PSP pointant vers un endpoint dédié à livrer en T9 réel.

  • TLS 1.2+ obligatoire en production (TLS 1.3 recommandé)
  • HSTS via @fastify/helmet (Strict-Transport-Security)
  • CORS fail-closed (liste blanche d’origines, rejet par défaut)
  • CSP configurée via Helmet