Maison >Java >javaDidacticiel >Concevoir un système d'achat de crédits sur Internet

Concevoir un système d'achat de crédits sur Internet

Susan Sarandon
Susan Sarandonoriginal
2025-01-15 18:05:45696parcourir

Lors de l'un des entretiens techniques auxquels j'ai eu lieu, on m'a demandé de concevoir un système de commerce électronique permettant aux utilisateurs d'acheter des crédits Internet auprès de fournisseurs tiers.

En toute confiance, j'ai proposé une solution simple : afficher les forfaits disponibles, permettre aux utilisateurs d'en sélectionner un, traiter les paiements via une passerelle externe et interagir avec le fournisseur pour délivrer les crédits. Cependant, lorsqu'on m'a interrogé sur les scénarios d'échec, comme un fournisseur en rupture de stock après qu'un utilisateur ait effectué le paiement, j'ai réalisé que ma conception manquait de résilience pour gérer efficacement de tels problèmes.

Il y a quelques semaines, j'ai mené des recherches sur les systèmes de vente flash et les modèles de réservation d'inventaire, en me concentrant particulièrement sur les stratégies de réservation d'inventaire. Les ventes flash font souvent face à une forte demande et à des stocks limités, nécessitant des mécanismes sophistiqués pour maintenir la stabilité du système et gérer les attentes des clients. Un concept que j'ai découvert était celui des réservations de stock temporaires, qui aident à éviter les ventes excessives pendant les heures de pointe.

Cette recherche m'a rappelé mon expérience d'entretien. J'ai reconnu que l'application de ces stratégies de réservation d'inventaire aurait pu remédier aux lacunes de ma conception initiale. En intégrant des retenues temporaires sur l'inventaire pendant le processus de paiement, le système pourrait gérer efficacement les scénarios dans lesquels le stock du fournisseur est épuisé.

Dans cette documentation de recherche, mon objectif est de partager les enseignements tirés de mes recherches et de proposer une approche raffinée pour concevoir un système d'achat de crédits sur Internet. En intégrant des stratégies de réservation d'inventaire, nous pouvons créer une plateforme à la fois robuste et conviviale, capable de gérer divers scénarios de défaillance tout en offrant une expérience transparente.

1. Considérations clés en matière de conception

Lors de la conception d'un système d'achat de crédits sur Internet, il y a quelques facteurs clés à prendre en compte pour garantir une expérience utilisateur transparente, sécurisée et agréable. Décomposons-les :

1.1 Gestion des quotas

  • Vérification des quotas en temps réel : le système doit vérifier instantanément si les packages de crédits Internet sont en stock, afin que les utilisateurs ne sélectionnent pas accidentellement des options indisponibles.
  • Réservation de quota temporaire : ajoutez un mécanisme permettant de conserver un package sélectionné pendant une courte période, donnant ainsi aux utilisateurs suffisamment de temps pour finaliser leur achat sans risquer de perdre l'article.
  • Gestion des conflits de quota : développez des stratégies pour gérer les situations dans lesquelles plusieurs utilisateurs tentent d'acheter le même package en même temps, garantissant ainsi une allocation équitable.
  • Gestion du cache pour les informations sur les packages : gardez les données du cache précises et à jour afin que les utilisateurs voient toujours les bons détails et la disponibilité.

1.2 Traitement des paiements

  • Traitement sécurisé des paiements : mettez en œuvre des mesures de sécurité strictes pour protéger les informations de paiement des utilisateurs pendant les transactions.
  • Système de dépôt fiduciaire pour la protection des paiements : utilisez un service de dépôt fiduciaire pour conserver les fonds jusqu'à ce que les crédits soient délivrés, assurant ainsi la sécurité des acheteurs et des fournisseurs.
  • Intégration de la passerelle de paiement : assurez-vous que le système se connecte facilement à des passerelles de paiement fiables pour garantir des transactions sans tracas.
  • Mécanismes de remboursement : créez des processus clairs et conviviaux pour émettre des remboursements en cas d'échec de paiement ou d'annulation.

1.3 Intégration du fournisseur

  • Disponibilité du système : collaborez avec des fournisseurs qui disposent de systèmes fiables pour garantir que les achats sont traités sans interruption.
  • Fiabilité des API : travaillez avec des fournisseurs proposant des API stables et bien documentées pour une intégration transparente.
  • Vérification de l'activation du service : incluez des vérifications pour confirmer que les crédits achetés sont activés correctement et rapidement.
  • Gestion des erreurs et tentatives : implémentez des protocoles pour détecter et résoudre rapidement les erreurs, avec des mécanismes de nouvelle tentative pour tout processus ayant échoué.

