BCard - Documentation technique

PhysicalCardController extends AbstractController
in package

Contrôleur de commande et de paiement des cartes de visite physiques NFC.

Il couvre le cycle complet : configuration de la carte physique à partir d'une carte virtuelle existante, création de la commande (Order) et de sa ligne (OrderItem), calcul du prix (produit de base + option d'impression + option de couleur), puis paiement par carte bancaire via Stripe ou paiement à la livraison. Deux règles de gratuité coexistent : la première carte physique d'un utilisateur disposant d'un abonnement premium (Payment::PRO_SUBSCRIPTION), et la première carte commandée par un manager pour un employé, dans la limite du quota d'employés de l'entreprise (Payment::COMPANY_FREE). Une commande gratuite est confirmée immédiatement sans passer par la page de paiement.

Préfixe de route : /physical-card, préfixe de nom physical_card_. Authentification complète requise (IS_AUTHENTICATED_FULLY) pour toutes les actions.

Attributes
#[IsGranted]
'IS_AUTHENTICATED_FULLY'
#[Route]
'/physical-card'
$name: 'physical_card_'

Table of Contents

Properties

$defaultProCardService  : DefaultProCardService
$entityManager  : EntityManagerInterface
$mailerService  : MailerService
$physicalCardRepository  : PhysicalCardRepository
$stripeService  : StripeService
$subscriptionService  : SubscriptionService
$uploaderService  : UploaderService

Methods

__construct()  : mixed
companyOrders()  : Response
Affiche les commandes de cartes physiques passées par un manager d'entreprise.
confirmFreeOrder()  : Response
Confirme sans paiement une commande de carte physique offerte au titre de l'abonnement Pro.
getModelPreview()  : JsonResponse
Retourne en JSON l'aperçu d'un modèle de carte (nom, type et visuels recto/verso).
getPhysicalCardProductInfo()  : JsonResponse
Retourne en JSON les caractéristiques d'un produit de carte et ses options disponibles.
myPhysicalCards()  : Response
Affiche les commandes de cartes physiques de l'utilisateur connecté.
orderPhysicalCard()  : Response
Affiche et traite le formulaire de commande d'une carte physique pour l'utilisateur connecté.
orderPhysicalCardForEmployee()  : Response
Permet à un manager d'entreprise de commander une carte physique pour un employé.
paymentByOrder()  : Response
Affiche la page de paiement d'une commande et déclenche le règlement choisi.
paymentSuccess()  : Response
Page de retour Stripe : vérifie le paiement d'une commande et la confirme.
processPayment()  : Response
Affiche la page de paiement d'une carte physique et traite la méthode choisie.
calculatePhysicalCardTotal()  : string
Calcule le montant total d'une carte physique.
createOrderForPhysicalCard()  : Order
Crée une commande et un article de commande pour une carte physique
processCashOnDeliveryPayment()  : Response
Confirme une carte physique payée à la livraison, en gérant le cas de la carte offerte.
processCashOnDeliveryPaymentForOrder()  : Response
Enregistre une commande réglée en paiement à la livraison.
processCreditCardPayment()  : Response
Crée une session Stripe Checkout à partir d'une carte physique et y redirige l'utilisateur.
processCreditCardPaymentForOrder()  : Response
Crée une session Stripe Checkout pour une commande et redirige l'utilisateur vers celle-ci.
processPaymentForOrder()  : Response
Aiguille le règlement d'une commande vers Stripe ou le paiement à la livraison.

Properties

Methods

__construct()

public __construct(SubscriptionService $subscriptionService, EntityManagerInterface $entityManager, StripeService $stripeService, PhysicalCardRepository $physicalCardRepository, DefaultProCardService $defaultProCardService, MailerService $mailerService, UploaderService $uploaderService) : mixed
Parameters
$subscriptionService : SubscriptionService

Détermine l'accès aux fonctionnalités premium (éligibilité à la carte offerte).

$entityManager : EntityManagerInterface

Accès aux repositories et persistance Doctrine.

$stripeService : StripeService

Création et récupération des sessions de paiement Stripe Checkout.

$physicalCardRepository : PhysicalCardRepository

Recherche des cartes physiques et comptage par utilisateur.

$defaultProCardService : DefaultProCardService

Fournit la carte offerte par défaut et détecte la première commande.

