custom/plugins/ShopWithMeMobileApi/src/Controller/AuthController.php line 94

Open in your IDE?
  1. <?php
  2. namespace ShopWithMe\MobileApi\Controller;
  3. use Shopware\Core\Checkout\Cart\CartException;
  4. use Shopware\Core\Checkout\Customer\CustomerDefinition;
  5. use Shopware\Core\Checkout\Customer\CustomerEntity;
  6. use Shopware\Core\Checkout\Customer\Event\CustomerBeforeLoginEvent;
  7. use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
  8. use Shopware\Core\Checkout\Customer\Password\LegacyPasswordVerifier;
  9. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractRegisterRoute;
  10. use Shopware\Core\Checkout\Customer\SalesChannel\CustomerResponse;
  11. use Shopware\Core\Checkout\Customer\Validation\Constraint\CustomerEmailUnique;
  12. use Shopware\Core\Checkout\Customer\Validation\CustomerValidationFactory;
  13. use Shopware\Core\Framework\Api\Controller\ApiController;
  14. use Shopware\Core\Framework\Api\Response\ResponseFactoryInterface;
  15. use Shopware\Core\Framework\Context;
  16. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Validation\EntityExists;
  23. use Shopware\Core\Framework\RateLimiter\Exception\RateLimitExceededException;
  24. use Shopware\Core\Framework\RateLimiter\RateLimiter;
  25. use Shopware\Core\Framework\Routing\RequestContextResolverInterface;
  26. use Shopware\Core\Framework\Struct\ArrayStruct;
  27. use Shopware\Core\Framework\Uuid\Uuid;
  28. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  29. use Shopware\Core\Framework\Validation\DataValidationDefinition;
  30. use Shopware\Core\Framework\Validation\DataValidator;
  31. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  32. use Shopware\Core\PlatformRequest;
  33. use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
  34. use Shopware\Core\System\SalesChannel\Context\CartRestorer;
  35. use Shopware\Core\System\SalesChannel\ContextTokenResponse;
  36. use Shopware\Core\System\SalesChannel\Event\SalesChannelContextSwitchEvent;
  37. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  38. use Shopware\Core\System\SalesChannel\StoreApiResponse;
  39. use Shopware\Core\System\SystemConfig\SystemConfigService;
  40. use Shopware\Storefront\Controller\StorefrontController;
  41. use ShopWithMe\MobileApi\Exception\CheckDeleteException;
  42. use ShopWithMe\MobileApi\Exception\CustomApiException;
  43. use ShopWithMe\MobileApi\Service\AccountService;
  44. use ShopWithMe\MobileApi\Service\AIStreamService;
  45. use ShopWithMe\Reward\Service\LuckyWheelService\LuckyWheelApiService;
  46. use ShopWithMe\Reward\Service\RewardPointService;
  47. use Symfony\Component\Filesystem\Exception\FileNotFoundException;
  48. use Symfony\Component\HttpFoundation\JsonResponse;
  49. use Symfony\Component\HttpFoundation\Request;
  50. use Symfony\Component\HttpFoundation\RequestStack;
  51. use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
  52. use Symfony\Component\Routing\Annotation\Route;
  53. use Symfony\Component\Validator\Constraints\Length;
  54. use Symfony\Component\Validator\Constraints\NotBlank;
  55. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
  56. use Shopware\Core\Framework\Routing\Annotation\Since;
  57. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextPersister;
  58. use Symfony\Component\Validator\ConstraintViolation;
  59. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  60. use Shopware\Core\Checkout\Customer\Exception\BadCredentialsException;
  61. use Shopware\Core\Checkout\Customer\Exception\CustomerAuthThrottledException;
  62. use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundException;
  63. use Shopware\Core\Checkout\Customer\Exception\InactiveCustomerException;
  64. use ShopWithMe\UI\ShopWithMeUI;
  65. use Symfony\Contracts\Translation\TranslatorInterface;
  66. use Symfony\Component\Validator\Constraints as Assert;
  67. use Symfony\Component\Validator\Validator\ValidatorInterface;
  68. use Symfony\Component\Validator\ConstraintViolationList;
  69. #[Route(defaults: ['_routeScope' => ['store-api']])]
  70. class AuthController extends StorefrontController
  71. {
  72.     private const SHIPPING_METHOD_ID SalesChannelContextService::SHIPPING_METHOD_ID;
  73.     private const PAYMENT_METHOD_ID SalesChannelContextService::PAYMENT_METHOD_ID;
  74.     private const BILLING_ADDRESS_ID SalesChannelContextService::BILLING_ADDRESS_ID;
  75.     private const SHIPPING_ADDRESS_ID SalesChannelContextService::SHIPPING_ADDRESS_ID;
  76.     private const COUNTRY_ID SalesChannelContextService::COUNTRY_ID;
  77.     private const STATE_ID SalesChannelContextService::COUNTRY_STATE_ID;
  78.     private const CURRENCY_ID SalesChannelContextService::CURRENCY_ID;
  79.     private const LANGUAGE_ID SalesChannelContextService::LANGUAGE_ID;
  80.     protected EntityRepository $tagRepository;
  81.     protected EntityRepository $customerRepository;
  82.     protected LuckyWheelApiService $luckyWheelApiService;
  83.     protected TranslatorInterface $translator;
  84.     private ValidatorInterface $validatorSWM;
  85.     public function __construct(
  86.         EntityRepository                             $tagRepository,
  87.         EntityRepository                             $repository,
  88.         protected ApiController                      $apiController,
  89.         protected ResponseFactoryInterface           $responseFactory,
  90.         protected NumberRangeValueGeneratorInterface $numberRangeValueGenerator,
  91.         protected EntityRepository                   $customerEntity,
  92.         protected EntityRepository                   $languageRepository,
  93.         protected CustomerValidationFactory          $customerValidationFactory,
  94.         protected DataValidator                      $dataValidator,
  95.         protected EntityRepository                   $countryRepository,
  96.         protected RequestContextResolverInterface    $contextResolver,
  97.         protected SystemConfigService                $systemConfigService,
  98.         protected AbstractRegisterRoute              $registerRoute,
  99.         protected EntityRepository                   $salutationRepository,
  100.         protected AIStreamService                    $aiStreamService,
  101.         protected DataValidator                      $validator,
  102.         protected SalesChannelContextPersister       $contextPersister,
  103.         protected EventDispatcherInterface           $eventDispatcher,
  104.         protected LegacyPasswordVerifier             $legacyPasswordVerifier,
  105.         protected CartRestorer                       $restorer,
  106.         protected RequestStack                       $requestStack,
  107.         protected RateLimiter                        $rateLimiter,
  108.         protected ?string                            $lifetimeInterval 'P1D',
  109.         LuckyWheelApiService                         $luckyWheelApiService,
  110.         TranslatorInterface                          $translator,
  111.         ValidatorInterface                           $validatorSWM,
  112.         protected EntityRepository                   $cmsPageRepository,
  113.         protected AccountService                     $accountService,
  114.         protected RewardPointService                 $rewardPointService,
  115.     )
  116.     {
  117.         $this->tagRepository $tagRepository;
  118.         $this->customerRepository $repository;
  119.         $this->luckyWheelApiService $luckyWheelApiService;
  120.         $this->translator $translator;
  121.         $this->validatorSWM $validatorSWM;
  122.     }
  123.     #[Route('/store-api/mobile/common'name'mobile.register.common'defaults: ["XmlHttpRequest" => true], methods: ['GET'])]
  124.     public function commonBeforeLogin(SalesChannelContext $context): JsonResponse
  125.     {
  126.         $request $this->requestStack->getCurrentRequest();
  127.         $languageCode $request->headers->get('language-code'$request->getLocale());
  128.         try {
  129.             $criteria = new Criteria();
  130.             $tags $this->tagRepository->search($criteria$context->getContext())->getEntities();
  131.             $languages $this->languageList($context)->getEntities()->jsonSerialize();
  132.             $policy $this->getPolicy($context);
  133.             return new JsonResponse(
  134.                 [
  135.                     'interest' => $this->aiStreamService->convertArray($tags),
  136.                     'languages' => $languages,
  137.                     'policy' => $policy['policy'],
  138.                     'eula' => $policy['eula']
  139.                 ]
  140.             );
  141.         } catch (\Exception $exception) {
  142.             return (new CustomApiException($this->translator->trans('message.common.00005', [], null$languageCode)))->jsonResponse();
  143.         }
  144.     }
  145.     #[Route('/store-api/mobile/register-interest'name'mobile.register.register-interest'defaults: ["XmlHttpRequest" => true], methods: ['GET'])]
  146.     public function getTags(SalesChannelContext $context)
  147.     {
  148.         $criteria = new Criteria();
  149.         $tags $this->tagRepository->search($criteria$context->getContext());
  150.         return new JsonResponse(
  151.             ['data' => $this->aiStreamService->convertArray($tags)]
  152.         );
  153.     }
  154.     public function getPolicy(SalesChannelContext $context)
  155.     {
  156.         $criteria = new Criteria(["60475197d7974ca4a731d844c990f93f"'5a6121d4321e4104a0f08ab0d296739d']);
  157.         $criteria->addAssociation('sections.blocks.slots');
  158.         $data $this->cmsPageRepository->search($criteria$context->getContext())->getElements();
  159.         $result array_map(function ($value) {
  160.             return $value->getId() === '60475197d7974ca4a731d844c990f93f' 'policy' 'eula';
  161.         }, $data);
  162.         $result array_combine($result$data);
  163.         foreach ($result as $key => &$value) {
  164.             $value $value->getSections()?->getBlocks()?->getSlots();
  165.         }
  166.         return $result;
  167.     }
  168.     #[Route('/store-api/mobile/country-code'name'mobile.register.country-code'defaults: ["XmlHttpRequest" => true], methods: ['GET'])]
  169.     public function getCountryCode(Request $requestSalesChannelContext $context): JsonResponse
  170.     {
  171.         $languageCode $request->headers->get('language-code'$request->getLocale());
  172.         $filePath dirname((new \ReflectionClass(ShopWithMeUI::class))->getFileName()) . '/Resources/config/countryCode.json';
  173.         if (!is_file($filePath)) {
  174.             return (new CustomApiException($this->translator->trans('message.auth.00103', [], null$languageCode) . $filePath))->jsonResponse();
  175.         }
  176.         $dataCountryCode json_decode(file_get_contents($filePath), true) ?? [];
  177.         return new JsonResponse(['data' => $dataCountryCode]);
  178.     }
  179.     #[Route('/store-api/mobile/language'name'mobile.register.language'defaults: ["XmlHttpRequest" => true], methods: ['GET'])]
  180.     public function getLanguageList(SalesChannelContext $context)
  181.     {
  182.         $languages $this->languageList($context);
  183.         return new JsonResponse(
  184.             ['data' => $languages'message' => 'success']
  185.         );
  186.     }
  187.     public function languageList(SalesChannelContext $context)
  188.     {
  189.         return $this->languageRepository->search((new Criteria())
  190.             ->addFilter(new EqualsFilter('salesChannelDomains.salesChannelId'$context->getSalesChannelId()))
  191.             ->addFilter(new NotFilter(MultiFilter::CONNECTION_OR, [
  192.                 new ContainsFilter('translationCode.code''th-TH'),
  193.                 new ContainsFilter('translationCode.code''en-HK'),
  194.                 new ContainsFilter('translationCode.code''es-ES'),
  195.             ]))
  196.             ->addAssociations(['locale''translationCode']), $context->getContext());
  197.     }
  198.     #[Route('/store-api/mobile/register'name'mobile.register.register'defaults: ["XmlHttpRequest" => true], methods: ['POST'])]
  199.     public function registerApi(RequestDataBag $dataBagRequest $requestSalesChannelContext $context)
  200.     {
  201.         $this->fillRequiredData($request$context);
  202.         $result $this->validateCustomer($request$context);
  203.         $languageCode $request->headers->get('language-code'$request->getLocale());
  204.         if (!empty($result) && $result->getCustomer() instanceof CustomerEntity) {
  205.             $this->contextResolver->resolve($request);
  206.             $context $request->attributes->get('sw-sales-channel-context');
  207.             $customer $result->getCustomer();
  208.             if (array_key_exists('tags'$request->request->all())) {
  209.                 $tags json_decode($request->request->all()['tags']);
  210.                 $this->aiStreamService->setInterests($context$customer$tags);
  211.             }
  212.             $this->setCustomFields($context$customer, [
  213.                 'customerCountryCode' => $request->request->get('countryCode'),
  214.                 'customerLanguageCode' => $request->request->get('languageCode'),
  215.                 'customerCountryId' => $request->request->get('countryId'),
  216.                 'customerLanguageId' => $request->request->get('languageId'),
  217.                 'luckyWheelFirstTurn' => false
  218.             ]);
  219.             $customerSerialized = (new CustomerResponse($customer))->getCustomer()->jsonSerialize();
  220.             if (!empty($customerSerialized['password'])) {
  221.                 unset($customerSerialized['password']);
  222.             }
  223.             return new JsonResponse([
  224.                 'data' => [
  225.                     'customer' => $customerSerialized,
  226.                     'contextToken' => $result->headers->get(PlatformRequest::HEADER_CONTEXT_TOKEN),
  227.                 ],
  228.                 'message' => 'Success',
  229.             ]);
  230.         }
  231.         return (new CustomApiException($this->translator->trans('message.common.00004', [], null$languageCode)))->jsonResponse();
  232.     }
  233.     #[Route('/store-api/mobile/update-affiliateCode'name'mobile.register.affiliateCode'defaults: ['_loginRequired' => true], methods: ['POST'])]
  234.     public function updateCampaignCode(Request $requestSalesChannelContext $context)
  235.     {
  236.         $languageCode $request->headers->get('language-code'$request->getLocale());
  237.         try {
  238.             $referral $this->validateAffiliateCode($request$context);
  239.             $checkCampaignCode $context->getCustomer()->getCampaignCode();
  240.             if (empty($referral)) {
  241.                 return (new CustomApiException($this->translator->trans('message.auth.00106', [], null$languageCode)))->jsonResponse();
  242.             }
  243.             if (empty($checkCampaignCode)) {
  244.                 $affiliateCode $request->request->get('affiliateCode');
  245.                 $this->customerRepository->update([['id' => $context->getCustomerId(), 'campaignCode' => $affiliateCode]], $context->getContext());
  246.                 $this->luckyWheelApiService->addTurnAfterAffiliateCode($referral$context->getCustomer(), $context->getContext());
  247.                 return new JsonResponse(['message' => $this->translator->trans('message.common.00002', [], null$languageCode)], 200);
  248.             }
  249.             return (new CustomApiException($this->translator->trans('message.auth.00107', [], null$languageCode)))->jsonResponse();
  250.         } catch (ConstraintViolationException $exception) {
  251.             return (new CustomApiException($this->translator->trans('message.common.00005', [], null$languageCode)))->jsonResponse();
  252.         }
  253.     }
  254.     #[Route('/store-api/mobile/create-interest'name'mobile.register.create-interest'defaults: ["XmlHttpRequest" => true], methods: ['POST'])]
  255.     public function createInterest(Request $requestSalesChannelContext $context): JsonResponse
  256.     {
  257.         $languageCode $request->headers->get('language-code'$request->getLocale());
  258.         $customer $context->getCustomer();
  259.         if (array_key_exists('tags'$request->request->all())) {
  260.             $tags json_decode($request->request->all()['tags']);
  261.             $this->aiStreamService->setInterests($context$customer$tags);
  262.             return new JsonResponse(['message' => $this->translator->trans('message.auth.00104', [], null$languageCode)]);
  263.         }
  264.         return (new CustomApiException($this->translator->trans('message.auth.00105', [], null$languageCode)))->jsonResponse();
  265.     }
  266.     private function fillRequiredData(Request $requestSalesChannelContext $context): void
  267.     {
  268.         $currentSalesChannelLanguageId $context->getContext()->getLanguageId();
  269.         $currentCountryId $context->getSalesChannel()->getCountryId();
  270.         $currentSalesChannelDomain $context->getSalesChannel()?->getDomains()?->filterByProperty('languageId'$currentSalesChannelLanguageId)?->first();
  271.         $this->handleFillData($request$currentCountryId$currentSalesChannelDomain$context);
  272.         $billingAddress = (array)$request->request->get('billingAddress');
  273.         if (empty($billingAddress)) {
  274.             $billingAddress = [
  275.                 'countryId' => $currentCountryId,
  276.                 'salutationId' => $request->request->get('salutationId'),
  277.                 'firstName' => $request->request->get('firstName') ?? '',
  278.                 'lastName' => $request->request->get('lastName') ?? '',
  279.                 'zipcode' => 'noinfo',
  280.                 'city' => 'noinfo',
  281.                 'street' => 'noinfo',
  282.                 'phoneNumber' => $request->request->get('phoneNumber'),
  283.             ];
  284.         } else {
  285.             $billingAddress['phoneNumber'] = $request->request->get('phoneNumber');
  286.         }
  287.         $request->request->set('billingAddress'$billingAddress);
  288.     }
  289.     private function validateCustomer(Request $requestSalesChannelContext $context): ?CustomerResponse
  290.     {
  291.         $languageCode $request->headers->get('language-code'$request->getLocale());
  292.         $definition $this->customerValidationFactory->create($context);
  293.         $minLength $this->systemConfigService->get('core.loginRegistration.passwordMinLength'$context->getSalesChannel()->getId());
  294.         $definition->add('password', new NotBlank(), new Length(['min' => $minLength]));
  295.         $options = ['context' => $context->getContext(), 'salesChannelContext' => $context];
  296.         $definition->add('email', new CustomerEmailUnique($options));
  297. //        $definition->add("phoneNumber", new NotBlank());
  298.         $definition->add('salutationId', new NotBlank());
  299.         $definition->add('userName', new NotBlank());
  300. //        $definition->add('birthday', new NotBlank());
  301.         $definition->add('tags', new NotBlank());
  302.         $constraints $this->getAffiliateCodeConstraints($context->getContext());
  303.         if (!empty($request->get('affiliateCode'))) {
  304.             foreach ($constraints as $constraint) {
  305.                 $definition->add('affiliateCode'$constraint);
  306.             }
  307.         }
  308.         $validation $this->dataValidator->getViolations($request->request->all(), $definition);
  309.         //check for validation errors.
  310.         if ($validation->count() > 0) {
  311.             $translatedViolations = new ConstraintViolationList();
  312.             foreach ($validation as $violation) {
  313.                 $message $violation->getMessageTemplate();
  314.                 $propertyPath $violation->getPropertyPath();
  315.                 switch ($propertyPath) {
  316.                     case '/firstName':
  317.                     {
  318.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  319.                         break;
  320.                     }
  321.                     case '/lastName':
  322.                     {
  323.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  324.                         break;
  325.                     }
  326.                     case '/password':
  327.                     {
  328.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  329.                         break;
  330.                     }
  331.                     case '/email':
  332.                     {
  333.                         if (isset($violation->getParameters()['{{ email }}'])) {
  334.                             $email $violation->getParameters()['{{ email }}'];
  335.                             $translatedMessage $this->translator->trans("message.auth.00110.$message", ['{{ email }}' => $email], null$languageCode);
  336.                         } else {
  337.                             $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  338.                         }
  339.                         break;
  340.                     }
  341.                     case '/userName':
  342.                     {
  343.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  344.                         break;
  345.                     }
  346.                     default:
  347.                     {
  348.                         $translatedMessage $this->translator->trans("message.auth.00111", [], null$languageCode);
  349.                     }
  350.                 }
  351.                 $translatedViolations->add(new ConstraintViolation(
  352.                     $translatedMessage,
  353.                     $violation->getMessageTemplate(),
  354.                     $violation->getParameters(),
  355.                     $violation->getRoot(),
  356.                     $violation->getPropertyPath(),
  357.                     $violation->getInvalidValue()
  358.                 ));
  359.             }
  360.             throw new ConstraintViolationException($translatedViolations$request->request->all());
  361.         }
  362.         $requestDataBag = new RequestDataBag($request->request->all());
  363.         return $this->registerRoute->register($requestDataBag$context);
  364.     }
  365.     public function validateAffiliateCode(Request $requestSalesChannelContext $context)
  366.     {
  367.         $affiliateCriteria = new Criteria();
  368.         $affiliateCriteria->addFilter(new EqualsFilter('affiliateCode'$request->request->get('affiliateCode')));
  369.         return $this->customerRepository->search($affiliateCriteria$context->getContext())->first();
  370.     }
  371.     protected function getAffiliateCodeConstraints(Context $context): array
  372.     {
  373.         $notBlankConstraint = new NotBlank();
  374.         $customerExistsConstraint = new EntityExists([
  375.             'entity' => CustomerDefinition::ENTITY_NAME,
  376.             'context' => $context,
  377.         ]);
  378.         $customerExistsConstraint->primaryProperty 'affiliateCode';
  379.         return [
  380.             $notBlankConstraint,
  381.             $customerExistsConstraint
  382.         ];
  383.     }
  384.     private function setCustomFields(SalesChannelContext $contextCustomerEntity $customer, array $customFields): void
  385.     {
  386.         $customerCustomFields $customer->getCustomFields() ?? [];
  387.         foreach ($customFields as $key => $value) {
  388.             $customerCustomFields[$key] = $value;
  389.         }
  390.         $this->customerEntity->update(
  391.             [
  392.                 [
  393.                     'id' => $customer->getId(),
  394.                     'customFields' => $customerCustomFields,
  395.                 ]
  396.             ],
  397.             $context->getContext()
  398.         );
  399.     }
  400.     /**
  401.      * @Since("6.2.0.0")
  402.      * @Route("/store-api/mobile/context", name="store-api.mobile.switch-context", methods={"PATCH"})
  403.      */
  404.     public function switchContext(RequestDataBag $dataSalesChannelContext $context): ContextTokenResponse
  405.     {
  406.         $definition = new DataValidationDefinition('context_switch');
  407.         $parameters $data->only(
  408.             self::SHIPPING_METHOD_ID,
  409.             self::PAYMENT_METHOD_ID,
  410.             self::BILLING_ADDRESS_ID,
  411.             self::SHIPPING_ADDRESS_ID,
  412.             self::COUNTRY_ID,
  413.             self::STATE_ID,
  414.             self::CURRENCY_ID,
  415.             self::LANGUAGE_ID
  416.         );
  417.         $addressCriteria = new Criteria();
  418.         if ($context->getCustomer()) {
  419.             $addressCriteria->addFilter(new EqualsFilter('customer_address.customerId'$context->getCustomer()->getId()));
  420.         } else {
  421.             // do not allow to set address ids if the customer is not logged in
  422.             if (isset($parameters[self::SHIPPING_ADDRESS_ID])) {
  423.                 throw CartException::customerNotLoggedIn();
  424.             }
  425.             if (isset($parameters[self::BILLING_ADDRESS_ID])) {
  426.                 throw CartException::customerNotLoggedIn();
  427.             }
  428.         }
  429.         $currencyCriteria = new Criteria();
  430.         $currencyCriteria->addFilter(
  431.             new EqualsFilter('currency.salesChannels.id'$context->getSalesChannel()->getId())
  432.         );
  433.         $languageCriteria = new Criteria();
  434.         $languageCriteria->addFilter(
  435.             new EqualsFilter('language.salesChannels.id'$context->getSalesChannel()->getId())
  436.         );
  437.         $paymentMethodCriteria = new Criteria();
  438.         $paymentMethodCriteria->addFilter(
  439.             new EqualsFilter('payment_method.salesChannels.id'$context->getSalesChannel()->getId())
  440.         );
  441.         $shippingMethodCriteria = new Criteria();
  442.         $shippingMethodCriteria->addFilter(
  443.             new EqualsFilter('shipping_method.salesChannels.id'$context->getSalesChannel()->getId())
  444.         );
  445.         $definition
  446.             ->add(self::LANGUAGE_ID, new EntityExists(['entity' => 'language''context' => $context->getContext(), 'criteria' => $languageCriteria]))
  447.             ->add(self::CURRENCY_ID, new EntityExists(['entity' => 'currency''context' => $context->getContext(), 'criteria' => $currencyCriteria]))
  448.             ->add(self::SHIPPING_METHOD_ID, new EntityExists(['entity' => 'shipping_method''context' => $context->getContext(), 'criteria' => $shippingMethodCriteria]))
  449.             ->add(self::PAYMENT_METHOD_ID, new EntityExists(['entity' => 'payment_method''context' => $context->getContext(), 'criteria' => $paymentMethodCriteria]))
  450.             ->add(self::BILLING_ADDRESS_ID, new EntityExists(['entity' => 'customer_address''context' => $context->getContext(), 'criteria' => $addressCriteria]))
  451.             ->add(self::SHIPPING_ADDRESS_ID, new EntityExists(['entity' => 'customer_address''context' => $context->getContext(), 'criteria' => $addressCriteria]))
  452.             ->add(self::COUNTRY_ID, new EntityExists(['entity' => 'country''context' => $context->getContext()]))
  453.             ->add(self::STATE_ID, new EntityExists(['entity' => 'country_state''context' => $context->getContext()]));
  454.         $this->validator->validate($parameters$definition);
  455.         $customer $context->getCustomer();
  456.         $this->contextPersister->save(
  457.             $context->getToken(),
  458.             $parameters,
  459.             $context->getSalesChannel()->getId(),
  460.             $customer && empty($context->getPermissions()) ? $customer->getId() : null
  461.         );
  462.         // Language was switched - Check new Domain
  463.         $changeUrl $this->checkNewDomain($parameters$context);
  464.         $event = new SalesChannelContextSwitchEvent($context$data);
  465.         $this->eventDispatcher->dispatch($event);
  466.         // dd([$context->getPaymentMethod(), $parameters]);
  467.         return new ContextTokenResponse($context->getToken(), $changeUrl);
  468.     }
  469.     private function checkNewDomain(array $parametersSalesChannelContext $context): ?string
  470.     {
  471. //        if (
  472. //            !isset($parameters[self::LANGUAGE_ID])
  473. //            || $parameters[self::LANGUAGE_ID] === $context->getLanguageId()
  474. //        ) {
  475. //            return null;
  476. //        }
  477.         $domains $context->getSalesChannel()->getDomains();
  478.         if ($domains === null) {
  479.             return null;
  480.         }
  481.         $langDomain $domains->filterByProperty('languageId'$parameters[self::LANGUAGE_ID])->first();
  482.         if ($langDomain === null) {
  483.             return null;
  484.         }
  485.         return $langDomain->getUrl();
  486.     }
  487.     /**
  488.      * @Since("6.2.0.0")
  489.      * @Route(path="/store-api/mobile/account/login", name="store-api.mobile.account.login", methods={"POST"})
  490.      */
  491.     public function login(RequestDataBag $dataSalesChannelContext $context): JsonResponse
  492.     {
  493.         $request $this->requestStack->getCurrentRequest();
  494.         $languageCode $request->headers->get('language-code'$request->getLocale());
  495.         $now = new \DateTimeImmutable();
  496.         $email $data->get('email'$data->get('username'));
  497.         if (empty($email)) {
  498.             return (new CustomApiException($this->translator->trans('message.auth.00101', [], null$languageCode)))->jsonResponse();
  499.         }
  500.         if (empty($data->get('password'))) {
  501.             return (new CustomApiException($this->translator->trans('message.auth.00102', [], null$languageCode)))->jsonResponse();
  502.         }
  503.         $event = new CustomerBeforeLoginEvent($context$email);
  504.         $this->eventDispatcher->dispatch($event);
  505.         if ($this->requestStack->getMainRequest() !== null) {
  506.             $cacheKey strtolower($email) . '-' $this->requestStack->getMainRequest()->getClientIp();
  507.             try {
  508.                 $this->rateLimiter->ensureAccepted(RateLimiter::LOGIN_ROUTE$cacheKey);
  509.             } catch (RateLimitExceededException $exception) {
  510.                 return (new CustomApiException($this->translator->trans('message.auth.00102', [], null$languageCode)))->jsonResponse();
  511.             }
  512.         }
  513.         try {
  514.             $customer $this->getCustomerByLogin(
  515.                 $email,
  516.                 $data->get('password'),
  517.                 $context
  518.             );
  519.         } catch (CustomerNotFoundException|BadCredentialsException $exception) {
  520.             return (new CustomApiException($this->translator->trans('message.auth.00108', [], null$languageCode)))->jsonResponse();
  521.         } catch (CheckDeleteException $exception) {
  522.             return (new CustomApiException($this->translator->trans('message.auth.00109', [], null$languageCode)))->jsonResponse();
  523.         }
  524.         if (isset($cacheKey)) {
  525.             $this->rateLimiter->reset(RateLimiter::LOGIN_ROUTE$cacheKey);
  526.         }
  527.         $context $this->restorer->restore($customer->getId(), $context);
  528.         $newToken $context->getToken();
  529.         $this->customerRepository->update([
  530.             [
  531.                 'id' => $customer->getId(),
  532.                 'lastLogin' => $now,
  533.                 'languageId' => $context->getLanguageId(),
  534.             ],
  535.         ], $context->getContext());
  536.         $event = new CustomerLoginEvent($context$customer$newToken);
  537.         $this->eventDispatcher->dispatch($event);
  538.         $firstTurn true;
  539.         if (isset($customer->getCustomFields()['luckyWheelFirstTurn'])) {
  540.             $firstTurn $customer->getCustomFields()['luckyWheelFirstTurn'];
  541.         }
  542.         $unView $this->accountService->getCountUnviewNotification($context$customer->getId());
  543.         $credits $this->rewardPointService->getCustomerTotalRewardPoint($customer->getId(), $context->getContext());
  544.         $turns $this->luckyWheelApiService->handleUserTurn($context);
  545.         $hasBoughtProduct $this->accountService->checkHasBoughtProduct($customer->getId(), $context);
  546.         $customer->common = [
  547.             'unView' => $unView,
  548.             'credits' => $credits,
  549.             'turns' => $turns == null $turns,
  550.             'hasBoughtProduct' => $hasBoughtProduct 0
  551.         ];
  552.         return new JsonResponse(new ArrayStruct(
  553.             [
  554.                 'contextToken' => $newToken,
  555.                 'redirectUrl' => null,
  556.                 'expiresAt' => $now->add(new \DateInterval($this->lifetimeInterval)),
  557.                 'active' => $customer->getActive(),
  558.                 'luckyWheelFirstTurn' => $firstTurn,
  559.                 'profile' => $customer
  560.             ]
  561.         ));
  562.     }
  563.     private function getCustomerByLogin(string $emailstring $passwordSalesChannelContext $context): CustomerEntity
  564.     {
  565.         $customer $this->getCustomerByEmail($email$context);
  566.         if ($customer->hasLegacyPassword()) {
  567.             if (!$this->legacyPasswordVerifier->verify($password$customer)) {
  568.                 throw new BadCredentialsException();
  569.             }
  570.             $this->updatePasswordHash($password$customer$context->getContext());
  571.             return $customer;
  572.         }
  573.         if (!password_verify($password$customer->getPassword() ?? '')) {
  574.             throw new BadCredentialsException();
  575.         }
  576.         return $customer;
  577.     }
  578.     private function getCustomerByEmail(string $emailSalesChannelContext $context): CustomerEntity
  579.     {
  580.         $criteria = new Criteria();
  581.         $criteria->setTitle('login-route');
  582.         $criteria->addFilter(new EqualsFilter('customer.email'$email));
  583.         $result $this->customerRepository->search($criteria$context->getContext());
  584.         $result $result->filter(static function (CustomerEntity $customer) use ($context) {
  585.             $isConfirmed = !$customer->getDoubleOptInRegistration() || $customer->getDoubleOptInConfirmDate();
  586.             // Skip guest and not active users
  587.             if (isset($customer->getCustomFields()['check_delete']) && $customer->getCustomFields()['check_delete'] && !$customer->getActive()) {
  588.                 throw new CheckDeleteException('deactive');
  589.             } else {
  590.                 if ($customer->getGuest() || (!$customer->getActive() && $isConfirmed)) {
  591.                     return null;
  592.                 }
  593.             }
  594.             // If not bound, we still need to consider it
  595.             if ($customer->getBoundSalesChannelId() === null) {
  596.                 return true;
  597.             }
  598.             // It is bound, but not to the current one. Skip it
  599.             if ($customer->getBoundSalesChannelId() !== $context->getSalesChannel()->getId()) {
  600.                 return null;
  601.             }
  602.             return true;
  603.         });
  604.         if ($result->count() !== 1) {
  605.             throw new BadCredentialsException();
  606.         }
  607.         return $result->first();
  608.     }
  609.     private function updatePasswordHash(string $passwordCustomerEntity $customerContext $context): void
  610.     {
  611.         $this->customerRepository->update([
  612.             [
  613.                 'id' => $customer->getId(),
  614.                 'password' => $password,
  615.                 'legacyPassword' => null,
  616.                 'legacyEncoder' => null,
  617.             ],
  618.         ], $context);
  619.     }
  620.     #[Route('/store-api/solomon/register'name'mobile.solomon.register'methods: ['POST'])]
  621.     public function solomonRegister(Request $requestSalesChannelContext $context): JsonResponse
  622.     {
  623.         $this->solomonFillData($request$context);
  624.         $languageCode $request->headers->get('language-code'$request->getLocale());
  625.         $result $this->solomonRegisterValidate($request$context);
  626.         if (!empty($result) && $result->getCustomer() instanceof CustomerEntity) {
  627.             $this->contextResolver->resolve($request);
  628.             $context $request->attributes->get('sw-sales-channel-context');
  629.             $customer $result->getCustomer();
  630.             $this->setCustomFields($context$customer, [
  631.                 'customerCountryCode' => $request->request->get('countryCode'),
  632.                 'customerLanguageCode' => $request->request->get('languageCode'),
  633.                 'customerCountryId' => $request->request->get('countryId'),
  634.                 'customerLanguageId' => $request->request->get('languageId'),
  635.                 'luckyWheelFirstTurn' => false
  636.             ]);
  637.             $customerSerialized = (new CustomerResponse($customer))->getCustomer()->jsonSerialize();
  638.             if (!empty($customerSerialized['password'])) {
  639.                 unset($customerSerialized['password']);
  640.             }
  641.             $dataLogin = new RequestDataBag();
  642.             $dataLogin->set('email'$customer->getEmail());
  643.             $dataLogin->set('password'$request->get('password'));
  644.             $data $this->solomonLogin($dataLogin$context);
  645.             return new JsonResponse(new ArrayStruct($data));
  646.         }
  647.         return (new CustomApiException($this->translator->trans('message.common.00004', [], null$languageCode)))->jsonResponse();
  648. //        return new JsonResponse(new ArrayStruct());
  649.     }
  650.     public function solomonFillData(Request $requestSalesChannelContext $context)
  651.     {
  652.         $currentSalesChannelLanguageId $context->getContext()->getLanguageId();
  653. //        $currentCountryId = $context->getSalesChannel()->getCountryId();
  654.         $currentCountryId $request->request->get('countryId');;
  655.         $currentSalesChannelDomain $context->getSalesChannel()?->getDomains()?->filterByProperty('languageId'$currentSalesChannelLanguageId)?->first();
  656.         $this->handleFillData($request$currentCountryId$currentSalesChannelDomain$context);
  657.         $language $this->languageRepository->search((new Criteria([$currentSalesChannelLanguageId]))->addAssociations(['translationCode''locale']), $context->getContext())->first();
  658.         $request->request->set('languageId'$currentSalesChannelLanguageId);
  659.         $languageCode strtolower(explode('-'$language?->getTranslationCode()?->getCode())[0]);
  660.         $request->request->set('languageCode'$languageCode);
  661.         if (!empty($currentCountryId)) {
  662.             $countryCriteria = new Criteria();
  663.             $countryCriteria->addFilter(new EqualsFilter('id'$currentCountryId));;
  664.             $country $this->countryRepository->search($countryCriteria$context->getContext())->first();
  665.             if (!empty($country)) {
  666.                 $request->request->set('countryCode'$country->getIso());
  667.             }
  668.         }
  669.         $billingAddress = [
  670.             'countryId' => $currentCountryId,
  671.             'salutationId' => $request->request->get('salutationId'),
  672.             'firstName' => $request->request->get('firstName') ?? '',
  673.             'lastName' => $request->request->get('lastName') ?? '',
  674.             'zipcode' => 'noinfo',
  675.             'city' => 'noinfo',
  676.             'street' => 'noinfo',
  677.             'phoneNumber' => $request->request->get('phoneNumber'),
  678.         ];
  679.         $request->request->set('billingAddress'$billingAddress);
  680.         if (empty($request->request->get('storefrontUrl')) && !empty($currentSalesChannelDomain)) {
  681.             $request->request->set('storefrontUrl'$currentSalesChannelDomain->getUrl());
  682.         }
  683.     }
  684.     public function solomonLogin($dataSalesChannelContext $context)
  685.     {
  686.         $request $this->requestStack->getCurrentRequest();
  687.         $languageCode $request->headers->get('language-code'$request->getLocale());
  688.         $now = new \DateTimeImmutable();
  689.         $email $data->get('email'$data->get('username'));
  690.         if (empty($email)) {
  691.             return (new CustomApiException($this->translator->trans('message.auth.00101', [], null$languageCode)))->jsonResponse();
  692.         }
  693.         if (empty($data->get('password'))) {
  694.             return (new CustomApiException($this->translator->trans('message.auth.00102', [], null$languageCode)))->jsonResponse();
  695.         }
  696.         $event = new CustomerBeforeLoginEvent($context$email);
  697.         $this->eventDispatcher->dispatch($event);
  698.         if ($this->requestStack->getMainRequest() !== null) {
  699.             $cacheKey strtolower($email) . '-' $this->requestStack->getMainRequest()->getClientIp();
  700.             try {
  701.                 $this->rateLimiter->ensureAccepted(RateLimiter::LOGIN_ROUTE$cacheKey);
  702.             } catch (RateLimitExceededException $exception) {
  703.                 return (new CustomApiException($this->translator->trans('message.auth.00102', [], null$languageCode)))->jsonResponse();
  704.             }
  705.         }
  706.         try {
  707.             $customer $this->getCustomerByLogin(
  708.                 $email,
  709.                 $data->get('password'),
  710.                 $context
  711.             );
  712.         } catch (CustomerNotFoundException|BadCredentialsException $exception) {
  713.             return (new CustomApiException($this->translator->trans('message.auth.00108', [], null$languageCode)))->jsonResponse();
  714.         } catch (CheckDeleteException $exception) {
  715.             return (new CustomApiException($this->translator->trans('message.auth.00109', [], null$languageCode)))->jsonResponse();
  716.         }
  717.         if (isset($cacheKey)) {
  718.             $this->rateLimiter->reset(RateLimiter::LOGIN_ROUTE$cacheKey);
  719.         }
  720.         $context $this->restorer->restore($customer->getId(), $context);
  721.         $newToken $context->getToken();
  722.         $this->customerRepository->update([
  723.             [
  724.                 'id' => $customer->getId(),
  725.                 'lastLogin' => $now,
  726.                 'languageId' => $context->getLanguageId(),
  727.             ],
  728.         ], $context->getContext());
  729.         $event = new CustomerLoginEvent($context$customer$newToken);
  730.         $this->eventDispatcher->dispatch($event);
  731.         $firstTurn true;
  732.         if (isset($customer->getCustomFields()['luckyWheelFirstTurn'])) {
  733.             $firstTurn $customer->getCustomFields()['luckyWheelFirstTurn'];
  734.         }
  735.         $unView $this->accountService->getCountUnviewNotification($context$customer->getId());
  736.         $credits $this->rewardPointService->getCustomerTotalRewardPoint($customer->getId(), $context->getContext());
  737.         $turns $this->luckyWheelApiService->handleUserTurn($context);
  738.         $hasBoughtProduct $this->accountService->checkHasBoughtProduct($customer->getId(), $context);
  739.         $customer->common = [
  740.             'unView' => $unView,
  741.             'credits' => $credits,
  742.             'turns' => $turns == null $turns,
  743.             'hasBoughtProduct' => $hasBoughtProduct 0
  744.         ];
  745.         return [
  746.             'contextToken' => $newToken,
  747.             'redirectUrl' => null,
  748.             'expiresAt' => $now->add(new \DateInterval($this->lifetimeInterval)),
  749.             'active' => $customer->getActive(),
  750.             'luckyWheelFirstTurn' => $firstTurn,
  751.             'profile' => $customer
  752.         ];
  753.     }
  754.     public function handleFillData($request$currentCountryId$currentSalesChannelDomainSalesChannelContext $context)
  755.     {
  756.         if (!empty($currentSalesChannelLanguageId)) {
  757.             $language $this->languageRepository->search((new Criteria([$currentSalesChannelLanguageId]))->addAssociations(['translationCode''locale']), $context->getContext())->first();
  758.             $request->request->set('languageId'$currentSalesChannelLanguageId);
  759.             if (!empty($language?->getTranslationCode()?->getCode())) {
  760.                 $languageCode strtolower(explode('-'$language?->getTranslationCode()?->getCode())[0]);
  761.                 $countryCode strtolower(explode('-'$language?->getTranslationCode()?->getCode())[1]);
  762.                 $request->request->set('languageCode'$languageCode);
  763.                 $request->request->set('countryCode'$countryCode);
  764.                 if (!empty($countryCode)) {
  765.                     $isoCode strtoupper($countryCode);
  766.                     if ($isoCode === 'GB') {
  767.                         $isoCode 'DE';
  768.                         $countryIds $this->countryRepository->searchIds((new Criteria())->addFilter(new EqualsFilter('iso'$isoCode)), $context->getContext())->getIds();
  769.                         if (!empty($countryIds) && $countryIds[0] != $currentCountryId) {
  770.                             $currentCountryId $countryIds[0];
  771.                         }
  772.                     }
  773.                     if (!empty($currentCountryId)) {
  774.                         $request->request->set('countryId'$currentCountryId);
  775.                     }
  776.                 }
  777.             }
  778.         }
  779.         if (empty($request->request->get('userName')) && !empty($request->request->get('email'))) {
  780.             $request->request->set('userName'$request->request->get('email'));
  781.         }
  782.         $birthDay = !empty($request->request->get('birthday')) ? date_parse_from_format('Y-m-d'$request->request->get('birthday')) : null;
  783.         if (!empty($birthDay)) {
  784.             $request->request->set('birthdayDay'$birthDay['day']);
  785.             $request->request->set('birthdayMonth'$birthDay['month']);
  786.             $request->request->set('birthdayYear'$birthDay['year']);
  787.         }
  788.         if (empty($request->request->get('salutationId'))) {
  789.             $salutation $this->salutationRepository->search(new Criteria(), $context->getContext())->first();
  790.             if (!empty($salutation)) {
  791.                 $request->request->set('salutationId'$salutation->getId());
  792.             }
  793.         }
  794.         if (empty($request->request->get('storefrontUrl')) && !empty($currentSalesChannelDomain)) {
  795.             $request->request->set('storefrontUrl'$currentSalesChannelDomain->getUrl());
  796.         }
  797.     }
  798.     public function solomonRegisterValidate($request$context)
  799.     {
  800.         $languageCode $request->headers->get('language-code'$request->getLocale());
  801.         $definition $this->customerValidationFactory->create($context);
  802.         $minLength $this->systemConfigService->get('core.loginRegistration.passwordMinLength'$context->getSalesChannel()->getId());
  803.         $definition->add('password', new NotBlank(), new Length(['min' => $minLength]));
  804.         $options = ['context' => $context->getContext(), 'salesChannelContext' => $context];
  805.         $definition->add('email', new CustomerEmailUnique($options));
  806. //        $definition->add("phoneNumber", new NotBlank());
  807.         $definition->add('affiliateCode', new NotBlank());
  808.         $definition->add('userName', new NotBlank());
  809.         $definition->add('birthday', new NotBlank());
  810.         $constraints $this->getAffiliateCodeConstraints($context->getContext());
  811.         if (!empty($request->get('affiliateCode'))) {
  812.             foreach ($constraints as $constraint) {
  813.                 $definition->add('affiliateCode'$constraint);
  814.             }
  815.         }
  816.         $validation $this->dataValidator->getViolations($request->request->all(), $definition);
  817.         //check for validation errors.
  818.         if ($validation->count() > 0) {
  819.             $translatedViolations = new ConstraintViolationList();
  820.             foreach ($validation as $violation) {
  821.                 $message $violation->getMessageTemplate();
  822.                 $propertyPath $violation->getPropertyPath();
  823.                 switch ($propertyPath) {
  824.                     case '/firstName':
  825.                     {
  826.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  827.                         break;
  828.                     }
  829.                     case '/lastName':
  830.                     {
  831.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  832.                         break;
  833.                     }
  834.                     case '/password':
  835.                     {
  836.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  837.                         break;
  838.                     }
  839.                     case '/email':
  840.                     {
  841.                         if (isset($violation->getParameters()['{{ email }}'])) {
  842.                             $email $violation->getParameters()['{{ email }}'];
  843.                             $translatedMessage $this->translator->trans("message.auth.00110.$message", ['{{ email }}' => $email], null$languageCode);
  844.                         } else {
  845.                             $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  846.                         }
  847.                         break;
  848.                     }
  849.                     case '/userName':
  850.                     {
  851.                         $translatedMessage $this->translator->trans("message.auth.00110.$message", [], null$languageCode);
  852.                         break;
  853.                     }
  854.                     default:
  855.                     {
  856.                         $translatedMessage $this->translator->trans("message.auth.00111", [], null$languageCode);
  857.                     }
  858.                 }
  859.                 $translatedViolations->add(new ConstraintViolation(
  860.                     $translatedMessage,
  861.                     $violation->getMessageTemplate(),
  862.                     $violation->getParameters(),
  863.                     $violation->getRoot(),
  864.                     $violation->getPropertyPath(),
  865.                     $violation->getInvalidValue()
  866.                 ));
  867.             }
  868.             throw new ConstraintViolationException($translatedViolations$request->request->all());
  869.         }
  870.         $requestDataBag = new RequestDataBag($request->request->all());
  871.         return $this->registerRoute->register($requestDataBag$context);
  872.     }
  873.     #[Route('/store-api/home/solomon-language-list'name'mobile.cms.language.list'methods: ['GET'])]
  874.     public function getSolomonLanguageList(Request $requestSalesChannelContext $context)
  875.     {
  876.         $languages $this->languageRepository->search((new Criteria())
  877.             ->addFilter(new EqualsFilter('salesChannelDomains.salesChannelId'$context->getSalesChannelId()))
  878. //            ->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
  879. //                new ContainsFilter('translationCode.code', 'en-GB'),
  880. //                new ContainsFilter('translationCode.code', 'de-DE'),
  881. //            ]))
  882.             ->addFilter(new NotFilter(MultiFilter::CONNECTION_OR, [
  883.                 new ContainsFilter('translationCode.code''th-TH'),
  884.                 new ContainsFilter('translationCode.code''en-HK'),
  885.                 new ContainsFilter('translationCode.code''es-ES'),
  886.             ]))
  887.             ->addAssociations(['locale''translationCode']), $context->getContext())->getEntities()->jsonSerialize();
  888.         return new JsonResponse(
  889.             ['data' => $languages'message' => 'success']
  890.         );
  891.     }
  892. }