1.4 Sécurité des transactions

  • Contrôle des flux d'argent : assurez-vous que les fonds ne sont débloqués qu'une fois les transactions terminées avec succès.
  • Cohérence des transactions : conservez des enregistrements précis et cohérents de toutes les transactions pour éviter les erreurs.
  • Mécanismes de restauration : prévoyez d'annuler les transactions en cas de problème, protégeant ainsi à la fois les utilisateurs et le système.
  • Piste d'audit : conservez des journaux détaillés pour vous aider à surveiller et à résoudre efficacement tout problème.

1.5 Expérience utilisateur

  • Messages d'erreur clairs : fournissez aux utilisateurs des messages d'erreur compréhensibles et informatifs pour les guider à travers tous les problèmes rencontrés.
  • Visibilité de l'état des transactions : permettez aux utilisateurs de suivre facilement l'état de leurs achats en temps réel, améliorant ainsi la transparence.
  • Chargement rapide des packages : optimisez le système pour charger rapidement les packages disponibles, réduisant ainsi les temps d'attente pour les utilisateurs.
  • Mises à jour en temps réel : tenez les utilisateurs informés rapidement de toute modification ou mise à jour de leurs transactions ou des forfaits disponibles.

En prenant en compte ces considérations, nous pouvons concevoir un système d'achat de crédits sur Internet efficace, sécurisé et convivial, conduisant à une satisfaction et une confiance accrues des utilisateurs.

2. Conception et flux du système

En s'appuyant sur les considérations fondamentales décrites ci-dessus, la prochaine étape consiste à traduire ces principes en une conception de système robuste et efficace. En cartographiant soigneusement les interactions entre les différents composants, nous pouvons garantir que le système répond non seulement aux exigences fonctionnelles, mais offre également une expérience utilisateur transparente tout en maintenant la fiabilité et l'évolutivité.

Dans cette section, nous examinerons l'architecture et le flux du système, en montrant comment les fonctionnalités de base, telles que la gestion des quotas, le traitement des paiements et l'activation des services, sont mises en œuvre de manière cohérente. L'objectif est de mettre en évidence comment chaque choix de conception contribue à relever les défis potentiels et à fournir une plateforme fiable d'achat de crédits pour le commerce électronique.

Commençons par un aperçu du flux du système, visualisé via un organigramme, pour illustrer comment les utilisateurs interagissent avec le système du début à la fin.

2.1 Organigramme

Designing an Internet Credit Purchase System

Le flux du système est divisé en six phases pour plus de clarté :

Phase de sélection des forfaits

  • Tout d'abord, l'utilisateur visite la page de sélection du package, où l'application récupère les données du package à partir d'un cache. Ces données incluent les packages disponibles et leurs informations de quota mises en cache, qui sont ensuite affichées à l'utilisateur.
  • L'utilisateur sélectionne un package et clique sur "Acheter".
  • Si le quota pour ce package n'est pas disponible, l'application affiche un message « Non disponible » et ramène l'utilisateur à la page de sélection. Sinon, le système réserve temporairement le quota pour l'utilisateur.

Initiation d'achat

  • Ensuite, le système tente de réserver le quota pour le forfait choisi.
  • Si la réservation échoue, l'utilisateur voit un message d'erreur et est redirigé vers la page de sélection.
  • Si la réservation réussit, l'utilisateur passe à la page de paiement.
Phase de paiement
  • À ce stade, l'utilisateur démarre le processus de paiement et est redirigé vers une passerelle de paiement tierce.
  • L'application attend une réponse (rappel) de la passerelle de paiement pour confirmer l'état du paiement.
Traitement des paiements
  • L'application vérifie le rappel de la passerelle de paiement pour valider le paiement :
    • Pour les rappels non valides, le système enregistre le problème et interrompt les étapes ultérieures.
    • Pour les rappels valides :
      • Si le paiement échoue : le système libère le quota réservé et informe l'utilisateur du problème.
      • Si le paiement réussit : le système vérifie le paiement, conserve les fonds en dépôt et crée une nouvelle commande.
Activation des services
  • Une fois le paiement réussi, le système demande au fournisseur d'activer le service.
    • Si l'activation échoue : les fonds bloqués sont remboursés au client et celui-ci est informé de l'échec.
    • Si l'activation réussit : Le système vérifie l'activation.
      • Si la vérification échoue, le client obtient un remboursement.
      • Si la vérification réussit, les fonds bloqués sont remis au fournisseur et le client reçoit une notification.