$mailerService : MailerService

Envoi des e-mails de confirmation de commande.

$uploaderService : UploaderService

Upload des visuels personnalisés (recto, verso) et du logo.

companyOrders()

Affiche les commandes de cartes physiques passées par un manager d'entreprise.

public companyOrders() : Response

L'accès est réservé aux utilisateurs portant ROLE_MANAGER. Les commandes sont celles dont le champ user est le manager connecté, triées par date de création décroissante ; seules celles auxquelles au moins une carte physique est rattachée sont conservées. Le template réutilisé est celui des commandes personnelles, avec is_pro_user forcé à false et un titre de page dédié.

Route : /physical-card/company-orders (toutes méthodes), nom physical_card_company_orders. Réservé aux managers (ROLE_MANAGER, vérifié dans le corps de la méthode).

Tags
throws
AccessDeniedException

Si l'utilisateur connecté n'est pas manager.

Attributes
#[Route]
'/company-orders'
$name: 'company_orders'
Return values
Response

Page HTML physical_card/my_orders.html.twig avec orders_with_physical_cards (list<array{order: Order, physical_cards: list<PhysicalCard>}>).

confirmFreeOrder()

Confirme sans paiement une commande de carte physique offerte au titre de l'abonnement Pro.

public confirmFreeOrder(PhysicalCard $physicalCard) : Response

Enchaîne plusieurs contrôles : accès aux fonctionnalités premium (sinon message d'erreur et retour à l'accueil), propriété de la carte (sinon accès refusé), présence d'une commande associée, puis abonnement actif dont le plan se nomme Pro ou Entreprise. Le contrôle d'unicité de la carte offerte est effectué en comptant les entités Card dont owner est l'utilisateur et dont l'identifiant diffère de celui de la carte physique courante — la requête porte donc sur les produits Card et non sur PhysicalCard, ce qui est un point ambigu de l'implémentation. Enfin, si le prix de la carte est strictement positif (options payantes), l'utilisateur est redirigé vers le paiement. Sinon le prix est mis à 0.00, la carte passe en STATUS_CONFIRMED, un paiement PRO_SUBSCRIPTION complété est créé et la commande passe en Order::EN_TRAITEMENT.

Route : /physical-card/confirm-free-order/{id} (toutes méthodes), nom physical_card_confirm_free_order.

Parameters
$physicalCard : PhysicalCard

Carte physique à confirmer gratuitement, résolue depuis le paramètre de route id.

Tags
throws
AccessDeniedException

Si la carte n'appartient pas à l'utilisateur connecté.

Attributes
#[Route]
'/confirm-free-order/{id}'
$name: 'confirm_free_order'
Return values
Response

Redirection vers physical_card_my_orders en cas de succès, vers app_home si l'utilisateur n'est pas premium, vers physical_card_order si aucune commande n'est associée, ou vers physical_card_payment / physical_card_payment_by_order si la gratuité ne s'applique pas.

getModelPreview()

Retourne en JSON l'aperçu d'un modèle de carte (nom, type et visuels recto/verso).

public getModelPreview(CardModel $cardModel) : JsonResponse

L'accès est réservé aux utilisateurs disposant d'un accès premium ou d'un rôle privilégié (ROLE_ADMIN, ROLE_OPERATOR ou ROLE_MANAGER) : sinon une réponse JSON d'erreur HTTP 403 est renvoyée. Un modèle inactif donne lieu à une réponse HTTP 404. Les chemins d'images sont préfixés par /uploads/card_models/ et valent null lorsqu'aucun fichier n'est défini.

Route : /physical-card/model-preview/{id} (GET), nom physical_card_model_preview.

Parameters
$cardModel : CardModel

Modèle de carte, résolu depuis le paramètre de route id.

Attributes
#[Route]
'/model-preview/{id}'
$name: 'model_preview'
$methods: ['GET']
Return values
JsonResponse

array{id: int, name: string, type: string, frontImage: string|null, backImage: string|null} (HTTP 200), array{error: string} en HTTP 403 si l'accès est refusé ou en HTTP 404 si le modèle est inactif.

getPhysicalCardProductInfo()

Retourne en JSON les caractéristiques d'un produit de carte et ses options disponibles.

public getPhysicalCardProductInfo(Card $card) : JsonResponse

