<?php
namespace App\Controller\Api\Product;
use App\Dto\Authorization\AuthorizationHeaderDto;
use App\Dto\Member\MemberCopecartCreatorDto;
use App\Entity\Product;
use App\Entity\Space;
use App\EventListener\Api\TokenInterceptor\MybizTokenAuthenticatorInterface;
use App\Repository\MemberRepository;
use App\Service\Authorization\MybizRequestJwtChecker;
use App\Service\GeoLocation\GeoLocationProvider;
use App\Service\Market\MarketProvider;
use App\Service\Member\Currency\MemberCurrencyProvider;
use App\Service\PaymentGateway\Copecart\CopecartCheckoutGenerator;
use App\Service\Product\ProductPriceProvider;
use App\Service\PromoSystem\PromoSystemProvider;
use App\Service\Subscription\SubscriptionProvider;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
class ProductApiController extends AbstractController implements MybizTokenAuthenticatorInterface
{
/**
* @Route("/v1/product/{sku}/purchase-link", methods={"POST"}, name="api_product_purchase_link", options={"expose": "true"})
*/
public function productPurchaseLink(
Request $request,
TranslatorInterface $translator,
CopecartCheckoutGenerator $copecartCheckoutGenerator,
GeoLocationProvider $geoLocationProvider,
MemberCurrencyProvider $memberCurrencyProvider,
MybizRequestJwtChecker $mybizRequestJwtChecker,
SubscriptionProvider $subscriptionProvider,
MemberRepository $memberRepository,
Product $product
): Response
{
// On peut provenir ici avec un membre déjà existant, auquel cas on va utiliser son marché + savoir s'il a déjà commandé pour lui proposer un autre tarif
try {
$member = $mybizRequestJwtChecker->checkJwt($request);
$hasAlreadyOrdered = $subscriptionProvider->hasAlreadyOrdered($member, Space::SPACE_FUTURES_INFINITY);
} catch (\Throwable $e) {
$member = null;
// Ce endpoint d'API peut être appelé or connexion côté Futures Learn, dans ce cas là on considère le membre comme nouveau
$hasAlreadyOrdered = false;
}
try {
$payload = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
} catch (\Throwable $e) {
return $this->json([
"message" => $translator->trans("validator.futures_learn.formation.parse_error", [], "validator"),
"error" => $e->getMessage()
], Response::HTTP_BAD_REQUEST);
}
// On créé un objet du type MemberCopecartCreatorDto pour rester dans les mêmes tuyaux sur Copecart
// Une idée pourrait être d'avoir une méthode $copecartCheckoutGenerator->generate avec les paramètres nécessaires et non un gros DTO
$memberCopecartCreatorDto = new MemberCopecartCreatorDto();
// On simule la création du DTO avec les paramètres nécessaire pour générer le lien de la page de checkout de la licence
$sponsor = null;
if (isset($payload["sponsorId"])) {
$sponsor = $memberRepository->find((int)$payload["sponsorId"]);
if (null === $sponsor) {
return $this->json([
"error" => "Sponsor ID not found : " . $payload["sponsorId"]
], Response::HTTP_NOT_FOUND);
}
}
$memberCopecartCreatorDto->setSponsor($sponsor);
$memberCopecartCreatorDto->setPlainPassword($payload["plainPassword"]);
$memberCopecartCreatorDto->setEmail($payload["email"]);
$memberCopecartCreatorDto->setBirthday($payload["birthday"]);
$memberCopecartCreatorDto->setPhone($payload["phone"]);
$memberCopecartCreatorDto->setProduct($product);
$memberCopecartCreatorDto->setisDecreasing($payload["isDecreasing"]);
$memberCopecartCreatorDto->setIpAddress($payload["ip"]);
$memberCopecartCreatorDto->setCountry($payload["country"]);
$memberCopecartCreatorDto->setPreferredLanguage($payload["locale"]);
$memberCopecartCreatorDto->setDuration($payload["duration"]);
$memberCopecartCreatorDto->setAcceptOffers(true);
$memberCopecartCreatorDto->setAcceptTerms(true);
$memberCopecartCreatorDto->setAcceptRefunds(true);
$memberCopecartCreatorDto->setHasAlreadyOrdered($hasAlreadyOrdered);
try {
// Une fois le formulaire validé on renvoie l'utilisateur sur Copecart pour le paiement
return $this->json([
"url" => $copecartCheckoutGenerator->generateFromMemberCopecartCreatorDto(
$memberCopecartCreatorDto,
$memberCurrencyProvider->getDefaultCurrency(),
null !== $member ? $member->getMarket() : $geoLocationProvider->getGeoLocationDtoByCountry(
$memberCopecartCreatorDto->getIpAddress(),
$memberCopecartCreatorDto->getCountry()
)->getMarket()
)
]);
} catch (\Throwable $e) {
return $this->json([
"message" => $translator->trans("validator.futures_learn.formation.no_member", [], "validator"),
"error" => $e->getMessage()
], Response::HTTP_BAD_REQUEST);
}
}
/**
* @Route("/v1/product/informations", methods={"GET", "POST"}, name="api_product_informations", options={"expose": "true"})
*/
public function productInformations(
Request $request,
TranslatorInterface $translator,
MemberCurrencyProvider $memberCurrencyProvider,
MybizRequestJwtChecker $mybizRequestJwtChecker,
MarketProvider $marketProvider,
ProductPriceProvider $productPriceProvider,
PromoSystemProvider $promoSystemProvider,
SubscriptionProvider $subscriptionProvider
): Response
{
try {
$member = $mybizRequestJwtChecker->checkJwt($request);
$isNewMember = $subscriptionProvider->hasAlreadyOrdered($member, Space::SPACE_FUTURES_INFINITY);
} catch (\Throwable $e) {
// Ce endpoint d'API peut être appelé or connexion côté Futures Learn, dans ce cas là on considère le membre comme nouveau
$isNewMember = true;
}
try {
$payload = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
} catch (\Throwable $e) {
return $this->json([
"message" => $translator->trans("validator.futures_learn.formation.parse_error", [], "validator"),
"error" => $e->getMessage()
], Response::HTTP_BAD_REQUEST);
}
$authorizationHeaderDto = AuthorizationHeaderDto::generateFromRequest($request);
$spaceName = $authorizationHeaderDto->getName();
$countryAlpha2 = $payload["countryAlpha2"];
if (null === $countryAlpha2) {
return $this->json([
"message" => $translator->trans("validator.futures_learn.formation.missing_information", [], "validator"),
"error" => "spaceName or countryAlpha2 not found"
], Response::HTTP_BAD_REQUEST);
}
$market = $marketProvider->getMarketByCountryAlpha2($countryAlpha2);
$defaultCurrency = $memberCurrencyProvider->getDefaultCurrency();
// On appelle le système de promo pour qu'elles se déclenchent
$date = new \DateTimeImmutable();
// TODO promo : il faudra changer ça avec l'affichage des promos côté Futures
try{
$promos = $promoSystemProvider->getPromosToDisplay($authorizationHeaderDto->getName(), $date);
}catch (\Throwable $e){
}
switch ($spaceName) {
case Space::SPACE_FUTURES_INFINITY:
$productPrices = $productPriceProvider->getFuturesLearnProductPrices($market, $defaultCurrency, $isNewMember);
break;
default:
return $this->json([
"message" => $translator->trans("validator.futures_learn.formation.space_not_found", [], "validator"),
"error" => "Unknown space : " . $spaceName
], Response::HTTP_BAD_REQUEST);
}
return $this->json([
"productPrices" => $productPrices,
]);
}
}