Processus en arrière-plan
  • Mises à jour périodiques du cache : le cache des données du package est mis à jour régulièrement.
  • Mises à jour des quotas en temps réel : les modifications des quotas sont communiquées via des connexions WebSocket.

Ce flux garantit une expérience fluide et fiable aux utilisateurs, tout en gérant efficacement les ressources et les erreurs potentielles.

2.2 Diagramme de séquence

Le diagramme de séquence ci-dessous permet d'illustrer l'interaction entre les différents rôles et composants.

Designing an Internet Credit Purchase System

Le flux du système est divisé en six phases pour plus de clarté :

Phase de sélection des forfaits

  • Le client commence par visiter la page de sélection du forfait.
  • Le frontend récupère les données des packages du cache et affiche tous les packages disponibles, ainsi que leurs informations de quota mises en cache, au client.

Initiation d'achat

  • Une fois que le client sélectionne un package et clique sur « Acheter », le frontend envoie une demande d'achat au backend.
  • Le backend vérifie auprès du fournisseur si le quota du package sélectionné est toujours disponible en temps réel.
  • Si le quota est disponible, le backend le réserve temporairement auprès du fournisseur pendant 15 minutes.
  • Le backend envoie ensuite une confirmation de réservation au frontend, et le client est redirigé vers la page de paiement.

Phase de paiement

  • Le client accède à la page de paiement et soumet ses informations de paiement.
  • Le frontend envoie ces informations au backend, qui initialise une session de paiement avec la passerelle de paiement.
  • Une fois la session de paiement prête, le backend partage les détails de la session avec le frontend.
  • Le frontend redirige le client vers la passerelle de paiement pour finaliser le paiement.

Traitement des paiements

  • Sur la passerelle de paiement, le client saisit ses informations de paiement et termine le processus de paiement.
  • La passerelle de paiement informe le backend de l'état du paiement via un rappel :
    • Si le paiement réussit :
      • Le backend vérifie l'état du paiement avec la passerelle de paiement.
      • Le paiement est conservé sous séquestre et le backend confirme le dépôt séquestre.
    • Si le paiement échoue :
      • Le backend libère le quota réservé auprès du fournisseur et met à jour l'état du paiement pour refléter l'échec.
    • Si aucun rappel n'est reçu dans le délai prévu :
      • Le backend interroge périodiquement la passerelle de paiement pour vérifier l'état du paiement. Si le paiement échoue ou expire, le backend libère le quota réservé.

Activation des services

  • Si le paiement réussit, le backend demande au fournisseur d'activer le service.
  • Le fournisseur répond avec l'état d'activation et le backend vérifie l'activation :
    • Si l'activation réussit :
      • Le backend libère le paiement du séquestre au fournisseur.
      • Une notification de réussite est envoyée au client, lui indiquant que le service est prêt.
    • Si l'activation échoue :
      • Le backend rembourse les fonds bloqués au client.
      • Une notification d'échec est envoyée pour informer le client du problème.

Processus en arrière-plan

  • Le fournisseur envoie des mises à jour en temps réel sur les quotas de packages au backend via des connexions WebSocket.
  • Ces mises à jour garantissent que le cache est toujours à jour, afin que les clients voient la disponibilité des packages la plus précise lors de la navigation.

3. Mise en œuvre technique

Maintenant que nous avons décrit le flux et les interactions du système, il est temps de découvrir comment tout cela s'articule dans le code. Cette section détaille la mise en œuvre étape par étape, montrant comment la conception est traduite en parties de travail qui gèrent tout, de la gestion des commandes à l'interaction avec les fournisseurs et les systèmes de paiement.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}

Voici ce que font ces cours :

  • Forfait : C'est ici que nous définissons les forfaits de crédits Internet que les utilisateurs peuvent acheter. Il conserve une trace des détails tels que l'ID du package, le nom, le prix, le coût du fournisseur, la description et si le package est actif ou non.

  • Commande : considérez cela comme un enregistrement des achats des utilisateurs. Il comprend des informations telles que l'ID de commande, l'ID client, l'ID de forfait sélectionné et des détails associés tels que l'ID de réservation, l'ID de paiement, l'ID de séquestre, l'état de la commande, le montant du paiement, le coût du fournisseur et les horodatages.

  • QuotaReservation : Ceci gère les réservations temporaires pour les quotas de packages. Il enregistre l'ID de réservation, le forfait auquel il est lié, son heure d'expiration et son statut actuel (comme actif ou expiré).

  • OrderStatus Enum : Cette énumération répertorie toutes les étapes possibles par lesquelles une commande peut passer, de CRÉÉE et RÉSERVÉE à PAYMENT_PENDING, COMPLETED ou même REFUNDED.

  • ReservationStatus Enum : De même, cette énumération suit l'état d'une réservation de quota, qu'elle soit ACTIVE, EXPIRÉE, UTILISÉE ou ANNULÉE.

