BCard - Documentation technique

SubscriptionApiController extends AbstractController
in package

Contrôleur d'API des abonnements et de leur souscription payante.

Il expose le catalogue des plans actifs, l'abonnement courant de l'utilisateur, puis le tunnel de passage au plan Pro : création d'un abonnement et d'un paiement en attente dans une transaction, génération d'un PaymentIntent Stripe destiné au client mobile, et confirmation ultérieure du paiement qui active l'abonnement et déclenche l'email de confirmation. La devise utilisée est le franc guinéen (gnf) et le paiement Orange Money est déclaré indisponible.

Préfixe de route : /api/subscription. Toutes les actions exigent IS_AUTHENTICATED_FULLY (jeton JWT).

Attributes
#[IsGranted]
'IS_AUTHENTICATED_FULLY'
#[Route]
'/api/subscription'
#[Tag]
$name: 'Subscription API'

Table of Contents

Properties

$entityManager  : EntityManagerInterface
$featuresService  : PredefinedFeaturesService
$mailerService  : MailerService
$stripeService  : StripeService
$subscriptionService  : SubscriptionService

Methods

__construct()  : mixed
confirmPayment()  : JsonResponse
Confirme le paiement et active l'abonnement
getCurrentSubscription()  : JsonResponse
Récupère l'abonnement actuel de l'utilisateur
getPlans()  : JsonResponse
Récupère tous les plans disponibles
upgradeToProProcess()  : JsonResponse
Initie le processus d'upgrade vers un plan Pro
processCreditCardPaymentApi()  : JsonResponse
Traite le paiement par carte bancaire via Stripe pour l'API mobile

Properties

Methods

__construct()

public __construct(EntityManagerInterface $entityManager, SubscriptionService $subscriptionService, StripeService $stripeService, MailerService $mailerService, PredefinedFeaturesService $featuresService) : mixed
Parameters
$entityManager : EntityManagerInterface

Gestionnaire d'entités Doctrine, utilisé pour les dépôts, la persistance et la transaction d'upgrade.

$subscriptionService : SubscriptionService

Service métier des abonnements (éligibilité à l'upgrade, quota de cartes, activation).

$stripeService : StripeService

Passerelle Stripe (création et récupération des PaymentIntent).

$mailerService : MailerService

Service d'envoi de l'email de confirmation d'abonnement.

$featuresService : PredefinedFeaturesService

Service de résolution des fonctionnalités d'un plan en libellés et icônes.

confirmPayment()

Confirme le paiement et active l'abonnement

public confirmPayment(Request $request, SessionInterface $session) : JsonResponse

L'identifiant d'abonnement est résolu dans l'ordre suivant : clé subscription_id du corps, valeur pending_subscription_id en session, puis métadonnée subscription_id du PaymentIntent. Le PaymentIntent est relu côté Stripe et doit être au statut succeeded. L'abonnement doit exister, appartenir à l'utilisateur authentifié et être encore au statut pending. Il est alors activé par SubscriptionService::activateSubscription(), le paiement correspondant (retrouvé par stripePaymentIntentId) passe au statut completed s'il existe, la clé de session est purgée, puis un email de confirmation est envoyé — un échec d'envoi est journalisé via error_log() sans compromettre la réponse de succès.

Route : /api/subscription/confirm-payment (POST), nom api_subscription_confirm_payment. Requiert IS_AUTHENTICATED_FULLY (hérité de la classe).

Corps attendu : {"payment_intent_id": "string — obligatoire, identifiant du PaymentIntent Stripe", "subscription_id": "int — optionnel, sinon repris de la session ou des métadonnées Stripe"}

Réponses : 200 abonnement activé ({success: true, message, subscription}), 400 payment_intent_id manquant, 400 paiement non abouti côté Stripe (payment_status renvoyé), 400 aucun abonnement identifiable, 400 abonnement déjà traité (current_status renvoyé), 403 abonnement appartenant à un autre utilisateur, 404 abonnement introuvable, 500 erreur de l'API Stripe ou exception interne, 401 requête non authentifiée.

Parameters
$request : Request

Requête HTTP dont le corps JSON porte les identifiants de paiement.

$session : SessionInterface

Session contenant éventuellement l'abonnement en attente, purgée en cas de succès.

Attributes
#[Post]
$path: '/api/subscription/confirm-payment'
$summary: 'Confirme le paiement et active l\'abonnement'
$tags: ['Subscription API']
#[RequestBody]
$required: true
$content: new OA\JsonContent(required: ['payment_intent_id'], properties: [new OA\Property(property: 'payment_intent_id', type: 'string', example: 'pi_1234567890'), new OA\Property(property: 'subscription_id', type: 'integer', example: 123)], type: 'object')
#[Response]
$response: 200
$description: 'Abonnement activé avec succès'
$content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'message', type: 'string', example: 'Abonnement activé avec succès'), new OA\Property(property: 'subscription', properties: [new OA\Property(property: 'id', type: 'integer', example: 123), new OA\Property(property: 'status', type: 'string', example: 'active'), new OA\Property(property: 'plan_name', type: 'string', example: 'Pro')], type: 'object')], type: 'object')
#[Route]
'/confirm-payment'
$name: 'api_subscription_confirm_payment'
$methods: ['POST']
Return values
JsonResponse

