middleware(function (Request $request, callable $next) { $this->authorize('fo.gameEnabled'); $this->assertUserHasEnoughInfoToUseMesBr(); return $next($request); }); } /** * Display participation form * * @return \Illuminate\Contracts\View\View */ public function participate(): \Illuminate\Contracts\View\View { if (!Gate::check('participate', Participation::class)) { return view('theme::pages.participation.participation-exhausted'); } return view('theme::pages.participation.participate'); } /** * Participate Method * * @param \App\Http\Requests\Fo\ParticipateRequest $request * @return \Illuminate\Contracts\View\View */ public function participateSubmit(ParticipateRequest $request): \Illuminate\Contracts\View\View { return DB::transaction(function () use ($request) { // ! On verrouille le ou les instants gagnant /** @var \App\Models\InstantWin|null */ $instantWin = InstantWin::query()->lockForUpdate() ->where('available_at', '<', now()) ->whereDoesntHave('participation') ->get()->sortBy('available_at')->first(); /** @var \App\Models\Settings */ $settings = app(Settings::class); /** @var \App\Models\Customer */ $customer = auth('frontend')->user(); /** @var \App\Models\Code */ $code = $request->post('unique_code'); /** @var \App\Models\Product|null */ $product = $request->post('product'); // * Make new participation $participation = Participation::makeNew( app('clientIp'), $customer, $code, $product ); // * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant. // ? As-t-on un instant gagnant ? if (Participation::customerAlreadyHasAnInstantWin($customer) or !$instantWin) { // * --- DISCOUNT WIN --- // ? Les BR sont activés ? if (!$settings->discounts_activated) { // * On enregistre la participation du joueur. $participation->save(); // * Pas de coupons disponibles, le joueur à perdu return view('theme::pages.participation.loose'); } // * Le joueur à gagné un coupon. /** @var \App\Lib\Discount Coupon aléatoire en fonction du ratio de chaque coupon */ $discount = Arr::get( $settings->web_coupons, $this->getRandomWeightedElementIndex($settings->web_coupons) ); $mesBrDiscount = $this->setDiscountAsWon($customer, $discount); // * Le joueur à remporté un coupon $participation->discount()->associate($mesBrDiscount); // * On enregistre la participation du joueur. $participation->save(); $crmCustomer = $this->fetchCrmCustomer($customer); $customer->notify(new HasWonDiscount( $discount, $mesBrDiscount, $crmCustomer->firstname )); return view('theme::pages.participation.won', compact('discount', 'mesBrDiscount')); } //end if // * --- INSTANT WIN --- // * On enregistre la participation du joueur. $participation->save(); // * Le joueur à remporté un instant gagnant $instantWin->participation()->associate($participation); $instantWin->save(); $crmCustomer = $this->fetchCrmCustomer($customer); // * Notify customer for his win $customer->notify(new HasWonInstantWin( $instantWin->prize, $crmCustomer->firstname )); // * Dispatch notification to mail list Mail::to($settings->notification_emails)->send(new PrizeWonAdminMail( $settings->name, $participation->created_at, $instantWin->prize->label, $crmCustomer, $code->code_unique, optional($product)->ean_code )); return view('theme::pages.participation.won', [ 'prizeModel' => $instantWin->prize, 'participationModel' => $participation, 'crmCustomer' => $crmCustomer ]); }); } // * --- PRIVATE METHODS --- /** * Try Call CRM + MesBr to setup discount * * @param \App\Models\Customer $customer * @param LibDiscount $discount * @return \App\Models\Discount * @throws \Illuminate\Http\Exceptions\HttpResponseException If Any error occurs. * @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber */ private function setDiscountAsWon(Customer $customer, LibDiscount $discount): \App\Models\Discount { // phpcs:enable // * Phase - 1 Recupération des informations CRM /** @var \LDC\LDCCRM\Models\Customer*/ $crmUser = $this->fetchCrmCustomer($customer); // * Phase - 2 Mise à jour du coupon dans MesBr try { /** @var \App\Models\Settings */ $settings = app(Settings::class); /** @var \LDC\MESBR\MesBr */ $mesBr = app('mesBrAPI'); // * MaJ Customer /** @var integer */ $crm_customer_id = retry(3, function () use ($mesBr, $crmUser) { /** * Parce que MesBr n'est pas fiable il faut retenter pour avoir * l'id client, des fois cet id sera dans hashcode... * * Id est necessaire pour update panier.. */ $result = $mesBr->updateCustomer($crmUser->hashcode, $crmUser->email, $crmUser->country); if (optional($result)->status !== "OK") { throw new \RuntimeException( \sprintf( 'MESBR : Une erreur est survenue. Code : %s Message : %s', $result->code ?? 'Undefined', $result->message ?? 'Undefined' ) ); } /** @var object|null */ $crm_customer = optional($result)->customer; if (empty(optional($crm_customer)->id)) { throw new \RuntimeException( 'MESBR : Une erreur est survenue. Update Customer n\'as pas retourné le customer id' ); } return intval($crm_customer->id); }, 100); // * Maj Panier $result = $mesBr->updatePanier( $crmUser, $crm_customer_id, $settings->discounts_site, strval($discount->web_coupon_identifiant) ); if (Arr::get($result, 'status') === "OK") { // * La réponse est conforme ? $needed_fields = ['link', 'verif', 'basketId']; if (!Arr::has($result, $needed_fields)) { throw new \RuntimeException(sprintf( 'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s', json_encode($needed_fields), json_encode($result), )); } $discountModel = new Discount(); $discountModel->link = Arr::get($result, 'link'); $discountModel->verification = Arr::get($result, 'verif'); $discountModel->basket_id = Arr::get($result, 'basketId'); $discountModel->save(); // * Tout est OK return $discountModel; } throw new \RuntimeException( \sprintf( 'MESBR : Une erreur est survenue. Code : %s Message : %s', $result["code"] ?? 'Undefined', $result["message"] ?? 'Undefined' ) ); } catch (\RuntimeException $e) { report($e); // * Retenter ? back()->with('error', trans('Une erreur est survenue, veuillez réssayer.'))->throwResponse(); } //end try throw new \Error('Should not reach this point'); } /** * Fetch Crm Customer * * @param \App\Models\Customer $customer * @return \LDC\LDCCRM\Models\Customer|\Illuminate\Http\RedirectResponse * @throws \Illuminate\Http\Exceptions\HttpResponseException In case of failure or customer not activated. */ private function fetchCrmCustomer(Customer $customer): \LDC\LDCCRM\Models\Customer|\Illuminate\Http\RedirectResponse { try { $crmUser = $customer->api_data; // ? Le compte du joueur est vérifié ? if (!$crmUser->status) { // * Le joueur doit avoir un compte vérifié pour participer. response()->redirectToRoute('fo.verification.notice') ->with('error', trans('Pour participer votre compte doit être activé')) ->throwResponse(); } return $crmUser; } catch (ApiErrorException $e) { // * Customer is not found. if ($e->getApiError()->equals(ApiError::modelNotFound())) { // * Déconnexion immédiate et retour maison. auth('frontend')->logout(); response()->redirectToRoute('fo.home') ->with('error', trans('Une erreur est survenue, compte introuvable.')) ->throwResponse(); } report($e); Log::error('Unhandled response ' . $e->getApiError()->label); // * Retenter ? back()->with('error', trans('Une erreur est survenue, veuillez réssayer.'))->throwResponse(); } catch (HttpResponseException $e) { // * continue this throw $e; } catch (CrmException | \Exception $e) { report($e); // * Retenter ? back()->with('error', trans('Une erreur est survenue, veuillez réssayer.'))->throwResponse(); } //end try return back()->withErrors(trans('Une erreur est survenue, merci de réessayer ultérieurement.')); } /** * Assert that user has enough info filled on his profile to win a Discount * Or redirect him to the fill form * ! Also validates its birth date (because beeing an adult is required to participate) * * @return void * @throws \Illuminate\Http\Exceptions\HttpResponseException If customer data doest not validate * or and error occurs. */ private function assertUserHasEnoughInfoToUseMesBr(): void { // * Valider que le client dispose des donnés requises pour MesBr try { /** @var \App\Models\Customer */ $authUser = auth('frontend')->user(); $crmCustomer = $this->fetchCrmCustomer($authUser); // * On valide les données selon les critères du site $rules = collect((new CreateAccountRequest())->rules()); Validator::make( [ 'birth_date' => $crmCustomer->birth_date, 'address' => $crmCustomer->address, 'postal_code' => $crmCustomer->postcode, 'country' => $crmCustomer->country->value, 'city' => $crmCustomer->city ], [ 'birth_date' => $rules->get('birth_date'), 'address' => $rules->get('address'), 'postal_code' => $rules->get('postal_code'), 'country' => $rules->get('country'), 'city' => $rules->get('city') ] )->validate(); Session::remove('needCrmInfoUpdate'); } catch (ValidationException) { response()->redirectToRoute('fo.profil.verify') ->with('warning', trans( 'Vous devez compléter des informations sur votre profil pour pouvoir participer.' ))->throwResponse(); } catch (\Exception $e) { report($e); // * Retenter ? response()->redirectToRoute('fo.homepage') ->with('error', trans('Une erreur est survenue, veuillez réssayer.')) ->throwResponse(); } //end try } // * --- END PRIVATE METHODS --- }