Ensemble, ces classes et énumérations constituent l'épine dorsale de la gestion des packages, des commandes et des réservations de quotas dans le système. Il s'agit d'une approche simple mais structurée pour gérer efficacement les fonctionnalités de commerce électronique.

// Request/Response DTOs
@Getter @Setter
public class OrderRequest {
    private String customerId;
    private String packageId;
    private BigDecimal amount;
}

@Getter @Setter
public class PaymentCallback {
    private String orderId;
    private String paymentId;
    private String status;
    private BigDecimal amount;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class QuotaResponse {
    private String packageId;
    private boolean available;
    private Integer remainingQuota;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class ReservationResponse {
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

@Getter @Setter
public class ActivationResponse {
    private String orderId;
    private boolean success;
    private String activationId;
    private String errorCode;
    private String errorMessage;
}

@Getter @Setter
public class VerificationResponse {
    private String orderId;
    private String activationId;
    private boolean success;
    private String status;
    private LocalDateTime activatedAt;
}

@Getter @Setter
public class PaymentRequest {
    private String orderId;
    private BigDecimal amount;
    private String currency;
    private String customerId;
    private String returnUrl;
    private String callbackUrl;
}

@Getter @Setter
public class PaymentSession {
    private String sessionId;
    private String paymentUrl;
    private LocalDateTime expiresAt;
    private String status;
}

@Getter @Setter
public class EscrowResponse {
    private String id;
    private String paymentId;
    private BigDecimal amount;
    private String status;
    private LocalDateTime createdAt;
}

Décomposons-le :

  • OrderRequest : Il s'agit des données nécessaires pour créer une nouvelle commande. Il comprend l’identifiant client, le forfait qu’il souhaite acheter et le montant total qu’il paiera.

  • PaymentCallback : considérez cela comme une notification de la passerelle de paiement. Après une tentative de paiement, il fournit des détails tels que l'ID de commande, l'ID de paiement, le statut (succès ou échec), le montant payé et la date du paiement.

  • QuotaResponse : Celui-ci consiste à vérifier la disponibilité. Il nous indique si un package est disponible, combien de quota il reste et quand les informations ont été mises à jour pour la dernière fois.

  • ReservationResponse : Une fois qu'un forfait est réservé, cela vous donne tous les détails : l'identifiant de réservation, le forfait associé, la date d'expiration de la réservation et son statut actuel (comme actif ou expiré) .

  • ActivationResponse : Cela nous indique comment s'est déroulée l'activation du service. S'il réussit ou échoue, il nous donne un identifiant d'activation et des détails sur l'erreur en cas de problème.

  • VerificationResponse : Après l'activation, nous vérifions si tout s'est bien passé. Cela inclut l'ID de commande, l'ID d'activation, l'état de réussite et l'heure à laquelle elle a été activée.

  • PaymentRequest : Avant de démarrer le processus de paiement, ce DTO collecte les détails nécessaires tels que l'ID de commande, le montant à payer, la devise, l'ID client et les URL de rappel.

  • PaymentSession : c'est ce qui est créé lorsque le processus de paiement démarre. Il comprend l'ID de session, l'URL de paiement (où l'utilisateur va payer), la date d'expiration et l'état de la session.

  • EscrowResponse : si les fonds sont détenus en dépôt, cela nous en dit long, comme l'identifiant du séquestre, l'identifiant du paiement, le montant détenu, le statut et la date de création.

Toutes ces classes définissent les éléments constitutifs de la communication entre les différentes parties du système, qu'il s'agisse de demandes envoyées ou de réponses renvoyées. Ils s'assurent que tout le monde (et tout) est sur la même longueur d'onde.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
Service de cache
1. Objectif :

Ce service prend en charge un cache local qui stocke les données du package. L'objectif est de rendre le système plus rapide et de réduire les appels inutiles à l'API du fournisseur.

2. Principales caractéristiques :
  • updateCache() : Cette méthode actualise le cache local toutes les 5 minutes en récupérant toutes les données du package auprès du fournisseur. Cela garantit que le cache reste à jour.
  • getPackage() : Cette méthode récupère les informations sur le package du cache en utilisant son ID.
  • updatePackageQuota() : lorsque les détails du quota changent, cette méthode met à jour le cache avec les nouvelles informations pour un package spécifique.
Intégration du fournisseur
1. Objectif :

Ce service gère la communication avec l'API du fournisseur. Il gère des tâches telles que la vérification des quotas, la réservation de packages, l'activation de services et la vérification de ces activations.

2. Principales caractéristiques :
  • checkQuota() : Cette méthode vérifie si un package dispose d'un quota disponible suffisant en appelant l'API du fournisseur.
  • reserveQuota() : Il réserve le quota d'un package pour un client en envoyant une demande au fournisseur.
  • activateService() : Lorsqu'il est temps d'activer un service pour une commande, cette méthode gère la demande auprès du fournisseur.
  • verifyActivation() : Après l'activation, cette méthode confirme si tout a réussi.
  • getAllPackages() : Cette méthode récupère tous les packages disponibles auprès du fournisseur, ce qui est utile pour mettre à jour le cache ou afficher les options du package aux utilisateurs.
3. Mécanisme de nouvelle tentative :

Le service utilise RetryTemplate pour réessayer automatiquement les requêtes adressées à l'API du fournisseur en cas de problèmes temporaires. Cela garantit que le système reste fiable et résilient même en cas de problèmes mineurs.

En combinant ces fonctionnalités, ce code garantit que le système gère efficacement les données des packages tout en maintenant une communication fluide et fiable avec l'API du fournisseur.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
Intégration de la passerelle de paiement

Cette classe joue un rôle clé dans la gestion de la manière dont le système interagit avec la passerelle de paiement pour gérer les transactions financières de manière fluide et sécurisée.

1. initializePayment (demande PaymentRequest) :
  • Considérez cela comme le démarrage du processus de paiement. Il envoie une demande à la passerelle de paiement avec tous les détails du paiement.
  • Il renvoie un objet PaymentSession, qui inclut des informations telles que l'URL de paiement et l'état de la session.
2. holdInEscrow (String paymentId) :
  • Cette méthode sécurise le paiement sur un compte séquestre en utilisant l'identifiant de paiement indiqué.
  • Il fournit un objet EscrowResponse qui contient tous les détails sur les fonds bloqués.
3. releaseToProvider (String escrowId) :
  • Une fois le service activé avec succès, cette méthode libère les fonds du séquestre vers le fournisseur de services.
  • L'identifiant séquestre est utilisé pour identifier et débloquer les fonds corrects.
4. returnToCustomer (String escrowId) :
  • Si quelque chose ne va pas, comme l'échec de l'activation du service, cette méthode rembourse les fonds bloqués au client.
  • Il utilise l'ID séquestre pour traiter le remboursement.
Principales caractéristiques :
  • La classe utilise WebClient pour envoyer des requêtes HTTP à l'API REST de la passerelle de paiement, garantissant ainsi une intégration transparente.
  • Il gère les opérations critiques telles que le démarrage des paiements, la gestion du séquestre et le traitement des remboursements.
  • Toutes les méthodes utilisent des appels synchrones (via .block()) pour s'assurer que les opérations sont terminées avant de continuer, garantissant ainsi la fiabilité.

Cette classe est une pièce cruciale du puzzle lorsqu'il s'agit de gérer des transactions financières sécurisées et efficaces dans le système.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
DTO de notification
1. Notification par e-mail :
  • Considérez cela comme un modèle pour l'envoi de notifications par e-mail. Il comprend :
    • L'e-mail (à) du destinataire.
    • L'objet de l'e-mail.
    • Un identifiant de modèle pour déterminer le format.
    • Données dynamiques (templateData) pour personnaliser le contenu.
2. Notification par SMS :
  • Similaire à la notification par e-mail mais adaptée aux SMS. Il comprend :
    • Le numéro de téléphone du destinataire (phoneNumber).
    • Un identifiant de modèle pour le format du message.
    • Données dynamiques (templateData) pour la personnalisation.
Service de notifications

Ce service gère toutes les notifications envoyées aux utilisateurs concernant l'état de leur commande. Voici comment cela fonctionne :

1. sendSuccessNotification (commande de commande) :
  • Cette méthode gère l'envoi de notifications de réussite. Il utilise :
    • buildSuccessEmail pour créer une notification par e-mail.
    • buildSuccessSms pour créer une notification SMS.
    • Il envoie également des mises à jour en temps réel via WebSocket à l'aide de QuotaWebSocketHandler.
2. sendFailureNotification (commande de commande) :
  • Celui-ci s'occupe des notifications d'échec. Il utilise :
    • buildFailureEmail pour les messages électroniques.
    • buildFailureSms pour les messages SMS.
    • Comme les notifications de réussite, il envoie également des mises à jour WebSocket.
3. Méthodes d'assistance :
  • buildSuccessEmail et buildFailureEmail : ces méthodes créent des notifications par e-mail selon que la commande a réussi ou échoué. Ils utilisent des modèles et les détails de la commande.
  • buildSuccessSms et buildFailureSms : ceux-ci font la même chose mais pour les notifications par SMS.
Caractéristiques supplémentaires :
  • Mises à jour WebSocket : maintient le front-end à jour en temps réel à l'aide de QuotaWebSocketHandler.
  • Journalisation des erreurs : si quelque chose ne va pas, il enregistre les erreurs pour le débogage.

Ce service garantit que les utilisateurs sont toujours informés de leurs commandes, que ce soit par e-mail, SMS ou mises à jour en temps réel.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}
Classe QuotaUpdate
  • Considérez cette classe comme un simple messager pour les mises à jour des quotas. Il contient trois informations clés :
    • packageId : l'ID du package en cours de mise à jour.
    • availableQuota : combien de quota reste-t-il pour ce package.
    • horodatage : date à laquelle la mise à jour a été effectuée.
Configuration du WebSocket
1. WebSocketConfig :
  • C'est la configuration qui rend la communication WebSocket possible.
  • Il enregistre un gestionnaire (quotaWebSocketHandler) pour écouter les connexions WebSocket dans /ws/quota.
  • Il autorise également les connexions depuis n'importe quelle origine en définissant AllowOrigins("*").
2. quotaWebSocketHandler() :
  • Ceci définit le bean gestionnaire WebSocket qui gérera les messages entrants et les connexions.
QuotaWebSocketHandler

C'est ici que toute la magie du WebSocket opère ! Il gère les mises à jour en temps réel entre le serveur et les clients.

1. Champs :
  • PackageCacheService : aide à mettre à jour le cache local chaque fois qu'une mise à jour de quota arrive.
  • ObjectMapper : gère la conversion des charges utiles JSON en objets Java et vice versa.
  • sessions : garde une trace de toutes les sessions WebSocket actives (clients actuellement connectés).
2. Méthodes :
  • afterConnectionEstablished (session WebSocketSession) :
    • Ajoute une nouvelle session client à la liste active dès qu'il se connecte.
  • afterConnectionClosed (session WebSocketSession, statut CloseStatus) :
    • Supprime la session client lorsqu'il se déconnecte.
  • handleTextMessage (session WebSocketSession, message TextMessage) :
    • Traite les messages entrants.
    • Convertit le JSON reçu en un objet QuotaUpdate et met à jour le cache local.
3. sendOrderUpdate (commande de commande) :
  • Envoie des mises à jour en temps réel sur les modifications de commande à tous les clients connectés.
  • Convertit l'objet Order en JSON et l'envoie sous forme de message aux sessions WebSocket actives.
  • Veille à ce que seules les connexions ouvertes reçoivent les mises à jour.
Principales caractéristiques du code :
  • Mises à jour en temps réel :
    • Tient les clients instantanément informés des modifications de quotas et des mises à jour des commandes.
  • Gestion Thread-Safe :
    • Utilise ConcurrentHashSet pour gérer les clients connectés, garantissant ainsi l'absence de conflits lorsque plusieurs clients sont actifs.
  • Gestion des erreurs :
    • Enregistre les erreurs en cas de problème d'envoi de messages, ce qui facilite le dépannage.

Cette configuration garantit une communication fluide et instantanée entre le backend et le front-end, afin que les utilisateurs disposent toujours d'informations à jour sur la disponibilité des quotas et l'état des commandes.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}