Objet JSON de succès décrivant l'abonnement activé, ou objet d'erreur.

getCurrentSubscription()

Récupère l'abonnement actuel de l'utilisateur

public getCurrentSubscription() : JsonResponse

Lit l'abonnement actif porté par l'utilisateur. En l'absence d'abonnement, la réponse reste un succès avec data à null et un message explicite plutôt qu'une 404. Sinon la charge utile agrège les données de l'abonnement (dates formatées au format ISO 8601, null si non renseignées) enrichies de deux valeurs calculées : l'éligibilité à un upgrade via SubscriptionService::canUpgrade() et le nombre de cartes restantes via getRemainingCardsForUser().

Route : /api/subscription/current (GET), nom api_subscription_current. Requiert IS_AUTHENTICATED_FULLY (hérité de la classe).

Réponses : 200 abonnement renvoyé, 200 aucun abonnement actif (data à null), 500 erreur interne ({success: false, message, error}), 401 requête non authentifiée.

Attributes
#[Get]
$path: '/api/subscription/current'
$summary: 'Récupère l\'abonnement actuel de l\'utilisateur'
$tags: ['Subscription API']
#[Response]
$response: 200
$description: 'Abonnement actuel de l\'utilisateur'
$content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'data', properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'plan_name', type: 'string', example: 'Free'), new OA\Property(property: 'status', type: 'string', example: 'active'), new OA\Property(property: 'billing_cycle', type: 'string', example: 'monthly'), new OA\Property(property: 'amount', type: 'string', example: '0.00'), new OA\Property(property: 'start_date', type: 'string', example: '2024-01-01T00:00:00+00:00'), new OA\Property(property: 'end_date', type: 'string', example: null), new OA\Property(property: 'next_billing_date', type: 'string', example: null), new OA\Property(property: 'can_upgrade', type: 'boolean', example: true), new OA\Property(property: 'cards_remaining', type: 'integer', example: 1)], type: 'object')], type: 'object')
#[Route]
'/current'
$name: 'api_subscription_current'
$methods: ['GET']
Return values
JsonResponse

Objet JSON {success: bool, data: array<string, mixed>|null}.

getPlans()

Récupère tous les plans disponibles

public getPlans() : JsonResponse

Charge uniquement les plans marqués isActive = true et les met en forme manuellement. Les fonctionnalités brutes du plan sont enrichies en libellés et icônes par PredefinedFeaturesService::getSelectedFeaturesWithIcons(), avec repli sur un tableau vide. Les champs is_popular et badge sont renvoyés bien qu'ils ne figurent pas dans le schéma OpenApi déclaré. Toute exception est interceptée et convertie en réponse 500 incluant le message technique.

Route : /api/subscription/plans (GET), nom api_subscription_plans. Requiert IS_AUTHENTICATED_FULLY (hérité de la classe).

Réponses : 200 catalogue renvoyé ({success: true, data: [...]}), 500 erreur lors du chargement ({success: false, message, error}), 401 requête non authentifiée.

Attributes
#[Get]
$path: '/api/subscription/plans'
$summary: 'Récupère tous les plans disponibles'
$tags: ['Subscription API']
#[Response]
$response: 200
$description: 'Liste des plans disponibles'
$content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'data', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'name', type: 'string', example: 'Pro'), new OA\Property(property: 'description', type: 'string', example: 'Plan professionnel'), new OA\Property(property: 'monthly_price', type: 'string', example: '50000'), new OA\Property(property: 'yearly_price', type: 'string', example: '500000'), new OA\Property(property: 'cards_included', type: 'integer', example: 10), new OA\Property(property: 'features', type: 'array', items: new OA\Items(type: 'string')), new OA\Property(property: 'is_active', type: 'boolean', example: true)], type: 'object'))], type: 'object')
#[Route]
'/plans'
$name: 'api_subscription_plans'
$methods: ['GET']
Return values
JsonResponse

Objet JSON {success: bool, data: list<array<string, mixed>>} où chaque plan porte id, name, description, monthly_price, yearly_price, cards_included, features, is_active, is_popular, badge.

upgradeToProProcess()

Initie le processus d'upgrade vers un plan Pro