Parcourt les options d'impression et de couleur du produit en ignorant celles qui ne sont pas actives. Les prix sont convertis en flottants (0 par défaut lorsqu'ils sont nuls). Destiné à alimenter dynamiquement le formulaire de commande côté client.

Route : /physical-card/product-info/{id} (GET), nom physical_card_product_info.

Parameters
$card : Card

Produit de carte, résolu depuis le paramètre de route id.

Attributes
#[Route]
'/product-info/{id}'
$name: 'product_info'
$methods: ['GET']
Return values
JsonResponse

array{id: int, name: string, price: float, type: string, printOptions: list<array{id: int, label: string, price: float, isDefault: bool}>, colorOptions: list<array{id: int, label: string, hexValue: string, price: float}>} (HTTP 200).

myPhysicalCards()

Affiche les commandes de cartes physiques de l'utilisateur connecté.

public myPhysicalCards() : Response

Récupère toutes les commandes de l'utilisateur triées par date de création décroissante et leur associe les cartes physiques correspondantes. Contrairement à PhysicalCardController::companyOrders(), les commandes sans carte physique ne sont pas filtrées et apparaissent avec une liste vide. Le drapeau is_pro_user transmis à la vue reflète l'accès premium de l'utilisateur.

Route : /physical-card/my-orders (toutes méthodes), nom physical_card_my_orders.

Attributes
#[Route]
'/my-orders'
$name: 'my_orders'
Return values
Response

Page HTML physical_card/my_orders.html.twig avec orders_with_physical_cards (list<array{order: Order, physical_cards: list<PhysicalCard>}>) et is_pro_user.

orderPhysicalCard()

Affiche et traite le formulaire de commande d'une carte physique pour l'utilisateur connecté.

public orderPhysicalCard(Request $request, BusinessCardAizeRepository $businessCardRepository, CardModelRepository $cardModelRepository, CardRepository $cardRepository) : Response

Exige au préalable au moins une carte virtuelle : sinon, message flash d'erreur et redirection vers app_cards_list. Charge les modèles de carte actifs et les produits non masqués (isHide = false). Détermine si l'utilisateur premium en est à sa première commande physique afin de pré-sélectionner la carte offerte par défaut. La carte physique est initialisée au statut STATUS_PENDING. À la soumission, les visuels recto/verso et le logo sont uploadés (chaque échec est signalé par un message flash sans interrompre le traitement). La gratuité de base est accordée uniquement si l'utilisateur n'a aucune carte physique, possède un accès premium et a bien choisi le produit par défaut. Le total est calculé par PhysicalCardController::calculatePhysicalCardTotal() et la commande créée par PhysicalCardController::createOrderForPhysicalCard(). Si le total est nul et la gratuité acquise, un paiement PRO_SUBSCRIPTION complété de 0,00 est enregistré, la commande passe en Order::EN_TRAITEMENT et la carte en STATUS_CONFIRMED ; sinon la commande reste en Order::DRAFT, la carte passe en STATUS_PENDING_PAYMENT et l'utilisateur est envoyé vers la page de paiement.

Route : /physical-card/order (toutes méthodes), nom physical_card_order.

Parameters
$request : Request

Requête HTTP courante.

$businessCardRepository : BusinessCardAizeRepository

Récupère les cartes virtuelles de l'utilisateur.

$cardModelRepository : CardModelRepository

Fournit les modèles de carte actifs.

$cardRepository : CardRepository

Fournit les produits de carte visibles.

Attributes
#[Route]
'/order'
$name: 'order'
Return values
Response

Page HTML physical_card/order.html.twig, redirection vers app_cards_list si aucune carte virtuelle, vers physical_card_my_orders si la commande gratuite est confirmée, ou vers physical_card_payment_by_order sinon.

orderPhysicalCardForEmployee()

Permet à un manager d'entreprise de commander une carte physique pour un employé.

public orderPhysicalCardForEmployee(BusinessCardAize $businessCard, Request $request, CardModelRepository $cardModelRepository, CardRepository $cardRepository) : Response