Voici un aperçu de ces classes d'exceptions personnalisées et de la manière dont elles sont utilisées pour gérer des scénarios d'erreur spécifiques dans le système :

QuotaNotAvailableException :

  • Cette exception est déclenchée lorsqu'un utilisateur tente d'acheter un package, mais que le quota pour ce package est déjà épuisé.
  • Il est accompagné d'un message par défaut simple : "Le quota de packages n'est pas disponible", afin que les développeurs et les utilisateurs comprennent clairement le problème.

OrderNotFoundException :

  • Celui-ci intervient lorsque le système ne parvient pas à trouver une commande basée sur l'ID de commande fourni.
  • Il comprend un message d'erreur détaillé tel que « Commande introuvable : [orderId] », ce qui permet d'identifier facilement quelle commande manque.

PaymentVerificationException :

  • En cas de problème lors de la vérification d'un paiement (peut-être que les montants ne correspondent pas ou que le statut du paiement n'est pas clair), cette exception est levée.
  • Il vous permet de transmettre un message personnalisé, ajoutant de la flexibilité et du contexte pour diagnostiquer les problèmes de paiement.

En utilisant ces exceptions, le système gère les erreurs de manière propre et prévisible. Ils rendent non seulement le débogage plus efficace pour les développeurs, mais garantissent également que les utilisateurs reçoivent des commentaires clairs et exploitables en cas de problème.