public upgradeToProProcess(Request $request, SessionInterface $session) : JsonResponse

Valide successivement la méthode de paiement (credit_card ou orange_money), le cycle de facturation (monthly par défaut, ou yearly), puis l'éligibilité de l'utilisateur via SubscriptionService::canUpgrade(). Le plan est retrouvé par son nom littéral « Pro » et le prix est choisi selon le cycle. Un Subscription au statut pending et un Payment au statut pending sont créés puis persistés dans une transaction explicite ; l'identifiant de l'abonnement est mémorisé en session sous pending_subscription_id pour la confirmation ultérieure. Pour Orange Money, la méthode de paiement est enregistrée, la transaction est validée, mais l'opération est refusée avec un code 503 — l'abonnement en attente reste créé. Pour la carte bancaire, le traitement est délégué à SubscriptionApiController::processCreditCardPaymentApi() avant validation de la transaction. Toute exception provoque un rollback() et une réponse 500.

Route : /api/subscription/upgrade-to-pro (POST), nom api_subscription_upgrade_to_pro. Requiert IS_AUTHENTICATED_FULLY (hérité de la classe).

Corps attendu : {"payment_method": "string — obligatoire, credit_card|orange_money", "billing_cycle": "string — optionnel, monthly|yearly, défaut monthly"}

Réponses : 200 PaymentIntent créé ({success: true, client_secret, payment_intent_id, subscription_id, amount, currency, message}), 400 méthode de paiement invalide, 400 cycle de facturation invalide, 400 abonnement actif déjà en place, 404 plan « Pro » absent en base, 503 paiement Orange Money indisponible, 500 échec Stripe ou exception interne, 401 requête non authentifiée.

Parameters
$request : Request

Requête HTTP dont le corps JSON porte le mode de paiement et le cycle.

$session : SessionInterface

Session utilisée pour mémoriser l'abonnement en attente.

Attributes
#[Post]
$path: '/api/subscription/upgrade-to-pro'
$summary: 'Initie le processus d\'upgrade vers le plan Pro'
$tags: ['Subscription API']
#[RequestBody]
$required: true
$content: new OA\JsonContent(required: ['billing_cycle', 'payment_method'], properties: [new OA\Property(property: 'billing_cycle', type: 'string', enum: ['monthly', 'yearly'], example: 'monthly'), new OA\Property(property: 'payment_method', type: 'string', enum: ['credit_card', 'orange_money'], example: 'credit_card')], type: 'object')
#[Response]
$response: 200
$description: 'Processus d\'upgrade initié avec succès'
$content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'client_secret', type: 'string', example: 'pi_1234567890_secret_abcdef'), new OA\Property(property: 'payment_intent_id', type: 'string', example: 'pi_1234567890'), new OA\Property(property: 'subscription_id', type: 'integer', example: 123), new OA\Property(property: 'amount', type: 'string', example: '50000'), new OA\Property(property: 'currency', type: 'string', example: 'gnf'), new OA\Property(property: 'message', type: 'string', example: 'PaymentIntent créé avec succès')], type: 'object')
#[Response]
$response: 400
$description: 'Erreur de validation'
$content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: false), new OA\Property(property: 'message', type: 'string', example: 'Données invalides')], type: 'object')
#[Route]
'/upgrade-to-pro'
$name: 'api_subscription_upgrade_to_pro'
$methods: ['POST']
Return values
JsonResponse

Réponse JSON de succès ou d'erreur (le type de retour n'est pas déclaré sur la signature).

processCreditCardPaymentApi()

Traite le paiement par carte bancaire via Stripe pour l'API mobile

private processCreditCardPaymentApi(Subscription $subscription, Payment $payment) : JsonResponse

Méthode interne appelée par SubscriptionApiController::upgradeToProProcess(). Elle construit les métadonnées transmises à Stripe (identifiant d'abonnement, identifiant utilisateur, nom du plan suffixé « Annuel » ou « Mensuel », cycle et description), marque le paiement comme réglé par carte, crée le PaymentIntent en gnf avec le montant converti en entier, puis enregistre l'identifiant du PaymentIntent sur le paiement. Le client_secret renvoyé est destiné à être consommé par le SDK flutter_stripe côté application mobile.

Aucune route : méthode privée, non exposée en HTTP.

Réponses : 200 PaymentIntent créé, 500 erreur remontée par l'API Stripe (ApiErrorException interceptée).

Parameters
$subscription : Subscription

Abonnement en attente à financer.

$payment : Payment

Paiement en attente associé, mis à jour avec la méthode et l'identifiant Stripe.

Return values
JsonResponse

Objet JSON {success, client_secret, payment_intent_id, subscription_id, amount, currency, message}, ou objet d'erreur en cas d'échec Stripe.


        
On this page

Search results