<?php
// src/Controller/EntrepriseController.php
namespace App\Controller;
use App\Entity\SujetFAQ;
use App\Entity\Article;
use App\Entity\AppUser;
use App\Entity\Product;
use App\Entity\Review;
use App\Entity\Devis;
use App\Entity\Category;
use App\Entity\Commande;
use App\Entity\Conversation;
use App\Entity\Service;
use App\Entity\ProductOrder;
use App\Entity\Entreprise;
use App\Entity\Notification;
use App\Entity\ProductMisDeCote;
use App\Entity\SubCategory;
use App\Entity\AvisUtilisateur;
use App\Form\SujetFAQType;
use App\Form\AvisUtilisateurType;
use App\Form\AvisUtilisateurEditType;
use App\Form\ProductType;
use App\Entity\ShippingMethod;
use App\Entity\RoleBoutique;
use App\Entity\Boutique;
use App\Form\BoutiqueType;
use App\Entity\Plan;
use App\Entity\ValiditeService;
use App\Entity\UserBoutique;
use App\Entity\RequeteAjoutBoutique;
use App\Entity\TVA;
use Spipu\Html2Pdf\Html2Pdf;
use App\Entity\CategorieEntreprise;
use App\Entity\Produit;
use App\Entity\ProductPhoto;
use App\Entity\OffreDevis;
use App\Form\ProduitType;
use App\Form\ProduitEditType;
use App\Entity\Ticket;
use App\Entity\Message;
use App\Services\CinetPay;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use App\Form\TicketType;
use App\Entity\CommentaireTicket;
use App\Form\CommentaireTicketType;
use App\Entity\PaiementService;
use App\Form\SettingBoutiqueOngletEntrepriseType;
use App\Form\SettingBoutiqueOngletBanqueType;
use App\Form\SettingBoutiqueOngletInfoLegaleType;
use App\Form\SettingBoutiqueOngletDocumentType;
use App\Form\SettingBoutiqueOngletEmailType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security as Security;
/**
* @Route("/{_locale}/gestion")
*/
class GestionController extends AbstractController
{
private $session;
public function __construct(SessionInterface $session)
{
$this->session = $session;
}
/**
* @Route("/mes-entreprises/", name="gestion_mes_entreprises")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function mesEntrepriseAction(Request $request)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$userBoutiques = $em->getRepository(UserBoutique::class)
->findByUser($user);
return $this->render('gestion/mes_entreprise.html.twig', [
'userBoutiques' => $userBoutiques
]);
}
/**
* @Route("/shop/{slug}", name="gestion.show_shop")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function afficherBoutiqueAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
if(null == $userBoutique){
return $this->redirectToRoute('security_login');
}
$abonnement = $em->getRepository(Commande::class)
->findBy(
array(
'boutique' => $boutique
),
array(
'laDate' => 'DESC'
),
1
);
if(!empty($abonnement)){
$abonnement = $abonnement[0];
}
return $this->render('gestion/afficher_boutique.html.twig', [
'boutique' => $boutique,
'userBoutique' => $userBoutique,
'abonnement' => $abonnement
]);
}
/**
* @Route("/devenir-premium/", name="gestion.devenir_premium")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function devenirPremiumAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
$transId = rand(4560, 100000);
$transDate = new \DateTime();
$amount = 15000;
if('POST' == $request->getMethod()){
var_dump($_POST);
}
$commande = new Commande;
//transaction id
$id_transaction = rand(); //CinetPay::generateTransId();
// Payment description
$description_du_paiement = "Devenir premium";
// Payment Date must be on date format
$date_transaction = date("Y-m-d H:i:s");
// Amount
$montant_a_payer = 15000;
// put a value that you can use to identify the buyer in your system
$identifiant_du_payeur = $user->getId().'@@@'.$boutique->getId();
//Veuillez entrer votre apiKey
$apiKey = "1182873575626ba8e6c712e9.05305545";
//Secret Key: 127127447062796bafS
//Veuillez entrer votre siteId
$site_id = "792421";
//platform , utiliser PROD si vous avez créé votre compte sur www.cinetpay.com ou TEST si vous avez créé votre compte sur www.sandbox.cinetpay.com
$plateform = "PROD";
//la version , utilisé V1 si vous voulez utiliser la version 1 de l'api
$version = "V2";
// nom du formulaire CinetPay
$formName = "goCinetPay";
// notify url
$notify_url = $this->generateUrl('gestion.cinetpay_notify', array('transaction' => $id_transaction), UrlGeneratorInterface::ABSOLUTE_URL);
// return url
$return_url = $this->generateUrl('gestion.cinetpay_return', array('transaction' => $id_transaction), UrlGeneratorInterface::ABSOLUTE_URL);
// cancel url
$cancel_url = $this->generateUrl('gestion.cinetpay_cancel', array('transaction' => $id_transaction), UrlGeneratorInterface::ABSOLUTE_URL);
// cinetpay button type, must be 1, 2, 3, 4 or 5
$btnType = 4;
// button size, can be 'small' , 'large' or 'larger'
$btnSize = 'large';
// fill command class
$commande->setCpmTransId($id_transaction);
$commande->setCpmAmount($montant_a_payer);
$commande->setUser($user);
$commande->setBoutique($boutique);
$em->persist($commande);
$em->flush();
return $this->render('gestion/devenir_premium.html.twig', [
'user' => $user,
'transId' => $transId,
'transDate' => $transDate,
'amount' => $amount,
'plan' => $plan,
'transaction' => $id_transaction,
'formName' => $formName,
'btnType' => $btnType,
'btnSize' => $btnSize,
'boutique' => $boutique,
'userBoutique' => $userBoutique,
]);
return $this->redirectToRoute('gestion.show_shop', array('slug' => $boutique->getSlug()));
}
/**
* @Route("/table-formule-pour-paiement/{slug}", name="gestion_table_formule_pour_paiement")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function tableFormulePourPaiementAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$plans = $em->getRepository(Plan::class)
->paidPlan();
return $this->render('gestion/table_formule_pour_paiement.html.twig', [
'boutique' => $boutique,
'plans' => $plans
]);
}
/**
* @Route("/paiement-abonnement/{idEntreprise}/{idPlan}/{idUser}/{plateforme}", name="gestion_paiement_abonnement")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function paiementAbonnementAction(Request $request, $idEntreprise, $idPlan, $idUser, $plateforme)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$boutique = $em->getRepository(Boutique::class)
->findOneById($idEntreprise);
if($plateforme == 'mobile'){
$user = $em->getRepository(AppUser::class)
->findOneById($idUser);
}
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
if(null == $userBoutique){
return $this->createNotFoundException();
}
$plan = $em->getRepository(Plan::class)
->findOneById($idPlan);
//Authentification
/*
$ch = curl_init($this->getJekoBaseUrl().'/stores');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-KEY:'. $this->getJekoAPI(),
'X-API-KEY-ID:'.$this->getJekoAPIID(),
'Content-Type: application/json'
]);
$response = curl_exec($ch);
curl_close($ch);
$paymentRequest = json_decode($response, true);
*/
$transId = rand(4560, 100000);
$transDate = new \DateTime();
$amount = 15000;
$message = '';
$error = '';
$qrUrl = '';
$authenticationUrl = 'https://billpayment.api.prod.run.billmap.net/api/authentication/token';
$id_transaction = '';
$apiKeyAndSecret = array(
'orangekey' => '2CSLsG7IDYHmR8Wp',
'orangesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'mtnkey' => '3AXHb9Q0ctzSJqbV',
'mtnsecret' => 'oDXwYw4XwervcUHof7u7M0tRfRuAYf9aGmgOWYF9y1aMjkWX3lZx312X5ge8L8I9',
'moovkey' => 'GX7fNUtKpzbaZbrM',
'moovesecret' => '2OZpbuvcRCDNapJrsVkHTE6gqocNdeN1DxIQIlEpBZjbR6GuQaBYkbim05fDbK1i',
'wavekey' => '2CSLsG7IDYHmR8Wp',
'wavesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF'
);
//variable mise pour la compatibilité mobile
$keyOperateur = array(
'orangekey' => '2CSLsG7IDYHmR8Wp',
'orangesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'orangedebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/Bill/orange/webpayment',
'orangecode' => 'MYHELPPLACE_ORANGE',
'mtnkey' => '2CSLsG7IDYHmR8Wp',
'mtnsecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'mtndebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/Bill/debit/openapi',
'mtncode' => 'MYHELPPLACE_MTNCI',
'moovkey' => 'GX7fNUtKpzbaZbrM',
'moovsecret' => '2OZpbuvcRCDNapJrsVkHTE6gqocNdeN1DxIQIlEpBZjbR6GuQaBYkbim05fDbK1i',
'moovdebiturl' =>'https://billpayment.api.prod.run.billmap.net/api/Bill/debit/moov',
'moovcode' => 'MYHELPPLACE_MOOVCI',
'wavekey' => '2CSLsG7IDYHmR8Wp',
'wavesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'wavedebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/bill/wave/checkout',
'wavecode' => 'MYHELPPLACE_WAVE'
);
if('POST' == $request->getMethod()){
$formData = json_decode($request->getContent());
if($plateforme == 'web'){
$operateurPaiement = $request->request->get('operateur_paiement');
$numeroDeTelephone = $request->request->get('numero_de_telephone');
$indicatifPays = $request->request->get('indicatif_pays');
$mois = (float)$request->request->get('mois');
//$montantTransaction = (float)$request->request->get('montant_transaction');
$montantTransaction = $plan->getTarif() * $mois;
$key = $request->request->get($operateurPaiement.'key');
$secret = $request->request->get($operateurPaiement.'secret');
$debitURL = $request->request->get($operateurPaiement.'debiturl');
$code = $request->request->get($operateurPaiement.'code');
$otp = $request->request->get('otp');
}else{
// mobile
$operateurPaiement = $formData->operateur_paiement;
$numeroDeTelephone = $formData->numero_de_telephone;
$indicatifPays = $formData->indicatif_pays;
$mois = (float)$formData->mois;
//$montantTransaction = (float)$formData->montant_transaction;
$montantTransaction = $plan->getTarif() * $mois;
$key = $keyOperateur[$operateurPaiement.'key'];
$secret = $keyOperateur[$operateurPaiement.'secret'];
$debitURL = $keyOperateur[$operateurPaiement.'debiturl'];
$code = $keyOperateur[$operateurPaiement.'code'];
$otp = $formData->otp;
}
$id_transaction = '';
$commande = new Commande;
$commande->setPlan($plan);
//transaction id
// Payment description
$description_du_paiement = "Devenir premium";
// Payment Date must be on date format
$date_transaction = date("Y-m-d H:i:s");
// Amount
$montant_a_payer = $montantTransaction;
// put a value that you can use to identify the buyer in your system
$identifiant_du_payeur = $user->getId().'@@@'.$boutique->getId();
$commande->setCpmTransId($id_transaction);
$commande->setCpmAmount($montant_a_payer);
$commande->setUser($user);
$commande->setBoutique($boutique);
$em->persist($commande);
$em->flush();
$laReference = $commande->getId().'@ABONNEMENT'.'@'.$mois;
// notify url
$returnUrls = $this->getJekoReturnUrls($laReference, $plateforme);
$successUrl = $returnUrls['successUrl'];
// return url
$errorUrl = $returnUrls['errorUrl'];
$ch = curl_init($this->getJekoBaseUrl().'/payment_requests');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-KEY:'. $this->getJekoAPI(),
'X-API-KEY-ID:'.$this->getJekoAPIID(),
'Content-Type: application/json'
]);
$amountCents = (int) round((float) $montantTransaction * 100);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'storeId' => $this->getJekoStoreId(),
'amountCents' => $amountCents,
'currency' => 'XOF',
'reference' =>$laReference,
'paymentDetails' => [
'type' => 'redirect',
'data' => [
'paymentMethod' => $operateurPaiement,
'successUrl' => $successUrl,
'errorUrl' => $errorUrl
]
]
]));
$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$paymentRequest = json_decode($response, true);
if ($response === false || $curlError || !is_array($paymentRequest) || empty($paymentRequest['redirectUrl'])) {
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'Unable to create Jèko payment',
'providerStatus' => $httpCode,
], Response::HTTP_BAD_GATEWAY));
}
$commande->setCpmTransId($paymentRequest['id'] ?? $id_transaction);
$commande->setCpmAmount($montant_a_payer);
$em->flush();
if($plateforme == 'web'){
return $this->redirect($paymentRequest['redirectUrl']);
}else{
return $this->allowRemoteRequest(new JsonResponse([
'error' => '',
'message' => '',
'redirectUrl' => $paymentRequest['redirectUrl'],
'reference' => $laReference,
'paymentReference' => $laReference,
'successUrl' => $successUrl,
'errorUrl' => $errorUrl,
]));
}
return $this->redirectToRoute('gestion_finaliser_abonnement', array('slug' => $boutique->getSlug(), 'plan' => $plan->getSlug()));
}
if($plateforme == 'mobile'){
return $this->render('api/paiement_abonnement.html.twig', [
'user' => $user,
'transId' => $transId,
'transDate' => $transDate,
'amount' => $amount,
'plan' => $plan,
'boutique' => $boutique,
'userBoutique' => $userBoutique,
'transaction' => $id_transaction,
'keyOperateur' => $keyOperateur
]);
}
return $this->render('gestion/paiement_abonnement.html.twig', [
'user' => $user,
'transId' => $transId,
'transDate' => $transDate,
'amount' => $amount,
//'CinetPay' => $CinetPay,
'plan' => $plan,
'boutique' => $boutique,
'userBoutique' => $userBoutique,
'transaction' => $id_transaction
]);
}
/**
* @Route("/paiement-frais-de-service/{idUser}/{typeService}/{plateforme}", name="gestion_paiement_frais_de_service")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function paiementFraisDeServiceAction(Request $request, $idUser, $typeService, $plateforme)
{
$em = $this->getDoctrine()
->getManager();
$user = $em->getRepository(AppUser::class)
->findOneById($idUser);
if(null == $user){
throw $this->createNotFoundException();
}
$transId = rand(4560, 100000);
$transDate = new \DateTime();
$amount = 15000;
$message = '';
$error = '';
$qrUrl = '';
$authenticationUrl = 'https://billpayment.api.prod.run.billmap.net/api/authentication/token';
$id_transaction = '';
$apiKeyAndSecret = array(
'orangekey' => '2CSLsG7IDYHmR8Wp',
'orangesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'mtnkey' => '3AXHb9Q0ctzSJqbV',
'mtnsecret' => 'oDXwYw4XwervcUHof7u7M0tRfRuAYf9aGmgOWYF9y1aMjkWX3lZx312X5ge8L8I9',
'moovkey' => 'GX7fNUtKpzbaZbrM',
'moovesecret' => '2OZpbuvcRCDNapJrsVkHTE6gqocNdeN1DxIQIlEpBZjbR6GuQaBYkbim05fDbK1i',
'wavekey' => '2CSLsG7IDYHmR8Wp',
'wavesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF'
);
//variable mise pour la compatibilité mobile
$keyOperateur = array(
'orangekey' => '2CSLsG7IDYHmR8Wp',
'orangesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'orangedebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/Bill/orange/webpayment',
'orangecode' => 'MYHELPPLACE_ORANGE',
'mtnkey' => '2CSLsG7IDYHmR8Wp',
'mtnsecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'mtndebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/Bill/debit/openapi',
'mtncode' => 'MYHELPPLACE_MTNCI',
'moovkey' => 'GX7fNUtKpzbaZbrM',
'moovsecret' => '2OZpbuvcRCDNapJrsVkHTE6gqocNdeN1DxIQIlEpBZjbR6GuQaBYkbim05fDbK1i',
'moovdebiturl' =>'https://billpayment.api.prod.run.billmap.net/api/Bill/debit/moov',
'moovcode' => 'MYHELPPLACE_MOOVCI',
'wavekey' => '2CSLsG7IDYHmR8Wp',
'wavesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'wavedebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/bill/wave/checkout',
'wavecode' => 'MYHELPPLACE_WAVE'
);
if('POST' == $request->getMethod()){
$formData = json_decode($request->getContent(), true);
if (!is_array($formData)) {
$formData = $request->request->all();
}
$getInput = static function (array $data, string $camel, ?string $snake = null) {
return $data[$camel] ?? ($snake !== null ? ($data[$snake] ?? null) : null);
};
if($plateforme == 'web'){
$operateurPaiement = strtolower(trim((string)$request->request->get('operateurPaiement')));
$numeroDeTelephone = $request->request->get('numeroDeTelephone');
$indicatifPays = $request->request->get('indicatifPays');
$montantTransaction = (float)$request->request->get('montant');
$key = $request->request->get($operateurPaiement.'key');
$secret = $request->request->get($operateurPaiement.'secret');
$debitURL = $request->request->get($operateurPaiement.'debiturl');
$code = $request->request->get($operateurPaiement.'code');
$otp = $request->request->get('otp');
}else{
// mobile
$operateurPaiement = strtolower(trim((string)$getInput($formData, 'operateurPaiement', 'operateur_paiement')));
$numeroDeTelephone = $getInput($formData, 'numeroDeTelephone', 'numero_de_telephone');
$indicatifPays = $getInput($formData, 'indicatifPays', 'indicatif_pays');
$montantTransaction = (float)$getInput($formData, 'montant');
$key = $keyOperateur[$operateurPaiement.'key'] ?? null;
$secret = $keyOperateur[$operateurPaiement.'secret'] ?? null;
$debitURL = $keyOperateur[$operateurPaiement.'debiturl'] ?? null;
$code = $keyOperateur[$operateurPaiement.'code'] ?? null;
$otp = $getInput($formData, 'otp');
}
$id_transaction = '';
$paiementService = new PaiementService;
$paiementService->setMontant($montantTransaction);
$paiementService->setUser($user);
$requestedPaymentType = strtoupper(trim((string)$typeService));
if ($requestedPaymentType === 'PRESTATION') {
$paymentType = 'PRESTATION';
} elseif (in_array($requestedPaymentType, ['DISCUTER', 'DISCUSSION'], true)) {
$paymentType = 'DISCUSSION';
} elseif (in_array($requestedPaymentType, ['DEVIS', 'DEMANDE_DEVIS', 'DEMANDE-DE-DEVIS'], true)) {
$paymentType = 'DEVIS';
} else {
$paymentType = 'SERVICE';
}
$idOffreDevis = $plateforme === 'web'
? $request->request->get('idOffreDevis')
: ($getInput($formData, 'idOffreDevis', 'id_offre_devis')
?? $getInput($formData, 'offreDevisId', 'offre_devis_id'));
$offreDevis = $idOffreDevis
? $em->getRepository(OffreDevis::class)->findOneById($idOffreDevis)
: null;
if ($offreDevis) {
$clientDuDevis = $offreDevis->getDevis() ? $offreDevis->getDevis()->getUser() : null;
if (!$clientDuDevis || (int) $clientDuDevis->getId() !== (int) $user->getId()) {
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'Accès refusé',
'message' => 'Ce devis appartient à un autre compte client.',
], Response::HTTP_FORBIDDEN));
}
$paymentType = 'PRESTATION';
$montantTransaction = $this->montantFinalOffre($offreDevis);
}
if (in_array($paymentType, ['DISCUSSION', 'DEVIS'], true)
&& $this->paiementFraisDuJourExiste($em, $user, $paymentType)) {
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'Frais déjà demandés',
'message' => 'Les frais de ' . strtolower($paymentType) . ' ne peuvent être demandés qu’une seule fois par jour.',
'typeService' => $paymentType,
], Response::HTTP_CONFLICT));
}
if (!$operateurPaiement || $montantTransaction <= 0 || !isset($keyOperateur[$operateurPaiement.'key'])) {
return $this->allowRemoteRequest(new JsonResponse(['error' => 'Invalid payment parameters'], Response::HTTP_BAD_REQUEST));
}
// Le montant final vient toujours de l'offre pour une prestation.
$paiementService->setMontant($montantTransaction);
$paiementService->setTypeService($paymentType);
$paiementService->setLaDate(new \DateTime());
if ($paymentType === 'PRESTATION') {
if (!$offreDevis || !$offreDevis->getBoutique()) {
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'idOffreDevis obligatoire pour un paiement de prestation',
], Response::HTTP_BAD_REQUEST));
}
$paiementService->setOffreDevis($offreDevis);
}
//transaction id
// Payment description
$description_du_paiement = "Devenir premium";
// Payment Date must be on date format
$date_transaction = date("Y-m-d H:i:s");
// Amount
$montant_a_payer = $montantTransaction;
//$commande->setCpmTransId($id_transaction);
//$commande->setCpmAmount($montant_a_payer);
//$commande->setUser($user);
$em->persist($paiementService);
$em->flush();
$laReference = $paiementService->getId() . '@' . $paymentType;
if ($paymentType === 'PRESTATION' && $offreDevis) {
$laReference .= '@' . $offreDevis->getId();
}
// notify url
$returnUrls = $this->getJekoReturnUrls($laReference, $plateforme);
$successUrl = $returnUrls['successUrl'];
// return url
$errorUrl = $returnUrls['errorUrl'];
$ch = curl_init($this->getJekoBaseUrl().'/payment_requests');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-KEY:'. $this->getJekoAPI(),
'X-API-KEY-ID:'.$this->getJekoAPIID(),
'Content-Type: application/json'
]);
$amountCents = (int) round((float) $montantTransaction * 100);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'storeId' => $this->getJekoStoreId(),
'amountCents' => $amountCents,
'currency' => 'XOF',
'reference' =>$laReference,
'paymentDetails' => [
'type' => 'redirect',
'data' => [
'paymentMethod' => $operateurPaiement,
'successUrl' => $successUrl,
'errorUrl' => $errorUrl
]
]
]));
$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$paymentRequest = json_decode($response, true);
if ($response === false || $curlError || !is_array($paymentRequest) || empty($paymentRequest['redirectUrl'])) {
$providerMessage = is_array($paymentRequest)
? ($paymentRequest['message'] ?? $paymentRequest['error'] ?? 'Jèko n’a pas fourni de redirectUrl')
: 'Réponse Jèko invalide';
$providerResponse = is_array($paymentRequest) ? $paymentRequest : ['raw' => substr((string)$response, 0, 2000)];
$providerDetails = $providerResponse['extras'] ?? $providerResponse['errors'] ?? $providerResponse['errorReason'] ?? null;
if ($providerDetails !== null) {
$providerMessage .= ' - ' . (is_string($providerDetails) ? $providerDetails : json_encode($providerDetails));
}
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'Unable to create Jèko payment',
'message' => $providerMessage,
'details' => $providerDetails,
'providerResponse' => $providerResponse,
'providerStatus' => $httpCode,
], Response::HTTP_BAD_GATEWAY));
}
$paiementService->setCpmTransId($paymentRequest['id'] ?? $id_transaction);
$em->flush();
if($plateforme == 'web'){
return $this->redirect($paymentRequest['redirectUrl']);
}else{
$data = array(
'error' => '',
'message' => '',
'redirectUrl' => $paymentRequest['redirectUrl'],
'successUrl' => $successUrl,
'errorUrl' => $errorUrl,
'reference' => $laReference,
'paymentReference' => $laReference
);
$response = new JsonResponse;
$response = $this->allowRemoteRequest($response);
$response->setContent(json_encode($data));
return $response;
}
$em->flush();
//return $this->redirectToRoute('gestion_finaliser_abonnement', array('slug' => $boutique->getSlug(), 'plan' => $plan->getSlug()));
}
if($plateforme == 'mobile'){
return $this->render('api/paiement_service.html.twig', [
]);
}
return $this->render('gestion/paiement_abonnement.html.twig', [
'user' => $user,
'transId' => $transId,
'transDate' => $transDate,
'amount' => $amount,
'boutique' => $boutique,
'userBoutique' => $userBoutique,
'transaction' => $id_transaction
]);
if($request->getMethod() == 'POST'){
$formData = json_decode($request->getContent());
$dateDebut = $formData->dateDebut;
$dateDebut = new \DateTime($dateDebut);
$dateDebut->setTime(0, 0);
$dateFin = $formData->dateFin;
//$dateFin = $request->request->get('dateFin');
$dateFin = new \DateTime($dateFin);
$dateFin->setTime(23, 59);
$demandeRetraits = $em->getRepository(DemandeRetrait::class)
->demandeDUnePeriode($boutique->getId(), $dateDebut, $dateFin, true);
}
$data = array(
'error' => '',
'message' => '',
'demandeRetraits' => $demandeRetraits
);
$response = new JsonResponse;
$response = $this->allowRemoteRequest($response);
$response->setContent(json_encode($data));
return $response;
return $this->render('api/envoyer_push_notification.html.twig', [
]);
}
/**
* @Route("/paiement-prestation/{idUser}/{idOffreDevis}/{plateforme}", name="gestion_paiement_de_prestation")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function paiementPrestationAction(Request $request, $idUser, $idOffreDevis, $plateforme)
{
$em = $this->getDoctrine()
->getManager();
$user = $em->getRepository(AppUser::class)
->findOneById($idUser);
if(null == $user){
return $this->createNotFoundException();
}
$offreDevis = $em->getRepository(OffreDevis::class)
->findOneById($idOffreDevis);
if (!$offreDevis || !$offreDevis->getBoutique()) {
return new JsonResponse(['error' => 'Offre de devis introuvable ou sans prestataire'], Response::HTTP_BAD_REQUEST);
}
$clientDuDevis = $offreDevis->getDevis() ? $offreDevis->getDevis()->getUser() : null;
if (!$clientDuDevis || (int) $clientDuDevis->getId() !== (int) $user->getId()) {
return new JsonResponse([
'error' => 'Accès refusé',
'message' => 'Ce devis appartient à un autre compte client.',
], Response::HTTP_FORBIDDEN);
}
$transId = rand(4560, 100000);
$transDate = new \DateTime();
$amount = 15000;
$message = '';
$error = '';
$qrUrl = '';
$authenticationUrl = 'https://billpayment.api.prod.run.billmap.net/api/authentication/token';
$id_transaction = '';
$apiKeyAndSecret = array(
'orangekey' => '2CSLsG7IDYHmR8Wp',
'orangesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'mtnkey' => '3AXHb9Q0ctzSJqbV',
'mtnsecret' => 'oDXwYw4XwervcUHof7u7M0tRfRuAYf9aGmgOWYF9y1aMjkWX3lZx312X5ge8L8I9',
'moovkey' => 'GX7fNUtKpzbaZbrM',
'moovesecret' => '2OZpbuvcRCDNapJrsVkHTE6gqocNdeN1DxIQIlEpBZjbR6GuQaBYkbim05fDbK1i',
'wavekey' => '2CSLsG7IDYHmR8Wp',
'wavesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF'
);
//variable mise pour la compatibilité mobile
$keyOperateur = array(
'orangekey' => '2CSLsG7IDYHmR8Wp',
'orangesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'orangedebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/Bill/orange/webpayment',
'orangecode' => 'MYHELPPLACE_ORANGE',
'mtnkey' => '2CSLsG7IDYHmR8Wp',
'mtnsecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'mtndebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/Bill/debit/openapi',
'mtncode' => 'MYHELPPLACE_MTNCI',
'moovkey' => 'GX7fNUtKpzbaZbrM',
'moovsecret' => '2OZpbuvcRCDNapJrsVkHTE6gqocNdeN1DxIQIlEpBZjbR6GuQaBYkbim05fDbK1i',
'moovdebiturl' =>'https://billpayment.api.prod.run.billmap.net/api/Bill/debit/moov',
'moovcode' => 'MYHELPPLACE_MOOVCI',
'wavekey' => '2CSLsG7IDYHmR8Wp',
'wavesecret' => '3puc5vA7cySjUGoCkAJZ5nzIesMg0uWdLVsNchbvClZz8ZqtBwzz1bdOMSxnWEzF',
'wavedebiturl' => 'https://billpayment.api.prod.run.billmap.net/api/bill/wave/checkout',
'wavecode' => 'MYHELPPLACE_WAVE'
);
if('POST' == $request->getMethod()){
$formData = json_decode($request->getContent());
if($plateforme == 'web'){
$operateurPaiement = $request->request->get('operateurPaiement');
$numeroDeTelephone = $request->request->get('numeroDeTelephone');
$indicatifPays = $request->request->get('indicatifPays');
$montantTransaction = $this->montantFinalOffre($offreDevis);
$otp = $request->request->get('otp');
}else{
// mobile
$operateurPaiement = $formData->operateurPaiement;
$numeroDeTelephone = $formData->numeroDeTelephone;
$indicatifPays = $formData->indicatifPays;
$montantTransaction = $this->montantFinalOffre($offreDevis);
$otp = $formData->otp;
}
$id_transaction = '';
if ($montantTransaction <= 0) {
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'Montant de prestation invalide',
'message' => 'Cette offre de devis ne possède pas de montant payable.',
], Response::HTTP_BAD_REQUEST));
}
$paiementService = new PaiementService;
$paiementService->setMontant($montantTransaction);
$paiementService->setUser($user);
$paiementService->setTypeService('PRESTATION');
$paiementService->setLaDate(new \DateTime());
$paiementService->setOffreDevis($offreDevis);
//transaction id
// Payment description
$description_du_paiement = "Devenir premium";
// Payment Date must be on date format
$date_transaction = date("Y-m-d H:i:s");
// Amount
$montant_a_payer = $montantTransaction;
//$commande->setCpmTransId($id_transaction);
//$commande->setCpmAmount($montant_a_payer);
//$commande->setUser($user);
$em->persist($paiementService);
$em->flush();
//Ici debute le paiement par Jeko
$laReference = $paiementService->getId() . '@PRESTATION@' . $offreDevis->getId();
// notify url
$returnUrls = $this->getJekoReturnUrls($laReference, $plateforme);
$successUrl = $returnUrls['successUrl'];
// return url
$errorUrl = $returnUrls['errorUrl'];
$ch = curl_init($this->getJekoBaseUrl().'/payment_requests');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-KEY:'. $this->getJekoAPI(),
'X-API-KEY-ID:'.$this->getJekoAPIID(),
'Content-Type: application/json'
]);
$amountCents = (int) round($montantTransaction * 100);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'storeId' => $this->getJekoStoreId(),
'amountCents' => $amountCents,
'currency' => 'XOF',
'reference' =>$laReference,
'paymentDetails' => [
'type' => 'redirect',
'data' => [
'paymentMethod' => $operateurPaiement,
'successUrl' => $successUrl,
'errorUrl' => $errorUrl
]
]
]));
$response = curl_exec($ch);
$curlError = curl_error($ch);
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$paymentRequest = json_decode($response, true);
if ($response === false || $curlError || !is_array($paymentRequest) || empty($paymentRequest['redirectUrl'])) {
$providerMessage = is_array($paymentRequest)
? ($paymentRequest['message'] ?? $paymentRequest['error'] ?? 'Jèko n’a pas fourni de redirectUrl')
: 'Réponse Jèko invalide';
$providerResponse = is_array($paymentRequest) ? $paymentRequest : ['raw' => substr((string)$response, 0, 2000)];
$providerDetails = $providerResponse['extras'] ?? $providerResponse['errors'] ?? $providerResponse['errorReason'] ?? null;
if ($providerDetails !== null) {
$providerMessage .= ' - ' . (is_string($providerDetails) ? $providerDetails : json_encode($providerDetails));
}
return $this->allowRemoteRequest(new JsonResponse([
'error' => 'Unable to create Jèko payment',
'message' => $providerMessage,
'details' => $providerDetails,
'providerResponse' => $providerResponse,
'providerStatus' => $httpCode,
], Response::HTTP_BAD_GATEWAY));
}
$paiementService->setCpmTransId($paymentRequest['id'] ?? $id_transaction);
$em->flush();
if($plateforme == 'web'){
return $this->redirect($paymentRequest['redirectUrl']);
}else{
$data = array(
'error' => '',
'message' => '',
'redirectUrl' => $paymentRequest['redirectUrl'],
'successUrl' => $successUrl,
'errorUrl' => $errorUrl,
'reference' => $laReference,
'paymentReference' => $laReference
);
$response = new JsonResponse;
$response = $this->allowRemoteRequest($response);
$response->setContent(json_encode($data));
return $response;
}
//là se termine le paiement par Jeko
$em->flush();
}
if($plateforme == 'mobile'){
return $this->render('api/paiement_prestation.html.twig', [
]);
}
return $this->render('gestion/paiement_prestation.html.twig', [
]);
if($request->getMethod() == 'POST'){
$formData = json_decode($request->getContent());
$dateDebut = $formData->dateDebut;
$dateDebut = new \DateTime($dateDebut);
$dateDebut->setTime(0, 0);
$dateFin = $formData->dateFin;
//$dateFin = $request->request->get('dateFin');
$dateFin = new \DateTime($dateFin);
$dateFin->setTime(23, 59);
$demandeRetraits = $em->getRepository(DemandeRetrait::class)
->demandeDUnePeriode($boutique->getId(), $dateDebut, $dateFin, true);
}
$data = array(
'error' => '',
'message' => '',
'demandeRetraits' => $demandeRetraits
);
$response = new JsonResponse;
$response = $this->allowRemoteRequest($response);
$response->setContent(json_encode($data));
return $response;
return $this->render('api/envoyer_push_notification.html.twig', [
]);
}
/**
* @Route("/finaliser-abonnement/{slug}/{plan}", name="gestion_finaliser_abonnement")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function finaliserAction(Request $request, Boutique $boutique, $plan)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
$plan = $em->getRepository(Plan::class)
->findOneBySlug($plan);
return $this->render('gestion/finaliser_abonnement.html.twig', [
'user' => $user,
'boutique' => $boutique,
'userBoutique' => $userBoutique,
'plan' => $plan
]);
}
/**
* @Route("/payment/billmap-return", name="gestion_billmap_return")
*/
public function billmapReturnAction(Request $request)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
/*
https://www.myhelpplace.com/fr/gestion/payment/billmap-return?
// Reference=66&
// Amount=100.00&
// MSISDN=2250574053562&
// NetworkMSISDN=2250574053562&
// BillMapTransactionId=T4398121&
// ExternalTransactionId=16464863303&
// ResponseCode=01&
// ResponseMessage=Successfully
*/
$reference = $request->query->get('Reference'); //Ici, c'est bien la valeur qui avait été passé dans "metaData" lors de la requete
$montant = $request->query->get('Amount');
$mois = $request->query->get('MetaData');
$numeroDeTelephone = $request->query->get('MSISDN');
$billMapTransactionId = $request->query->get('BillMapTransactionId');
$responseMessage = $request->query->get('ResponseMessage');
$responseCode = $request->query->get('ResponseCode');
if($responseCode == '01'){
$reference = explode('@', $reference); //'metaData' => ''.$paiementService->getId().'@SERVICE',
if($reference[1] == 'ABONNEMENT'){
//Si la transaction a marché
$commande = $em->getRepository(Commande::class)
->findOneByCpmTransId($billMapTransactionId);
$dateFin = new \DateTime();
//
$dateFin->modify('+'.$mois .' month');
$commande->setDateFin($dateFin);
$em->flush();
}
if($reference[1] == 'SERVICE'){
//Si la transaction a marché
$paiementService = $em->getRepository(PaiementService::class)
->findOneByCpmTransId($billMapTransactionId);
$paiementService->setEtat('VALIDE');
$em->flush();
}
if($reference[1] == 'PRESTATION'){
// Le lien paiement -> offre permet d'identifier le prestataire.
// La référence contient l'id du paiement, pas "PRESTATION".
$paiementService = $em->getRepository(PaiementService::class)
->findOneById($reference[0]);
if ($paiementService) {
$previousStatus = $paiementService->getEtat();
$paiementService->setEtat('VALIDE');
$offreDevis = $paiementService->getOffreDevis();
$boutique = $offreDevis ? $offreDevis->getBoutique() : null;
if ($boutique && $previousStatus !== 'VALIDE') {
$boutique->setSolde((float) ($boutique->getSolde() ?? 0) + ((float) $paiementService->getMontant() * 90 / 100));
}
$paiementService->setCpmTransId($billMapTransactionId);
$em->flush();
}
}
}
$response = new JsonResponse;
$data = array(
'mois' => 'Mois souscrit '. $mois,
'montant' => 'Montant ' . $montant,
'bonjour' => 'Bonjour tout le monde'
);
$response = $this->allowRemoteRequest($response);
$response->setContent(json_encode($data));
return $response;
return $this->render('gestion/billmap_return.html.twig', [
'user' => $user
]);
}
/**
* Jèko is the source of truth for payment state. The return URLs only
* notify the client that it can ask this backend for the current state.
*
* @Route("/payment/jeko-webhook", name="gestion_jeko_webhook", methods={"POST"})
*/
public function jekoWebhookAction(Request $request)
{
$rawPayload = $request->getContent();
$signature = $request->headers->get('Jeko-Signature');
$secret = $this->getJekoWebhookSecret();
if (!$secret || !$signature || !hash_equals(hash_hmac('sha256', $rawPayload, $secret), $signature)) {
return new Response('Invalid webhook signature', Response::HTTP_UNAUTHORIZED);
}
$payload = json_decode($rawPayload, true);
if (!is_array($payload)) {
return new Response('Invalid webhook payload', Response::HTTP_BAD_REQUEST);
}
// Selon le produit/version Jèko, la référence peut être dans
// transactionDetails ou directement à la racine du payload.
$reference = $payload['transactionDetails']['reference'] ?? ($payload['reference'] ?? null);
$rawStatus = strtolower((string)($payload['status'] ?? ''));
$statusMap = [
'success' => 'success',
'completed' => 'success',
'paid' => 'success',
'pending' => 'pending',
'processing' => 'pending',
'error' => 'error',
'failed' => 'error',
];
$status = $statusMap[$rawStatus] ?? null;
if (($payload['transactionType'] ?? null) !== 'PaymentRequest' || !$reference || $status === null) {
return new Response('Invalid webhook payload', Response::HTTP_BAD_REQUEST);
}
$parts = explode('@', $reference);
$paymentId = $parts[0] ?? null;
$paymentType = $parts[1] ?? null;
$em = $this->getDoctrine()->getManager();
if ($paymentType === 'ABONNEMENT') {
$commande = $em->getRepository(Commande::class)->findOneById($paymentId);
if (!$commande) {
return new Response('Unknown payment reference', Response::HTTP_NOT_FOUND);
}
// Jèko may retry a webhook. Do not extend an abonnement twice.
if ($commande->getCpmTransStatus() !== 'success' && $status === 'success') {
$months = (int)($parts[2] ?? 0);
$dateFin = new \DateTime();
$dateFin->modify('+' . $months . ' month');
$commande->setDateFin($dateFin);
}
$commande->setCpmTransStatus($status);
$em->flush();
} elseif (in_array($paymentType, ['SERVICE', 'DISCUSSION', 'DEVIS', 'PRESTATION'], true)) {
$paiementService = $em->getRepository(PaiementService::class)->findOneById($paymentId);
if (!$paiementService) {
return new Response('Unknown payment reference', Response::HTTP_NOT_FOUND);
}
$this->rattacherOffreDepuisReference($paiementService, $parts, $em);
// Keep the status vocabulary already used by PaiementService.
$previousStatus = $paiementService->getEtat();
$paiementService->setEtat($status === 'success' ? 'VALIDE' : ($status === 'error' ? 'ECHEC' : 'EN ATTENTE'));
// Credit the provider only once, after Jeko confirms the prestation payment.
if ($paymentType === 'PRESTATION' && $status === 'success' && $previousStatus !== 'VALIDE') {
$offreDevis = $paiementService->getOffreDevis();
$boutique = $offreDevis ? $offreDevis->getBoutique() : null;
if ($boutique) {
$boutique->setSolde((float) ($boutique->getSolde() ?? 0) + ((float) $paiementService->getMontant() * 90 / 100));
}
}
$transactionDetails = is_array($payload['transactionDetails'] ?? null) ? $payload['transactionDetails'] : [];
$transaction = is_array($payload['transaction'] ?? null) ? $payload['transaction'] : [];
$transactionId = $transactionDetails['id']
?? ($transaction['id'] ?? ($payload['transactionId'] ?? ($payload['id'] ?? $paiementService->getCpmTransId())));
$paiementService->setCpmTransId((string)$transactionId);
$em->flush();
if ($paymentType === 'PRESTATION' && $status === 'success' && $previousStatus !== 'VALIDE') {
$this->notifierPaiementValide($paiementService);
}
} else {
return new Response('Unknown payment reference', Response::HTTP_NOT_FOUND);
}
// Jèko considère uniquement HTTP 200 comme accusé de réception.
return new Response('', Response::HTTP_OK);
}
/**
* @Route("/payment/status/{reference}", name="gestion_payment_status", requirements={"reference"=".+"}, methods={"GET"})
*/
public function paymentStatusAction(Request $request, $reference)
{
$user = $this->getUser();
$reference = rawurldecode($reference);
$parts = explode('@', $reference);
$paymentId = $parts[0] ?? null;
$paymentType = $parts[1] ?? null;
$em = $this->getDoctrine()->getManager();
$status = null;
$owner = null;
$payment = null;
if ($paymentType === 'ABONNEMENT') {
$payment = $em->getRepository(Commande::class)->findOneById($paymentId);
if ($payment) {
$status = $payment->getCpmTransStatus();
$owner = $payment->getUser();
}
} elseif (in_array($paymentType, ['SERVICE', 'DISCUSSION', 'DEVIS', 'PRESTATION'], true)) {
$payment = $em->getRepository(PaiementService::class)->findOneById($paymentId);
if ($payment) {
$status = $payment->getEtat();
$owner = $payment->getUser();
$this->rattacherOffreDepuisReference($payment, $parts, $em);
}
}
if (!$payment) {
return new JsonResponse(['error' => 'Payment not found'], Response::HTTP_NOT_FOUND);
}
// Fallback recommandé par Jèko : si la webhook est retardée, confirmer
// le statut directement auprès de l’API avec l’ID de la demande.
$providerId = $payment instanceof Commande ? $payment->getCpmTransId() : $payment->getCpmTransId();
if ($providerId && in_array($status, [null, 'EN ATTENTE'], true)) {
$providerPayment = $this->getJekoPaymentRequest($providerId);
$providerStatus = strtolower((string)($providerPayment['status'] ?? ''));
$providerStatusMap = [
'success' => 'success',
'completed' => 'success',
'paid' => 'success',
'pending' => 'pending',
'processing' => 'pending',
'error' => 'error',
'failed' => 'error',
];
$confirmedStatus = $providerStatusMap[$providerStatus] ?? null;
if ($confirmedStatus !== null) {
$status = $confirmedStatus;
if ($paymentType === 'ABONNEMENT') {
$payment->setCpmTransStatus($confirmedStatus);
$months = (int)($parts[2] ?? 0);
if ($confirmedStatus === 'success' && $months > 0 && !$payment->getDateFin()) {
$dateFin = new \DateTime();
$dateFin->modify('+' . $months . ' month');
$payment->setDateFin($dateFin);
}
} else {
$previousStatus = $payment->getEtat();
$payment->setEtat($confirmedStatus === 'success' ? 'VALIDE' : ($confirmedStatus === 'error' ? 'ECHEC' : 'EN ATTENTE'));
if ($paymentType === 'PRESTATION' && $confirmedStatus === 'success' && $previousStatus !== 'VALIDE') {
$offreDevis = $payment->getOffreDevis();
$boutique = $offreDevis ? $offreDevis->getBoutique() : null;
if ($boutique) {
$boutique->setSolde((float) ($boutique->getSolde() ?? 0) + ((float) $payment->getMontant() * 90 / 100));
}
}
$payment->setCpmTransId($providerId);
}
$em->flush();
if ($paymentType === 'PRESTATION' && $confirmedStatus === 'success' && $previousStatus !== 'VALIDE') {
$this->notifierPaiementValide($payment);
}
}
}
// Le mobile consulte le statut après le retour Jèko et ne possède pas
// forcément une session Symfony. Une session connectée reste contrôlée.
if ($user && $owner && $owner->getId() !== $user->getId()) {
return new JsonResponse(['error' => 'Payment not found'], Response::HTTP_NOT_FOUND);
}
$jekoStatus = $status;
if ($status === 'VALIDE') {
$jekoStatus = 'success';
} elseif ($status === 'ECHEC') {
$jekoStatus = 'error';
} elseif ($status === 'EN ATTENTE') {
$jekoStatus = 'pending';
}
$response = new JsonResponse([
'reference' => $reference,
'paymentReference' => $reference,
'status' => $status,
'localStatus' => $status,
'jekoStatus' => $jekoStatus,
'success' => $jekoStatus === 'success',
]);
return $this->allowRemoteRequest($response);
}
/**
* Enregistre les notifications après confirmation Jèko. Le subscriber
* Doctrine se charge ensuite de l'envoi push Firebase.
* Cette méthode n'est appelée que lors du passage vers VALIDE, donc les
* webhooks répétés ne créent pas de doublons.
*/
private function rattacherOffreDepuisReference(PaiementService $paiement, array $parts, $em): void
{
if ($paiement->getOffreDevis() || empty($parts[2]) || !ctype_digit((string) $parts[2])) {
return;
}
$offreDevis = $em->getRepository(OffreDevis::class)->findOneById((int) $parts[2]);
if ($offreDevis && $offreDevis->getBoutique()) {
$paiement->setOffreDevis($offreDevis);
$em->persist($paiement);
$em->flush();
}
}
private function notifierPaiementValide(PaiementService $paiement): void
{
$offreDevis = $paiement->getOffreDevis();
$prestataire = $offreDevis ? $offreDevis->getUser() : null;
// Certaines anciennes offres n'ont pas de user renseigné alors que la
// boutique possède bien un propriétaire dans UserBoutique. Dans ce cas,
// la notification doit quand même parvenir au prestataire.
if (!$prestataire && $offreDevis && $offreDevis->getBoutique()) {
$prestataire = $this->getDoctrine()->getManager()
->getRepository(UserBoutique::class)
->findOneBy(['boutique' => $offreDevis->getBoutique()]);
$prestataire = $prestataire ? $prestataire->getUser() : null;
}
$client = $paiement->getUser();
$destinataires = [];
if ($prestataire) {
$destinataires[$prestataire->getId()] = [
'user' => $prestataire,
'title' => 'Paiement reçu',
'body' => 'Le paiement de votre prestation a été confirmé.',
];
}
if ($client && !isset($destinataires[$client->getId()])) {
$destinataires[$client->getId()] = [
'user' => $client,
'title' => 'Paiement confirmé',
'body' => 'Votre paiement de ' . (float) $paiement->getMontant() . ' XOF a été confirmé.',
];
}
$em = $this->getDoctrine()->getManager();
foreach ($destinataires as $destinataire) {
$notification = new Notification();
$notification->setUser($destinataire['user']);
$notification->setTypeNotification('paiement');
$notification->setDescription($destinataire['body']);
$notification->setExtraData((string) $paiement->getId());
$notification->setLien('');
$em->persist($notification);
}
$em->flush();
}
private function montantFinalOffre(OffreDevis $offreDevis): float
{
$valeurFinanciere = $offreDevis->getValeurFinanciere();
if ($valeurFinanciere !== null && (float) $valeurFinanciere > 0) {
return (float) $valeurFinanciere;
}
$contreProposition = $offreDevis->getContrePropositionFinanciere();
if ($contreProposition !== null && (float) $contreProposition > 0) {
return (float) $contreProposition;
}
// Les anciennes demandes directes vers une boutique créaient une offre
// sans montant propre, alors que leur devis possède un budget fixe.
// Ce fallback est volontairement limité au cas min == max afin de ne
// jamais transformer une fourchette budgétaire en montant payable.
$devis = $offreDevis->getDevis();
if ($devis) {
$budgetMin = is_numeric($devis->getBudgetMin()) ? (float) $devis->getBudgetMin() : 0.0;
$budgetMax = is_numeric($devis->getBudgetMax()) ? (float) $devis->getBudgetMax() : 0.0;
if ($budgetMin > 0 && $budgetMin === $budgetMax) {
return $budgetMin;
}
}
return 0.0;
}
/**
* Retourne vrai si un paiement de frais non échoué existe déjà aujourd'hui.
* Les paiements échoués restent retentables ; les paiements en attente ou
* validés réservent le droit quotidien.
*/
private function paiementFraisDuJourExiste($em, AppUser $user, string $paymentType): bool
{
$debut = new \DateTime('today');
$fin = (clone $debut)->modify('+1 day');
$paiement = $em->getRepository(PaiementService::class)
->createQueryBuilder('p')
->where('p.user = :user')
->andWhere('p.typeService = :typeService')
->andWhere('p.laDate >= :debut')
->andWhere('p.laDate < :fin')
->andWhere('p.etat IN (:etats)')
->setParameter('user', $user)
->setParameter('typeService', $paymentType)
->setParameter('debut', $debut)
->setParameter('fin', $fin)
->setParameter('etats', ['EN ATTENTE', 'VALIDE'])
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
return $paiement !== null;
}
private function getJekoPaymentRequest($paymentRequestId)
{
$ch = curl_init($this->getJekoBaseUrl() . '/payment_requests/' . rawurlencode($paymentRequestId));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-KEY:' . $this->getJekoAPI(),
'X-API-KEY-ID:' . $this->getJekoAPIID(),
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$body = curl_exec($ch);
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($body === false || $httpCode < 200 || $httpCode >= 300) {
return null;
}
$data = json_decode($body, true);
return is_array($data) ? $data : null;
}
private function getJekoReturnUrls($reference, $plateforme)
{
if ($plateforme === 'mobile') {
return [
// Jèko exige une URL HTTP(S) valide dans la requête. Ces routes
// redirigeront ensuite vers le deep link de l’application mobile.
'successUrl' => $this->generateUrl('gestion_payment_success', ['transaction' => $reference, 'plateforme' => 'mobile'], UrlGeneratorInterface::ABSOLUTE_URL),
'errorUrl' => $this->generateUrl('gestion_payment_error', ['transaction' => $reference, 'plateforme' => 'mobile'], UrlGeneratorInterface::ABSOLUTE_URL),
];
}
return [
'successUrl' => $this->generateUrl('gestion_payment_success', ['transaction' => $reference, 'plateforme' => 'web'], UrlGeneratorInterface::ABSOLUTE_URL),
'errorUrl' => $this->generateUrl('gestion_payment_error', ['transaction' => $reference, 'plateforme' => 'web'], UrlGeneratorInterface::ABSOLUTE_URL),
];
}
private function getJekoWebhookSecret()
{
return $_ENV['JEKO_WEBHOOK_SECRET'] ?? getenv('JEKO_WEBHOOK_SECRET') ?: null;
}
/**
* @Route("/payment/success/{transaction}/{plateforme}", name="gestion_payment_success")
*/
public function paymentSuccessAction(Request $request, $transaction, $plateforme)
{
$user = $this->getUser();
if($plateforme == 'web'){
return $this->render('entreprise/payment_success.html.twig', ['user' => $user]);
}else{
return new RedirectResponse('helpplace://payment/success?reference=' . rawurlencode($transaction));
}
return new Response('', Response::HTTP_NO_CONTENT);
}
/**
* @Route("/payment/error/{transaction}/{plateforme}", name="gestion_payment_error")
*/
public function paymentErrorAction(Request $request, $transaction, $plateforme)
{
if($plateforme == 'web'){
return $this->render('entreprise/payment_error.html.twig', []);
}else{
return new RedirectResponse('helpplace://payment/error?reference=' . rawurlencode($transaction));
}
return new Response('', Response::HTTP_NO_CONTENT);
}
/**
* @Route("/payment/cancel/{transaction}", name="gestion_payment_cancel")
*/
public function paymentCancelction($transaction)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$requestStack = $this->get('request_stack');
$request = $requestStack->getCurrentRequest();
$ok_submission = '';
$error = '';
$commande = $em->getRePository(Commande::class)
->findOneByCpmTransId($transaction);
$messageRequete = '';
if('POST' == $request->getMethod()){
var_dump($_POST);
}
return $this->render('gestion/cinetpay_cancel.html.twig', [
'user' => $user
]);
}
/**
* @Route("/shop/{slug}/mini-statistic", name="gestion.mini_statistic")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function miniStatisticAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$mois = date('mY');
$annee = date('Y');
$nombreOffreDevis = $em->getRepository(OffreDevis::class)
->nombreOffreDevis($boutique->getId());
$conversations = $em->getRepository(Conversation::class)
->nombreMessageDUneBoutique($boutique->getId());
$nombreTiers = 0;
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
return $this->render('gestion/mini_statistic.html.twig', [
'boutique' => $boutique,
'userBoutique' => $userBoutique,
'nombreOffreDevis' => $nombreOffreDevis,
'conversations' => $conversations
]);
}
/**
* @Route("/{slug}/settings", name="gestion.settings")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function settingsAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
$form = $this->createForm(SettingBoutiqueOngletEntrepriseType::class, $boutique);
$aUnAbonnementPayant = false;
$commande = $em->getRepository(Commande::class)
->findOneBy(
array(
'boutique' => $boutique
),
array(
'dateFin' => 'DESC'
)
);
if($commande != null){
$aujourdhui = new \DateTime();
if($commande->getDateFin() > $aujourdhui){
$aUnAbonnementPayant = true;
}
}
if($request->getMethod() == 'POST'){
$form->handleRequest($request);
if($form->isValid()){
$em->flush();
} else {
$error =" Veillez corrigez vos erreurs";
}
}
return $this->render('gestion/settings.html.twig', [
'boutique' => $boutique,
'form' => $form->createView(),
'userBoutique' => $userBoutique,
'aUnAbonnementPayant' => $aUnAbonnementPayant
]);
}
/**
* @Route("/{slug}/edit-entreprise", name="gestion.edit_entreprise")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function editEntrepriseAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
$form = $this->createForm(SettingBoutiqueOngletEntrepriseType::class, $boutique);
$aUnAbonnementPayant = false;
$commande = $em->getRepository(Commande::class)
->findOneBy(
array(
'boutique' => $boutique
),
array(
'dateFin' => 'DESC'
)
);
if($commande != null){
$aujourdhui = new \DateTime();
if($commande->getDateFin() >= $aujourdhui){
$aUnAbonnementPayant = true;
}
}
if($request->getMethod() == 'POST'){
$form->handleRequest($request);
if($form->isValid()){
if(isset($_FILES['logo']) && $_FILES['logo']['error'] == 0 ){
$this->gererFichierMultipleLogo($boutique, $_FILES['logo']);
}
$em->flush();
} else {
$error =" Veillez corrigez vos erreurs";
}
}
return $this->render('gestion/edit_entreprise.html.twig', [
'boutique' => $boutique,
'form' => $form->createView(),
'userBoutique' => $userBoutique,
'aUnAbonnementPayant' => $aUnAbonnementPayant
]);
}
/**
* @Route("/{slug}/products", name="gestion.products")
*/
public function productsAction(Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$products = $em->getRePository(Produit::class)
->findByBoutique($boutique);
return $this->render('gestion/products.html.twig', [
'products' => $products,
'boutique' => $boutique
]);
}
/**
* @Route("/{slug}/new-product", name="gestion.new_product")
*/
public function newProductAction(Boutique $boutique)
{
$requestStack = $this->get('request_stack');
$request = $requestStack->getCurrentRequest();
$em = $this->getDoctrine()
->getManager();
$ok_submission = '';
$error = '';
$produit = new Produit;
$form = $this->createForm(ProduitType::class, $produit);
$user = $this->getUser();
if('POST' == $request->getMethod() )
{
$form->handleRequest($request);
if( $form->isValid()){
$produit->setBoutique($boutique);
if(isset($_FILES['imagePrincipale']) && $_FILES['imagePrincipale']['error'] == 0){
//var_dump($_FILES['fichier']);
$em->persist($produit);
$em->flush();
$this->gererFichierMultiple($produit, $_FILES['imagePrincipale'], true);
$ok_submission = 'Enregistrement effectué';
} else{
$error = 'Veuillez indiquer l\'image principale';
}
if(isset($_FILES['photos']) && $_FILES['photos']['name'][0] != ''){
$this->gererFichierMultiple($produit, $_FILES['photos']);
}
$produit = new Produit;
$form = $this->createForm(ProduitType::class, $produit);
} else {
$form->getErrors(true, false);
$error = 'Une erreur s\'est produite';
}
}
return $this->render('gestion/new_product.html.twig', [
'form' => $form->createView(),
'error' => $error,
'ok_submission' => $ok_submission,
'boutique' => $boutique
]);
}
/**
* @Route("/edit-main-image/{id}", name="gestion.edit_main_image")
*/
public function editMainImageAction(Produit $produit)
{
$requestStack = $this->get('request_stack');
$request = $requestStack->getCurrentRequest();
$em = $this->getDoctrine()
->getManager();
$ok_submission = '';
$error = '';
$user = $this->getUser();
if('POST' == $request->getMethod()){
$image = $produit->getImagePrincipale();
if(isset($_FILES['imagePrincipale']) && $_FILES['imagePrincipale']['error'] == 0){
$data = $_FILES['imagePrincipale'];
$ex = pathinfo($data['name'])['extension'];
$exA = array('jpg', 'jpeg', 'png', 'gif');
if (in_array(strtolower($ex), $exA)){
$image->setExtension($ex);
} else {
throw new \RuntimeException('Désolé, vous n\'avez pas le droit de télécharger ce type de fichier: '.$ex);
}
$name = $produit->getId().time().'.'.$ex;
//Delete old image
unlink($image->getUploadRootDir().'/original/'.$image->getUrl());
if(!is_dir($image->getUploadRootDir().'/original/') && !mkdir($image->getUploadRootDir().'/original', 0777, true))
{
throw new \RuntimeException('Impossible de créer le dossier de destination');
}
if (!move_uploaded_file($data['tmp_name'], $image->getUploadRootDir().'/original/'.$name))
{
throw new \RuntimeException('Désolé, une erreur s\'est produite.');
}
$image->setUrl($name);
} else{
$error = 'Veuillez indiquer l\'image principale';
}
$ok_submission = 'Enregistrement effectué';
$em->flush();
}
return $this->render('gestion/edit_main_image.html.twig', [
'produit' => $produit,
'error' => $error,
'ok_submission' => $ok_submission
]);
}
/**
* @Route("/delete-prodcut-image/{id}/{photo}", name="gestion.delete_product_image")
*/
public function deleteProductImageAction(Produit $produit, $photo)
{
$em = $this->getDoctrine()
->getManager();
$photo = $em->getRePository(ProductPhoto::class)
->findOneById($photo);
$em->remove($photo);
$link = substr($photo->getUrl(), 32); //taille de https://www.lika.ci/uploads/img/
unlink($photo->getUploadRootDir().'/'.$link);
$em->flush();
return $this->redirectToRoute('gestion.products', array('slug' => $produit->getBoutique()->getSlug()));
}
/**
* @Route("/{slug}/add-more-image/{id}", name="gestion.add_more_image")
*/
public function addMoreImageAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$produit = $em->getRepository(Produit::class)
->findOneById($id);
$ok_submission = '';
$error = '';
$user = $this->getUser();
if('POST' == $request->getMethod() )
{
if(isset($_FILES['photos'])){
$this->gererFichierMultiple($produit, $_FILES['photos']);
$ok_submission = 'Enregistrement effectué';
}
}
return $this->render('gestion/add_more_image.html.twig', [
'produit' => $produit,
'error' => $error,
'ok_submission' => $ok_submission,
'boutique' => $boutique
]);
}
/**
* @Route("/edit-product/{boutique}/{produit}", name="gestion.edit_product")
*/
public function editProductAction($boutique, $produit)
{
$requestStack = $this->get('request_stack');
$request = $requestStack->getCurrentRequest();
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneById($boutique);
$produit = $em->getRepository(Produit::class)
->findOneById($produit);
$ok_submission = '';
$error = '';
$form = $this->createForm(ProduitEditType::class, $produit);
$user = $this->getUser();
if('POST' == $request->getMethod())
{
$form->handleRequest($request);
if( $form->isValid()){
$em->flush();
$ok_submission = 'Enregistrement effectué';
if(isset($_FILES['imagePrincipale']) && $_FILES['imagePrincipale']['error'] == 0){
//var_dump($_FILES['fichier']);
$em->persist($produit);
$em->flush();
$this->gererFichierMultiple($produit, $_FILES['imagePrincipale'], true);
$ok_submission = 'Enregistrement effectué';
}
if(isset($_FILES['photos']) && $_FILES['photos']['name'][0] != ''){
$this->gererFichierMultiple($produit, $_FILES['photos']);
}
} else {
$error = 'Une erreur s\'est produite';
}
}
return $this->render('gestion/edit_product.html.twig', [
'form' => $form->createView(),
'error' => $error,
'ok_submission' => $ok_submission,
'boutique' => $boutique
]);
}
//Produit/Service
/**
* @Route("/{slug}/delete-product/{id}", name="gestion.delete_product")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function deleteProductAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$product = $em->getRepository(Produit::class)
->findOneBy(
array(
'boutique' => $boutique->getId(),
'id' => $id
)
);
if(null !== $product){
//les auttres images
foreach($product->getPhotos() as $photo){
$link = substr($photo->getUrl(), 32); //taille de https://www.lika.ci/uploads/img/
unlink($photo->getUploadRootDir().'/'.$link);
$em->remove($photo);
}
$em->flush();
$em->remove($product);
//Image principale
if($product->getImagePrincipale() != null){
$link = substr($product->getImagePrincipale()->getUrl(), 32); //taille de https://www.lika.ci/uploads/img/
unlink($product->getImagePrincipale()->getUploadRootDir().'/'.$link);
$em->remove($product->getImagePrincipale());
}
$em->flush();
}
return $this->redirectToRoute('gestion.products', array('slug' => $boutique->getSlug()));
}
/**
* @Route("/{slug}/proposals", name="gestion.proposals")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function proposalsAction(Request $request, Boutique $boutique)
{
$em = $this->getDoctrine()
->getManager();
$offreDevis = $em->getRepository(OffreDevis::class)
->findByBoutique($boutique);
$devis = $em->getRepository(Devis::class)
->findByCategorieEntreprise($boutique->getCategorieEntreprise());
$user = $this->getUser();
$notifications = $em->getRepository(Notification::class)
->findBy(
array(
'user' => $user,
'vu' => false,
'typeNotification' => 'devis'
)
);
foreach($notifications as $notification){
$notification->setVu(true);
}
$em->flush();
return $this->render('gestion/proposals.html.twig', [
'offreDevis' => $offreDevis,
'devis' => $devis,
'boutique' => $boutique
]);
}
/**
* @Route("/{slug}/my-tickets", name="gestion.my_tickets")
*/
public function ticketsAction(Boutique $boutique)
{
$em = $this->getDoctrine()->getManager();
$tickets = $em->getRepository(Ticket::class)
->findByBoutique($boutique);
return $this->render('gestion/tickets.html.twig', [
'tickets' => $tickets,
'boutique' => $boutique
]);
}
/**
* @Route("/{slug}/new-ticket", name="gestion.new_ticket")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function newTicketAction(Request $request, $slug)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$user = $this->getUser();
$ticket = new Ticket;
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
$form = $this->createForm(TicketType::class, $ticket);
$ok_soumission = '';
$error = '';
if($request->getMethod() == 'POST'){
$form->handleRequest($request);
if($form->isValid()){
$ticket->setBoutique($boutique);
$ticket->setUser($user);
$em->persist($ticket);
$em->flush();
return $this->redirectToRoute('gestion.display_ticket', array('slug' => $boutique->getSlug(), 'id' => $ticket->getId()));
$ok_soumission =" Votre message a été pris en compte";
$ticket = new Ticket;
$form = $this->createForm(TicketType::class, $ticket);
} else {
$error =" Veillez corrigez vos erreurs";
}
}
return $this->render('gestion/new_ticket.html.twig', [
'boutique' => $boutique,
'form' => $form->createView(),
'userBoutique' => $userBoutique
]);
}
/**
* @Route("/{slug}/display-ticket/{id}", name="gestion.display_ticket")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function displayTicketAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$user = $this->getUser();
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $boutique->getId()
)
);
$ticket = $em->getRepository(Ticket::class)
->findOneBy(
array(
'boutique' => $boutique->getId(),
'id' => $id
)
);
$commentaires = $em->getRepository(CommentaireTicket::class)
->findByTicket($ticket->getId());
$commentaireTicket = new CommentaireTicket;
$user = $this->getUser();
$form = $this->createForm(CommentaireTicketType::class, $commentaireTicket);
$ok_soumission = '';
$error = '';
if($request->getMethod() == 'POST'){
$form->handleRequest($request);
if($form->isValid()){
$commentaireTicket->setTicket($ticket);
$commentaireTicket->setUser($user);
$em->persist($commentaireTicket);
$em->flush();
$commentaires = $em->getRepository(CommentaireTicket::class)
->findByTicket($ticket->getId());
$ok_soumission =" Votre message a été pris en compte";
$commentaireTicket = new CommentaireTicket;
$form = $this->createForm(CommentaireTicketType::class, $commentaireTicket);
} else {
$error =" Veillez corrigez vos erreurs";
}
}
return $this->render('gestion/display_ticket.html.twig', [
'boutique' => $boutique,
'ticket' => $ticket,
'form' => $form->createView(),
'commentaires' => $commentaires,
'userBoutique' => $userBoutique
]);
}
/**
* @Route("/{slug}/close-ticket/{id}", name="gestion.close_ticket")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function closeTicketAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$ticket = $em->getRepository(Ticket::class)
->findOneBy(
array(
'boutique' => $boutique->getId(),
'id' => $id
)
);
if(null !== $ticket){
$ticket->setLesCommentairesSontFermes(true);
$em->flush();
}
return $this->redirectToRoute('gestion.display_ticket',array('slug'=>$boutique->getSlug(), 'id'=>$ticket->getId()));
}
/**
* @Route("/{slug}/delete-ticket/{id}", name="gestion.delete_ticket")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function deleteTicketAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$user = $this->getUser();
$ok_soumission = '';
$error = '';
$ticket = $em->getRepository(Ticket::class)
->findOneBy(
array(
'boutique' => $boutique->getId(),
'id' => $id
)
);
if(null !== $ticket){
$commentaireTickets = $em->getRepository(CommentaireTicket::class)
->findByTicket($ticket->getId());
foreach($commentaireTickets as $commentaireTicket)
{
$em->remove($commentaireTicket);
}
$em->remove($ticket);
$em->flush();
}
return $this->redirectToRoute('gestion.tickets',array('slug'=>$boutique->getSlug(), 'id'=>$user->getId()));
}
/**
* @Route("/{slug}/delete-commentaire-ticket/{id}", name="gestion.delete_commentaire_ticket")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function deleteCommentaireTicketAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()
->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$user = $this->getUser();
$commentaireTicket = $em->getRepository(CommentaireTicket::class)
->findOneById($id);
if(null !== $commentaireTicket){
$em->remove($commentaireTicket);
$em->flush();
}
return $this->redirectToRoute('gestion.tickets',array('slug'=>$boutique->getSlug(), 'id'=>$user->getId()));
}
/**
* @Route("/{slug}/reviews", name="gestion.reviews")
*/
public function reviewsAction(Boutique $boutique)
{
$em = $this->getDoctrine()->getManager();
$user = $this->getUser();
$reviews = $em->getRepository(AvisUtilisateur::class)
->findByBoutique($boutique);
$notifications = $em->getRepository(Notification::class)
->findBy(
array(
'user' => $user,
'vu' => false,
'typeNotification' => 'avis'
)
);
foreach($notifications as $notification){
$notification->setVu(true);
}
$em->flush();
return $this->render('gestion/reviews.html.twig', [
'reviews' => $reviews,
'boutique' => $boutique
]);
}
/**
* @Route("/{id}/delete-review", name="gestion.delete_review")
*/
public function deleteReviewAction(AvisUtilisateur $review)
{
$em = $this->getDoctrine()->getManager();
$slug = $review->getBoutique()->getSlug();
$em->remove($review);
$em->flush();
return $this->redirectToRoute('gestion.reviews', array('slug' => $slug));
}
/**
* @Route("/{id}/reply-review", name="gestion.reply_review")
*/
public function replyReviewAction(Request $request, AvisUtilisateur $review)
{
$em = $this->getDoctrine()->getManager();
if($request->getMethod() == 'POST'){
$reponseAvis = $request->request->get('reponseAvis');
$review->setReponseAvis($reponseAvis);
$em->flush();
$slug = $review->getBoutique()->getSlug();
return $this->redirectToRoute('gestion.reviews', array('slug' => $slug));
}
return $this->render('gestion/reply_review.html.twig', [
'review' => $review,
'boutique' => $review->getBoutique()
]);
}
/**
* @Route("/{slug}/messages", name="gestion.messages")
*/
public function messagesAction(Boutique $boutique)
{
$em = $this->getDoctrine()->getManager();
$conversations = $em->getRepository(Conversation::class)
->findByBoutique($boutique);
$user = $this->getUser();
return $this->render('gestion/messages.html.twig', [
'conversations' => $conversations,
'boutique' => $boutique
]);
}
/**
* @Route("/{slug}/{id}/conversation", name="gestion.one_conversation")
*/
public function oneConversationAction(Request $request, $slug, $id)
{
$em = $this->getDoctrine()->getManager();
$boutique = $em->getRepository(Boutique::class)
->findOneBySlug($slug);
$user = $this->getUser();
$notifications = $em->getRepository(Notification::class)
->findBy(
array(
'user' => $user,
'vu' => false,
'typeNotification' => 'message',
'extraData' => $id
)
);
foreach($notifications as $notification){
$notification->setVu(true);
}
$em->flush();
$conversation = $em->getRepository(Conversation::class)
->findOneById($id);
if($request->getMethod() == 'POST'){
$reponseMessage = $request->request->get('reponseMessage');
$message = new Message;
$message->setContenu($reponseMessage);
$message->setConversation($conversation);
$em->persist($message);
$em->flush();
$notification = new Notification;
$notification->setUser($conversation->getUser());
$lien = $this->generateUrl('profile.one_conversation', array('slug'=>$conversation->getBoutique()->getSlug(), 'id' => $conversation->getId()));
$notification->setExtraData($conversation->getId());
$notification->setLien($lien);
$notification->setTypeNotification('message');
$notification->setDescription('Vous avez un nouveau message');
$conversation->setLastMessage($reponseMessage);
$em->persist($notification);
$em->flush();
}
$messages = $em->getRepository(Message::class)
->findBy(
array(
'conversation' => $conversation
)
);
return $this->render('gestion/one_conversation.html.twig', [
'conversation' => $conversation,
'boutique' => $boutique,
'messages' => $messages
]);
}
/**
* @Route("/notification-to-join-team/{email}", name="notification_requette_ajout_boutique")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function notificationRequetteAjoutBoutiqueAction(Request $request, $email)
{
$em = $this->getDoctrine()
->getManager();
$requeteBoutiques = $em->getRepository(RequeteAjoutBoutique::class)
->findBy(
array(
'email' => $email
)
);
$nbRequete = count($requeteBoutiques);
return $this->render('entreprise/notification_requette_ajout_boutique.html.twig', [
'nbRequete' => $nbRequete
]);
}
/**
* @Route("/request-to-join-team/{email}", name="afficher_requette_ajout_boutique")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function afficherRequetteAjoutBoutiqueAction(Request $request, $email)
{
$em = $this->getDoctrine()
->getManager();
$requeteBoutiques = $em->getRepository(RequeteAjoutBoutique::class)
->findByEmail($email);
foreach($requeteBoutiques as $requeteBoutique){
$requeteBoutique->setVu(true);
}
$em->flush();
return $this->render('entreprise/afficher_requette_ajout_boutique.html.twig', [
'requeteBoutiques' => $requeteBoutiques
]);
}
/**
* @Route("/respond-request-to-join-team/{id}/{status}", name="repondre_requette_ajout_boutique")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function repondreRequetteAjoutBoutiqueAction(Request $request, $id, $status)
{
$em = $this->getDoctrine()
->getManager();
$requeteAjout = $em->getRepository(RequeteAjoutBoutique::class)
->findOneById($id);
$user = $this->getUser();
if($status){
$userBoutique = $em->getRepository(UserBoutique::class)
->findOneBy(
array(
'user' => $user->getId(),
'boutique' => $requeteAjout->getBoutique()->getId()
)
);
if(null === $userBoutique){
$userBoutique = new UserBoutique;
$userBoutique->setUser($user);
$userBoutique->setBoutique($requeteAjout->getBoutique());
$userBoutique->setRole($requeteAjout->getRole());
$em->persist($userBoutique);
}
}
$em->remove($requeteAjout);
$em->flush();
return $this->redirectToRoute('home');
}
public function getDomain()
{
$domaine = $_SERVER['HTTP_HOST'];
$protocol = '';
if(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'){
$protocol = 'https://';
} else{
$protocol = 'http://';
}
return $protocol.$domaine;
}
public function gererFichierMultipleLogo(\App\Entity\Boutique $boutique, $data)
{
$ex = pathinfo($data['name'])['extension'];
$exA = array('jpg', 'jpeg', 'png', 'gif', 'svg');
if (!in_array(strtolower($ex), $exA)){
throw new \RuntimeException('Désolé, vous n\'avez pas le droit de télécharger ce type de fichier: '.$ex);
}
$name = $boutique->getId().time();
$namePourRepertoire = $name;
//hydratation des attributs de l'objet
$domaine = $_SERVER['HTTP_HOST'];
$protocol = '';
if(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'){
$protocol = 'https://';
} else{
$protocol = 'http://';
}
$url = $protocol.$domaine.'/uploads/img/logo/'.$name.'.'.$ex;
$boutique->getLogo()->setUrl($url);
$boutique->getLogo()->setAlt('Logo '.$boutique->getNom());
//deplacement (création de l'image original)
//$this->file->move($photo->getUploadRootDir().'/original/', $name.'.'.photo->getExtension()));
if(!is_dir($boutique->getLogo()->getUploadRootDir().'/featured/') && !mkdir($boutique->getLogo()->getUploadRootDir().'/featured', 0777, true))
{
throw new \RuntimeException('Impossible de créer le dossier de destination');
}
if (!move_uploaded_file($data['tmp_name'], $boutique->getLogo()->getUploadRootDir().'/featured/'.$name.'.'.$ex))
{
throw new \RuntimeException('Désolé, une erreur s\'est produite.');
}
$name = $boutique->getLogo()->getUploadRootDir().'/featured/'.$name.'.'.$ex;
$extension = strtolower($ex);
try{
$nvlleImgMenu = ( $extension =='jpg' || $extension == 'jpeg') ? imagecreatefromjpeg($name) : imagecreatefrompng($name);
$miniatureMenu = imagecreatetruecolor(48, 48);
$largeurNvlleImgMenu = imagesx($nvlleImgMenu);
$hauteurNvlleImgMenu = imagesy($nvlleImgMenu);
$largeurMiniatureMenu = imagesx($miniatureMenu);
$hauteurMiniatureMenu = imagesy($miniatureMenu);
//fusion et generation des miniatures
imagecopyresampled($miniatureMenu, $nvlleImgMenu, 0, 0, 0, 0, $largeurMiniatureMenu, $hauteurMiniatureMenu,
$largeurNvlleImgMenu, $hauteurNvlleImgMenu);
//deplacer les nouvelles images(miniatures) dans les repertoires correspondants
if(!is_dir($boutique->getLogo()->getUploadRootDir().'/logo/') && !mkdir($boutique->getLogo()->getUploadRootDir().'/logo/', 0777, true)){
throw \RuntimeException('Impossible de créer le dossier de destination');
}
$name = $boutique->getLogo()->getUploadRootDir().'/logo/'.$namePourRepertoire.'.'.$extension;
if($extension == 'jpg' || $extension == 'jpeg'){
imagejpeg($miniatureMenu, $name, 90);
} else{
imagepng($miniatureMenu, $name, 9);
}
$fromSecure = 'http://';
if( isset($_SERVER['HTTPS'] ) ){
$fromSecure = 'https://';
}
$boutique->getLogo()->setUrl($fromSecure.$_SERVER['HTTP_HOST'].'/uploads/img/logo/'.$namePourRepertoire.'.'.$extension);
//
} catch(Exception $e){
}
$this->getDoctrine()->getManager()->flush();
}
public function gererFichierMultiple(\App\Entity\Produit $produit, $data, $imagePrincipale = false)
{
$nbFichier = 0;
if($imagePrincipale){
$photo = new ProductPhoto;
$photo->setFile($data);
$produit->setImagePrincipale($photo);
$ex = pathinfo($data['name'])['extension'];
$exA = array('jpg', 'jpeg', 'png', 'gif');
if (in_array(strtolower($ex), $exA)){
$photo->setExtension($ex);
} else {
throw new \RuntimeException('Désolé, vous n\'avez pas le droit de télécharger ce type de fichier: '.$ex);
}
$name = $produit->getId().time();
$namePourRepertoire = $name;
//hydratation des attributs de l'objet
$photo->setUrl($name.'.'.$photo->getExtension());
$photo->setAlt('Image de '.$produit->getNom());
$extension = $photo->getExtension();
//deplacement (création de l'image original)
//$this->file->move($photo->getUploadRootDir().'/original/', $name.'.'.photo->getExtension()));
if(!is_dir($photo->getUploadRootDir().'/original/') && !mkdir($photo->getUploadRootDir().'/original', 0777, true))
{
throw new \RuntimeException('Impossible de créer le dossier de destination');
}
if (!move_uploaded_file($data['tmp_name'], $photo->getUploadRootDir().'/original/'.$name.'.'.$photo->getExtension()))
{
throw new \RuntimeException('Désolé, une erreur s\'est produite.');
}
try{
$name = $photo->getUploadRootDir().'/original/'.$name.'.'.$photo->getExtension();
$nvlleImgMenu = ( $ex =='jpg' || $ex == 'jpeg') ? imagecreatefromjpeg($name) : imagecreatefrompng($name);
$miniatureMenu = imagecreatetruecolor(600, 600);
$largeurNvlleImgMenu = imagesx($nvlleImgMenu);
$hauteurNvlleImgMenu = imagesy($nvlleImgMenu);
$largeurMiniatureMenu = imagesx($miniatureMenu);
$hauteurMiniatureMenu = imagesy($miniatureMenu);
//fusion et generation des miniatures
imagecopyresampled($miniatureMenu, $nvlleImgMenu, 0, 0, 0, 0, $largeurMiniatureMenu, $hauteurMiniatureMenu,
$largeurNvlleImgMenu, $hauteurNvlleImgMenu);
//deplacer les nouvelles images(miniatures) dans les repertoires correspondants
if(!is_dir($photo->getUploadRootDir().'/featured/') && !mkdir($photo->getUploadRootDir().'/featured/', 0777, true)){
throw \RuntimeException('Impossible de créer le dossier de destination');
}
$name = $photo->getUploadRootDir().'/featured/'.$photo->getUrl();
if($extension == 'jpg' || $extension == 'jpeg'){
imagejpeg($miniatureMenu, $name, 85);
} else{
imagepng($miniatureMenu, $name, 8);
}
$fromSecure = 'http://';
if( isset($_SERVER['HTTPS'] ) ){
$fromSecure = 'https://';
}
$photo->setUrl($fromSecure.$_SERVER['HTTP_HOST'].'/'.$photo->getUploadDir().'/featured/'.$photo->getUrl());
//
} catch(Exception $e){
}
$this->getDoctrine()->getManager()->persist($photo);
} else{
$tableauFichier = $this->reArrayFiles($data);
//var_dump($tableauFichier);
$i = 0;
foreach ($tableauFichier as $file)
{
$i++;
//print 'File Name: ' . $file['name'];
//print 'File Type: ' . $file['type'];
//print 'File Size: ' . $file['size'];
$photo = new ProductPhoto;
$photo->setFile($file);
$ex = pathinfo($file['name'])['extension'];
$exA = array('jpg', 'jpeg', 'png', 'gif');
if (in_array(strtolower($ex), $exA)){
$photo->setExtension($ex);
} else {
throw new \RuntimeException('Désolé, vous n\'avez pas le droit de télécharger ce type de fichier: '.$ex);
}
$name = $produit->getId().$i.time();
$namePourRepertoire = $name;
//hydratation des attributs de l'objet
$photo->setUrl($name.'.'.$photo->getExtension());
$photo->setAlt('Image de '.$produit->getNom());
$fromSecure = 'http://';
if( isset($_SERVER['HTTPS'] ) ){
$fromSecure = 'https://';
}
$photo->setUrl($fromSecure.$_SERVER['HTTP_HOST'].'/'.$photo->getUploadDir().'/original/'.$photo->getUrl());
//deplacement (création de l'image original)
//$this->file->move($photo->getUploadRootDir().'/original/', $name.'.'.photo->getExtension()));
$produit->addPhoto($photo);
if(!is_dir($photo->getUploadRootDir().'/original/') && !mkdir($photo->getUploadRootDir().'/original', 0777, true))
{
throw new \RuntimeException('Impossible de créer le dossier de destination');
}
if (!move_uploaded_file($file['tmp_name'], $photo->getUploadRootDir().'/original/'.$name.'.'.$photo->getExtension()))
{
throw new \RuntimeException('Désolé, une erreur s\'est produite.');
}
$this->getDoctrine()->getManager()->persist($photo);
}
}
$this->getDoctrine()->getManager()->flush();
}
public function reArrayFiles($file_post)
{
$file_ary = array();
$file_count = count($file_post['name']);
$file_keys = array_keys($file_post);
for ($i=0; $i<$file_count; $i++)
{
foreach ($file_keys as $key)
{
$file_ary[$i][$key] = $file_post[$key][$i];
}
}
return $file_ary;
}
public function allowRemoteRequest($response)
{
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Content-Type', 'application/json;');
$response->headers->set('Access-Control-Allow-Headers', 'Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Allow-Origin,Authorization,Access-Control-Request-Headers');
//$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
//$response->headers->set('Access-Control-Allow-Headers', 'Origin, Accept, Access-Control-Allow-Origin, Content-Type, X-Auth-Token');
//$response->headers->set('Access-Control-Allow-Headers', 'Content-Type');
//$response->headers->set('Access-Control-Allow-Credentials', true);
//$response->headers->set('Access-Control-Allow-Headers', 'Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Authorization,Access-Control-Request-Headers');
//$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
//header("Access-Control-Allow-Headers: 'Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Allow-Origin,Authorization,Access-Control-Request-Headers'");
return $response;
}
public function getJekoBaseUrl()
{
return 'https://api.jeko.africa/partner_api' ;
}
public function getJekoAPI()
{
return 'jeko_66ecb6a64777b553e7655db916e8cd8f3484344f86ee381d2adc80f56a1b25b6';
}
public function getJekoAPIID()
{
return $_ENV['JEKO_API_KEY_ID'] ?? getenv('JEKO_API_KEY_ID') ?: '2186eab1-2e47-47f7-98d3-71fd860fb7c2';
}
public function getJekoStoreId()
{
return 'e3248694-5eeb-4184-80f8-01b7c7a6e428';
}
}