// Request/Response DTOs
@Getter @Setter
public class OrderRequest {
    private String customerId;
    private String packageId;
    private BigDecimal amount;
}

@Getter @Setter
public class PaymentCallback {
    private String orderId;
    private String paymentId;
    private String status;
    private BigDecimal amount;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class QuotaResponse {
    private String packageId;
    private boolean available;
    private Integer remainingQuota;
    private LocalDateTime timestamp;
}

@Getter @Setter
public class ReservationResponse {
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

@Getter @Setter
public class ActivationResponse {
    private String orderId;
    private boolean success;
    private String activationId;
    private String errorCode;
    private String errorMessage;
}

@Getter @Setter
public class VerificationResponse {
    private String orderId;
    private String activationId;
    private boolean success;
    private String status;
    private LocalDateTime activatedAt;
}

@Getter @Setter
public class PaymentRequest {
    private String orderId;
    private BigDecimal amount;
    private String currency;
    private String customerId;
    private String returnUrl;
    private String callbackUrl;
}

@Getter @Setter
public class PaymentSession {
    private String sessionId;
    private String paymentUrl;
    private LocalDateTime expiresAt;
    private String status;
}

@Getter @Setter
public class EscrowResponse {
    private String id;
    private String paymentId;
    private BigDecimal amount;
    private String status;
    private LocalDateTime createdAt;
}

La classe OrderService gère le gros du travail en matière de gestion des commandes. Décrivons comment cela fonctionne :

Principales responsabilités
  1. createOrder (demande OrderRequest) :