L'accès est réservé aux utilisateurs portant ROLE_MANAGER, et la carte virtuelle passée en paramètre doit appartenir à un employé rattaché à la même entreprise que le manager ; dans le cas contraire une exception d'accès refusé est levée. L'éligibilité à la gratuité est calculée avant la construction du formulaire : il faut qu'aucune carte physique n'existe déjà pour cette carte virtuelle et que le nombre de paiements Payment::COMPANY_FREE déjà consommés par l'entreprise soit inférieur à son effectif déclaré (getEmployeeCount()). Quand la gratuité s'applique, le produit est forcé sur la carte par défaut fournie par DefaultProCardService::getDefaultProCard(). Les visuels et le logo sont uploadés avec gestion d'erreur par message flash. Si la gratuité est acquise et le total nul, un paiement COMPANY_FREE complété de 0,00 rattaché à l'entreprise est créé, la commande passe en Order::EN_TRAITEMENT et la carte en STATUS_CONFIRMED ; sinon la commande reste en Order::EN_ATTENTE, la carte passe en STATUS_PENDING_PAYMENT et le manager est redirigé vers le paiement.

Route : /physical-card/manager/order/{id} (toutes méthodes), nom physical_card_manager_order. Réservé aux managers (ROLE_MANAGER, vérifié dans le corps de la méthode).

Parameters
$businessCard : BusinessCardAize

Carte virtuelle de l'employé, résolue depuis le paramètre de route id.

$request : Request

Requête HTTP courante.

$cardModelRepository : CardModelRepository

Fournit les modèles de carte actifs.

$cardRepository : CardRepository

Fournit les produits de carte visibles.

Tags
throws
AccessDeniedException

Si l'utilisateur n'est pas manager ou si la carte virtuelle n'appartient pas à son entreprise.

Attributes
#[Route]
'/manager/order/{id}'
$name: 'manager_order'
Return values
Response

Page HTML physical_card/order.html.twig, redirection vers physical_card_company_orders si la commande gratuite est confirmée, ou vers physical_card_payment_by_order sinon.

paymentByOrder()

Affiche la page de paiement d'une commande et déclenche le règlement choisi.

public paymentByOrder(int $orderId, Request $request) : Response

Vérifie qu'un utilisateur est connecté, que la commande existe et qu'elle lui appartient (sinon exception 404), puis qu'une carte physique y est bien rattachée (sinon message flash d'erreur et redirection vers le formulaire de commande). Le traitement effectif est délégué à PhysicalCardController::processPaymentForOrder().

Route : /physical-card/payment-by-order/{orderId} (GET, POST), nom physical_card_payment_by_order.

Parameters
$orderId : int

Identifiant de la commande à régler.

$request : Request

Requête HTTP courante (méthode et champ payment_method en POST).

Tags
throws
ApiErrorException

Si la création de la session Stripe échoue.

AccessDeniedException

Si aucun utilisateur n'est connecté.

NotFoundHttpException

Si la commande est introuvable ou n'appartient pas à l'utilisateur.

Attributes
#[Route]
'/payment-by-order/{orderId}'
$name: 'payment_by_order'
$methods: ['GET', 'POST']
Return values
Response

Page de paiement, redirection vers Stripe, ou redirection vers physical_card_order si aucune carte physique n'est associée.

paymentSuccess()

Page de retour Stripe : vérifie le paiement d'une commande et la confirme.

public paymentSuccess(int $orderId) : Response

Contrôle que la commande existe et appartient à l'utilisateur connecté (sinon exception 404), puis qu'une carte physique y est rattachée. Si la commande porte un identifiant de session Stripe, celle-ci est récupérée : lorsque payment_status vaut paid, la carte passe en STATUS_CONFIRMED, la commande en Order::EN_TRAITEMENT, et le paiement existant est réutilisé ou créé puis marqué CREDIT_CARD / COMPLETED_STATUS avec l'identifiant de PaymentIntent ; l'e-mail de confirmation est ensuite envoyé (échec seulement journalisé). Un statut différent de paid ou une exception lors de la vérification produit un message flash d'erreur. En l'absence d'identifiant de session Stripe, aucune vérification n'est effectuée et la redirection a lieu silencieusement.

Route : /physical-card/payment/success/{orderId} (toutes méthodes), nom physical_card_payment_success.

Parameters
$orderId : int

Identifiant de la commande revenant de Stripe.

Tags
throws
NotFoundHttpException

Si la commande est introuvable ou n'appartient pas à l'utilisateur connecté.

Attributes
#[Route]
'/payment/success/{orderId}'
$name: 'payment_success'
Return values
Response

