BCard - Documentation technique

CompanyRegistrationController extends AbstractController
in package

Contrôleur du tunnel d'inscription d'une entreprise à l'offre B-Card.

Il orchestre un parcours en cinq étapes : saisie de l'identité du contact et création du compte (ROLE_MANAGER), vérification de l'adresse email par code, renseignement des informations de l'entreprise, choix de l'abonnement au plan « Entreprise » puis paiement en ligne via Djomy (ou enregistrement sans paiement en ligne) et confirmation. L'état intermédiaire du parcours n'est pas persisté en base mais conservé en session par CompanyRegistrationService ; l'entreprise, l'abonnement et le mot de passe définitif ne sont créés qu'au retour d'un paiement effectivement encaissé, le mot de passe temporaire étant alors envoyé par email au gestionnaire.

Préfixe de route : /company. Aucun contrôle d'accès au niveau de la classe : le parcours est public puisqu'il précède l'existence d'un compte utilisable.

Attributes
#[Route]
'/company'

Table of Contents

Properties

$auditLogger  : AuditLogger
$djomyCheckoutService  : DjomyCheckoutService
$djomyService  : DjomyService
$emailVerificationService  : EmailVerificationService
$entityManager  : EntityManagerInterface
$logger  : LoggerInterface
$mailerService  : MailerService
$passwordHasher  : UserPasswordHasherInterface
$paymentService  : PaymentService
$planRepository  : PlanRepository
$registrationService  : CompanyRegistrationService
$subscriptionPaymentService  : SubscriptionPaymentService
$userRepository  : UserRepository
$validator  : ValidatorInterface

Methods

__construct()  : mixed
calculateAmount()  : Response
AJAX endpoint for calculating subscription amount
cancel()  : Response
Cancel registration process
confirmation()  : Response
Step 5: Confirmation and Final Processing
details()  : Response
Step 2: Company Details (after email verification)
djomyRetry()  : Response
Relance un paiement Djomy pour une inscription déjà enregistrée.
djomyReturn()  : Response
Retour du navigateur depuis la page de paiement Djomy.
payment()  : Response
Step 4: Payment and Address Information
paymentProcess()  : Response
Enregistre le moyen de paiement choisi et déclenche le paiement correspondant.
register()  : Response
Step 1: Company Details Registration
resendVerification()  : Response
Renvoie un code de vérification d'email au contact de l'entreprise.
subscription()  : Response
Step 3: Subscription Selection
verifyEmail()  : Response
Affiche l'écran de saisie du code de vérification d'email et le valide.
createDjomyTransaction()  : string
Crée la transaction Djomy d'un paiement via {@see DjomyCheckoutService::start()}.
findSessionPendingPayment()  : Payment|null
Retrouve le paiement d'inscription en attente mémorisé en session.
resolveContactPhone()  : string
Téléphone de contact saisi à l'étape 2, pour préremplir le paiement en ligne.
retryPendingDjomyPayment()  : Response
Relance le paiement Djomy d'un paiement en attente, à partir du champ `payer_phone` de la requête.
sendTemporaryPasswordEmail()  : void
Send temporary password email to user
startDjomyPayment()  : Response
Crée l'inscription en attente de paiement puis lance le paiement Djomy.
switchPendingPaymentToOffline()  : Response
Bascule un paiement en attente vers le règlement hors plateforme.

Properties

Methods

__construct()

public __construct(CompanyRegistrationService $registrationService, PaymentService $paymentService, PlanRepository $planRepository, EmailVerificationService $emailVerificationService, UserRepository $userRepository, ValidatorInterface $validator, UserPasswordHasherInterface $passwordHasher, EntityManagerInterface $entityManager, MailerService $mailerService, AuditLogger $auditLogger, LoggerInterface $logger, DjomyService $djomyService, DjomyCheckoutService $djomyCheckoutService, SubscriptionPaymentService $subscriptionPaymentService) : mixed
Parameters
$registrationService : CompanyRegistrationService

Conserve en session les données du parcours et finalise l'inscription.

$paymentService : PaymentService

Calcule et formate le montant de l'abonnement selon le plan, le cycle et l'effectif.

$planRepository : PlanRepository

