Maison >Java >javaDidacticiel >Concevoir un système d'achat de crédits sur Internet
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.
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 :
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.
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.
Le flux du système est divisé en six phases pour plus de clarté :
Ce flux garantit une expérience fluide et fiable aux utilisateurs, tout en gérant efficacement les ressources et les erreurs potentielles.
Le diagramme de séquence ci-dessous permet d'illustrer l'interaction entre les différents rôles et composants.
Le flux du système est divisé en six phases pour plus de clarté :
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 }
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.
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.
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 }
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.
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 }
Ce service gère toutes les notifications envoyées aux utilisateurs concernant l'état de leur commande. Voici comment cela fonctionne :
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 }
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.
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 :
OrderNotFoundException :
PaymentVerificationException :
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 :
createOrder (demande OrderRequest) :
processPayment(String orderId, rappel PaymentCallback) :
verifyActivation(Commande de commande) :
completeOrder(Commande de commande) :
handleActivationFailure(Ordre de commande) :
getOrder(String orderId):
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.
POST /api/orders (createOrder) :
POST /api/orders/callback (handlePaymentCallback) :
GET /api/orders/{orderId} (getOrder) :
Ce contrôleur garantit que le client et le backend communiquent de manière transparente, rendant la gestion des commandes aussi fluide que possible.
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 :
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 :
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!