Redirection vers physical_card_my_orders, ou vers physical_card_order si aucune carte physique n'est associée à la commande.

processPayment()

Affiche la page de paiement d'une carte physique et traite la méthode choisie.

public processPayment(PhysicalCard $physicalCard, Request $request) : Response

Refuse l'accès si la carte n'appartient pas à l'utilisateur connecté, et redirige vers le formulaire de commande si aucune commande n'y est rattachée. En POST, le champ payment_method oriente vers PhysicalCardController::processCreditCardPayment() (valeur stripe) ou PhysicalCardController::processCashOnDeliveryPayment() (valeur cash_on_delivery) ; toute autre valeur retombe sur l'affichage de la page. L'éligibilité à la carte offerte est évaluée en considérant que l'utilisateur ne possède que cette carte (countByUser() === 1) et qu'il a un accès premium.

Route : /physical-card/payment/{id} (toutes méthodes), nom physical_card_payment.

Parameters
$physicalCard : PhysicalCard

Carte physique à régler, résolue depuis le paramètre de route id.

$request : Request

Requête HTTP courante.

Tags
throws
AccessDeniedException

Si la carte n'appartient pas à l'utilisateur connecté.

Attributes
#[Route]
'/payment/{id}'
$name: 'payment'
Return values
Response

Page HTML physical_card/payment.html.twig, réponse de redirection produite par la méthode de paiement, ou redirection vers physical_card_order si aucune commande n'est associée.

calculatePhysicalCardTotal()

Calcule le montant total d'une carte physique.

private calculatePhysicalCardTotal(PhysicalCard $physicalCard, bool $baseFree) : string

Additionne le prix du produit de carte, celui de l'option d'impression et celui de l'option de couleur, chaque composant valant zéro s'il est absent. Lorsque $baseFree est vrai, le prix du produit de base est ramené à zéro : les options éventuellement choisies restent facturées, ce qui explique qu'une commande « offerte » puisse avoir un total non nul.

Parameters
$physicalCard : PhysicalCard

Carte physique dont les prix sont agrégés.

$baseFree : bool

Vrai si le produit de base est offert (abonnement Pro ou quota entreprise).

Return values
string

Montant total formaté avec deux décimales et un point comme séparateur (ex. 25000.00).

createOrderForPhysicalCard()

Crée une commande et un article de commande pour une carte physique

private createOrderForPhysicalCard(PhysicalCard $physicalCard, User $user) : Order

La commande est initialisée avec le nom, l'e-mail et le téléphone de l'utilisateur (le champ lastname reste vide, getName() étant placé dans firstname), le statut Order::EN_ATTENTE, le nom de l'entreprise si elle existe et l'adresse de livraison de la carte physique. Si un produit de carte est sélectionné, un OrderItem de quantité 1 est créé avec la police (Arial par défaut), la couleur (#000000 par défaut) et un design valant model ou custom selon la présence d'un modèle. Lorsque la carte virtuelle est rattachée à un employé, celui-ci est lié à l'article et les coordonnées de la commande sont écrasées par celles de l'employé, avec une note récapitulative. Le total de la commande n'est renseigné que dans cette branche : sans produit de carte, aucun OrderItem n'est ajouté et le total reste non défini. La commande est persistée mais aucun flush() n'est effectué ici.

Parameters
$physicalCard : PhysicalCard

Carte physique dont découlent le produit, les options et l'adresse de livraison.

$user : User

Utilisateur à l'origine de la commande (l'utilisateur connecté, éventuellement un manager).

Return values
Order

Commande nouvellement créée et persistée (non encore flushée).

processCashOnDeliveryPayment()

Confirme une carte physique payée à la livraison, en gérant le cas de la carte offerte.

private processCashOnDeliveryPayment(PhysicalCard $physicalCard) : Response

Deux branches. Si l'utilisateur est premium, n'a que cette carte physique (countByUser() === 1) et que son prix est nul ou négatif, la commande est traitée comme offerte : prix forcé à 0.00, carte en STATUS_CONFIRMED, paiement PRO_SUBSCRIPTION au statut COMPLETED_STATUS et commande en Order::EN_TRAITEMENT avec la méthode Order::PRO_SUBSCRIPTION. Sinon, la carte est également confirmée mais le paiement est créé en Payment::DELIVERY_CASH au statut ACCEPTED_STATUS pour le montant de la carte. Dans les deux cas l'e-mail de confirmation est envoyé, un échec étant seulement écrit via error_log (et une TransportExceptionInterface silencieusement ignorée).