Charge les plans d'abonnement, notamment le plan « Entreprise » actif.

$emailVerificationService : EmailVerificationService

Envoie et valide les codes de vérification d'email.

$userRepository : UserRepository

Recherche les utilisateurs par identifiant ou par email.

$validator : ValidatorInterface

Valide l'adresse email saisie à la première étape.

$passwordHasher : UserPasswordHasherInterface

Hache le mot de passe temporaire du compte gestionnaire créé.

$entityManager : EntityManagerInterface

Gestionnaire d'entités Doctrine (persistance de l'utilisateur).

$mailerService : MailerService

Envoie l'email contenant les identifiants de connexion.

$auditLogger : AuditLogger

Trace la création ou la mise à jour du compte gestionnaire.

$logger : LoggerInterface

Journalise les échecs de l'inscription sans paiement en ligne et du paiement Djomy.

$djomyService : DjomyService

Relit les paiements de la passerelle Djomy.

$djomyCheckoutService : DjomyCheckoutService

Ouvre les transactions Djomy (première tentative et relances).

$subscriptionPaymentService : SubscriptionPaymentService

Marque un paiement d'abonnement comme encaissé et active l'abonnement.

calculateAmount()

AJAX endpoint for calculating subscription amount

public calculateAmount(Request $request) : Response

Calcule à la volée le montant d'un abonnement pour l'affichage dynamique du tunnel.

Lit plan_id, billing_cycle et employee_count (converti en entier, 1 par défaut) dans le corps de la requête, puis délègue le calcul et le formatage au service de paiement. Aucune donnée n'est modifiée ni stockée en session : le résultat sert uniquement à rafraîchir l'affichage côté client.

Route : /company/calculate-amount (POST), nom company_calculate_amount.

Parameters
$request : Request

Requête HTTP contenant plan_id, billing_cycle et employee_count.

Attributes
#[Route]
'/calculate-amount'
$name: 'company_calculate_amount'
$methods: ['POST']
Return values
Response —

Réponse JSON array{success: true, amount: mixed, formatted_amount: string} en HTTP 200, ou array{success: false, message: string} en HTTP 400 si le calcul lève une exception (plan inconnu, cycle invalide, etc.).

cancel()

Cancel registration process

public cancel() : Response

Annule volontairement le tunnel d'inscription en cours.

Vide les données du parcours stockées en session et informe l'utilisateur. Le compte utilisateur éventuellement créé à l'étape 1 et déjà persisté en base n'est pas supprimé.

Route : /company/cancel (POST), nom company_registration_cancel.

Attributes
#[Route]
'/cancel'
$name: 'company_registration_cancel'
$methods: ['POST']
Return values
Response —

Redirection vers app_home.

confirmation()

Step 5: Confirmation and Final Processing

public confirmation(Request $request) : Response

Étape 5 du tunnel : affiche le récapitulatif final de l'inscription.

Relit les données d'entreprise et d'utilisateur conservées en session et les fusionne en injectant l'email et le nom du contact dans le tableau transmis au template. Malgré son intitulé, la méthode n'effectue aucun traitement final : la création effective de l'entreprise est réalisée par CompanyRegistrationController::paymentProcess() lors du choix du moyen de paiement. Aucune vérification de présence des données n'est faite, la page peut donc être atteinte avec un contexte vide.

Route : /company/confirmation (GET, POST), nom company_confirmation.

Si la session porte djomy_retry_payment_id et que le paiement correspondant est encore en attente, il est transmis au template pour proposer de relancer le paiement en ligne.

Le paiement d'inscription mémorisé sous registration_payment_id en session détermine confirmationState (paid, online_pending ou offline, valeur par défaut) ; contactEmail provient de l'utilisateur du paiement, à défaut des données de session.

Parameters
$request : Request

Requête HTTP (accès à la session).

Attributes
#[Route]
'/confirmation'
$name: 'company_confirmation'
$methods: ['GET', 'POST']
Return values
Response —

Page de confirmation rendue (company/registration/confirmation.html.twig, étape 5/5).

details()

Step 2: Company Details (after email verification)

public details(Request $request, int $user_id, SessionInterface $session) : Response

Étape 2 du tunnel : collecte les informations de l'entreprise et du contact.