    • Cette méthode consiste à créer une nouvelle commande. Il vérifie si le package est disponible, récupère les détails, réserve le quota et enregistre la commande dans la base de données avec un statut initial de RÉSERVÉ.
  2. processPayment(String orderId, rappel PaymentCallback) :

    • Ici, le paiement est géré. Le système vérifie les détails du paiement, met à jour la commande, met le paiement en dépôt et démarre le processus d'activation du service. Si quelque chose ne va pas, il gère les échecs avec élégance.
  3. verifyActivation(Commande de commande) :

    • Cette méthode vérifie si l'activation du service s'est bien déroulée. Il essaie jusqu'à trois fois, et s'il échoue toujours, le système se replie pour gérer l'échec.
  4. completeOrder(Commande de commande) :

    • Une fois que tout est vérifié, cette méthode finalise la commande. Il libère les fonds bloqués au fournisseur, met à jour le statut et informe l'utilisateur du succès.
  5. handleActivationFailure(Ordre de commande) :

    • Si l'activation échoue, cette méthode garantit que le client reçoit un remboursement et une notification concernant ce qui n'a pas fonctionné.
  6. getOrder(String orderId):

    • Cette méthode simple récupère une commande par son identifiant. Si la commande n’existe pas, elle lève une exception spécifique.
Pourquoi ça marche
  • Il garantit que les transactions sont soit terminées, soit annulées, grâce à sa nature transactionnelle.
  • Avec une gestion claire des erreurs et des tentatives, il est suffisamment robuste pour gérer les problèmes du monde réel.
  • Les notifications tiennent les utilisateurs informés à chaque étape.

Ce service est l'épine dorsale du processus de gestion des commandes, reliant le tout pour une expérience utilisateur transparente.

// Domain Models
@Getter @Setter
@Entity
public class Package {
    @Id
    private String id;
    private String name;
    private BigDecimal price;
    private BigDecimal providerCost;
    private String description;
    private boolean active;
}

@Getter @Setter
@Entity
public class Order {
    @Id
    private String id;
    private String customerId;
    private String packageId;
    private String reservationId;
    private String paymentId;
    private String escrowId;
    private OrderStatus status;
    private BigDecimal amount;
    private BigDecimal providerCost;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Getter @Setter
@Entity
public class QuotaReservation {
    @Id
    private String id;
    private String packageId;
    private LocalDateTime expiresAt;
    private ReservationStatus status;
}

// Enums
public enum OrderStatus {
    CREATED, RESERVED, PAYMENT_PENDING, PAYMENT_COMPLETED, 
    IN_ESCROW, ACTIVATING, ACTIVATION_FAILED, COMPLETED, REFUNDED
}

public enum ReservationStatus {
    ACTIVE, EXPIRED, USED, CANCELLED
}

La classe OrderController s'occupe des points de terminaison de l'API REST qui gèrent les commandes dans le système. Think est le pont entre le client qui fait les demandes et les services backend qui font le gros du travail.

Paramètres clés
  1. POST /api/orders (createOrder) :