Parameters
$physicalCard : PhysicalCard

Carte physique à confirmer, dont la commande est mise à jour.

Return values
Response

Redirection vers physical_card_my_orders, avec un message flash adapté à la branche empruntée.

processCashOnDeliveryPaymentForOrder()

Enregistre une commande réglée en paiement à la livraison.

private processCashOnDeliveryPaymentForOrder(Order $order, PhysicalCard $physicalCard) : Response

La carte physique passe en STATUS_CONFIRMED et la commande en Order::EN_TRAITEMENT. Un paiement Payment::DELIVERY_CASH au statut ACCEPTED_STATUS est créé pour le montant total de la commande. L'e-mail de confirmation est ensuite envoyé ; un échec d'envoi est simplement écrit dans le journal d'erreurs PHP (error_log) et n'interrompt pas le flux, une TransportExceptionInterface étant même silencieusement ignorée.

Parameters
$order : Order

Commande confirmée.

$physicalCard : PhysicalCard

Carte physique associée.

Return values
Response

Redirection vers physical_card_my_orders, avec un message flash de succès.

processCreditCardPayment()

Crée une session Stripe Checkout à partir d'une carte physique et y redirige l'utilisateur.

private processCreditCardPayment(PhysicalCard $physicalCard) : Response

Variante de PhysicalCardController::processCreditCardPaymentForOrder() utilisée par PhysicalCardController::processPayment() : les URL de succès et d'annulation sont construites à partir de l'identifiant de la carte physique, et les métadonnées transmises à Stripe contiennent physical_card_id, order_id et user_id. L'identifiant de session est enregistré sur la commande. Toute exception est ici capturée : un message flash d'erreur est affiché et l'utilisateur revient sur la page de paiement.

Parameters
$physicalCard : PhysicalCard

Carte physique à facturer, dont la commande reçoit l'identifiant de session Stripe.

Return values
Response

Redirection vers l'URL Stripe Checkout, ou redirection vers physical_card_payment en cas d'erreur.

processCreditCardPaymentForOrder()

Crée une session Stripe Checkout pour une commande et redirige l'utilisateur vers celle-ci.

private processCreditCardPaymentForOrder(Order $order, PhysicalCard $physicalCard) : Response

L'article facturé est une ligne unique « Carte physique B-Card » en francs guinéens (gnf), dont le montant unitaire est le prix de la carte physique converti en entier. Les URL de succès et d'annulation pointent respectivement vers physical_card_payment_success et physical_card_payment_by_order, et l'identifiant de commande est transmis en métadonnée order_id. L'identifiant de session Stripe est enregistré sur la commande avant la redirection. Aucune capture d'exception : un échec Stripe remonte à l'appelant.

Parameters
$order : Order

Commande à régler.

$physicalCard : PhysicalCard

Carte physique fournissant le montant à facturer.

Tags
throws
ApiErrorException

Si l'appel à l'API Stripe échoue.

Return values
Response

Redirection HTTP vers l'URL de la session Stripe Checkout.

processPaymentForOrder()

Aiguille le règlement d'une commande vers Stripe ou le paiement à la livraison.

private processPaymentForOrder(Order $order, PhysicalCard $physicalCard, Request $request) : Response

En POST, la valeur du champ payment_method détermine la branche : PhysicalCardController::processCreditCardPaymentForOrder() pour stripe, PhysicalCardController::processCashOnDeliveryPaymentForOrder() pour cash_on_delivery. Sinon, la page de paiement est affichée avec can_get_free_card forcé à false : à ce stade la commande n'est jamais éligible à la gratuité, les commandes gratuites étant confirmées sans passer par cette page.

Parameters
$order : Order

Commande à régler.

$physicalCard : PhysicalCard

Carte physique rattachée à la commande.

$request : Request

Requête HTTP courante.

Tags
throws
ApiErrorException

Si la création de la session Stripe échoue.

Return values
Response

Redirection vers Stripe, redirection après enregistrement du paiement à la livraison, ou page HTML physical_card/payment.html.twig.


        
On this page

Search results