L'accès exige un utilisateur existant ET vérifié, sinon retour à l'étape 1 ; si le compte est déjà rattaché à une entreprise, le parcours est interrompu au profit du tableau de bord. Les données de l'utilisateur sont poussées en session par le service d'inscription, et le formulaire est pré-alimenté avec le contenu déjà présent sous la clé de session company_registration, complété de l'email et du nom du contact. À la soumission valide, la position et le téléphone du contact sont écrits sur l'entité User et immédiatement persistés, tandis qu'un objet Company est seulement hydraté puis stocké en session — l'entreprise n'est pas encore enregistrée en base à ce stade.

Route : /company/details/{user_id} (GET, POST), nom company_details.

Parameters
$request : Request

Requête HTTP portant le formulaire CompanyDetailsType.

$user_id : int

Identifiant du contact/gestionnaire à l'origine de l'inscription.

$session : SessionInterface

Session utilisée pour relire les données d'inscription déjà saisies.

Attributes
#[Route]
'/details/{user_id}'
$name: 'company_details'
$methods: ['GET', 'POST']
Return values
Response —

Redirection vers company_register ou app_dashboard selon les contrôles d'accès, vers company_subscription après soumission valide, sinon le formulaire rendu (company/registration/details.html.twig, étape 2/5).

djomyRetry()

Relance un paiement Djomy pour une inscription déjà enregistrée.

public djomyRetry(Request $request) : Response

Protégée par le jeton CSRF djomy_retry. Le paiement est celui mémorisé en session sous djomy_retry_payment_id ; il doit être en attente et provenir de Djomy ou d'un règlement manuel. La relance passe par DjomyCheckoutService::start() : l'ancienne transaction est relue auprès de Djomy (SUCCESS valide le paiement sans nouvelle transaction ; PENDING démarré depuis moins de dix minutes ou une erreur de lecture refusent le réessai ; sinon la nouvelle transaction est ouverte). Elle porte une référence distincte (BCARD-PAY-<id>-R<timestamp>) et un nouveau jeton de retour ; les références de l'ancienne ne sont remplacées qu'en cas de succès de l'appel Djomy.

Route : /company/payment/djomy/retry (POST), nom company_registration_djomy_retry.

Parameters
$request : Request

Requête HTTP portant _token et payer_phone.

Attributes
#[Route]
'/payment/djomy/retry'
$name: 'company_registration_djomy_retry'
$methods: ['POST']
Return values
Response —

Redirection vers Djomy, ou vers company_confirmation / app_home sinon.

djomyReturn()

Retour du navigateur depuis la page de paiement Djomy.

public djomyReturn(Request $request, string $token) : Response