    • Ce point de terminaison gère la création d'une nouvelle commande.
    • Voici ce qui se passe :
      • Il reçoit une demande de commande du client.
      • Appelle OrderService.createOrder pour traiter la demande et créer la commande.
      • Renvoie :
        • Une réponse 200 OK avec la commande nouvellement créée si tout se passe bien.
        • Un conflit 409 si le quota de packages est indisponible.
        • Une erreur de serveur interne 500 pour tout problème inattendu.
  2. POST /api/orders/callback (handlePaymentCallback) :

    • Celui-ci traite les mises à jour de paiement envoyées par la passerelle de paiement.
    • Voici le déroulement :
      • Il reçoit un PaymentCallback avec tous les détails du paiement.
      • Appelle OrderService.processPayment pour gérer le paiement et mettre à jour le statut de la commande.
      • Les réponses possibles sont :
        • 200 OK si le paiement est traité avec succès.
        • 404 Not Found si l’ID de commande fourni n’existe pas.
        • 422 Entité non traitable s'il y a une incohérence dans la vérification du paiement.
        • 500 Erreur de serveur interne pour quelque chose d'inattendu.
  3. GET /api/orders/{orderId} (getOrder) :

    • Ce point de terminaison récupère les détails d'une commande spécifique par son ID.
    • Voici comment cela fonctionne :
      • Il appelle OrderService.getOrder pour récupérer la commande.
      • Retours :
        • Une réponse 200 OK avec les détails de la commande si trouvé.
        • Un 404 Not Found si l'ID de commande ne correspond à aucun enregistrement.
Caractéristiques
  • Séparation des préoccupations : OrderController délègue toute la logique métier à OrderService, gardant les choses propres et ciblées.
  • Validation : les charges utiles des demandes sont validées à l'aide de l'annotation @Valid pour garantir que les données entrantes répondent aux attentes.
  • Gestion des erreurs :
    • Fournit des réponses spécifiques et utiles aux problèmes courants, tels que des quotas indisponibles ou des commandes manquantes.
    • Enregistre tous les problèmes pour faciliter le débogage.
  • Journalisation : suit les événements clés tels que les demandes entrantes, les erreurs et les détails des commandes pour une meilleure visibilité.

Ce contrôleur garantit que le client et le backend communiquent de manière transparente, rendant la gestion des commandes aussi fluide que possible.

Conclusion

Cette documentation de recherche pose les bases de la conception d'un système de vente à crédit pour le commerce électronique, abordant des défis importants tels que la gestion des quotas, le traitement des paiements et l'activation des services. Bien que cette conception couvre l’essentiel, il est toujours possible d’améliorer les choses !

Voici quelques idées pour améliorer ce design :

  • Utilisez une architecture basée sur les événements pour rendre le système plus flexible et évolutif.
  • Ajoutez un traitement basé sur une file d'attente de messages pour gérer de nombreuses transactions en douceur.
  • Explorez les stratégies de mise en cache avancées pour accélérer les choses et réduire la dépendance aux API externes.
  • Considérez les modèles de système distribués pour une mise à l'échelle plus facile et une meilleure fiabilité.
  • Mettez en œuvre des disjoncteurs pour gérer les problèmes de service tiers avec élégance.
  • Configurez une surveillance et des alertes pour détecter les problèmes rapidement et les résoudre rapidement.
  • Renforcer les mesures de sécurité pour protéger les utilisateurs et leurs données.

Merci beaucoup d'avoir lu ! J'espère que cette documentation a été utile et apporte des éclaircissements à tous ceux qui explorent des défis similaires. Bien sûr, cette conception n’est pas parfaite : il y a toujours place à l’amélioration. Si vous avez des idées ou des suggestions, j’aimerais les entendre.

ressources :

  • Une plongée approfondie dans l'architecture propre et les principes SOLID
  • Concevoir des applications de commerce électronique avec une architecture de microservices
  • Concevoir un backend évolutif pour les ventes flash
  • Escrow sur Wikipédia

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Héritage avec classesArticle suivant:Héritage avec classes