*/ class BoutiqueController extends Controller { public function index() { ClubController::getNotifications(); $sellItems = SellItem::all(); return view('front.club.boutique.boutique', compact('sellItems')); } /** * Ajax service to validate transaction details * * @param BoutiqueBuyRequest $request * @return void */ public function validateBuy(BoutiqueBuyRequest $request) { $items = []; $orderTotalPrice = 0; foreach ($request->cart as $item) { $sellItem = SellItem::where('id', $item['item']['id'])->firstOrFail(); $items[] = [ 'id' => $sellItem->id, 'name' => $sellItem->name, 'quantity' => $item['count'], 'unitPrice' => $sellItem->price, 'totalPrice' => $item['count'] * $sellItem->price, 'subject' => $sellItem->archadia_id ]; $orderTotalPrice += $item['count'] * $sellItem->price; } $order = new Order(); /** @var \App\Models\Customers */ $customer = Auth::guard('customer')->user(); if ($customer) { $customer->updateApiData(new CustomerCrmResource($customer)); $order->customer()->associate($customer); } $order->fill($request->coords); $order->payementType = OrderType::make((int)$request->payementMethod); $order->status = OrderStatus::waiting(); $order->saveOrFail(); foreach ($items as $item) { $order->sellItems()->withTimestamps()->attach($item['id'], ['quantity' => $item['quantity']]); } $order->saveOrFail(); if ($request->shipping) { $shipping = new DeliveryAddress(); $shipping->fill($request->shipping); $shipping->order()->associate($order); $shipping->saveOrFail(); } switch ($request->payementMethod) { case OrderType::cb(): \session()->put(sprintf('boutique.order.%d.time', $order->id), \time()); \session()->save(); return response()->json(['redirect' => route('club.boutique.payementCB', compact('order'))]); case OrderType::check(): try { \session()->put(sprintf('boutique.order.%d.time', $order->id), \time()); \session()->save(); SendOrderToArchadia::dispatch($order)->onQueue('boutique'); SendBoutiqueMails::dispatch($order)->onQueue('emails'); $order->status = OrderStatus::waitingCheck(); $order->saveOrFail(); } catch (Exception $e) { report($e); Log::critical(sprintf( 'Could not end customer transaction (save and send mail) in boutique for order %d, %s', $order->id, $e->getMessage() )); } finally { return response()->json(['redirect' => route('club.boutique.validated', compact('order'))]); } break; // not used default: abort(422); } } /** * Redirection After Ajax validate * * @param Order $order * @return void */ public function validatedBuy(Order $order) { $time = session()->get(sprintf('boutique.order.%d.time', $order->id), 0); // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace if ( !($order->status->equals(OrderStatus::payed()) || $order->status->equals(OrderStatus::waitingCheck())) ) { return response()->redirectToRoute('club.boutique.index'); } // 4 minutes if (($time + (60 * 4)) < \time()) { return response()->redirectToRoute('club.boutique.index'); } return view('front.club.boutique.validated'); } public function payWithCb(Order $order) { try { $time = session()->get(sprintf('boutique.order.%d.time', $order->id), 0); if ($order->status->equals(OrderStatus::failed())) { return response()->redirectToRoute('club.boutique.index'); } // 4 minutes if (($time + (60 * 4)) < \time()) { return response()->redirectToRoute('club.boutique.index'); } $totalToPay = $order->sellItems->map(function ($sellItem) { return $sellItem->pivot->quantity * $sellItem->price; })->sum(); // Convert total in cents $totalToPay = (int)(\number_format((float)$totalToPay, 2) * 100); $mercanet = $this->getMercanet(); $payment = $mercanet->QuickPayment($totalToPay, $order->ref); switch ($payment->code) { case RedirectionStatusCode::redirect(): return view('front.club.boutique.redirectMercanet', [ 'url' => $payment->url, 'data' => $payment->data, 'version' => $payment->version ]); case RedirectionStatusCode::referenceAlreadyUsed(): return response()->redirectToRoute('club.boutique.index'); case RedirectionStatusCode::requestError03(): case RedirectionStatusCode::requestError12(): case RedirectionStatusCode::requestError30(): case RedirectionStatusCode::requestError34(): case RedirectionStatusCode::serviceDisponibiltyIssue(): $order->status = OrderStatus::failed(); $order->save(); throw new CBPayementError(sprintf( "Error response from Mercanet Lib : %s\n %s", RedirectionStatusCode::make($payment->code)->label, json_encode($payment) )); default: $order->status = OrderStatus::failed(); $order->save(); throw new CBPayementError(sprintf( 'Un handled response from Mercanet Lib %s', json_encode($payment) )); } } catch (ModelNotFoundException | CBPayementError $e) { report($e); Log::critical(sprintf( 'Could not end customer transaction in boutique for order %d, %s', $order->id, $e->getMessage() )); return view('front.club.boutique.error'); } } /** ** Pas de validate ou custom request sur la réponse de mercanet, ** c'est la Lib mercanet qui fait * TODO: tester cette methode (automatique url une fois en Dev) */ public function confirmCbFromMercanet(Request $request, Order $order) { try { \extract($this->getMercanetData($request)); $order = Order::where('ref', $ref) ->findOrFail(); $mercanet = $this->getMercanet(); $mercanet->setResponse($request->post()); if (!$mercanet->isValid()) { $order->status = OrderStatus::failed(); $order->save(); throw new CBPayementError('Data were altered from response'); } switch ($responseCode) { case TransactionResponse::accepted(): $order->status = OrderStatus::payed(); $order->save(); break; case TransactionResponse::refused(): case TransactionResponse::fraud(): case TransactionResponse::maxReached(): $order->status = OrderStatus::failed(); $order->save(); break; case TransactionResponse::technicalIssue(): throw new CBPayementError(sprintf( 'Error from Mercanet Server %s on ref %s, %s', $responseCode, $ref, $formDatas )); default: // ERROR $order->status = OrderStatus::failed(); $order->save(); throw new CBPayementError(sprintf( 'Un handled response from Mercanet Server %s on ref %s', $responseCode, $ref, $formDatas )); } } catch (ModelNotFoundException | CBPayementError $e) { report($e); Log::critical(sprintf( 'Could not end customer transaction in boutique for order %d, %s', $order->id, $e->getMessage() )); } finally { return response('', 200); } } /** * TODO ICI manque un appel à ARCADIA pour dire que la commande est passée cf: legaulois2016 * ** Pas de validate ou custom request sur la réponse de mercanet, ** c'est la Lib mercanet qui fait * * @param Request $request * @param Order $order * @return void */ public function confirmCb(Request $request, Order $order) { try { if ($order->status->equals(OrderStatus::failed())) { return response()->redirectToRoute('club.boutique.error'); } if (!$order->status->equals(OrderStatus::waiting())) { return response()->redirectToRoute('club.boutique.index'); } $mercanet = $this->getMercanet(); $mercanet->setResponse($request->post()); if (!$mercanet->isValid()) { try { $order->status = OrderStatus::failed(); $order->saveOrFail(); } finally { throw new CBPayementError('Data were altered from response'); } } \extract($this->getMercanetData($request)); switch ($responseCode) { case PaymentResponse::ok(): try { SendOrderToArchadia::dispatch($order)->onQueue('boutique'); SendBoutiqueMails::dispatch($order)->onQueue('emails'); $order->status = OrderStatus::payed(); $order->saveOrFail(); } catch (Exception $e) { report($e); Log::critical(sprintf( 'Could not end customer transaction (save and send mail) in boutique for order %d, %s', $order->id, $e->getMessage() )); } finally { return view('front.club.boutique.validated'); } break; // not used case PaymentResponse::invalidIds(): case PaymentResponse::invalidRequestFormat(): case PaymentResponse::invalidParameters(): case PaymentResponse::unreachableService(): case PaymentResponse::securityIssue(): case PaymentResponse::transactionAlreadyExists(): case PaymentResponse::internalIssue(): $order->status = OrderStatus::failed(); $order->save(); throw new CBPayementError(sprintf( "Error response from Mercanet Lib : %s\n %s", PaymentResponse::make($responseCode)->label, json_encode($returnDatas) )); default: $order->status = OrderStatus::failed(); $order->save(); throw new CBPayementError(sprintf( 'Un handled response from Mercanet Lib %s', json_encode($returnDatas) )); } } catch (ModelNotFoundException | CBPayementError $e) { report($e); Log::critical(sprintf( 'Could not end customer transaction in boutique for order %d, %s', $order->id, $e->getMessage() )); return view('front.club.boutique.error'); } } /** * Get Initialized Mercanet object * * @return Mercanet */ private function getMercanet(): Mercanet { return new Mercanet( config('mercanet.secret'), config('mercanet.merchantId'), config('mercanet.env'), route('club.boutique.reglement.payementConfirmCB', compact('order')), route('club.boutique.reglement.payementConfirmCBServ', compact('order')) ); } /** * Get Mercanet ref and responseCode for request * @return array['ref' => 'value', 'responseCode' => 'value'] */ private function getMercanetData(Request $request): array { $formDatas = []; $returnDatas = explode("|", $request->post('Data')); foreach ($returnDatas as $item) { $tmp = explode("=", $item); $formDatas[$tmp[0]] = $tmp[1]; } $ref = $formDatas['transactionReference']; $responseCode = $formDatas['responseCode']; return compact('ref', 'responseCode'); } }