Le paiement est retrouvé par son jeton de retour aléatoire {token} (404 s'il est inconnu, ou si le paiement n'est pas Djomy ou n'a pas d'identifiant de transaction). S'il est déjà encaissé, l'utilisateur est simplement redirigé. Sinon le statut est relu auprès de Djomy : seul SUCCESS fait foi (le montant payé n'est pas fiable) et valide le paiement via SubscriptionPaymentService::markPaymentCompleted() ; FAILED et CANCELLED laissent l'abonnement en attente ; tout autre statut ou une erreur de lecture annonce un traitement en cours (le webhook finalisera). djomy_retry_payment_id n'est maintenu en session que si elle porte déjà cette valeur, c'est-à-dire si c'est la session qui a démarré le paiement.

Route : /company/payment/djomy/return/{token} (GET), nom company_registration_djomy_return.

Parameters
$request : Request

Requête HTTP (accès à la session).

$token : string

Jeton de retour du paiement.

Attributes
#[Route]
'/payment/djomy/return/{token}'
$name: 'company_registration_djomy_return'
$requirements: ['token' => '[0-9a-f]{32,64}']
$methods: ['GET']
Return values
Response —

Redirection vers company_confirmation.

payment()

Step 4: Payment and Address Information

public payment(Request $request) : Response

Étape 4 du tunnel : récapitule la commande avant le choix du moyen de paiement.

Les données d'abonnement sont relues en session ; leur absence interrompt le parcours et renvoie à l'étape 1. Le nom du plan est résolu depuis son identifiant (repli sur « Entreprise » si le plan n'est plus trouvé) et le montant est formaté en francs guinéens (séparateur décimal virgule, séparateur de milliers espace, suffixe GNF). Le téléphone de contact saisi à l'étape 2 est transmis pour préremplir le champ du paiement en ligne, dont le bloc n'est affiché que si Djomy est configuré. Aucune donnée n'est modifiée ni persistée.

Route : /company/payment (GET, POST), nom company_payment.

Si les données de session ont été vidées parce que l'inscription est déjà enregistrée (retour arrière depuis Djomy, rechargement), le récapitulatif est reconstruit depuis le paiement en attente mémorisé en session (voir CompanyRegistrationController::findSessionPendingPayment()). La réponse n'est jamais mise en cache par le navigateur.

Parameters
$request : Request

Requête HTTP (accès à la session).

Attributes
#[Route]
'/payment'
$name: 'company_payment'
$methods: ['GET', 'POST']
Return values
Response —

Redirection vers company_register si ni les données de souscription ni un paiement en attente ne sont trouvés, sinon la page de paiement rendue (company/registration/payment.html.twig, étape 4/5).

paymentProcess()

Enregistre le moyen de paiement choisi et déclenche le paiement correspondant.

public paymentProcess(Request $request) : Response

Le jeton CSRF company_payment_process (champ _token) est contrôlé en premier : s'il est invalide, un message d'erreur est affiché et l'utilisateur revient à company_payment sans rien créer. La valeur djomy crée l'inscription en attente de paiement puis redirige vers la passerelle Djomy (voir CompanyRegistrationController::startDjomyPayment()). La valeur offline finalise l'inscription sans paiement en ligne via CompanyRegistrationService::completeRegistrationPendingPayment() : l'abonnement reste en attente de paiement jusqu'à son activation manuelle par un administrateur, l'email du mot de passe temporaire est envoyé et l'utilisateur est redirigé vers company_confirmation ; en cas d'erreur, un message générique est affiché et l'utilisateur revient à company_payment. Toute autre valeur est journalisée et ramène à la page de paiement avec un message d'erreur.

Route : /company/payment-process (POST), nom company_registration_payment_process.

Parameters
$request : Request

Requête HTTP contenant le champ method.

Attributes
#[Route]
'/payment-process'
$name: 'company_registration_payment_process'
$methods: ['POST']
Return values
Response —

Redirection vers Djomy (djomy), vers company_confirmation (offline) ou vers company_payment si la méthode est invalide.

register()

Step 1: Company Details Registration

public register(Request $request) : Response

Étape 1 du tunnel : identifie le contact et crée (ou réutilise) le compte gestionnaire.

En POST, les champs email et name sont lus directement dans la requête et doivent être renseignés ; l'email est ensuite validé par les contraintes NotBlank et Email (la contrainte BusinessEmail, qui interdirait les adresses personnelles, est présente mais commentée). Trois cas se présentent ensuite : un compte existant et déjà vérifié bloque l'inscription ; un compte existant non vérifié est réutilisé, son nom étant mis à jour si nécessaire ; sinon un nouvel utilisateur est créé avec le rôle ROLE_MANAGER et un mot de passe temporaire aléatoire haché. La création ou la mise à jour est tracée au journal d'audit, puis un code de vérification est envoyé par email. Toute exception pendant cette phase est écrite dans le journal PHP et convertie en message flash, le formulaire étant alors réaffiché.

Route : /company/register (GET, POST), nom company_register.

Parameters
$request : Request

Requête HTTP contenant les champs email et name en POST.

Tags
throws
RandomException|TransportExceptionInterface
Attributes
#[Route]
'/register'
$name: 'company_register'
$methods: ['GET', 'POST']
Return values
Response —

Redirection vers company_verify_email en cas de succès, sinon le formulaire rendu (company/registration/register.html.twig) avec l'état de progression (étape 1/5).

resendVerification()

Renvoie un code de vérification d'email au contact de l'entreprise.

public resendVerification(int $user_id) : Response

Refuse l'envoi si l'utilisateur est introuvable (retour à l'étape 1), s'il est déjà vérifié (passage à l'étape des informations d'entreprise) ou si le délai anti-spam de EmailVerificationService::canResendCode() n'est pas écoulé. Les échecs d'envoi sont silencieusement écrits dans le journal PHP : une exception applicative produit en plus un message flash d'erreur, tandis qu'une erreur de transport n'est que journalisée, l'utilisateur voyant alors potentiellement un message de succès trompeur.

Route : /company/resend-verification/{user_id} (GET, POST), nom company_resend_verification.

Parameters
$user_id : int

Identifiant de l'utilisateur destinataire du nouveau code.

Attributes
#[Route]
'/resend-verification/{user_id}'
$name: 'company_resend_verification'
Return values
Response —

Redirection vers company_verify_email dans le cas nominal, vers company_register si l'utilisateur est introuvable ou vers company_details s'il est déjà vérifié.

subscription()

Step 3: Subscription Selection

public subscription(Request $request) : Response

Étape 3 du tunnel : choix du cycle de facturation de l'abonnement « Entreprise ».

L'effectif est repris des données d'entreprise en session (1 par défaut) et sert à dimensionner le formulaire. Seul le plan nommé « Entreprise » et actif est proposé ; s'il est absent du catalogue, un message d'erreur est affiché et l'utilisateur est renvoyé vers company_details — redirection générée sans le paramètre user_id, ce qui peut échouer si la route l'exige. À la soumission valide, le montant total est calculé par le service de paiement à partir du plan, du cycle de facturation et de l'effectif, affecté à l'abonnement, puis l'ensemble est stocké en session avant de passer au paiement. Comme à l'étape 2, aucun abonnement n'est persisté en base ici.

Route : /company/subscription (GET, POST), nom company_subscription.

Parameters
$request : Request

Requête HTTP portant le formulaire SubscriptionType.

Attributes
#[Route]
'/subscription'
$name: 'company_subscription'
$methods: ['GET', 'POST']
Return values
Response —

Redirection vers company_details si le plan « Entreprise » est indisponible, vers company_payment après soumission valide, sinon le formulaire rendu (company/registration/subscription.html.twig, étape 3/5).

verifyEmail()

Affiche l'écran de saisie du code de vérification d'email et le valide.

public verifyEmail(Request $request, int $user_id) : Response

L'utilisateur est identifié par le paramètre d'URL {user_id} : s'il est introuvable, retour à l'étape 1 ; s'il est déjà vérifié, le parcours saute directement à l'étape des informations d'entreprise. En POST, un code vide produit un message d'erreur et le réaffichage du formulaire ; un code correct fait passer à company_details, un code invalide ou expiré n'affiche qu'un message flash sans limitation du nombre de tentatives. Aucun contrôle d'accès ne protège cette route : le seul identifiant numérique de l'utilisateur suffit à y accéder.

Route : /company/verify-email/{user_id} (GET, POST), nom company_verify_email.

Parameters
$request : Request

Requête HTTP contenant le champ verification_code en POST.

$user_id : int

Identifiant de l'utilisateur dont l'email doit être vérifié.

Attributes
#[Route]
'/verify-email/{user_id}'
$name: 'company_verify_email'
Return values
Response —

Redirection vers company_register si l'utilisateur est introuvable, vers company_details si l'email est vérifié, sinon le formulaire rendu (company/registration/email-verification.html.twig).

createDjomyTransaction()

Crée la transaction Djomy d'un paiement via {@see DjomyCheckoutService::start()}.

private createDjomyTransaction(Payment $payment, string $rawPhone) : string

Le retour du navigateur est la route d'inscription company_registration_djomy_return (jeton aléatoire, repris comme URL d'annulation). Les références, l'heure de démarrage et la méthode DJOMY sont enregistrées par le service, seulement si l'appel à Djomy réussit.

Parameters
$payment : Payment

Paiement d'abonnement en attente.

$rawPhone : string

Numéro du payeur tel que saisi.

Tags
throws
DjomyException

Si Djomy refuse, n'est pas joignable ou si une tentative précédente bloque.

Return values
string —

URL de la page de paiement Djomy.

findSessionPendingPayment()

Retrouve le paiement d'inscription en attente mémorisé en session.

private findSessionPendingPayment(Request $request) : Payment|null

L'identifiant vient uniquement de djomy_retry_payment_id en session, jamais de la requête. Le paiement n'est renvoyé que s'il existe, est en attente et est lié à une souscription.

Parameters
$request : Request

Requête HTTP (accès à la session).

Return values
Payment|null —

Le paiement en attente, ou null dans tous les autres cas.

resolveContactPhone()

Téléphone de contact saisi à l'étape 2, pour préremplir le paiement en ligne.

private resolveContactPhone() : string

Utilise le téléphone de l'utilisateur en base, à défaut celui de l'entreprise conservé en session.

Return values
string —

Numéro tel que saisi, ou chaîne vide.

retryPendingDjomyPayment()

Relance le paiement Djomy d'un paiement en attente, à partir du champ `payer_phone` de la requête.

private retryPendingDjomyPayment(Request $request, Payment $payment) : Response

Logique commune à CompanyRegistrationController::djomyRetry() et à CompanyRegistrationController::paymentProcess() (retour sur l'étape 4 après l'enregistrement). Le numéro est normalisé et validé, puis DjomyCheckoutService::start() relit l'ancienne transaction (fenêtre de dix minutes) avant d'en ouvrir une nouvelle. Tout refus est annoncé par DjomyCheckoutService::describeFailure() avec retour vers company_confirmation.

Parameters
$request : Request

Requête HTTP portant payer_phone.

$payment : Payment

Paiement en attente, déjà contrôlé par l'appelant.

Return values
Response —

Redirection vers Djomy, ou vers company_confirmation en cas de refus.

sendTemporaryPasswordEmail()

Send temporary password email to user

private sendTemporaryPasswordEmail(User $user, string $temporaryPassword) : void

Envoie au gestionnaire l'email contenant ses identifiants de connexion.

Utilise le template company_registration/temporary_password avec l'utilisateur, son entreprise, le mot de passe temporaire en clair et l'URL absolue de connexion. La méthode est volontairement tolérante aux pannes : les exceptions applicatives comme les erreurs de transport sont uniquement écrites dans le journal PHP, afin qu'un échec d'envoi ne remette pas en cause une inscription déjà payée et finalisée.

Parameters
$user : User

Gestionnaire destinataire, dont l'entreprise est jointe au message.

$temporaryPassword : string

Mot de passe temporaire en clair généré lors de la finalisation.

startDjomyPayment()

Crée l'inscription en attente de paiement puis lance le paiement Djomy.

private startDjomyPayment(Request $request, array<string, mixed>|null $subscriptionData) : Response

Valide d'abord le numéro du payeur (payer_phone) et la configuration de Djomy, avant toute écriture en base. L'inscription est ensuite créée avec la méthode DJOMY, l'email du mot de passe temporaire est envoyé, provider et la référence marchande BCARD-PAY-<id du paiement> sont enregistrés, et l'identifiant du paiement et le téléphone saisi sont mémorisés en session pour un éventuel réessai. Si la création de la transaction échoue, l'inscription reste enregistrée : l'erreur est journalisée sans donnée sensible et l'utilisateur est renvoyé vers la confirmation, où il peut réessayer ou régler hors plateforme.

Parameters
$request : Request

Requête HTTP portant payer_phone.

$subscriptionData : array<string, mixed>|null

Données d'abonnement en session.

Return values
Response —

Redirection vers la page de paiement Djomy, ou vers company_payment / company_confirmation / company_register selon le cas.

switchPendingPaymentToOffline()

Bascule un paiement en attente vers le règlement hors plateforme.

private switchPendingPaymentToOffline(Request $request, Payment $payment) : Response

DjomyCheckoutService::assertCanSwitchToOffline() relit d'abord la dernière transaction Djomy ; en cas de refus (paiement en cours, vérification impossible, déjà payé), le message adapté est affiché. Sinon la méthode du paiement et de la commande passe à MANUAL et le paiement est retiré de la session. L'email des identifiants n'est pas renvoyé : il a été envoyé à l'enregistrement.

Parameters
$request : Request

Requête HTTP (accès à la session).

$payment : Payment

Paiement en attente issu de la session.

Return values
Response —

Redirection vers company_confirmation.


        
On this page

Search results