fix conflicts

This commit is contained in:
System User 2026-03-25 10:27:46 +01:00
commit 7173bdb143
51 changed files with 2084 additions and 1727 deletions

View file

@ -75,11 +75,6 @@ GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=
## MERCANET
MERCANET_SECRET=
MERCANET_MERCHAND_ID=
MERCANET_ENV=
## TELESCOPE
TELESCOPE_ENABLED=
@ -101,3 +96,9 @@ REDIS_USERNAME=
REDIS_PASSWORD=
REDIS_PREFIX=
REDIS_SCHEME=
## AXEPTA
AXEPTA_ENDPOINT=
AXEPTA_MERCHANT_ID=
AXEPTA_APIKEY=
AXEPTA_HMAC=

View file

@ -1,44 +0,0 @@
<?php
namespace App\Enums\Mercanet;
use App\Traits\Enums\BaseEnum;
enum PaymentResponse: int
{
use BaseEnum;
case ok = 00;
case invalidIds = 03;
case invalidParameters = 12;
case canceled = 17;
case invalidRequestFormat = 30;
case securityIssue = 34;
case transactionAlreadyExists = 94;
case expired = 97;
case unreachableService = 99;
case internalIssue = 500;
/**
* Optionnal labels definition.
*
* @phpcs:disable Generic.Files.LineLength.TooLong
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::ok->name => 'Situation normale suivie du processus normal
d\'affichage des pages de paiement.',
self::invalidIds->name => 'L’identifiant commerçant ou le contrat acquéreur sont invalides.',
self::invalidParameters->name => 'Les paramètres de la transaction sont invalides. Vérifiez les
paramètres de la requête.',
self::canceled->name => 'Transaction annulée',
self::invalidRequestFormat->name => 'Le format de la requête est invalide.',
self::securityIssue->name => 'Problème de sécurité, par ex. le sceau calculé est incorrect.',
self::transactionAlreadyExists->name => 'La transaction existe déjà.',
self::expired->name => 'Délais expiré',
self::unreachableService->name => 'Service temporairement indisponible',
self::internalIssue->name => 'Erreur interne, eg: probleme appel CURL'
];
//phpcs:enable
}

View file

@ -1,35 +0,0 @@
<?php
namespace App\Enums\Mercanet;
use App\Traits\Enums\BaseEnum;
enum RedirectionStatusCode: int
{
use BaseEnum;
case redirect = 0;
case requestError03 = 3;
case requestError12 = 12;
case requestError30 = 30;
case requestError34 = 34;
case referenceAlreadyUsed = 94;
case serviceDisponibiltyIssue = 99;
case internalIssue = 500;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::redirect->name => 'Redirection Acceptée',
self::requestError03->name => 'Problème de request code 03',
self::requestError12->name => 'Problème de request code 12',
self::requestError30->name => 'Problème de request code 30',
self::requestError34->name => 'Problème de request code 34',
self::referenceAlreadyUsed->name => 'La référence de transaction à déja été utilisée',
self::serviceDisponibiltyIssue->name => 'Problème de disponibilité du service de paiement',
self::internalIssue->name => 'Erreur interne, eg: probleme appel CURL'
];
}

View file

@ -1,37 +0,0 @@
<?php
namespace App\Enums\Mercanet;
use App\Traits\Enums\BaseEnum;
enum TransactionResponse: int
{
use BaseEnum;
case accepted = 00;
case refused = 05;
case canceled = 17;
case fraud = 34;
case maxReached = 75;
case technicalIssue = 90;
case duplicate = 94;
case expired = 97;
case internalIssue = 500;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::accepted->name => 'Paiement accepté',
self::refused->name => 'Paiement refusé',
self::canceled->name => 'Paiement annulé',
self::fraud->name => 'Paiement refusé pour fraude',
self::maxReached->name => 'Paiement refusé pour nombre max essais atteint',
self::technicalIssue->name => 'Paiement refusé pour problème technique',
self::duplicate->name => 'Transaction dupliquée',
self::expired->name => 'Paiement expiré',
self::internalIssue->name => 'Erreur interne, eg: probleme appel CURL'
];
}

View file

@ -2,9 +2,6 @@
namespace App\Http\Controllers\Club;
use App\Enums\Mercanet\PaymentResponse;
use App\Enums\Mercanet\RedirectionStatusCode;
use App\Enums\Mercanet\TransactionResponse;
use App\Enums\Shop\ContactFormCivility;
use App\Enums\Shop\OrderStatus;
use App\Enums\Shop\OrderType;
@ -13,7 +10,9 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\Club\Shop\ShopBuyRequest;
use App\Jobs\Arcadia\SendOrderToArcadia;
use App\Jobs\Shop\SendShopMail;
use App\Lib\Mercanet\Mercanet;
use App\Lib\Axepta\AxeptaPayment;
use App\Lib\Axepta\Enums\PaymentResponse;
use App\Lib\Axepta\Objects\AxeptaOrder;
use App\Models\DeliveryAddress;
use App\Models\Notification;
use App\Models\Order;
@ -68,7 +67,6 @@ class ShopController extends Controller
try {
$order = new Order();
$order->ref = str_replace('-', '', $request->ref);
$order->civility = ContactFormCivility::make(intval($request->coords['civility']));
$order->payementType = OrderType::make(intval($request->payementMethod));
$order->status = OrderStatus::waiting;
$order->customer_id = $customerModel->getKey();
@ -82,13 +80,12 @@ class ShopController extends Controller
compact('order')
)]);
}
// FIXME: uncomment when mercanet cb is ready.
// if ($order->payementType->equals(OrderType::cb)) {
// return response()->json(['redirect' => route(
// 'club.shop.payementCB',
// compact('order')
// )]);
// }
if ($order->payementType->equals(OrderType::cb)) {
return response()->json(['redirect' => route(
'club.shop.payementCB',
compact('order')
)]);
}
abort(500, sprintf('Order already exists but type is unkown %d', $order->getKey()));
} //end try
@ -107,11 +104,10 @@ class ShopController extends Controller
}
switch ($request->payementMethod) {
// FIXME: uncomment when mercanet cb is ready.
// case OrderType::cb->value():
// \session()->put(sprintf('shop.order.%d.time', $order->getKey()), \time());
// \session()->save();
// return response()->json(['redirect' => route('club.shop.payementCB', compact('order'))]);
case OrderType::cb->value():
\session()->put(sprintf('shop.order.%d.time', $order->getKey()), \time());
\session()->save();
return response()->json(['redirect' => route('club.shop.payementCB', compact('order'))]);
case OrderType::check->value():
try {
\session()->put(sprintf('shop.order.%d.time', $order->getKey()), \time());
@ -161,7 +157,7 @@ class ShopController extends Controller
*
* @param \App\Models\Order $order
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
* @throws \App\Exceptions\CBPayementError Error response from Mercanet Lib.
* @throws \App\Exceptions\CBPayementError Error response from Axepta Lib.
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public function payWithCb(Order $order): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
@ -171,14 +167,17 @@ class ShopController extends Controller
try {
$time = session()->get(sprintf('shop.order.%d.time', $order->getKey()), 0);
if ($order->status->equals(OrderStatus::failed)) {
return view('club.pages.shop.error')
->with('error', trans('Commande annulée, veuillez réessayer'));
return view('club.pages.shop.info')
->with('title', trans('Commande annulée'))
->with('message', trans("Le paiement n'a pu aboutir. Veuillez réessayer")
);
}
// 4 minutes
if (($time + (60 * 4)) < \time()) {
return view('club.pages.shop.error')
->with('error', trans('Délai d\'attente expiré, veuillez réessayer'));
return view('club.pages.shop.info')
->with('title', trans('Erreur'))
->with('message', trans('Délai d\'attente expiré, veuillez réessayer'));
}
$totalToPay = $order->sellItems->map(function ($sellItem) {
@ -193,39 +192,36 @@ class ShopController extends Controller
// * Convert total in cents
$totalToPay = (int) (\number_format((float) $totalToPay, 2)) * 100;
$mercanet = $this->getMercanet($order);
$aPayment = new AxeptaPayment(
config('axepta.endpoint'),
config('axepta.merchantId'),
config('axepta.apiKey'),
config('axepta.hmac')
);
$aOrder = new AxeptaOrder(
$order->ref,
$totalToPay,
$order->customer->email
);
$payment = $mercanet->QuickPayment($totalToPay, $order->ref);
try {
$payment = $aPayment->getCheckoutUrl(
$aOrder,
route('club.shop.reglement.payementConfirmCB', $order),
route('club.shop.reglement.payementConfirmCB', ['order' => $order, 'cancel' => true]),
route('club.shop.reglement.payementConfirmCBServ', $order)
);
return response()->redirectTo($payment);
} catch (\RuntimeException $e) {
$order->status = OrderStatus::failed;
$order->save();
throw new CBPayementError(sprintf(
"Error response from Axepta Lib : %s",
$e->getMessage()
));
}
switch ($payment->code) {
case RedirectionStatusCode::redirect->value():
return view('club.pages.shop.redirectMercanet', [
'url' => $payment->url,
'data' => $payment->data,
'version' => $payment->version
]);
case RedirectionStatusCode::referenceAlreadyUsed:
return response()->redirectToRoute('club.shop.index');
case RedirectionStatusCode::requestError03->value():
case RedirectionStatusCode::requestError12->value():
case RedirectionStatusCode::requestError30->value():
case RedirectionStatusCode::requestError34->value():
case RedirectionStatusCode::serviceDisponibiltyIssue->value():
$order->status = OrderStatus::failed;
$order->save();
throw new CBPayementError(sprintf(
"Error response from Mercanet Lib : %s\n %s",
RedirectionStatusCode::make(intval($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)
));
} //end switch
} catch (ModelNotFoundException | CBPayementError $e) {
report($e);
Log::critical(sprintf(
@ -233,14 +229,16 @@ class ShopController extends Controller
$order->getKey(),
$e->getMessage()
));
return view('club.pages.shop.error')
->with('error', trans('La commande que vous venez de passer n\'a pu aboutir, veuillez réessayer'));
return view('club.pages.shop.info')
->with('title', trans('Commande annulée'))
->with('message', trans("Le paiement n'a pu aboutir. Veuillez réessayer")
);
} //end try
}
/**
* Pas de validate ou custom request sur la réponse de mercanet,
* c'est la Lib mercanet qui fait.
* Pas de validate ou custom request sur la réponse de Axepta,
* C'est la Lib Axepta qui fait.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Order $order
@ -248,40 +246,56 @@ class ShopController extends Controller
* @throws \App\Exceptions\CBPayementError Data were altered from response.
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public function confirmCbFromMercanet(
public function confirmCbFromAxepta(
Request $request,
Order $order
): \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory {
// phpcs:enable
try {
$data = $this->getMercanetData($request);
$aPayment = new AxeptaPayment(
config('axepta.endpoint'),
config('axepta.merchantId'),
config('axepta.apiKey'),
config('axepta.hmac')
);
/** @var \App\Models\Order */
$order = Order::query()->where('ref', $data['ref'])->firstOrFail();
$mercanet = $this->getMercanet($order);
$mercanet->setResponse($request->post());
if (!$mercanet->isValid()) {
if (!$aPayment->isValidWebhookAccess($request)) {
$order->status = OrderStatus::failed;
$order->save();
// @phpstan-ignore finally.exitPoint
throw new CBPayementError('Data were altered from response');
}
$response = $aPayment->getPaymentStatusById($request->payId);
switch ($data['responseCode']) {
case TransactionResponse::accepted:
$order->status = OrderStatus::payed;
if (is_null($response)) {
if (
!$order->confirmMailSent and
!$order->status->equals(OrderStatus::payed)
) {
$order->status = OrderStatus::failed;
$order->save();
SendOrderToArcadia::dispatch($order);
SendShopMail::dispatch($order);
}
return response('', 200);
}
switch (intval($response[0])) {
case PaymentResponse::ok->value:
if (
!$order->confirmMailSent and
!$order->status->equals(OrderStatus::payed)
) {
$order->status = OrderStatus::payed;
$order->save();
SendOrderToArcadia::dispatch($order);
SendShopMail::dispatch($order);
}
break;
case TransactionResponse::canceled:
case TransactionResponse::expired:
case TransactionResponse::refused:
case TransactionResponse::fraud:
case TransactionResponse::duplicate:
case TransactionResponse::maxReached:
case PaymentResponse::transient->value:
case PaymentResponse::emvinfo->value:
break;
case PaymentResponse::error->value:
case PaymentResponse::fatalerror->value:
if (
!$order->confirmMailSent and
!$order->status->equals(OrderStatus::payed)
@ -290,17 +304,9 @@ class ShopController extends Controller
$order->save();
}
break;
case TransactionResponse::technicalIssue:
// @phpstan-ignore finally.exitPoint
throw new CBPayementError(sprintf(
'Error from Mercanet Server %s on ref %s, %s',
$data['responseCode'],
$data['ref'],
json_encode($data['returnDatas']),
));
default:
// * ERROR
if (
// * ERROR
if (
!$order->confirmMailSent and
!$order->status->equals(OrderStatus::payed)
) {
@ -309,12 +315,12 @@ class ShopController extends Controller
}
// @phpstan-ignore finally.exitPoint
throw new CBPayementError(sprintf(
'Un handled response from Mercanet Server %s on ref %s, %s',
$data['responseCode'],
$data['ref'],
json_encode($data['returnDatas']),
'Unhandled response from Axepta Server %s on ref %s',
$response,
$order->ref
));
} //end switch
} //end switch
} catch (ModelNotFoundException | CBPayementError | \Exception $e) {
report($e);
Log::critical(sprintf(
@ -329,135 +335,63 @@ class ShopController extends Controller
}
/**
*
* Pas de validate ou custom request sur la réponse de mercanet,
* c'est la Lib mercanet qui fait.
* Confirm CB method
*
* @param Request $request
* @param Order $order
* @return \Illuminate\Contracts\View\View
* @throws \App\Exceptions\CBPayementError Data were altered from response.
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public function confirmCb(Request $request, Order $order): \Illuminate\Contracts\View\View
{
// phpcs:enable
Notification::getNotifications();
try {
if ($order->status->equals(OrderStatus::failed)) {
return view('club.pages.shop.error');
}
if ($order->status->equals(OrderStatus::payed)) {
return view('club.pages.shop.validated');
}
$mercanet = $this->getMercanet($order);
$mercanet->setResponse($request->post());
if (!$mercanet->isValid()) {
try {
$order->status = OrderStatus::failed;
$order->saveOrFail();
} finally {
throw new CBPayementError("Data were altered from response");
}
}
$data = $this->getMercanetData($request);
switch ($data['responseCode']) {
case PaymentResponse::ok:
try {
$status = $order->status;
$order->status = OrderStatus::payed;
$order->saveOrFail();
if ($status->equals(OrderStatus::waiting)) {
SendOrderToArcadia::dispatch($order);
SendShopMail::dispatch($order);
}
} catch (Exception $e) {
report($e);
Log::critical(sprintf(
'Could not end customer transaction (save and send mail) in boutique for order %d, %s',
$order->getKey(),
$e->getMessage()
));
} finally {
return view('club.pages.shop.validated');
}
case PaymentResponse::canceled:
$order->status = OrderStatus::failed;
$order->saveOrFail();
return view('club.pages.shop.error');
case PaymentResponse::invalidIds:
case PaymentResponse::invalidRequestFormat:
case PaymentResponse::invalidParameters:
case PaymentResponse::unreachableService:
case PaymentResponse::securityIssue:
case PaymentResponse::transactionAlreadyExists:
case PaymentResponse::expired:
case PaymentResponse::internalIssue:
$order->status = OrderStatus::failed;
$order->saveOrFail();
throw new CBPayementError(sprintf(
"Error response from Mercanet Lib : %s\n %s",
PaymentResponse::make($data['responseCode'])->label(),
json_encode($data['returnDatas'])
));
default:
$order->status = OrderStatus::failed;
$order->save();
throw new CBPayementError(sprintf(
'Un handled response from Mercanet Lib %s',
json_encode($data['returnDatas'])
));
} //end switch
} catch (ModelNotFoundException | CBPayementError $e) {
report($e);
Log::critical(sprintf(
'Could not end customer transaction in boutique for order %d, %s',
$order->getKey(),
$e->getMessage()
));
return view('club.pages.shop.error');
} //end try
}
/**
* Get initialized Mercanet object.
*
* @param \App\Models\Order $order
* @return \App\Lib\Mercanet\Mercanet
*/
private function getMercanet(Order $order): \App\Lib\Mercanet\Mercanet
{
return new Mercanet(
config('mercanet.secret'),
config('mercanet.merchantId'),
config('mercanet.env'),
route('club.shop.reglement.payementConfirmCB', $order),
route('club.shop.reglement.payementConfirmCBServ', $order)
);
}
/**
* Get Mercanet ref and responseCode for request.
*
* @param \Illuminate\Http\Request $request
* @return array<string,string> array['ref' => 'value', 'responseCode' => 'value', 'returnDatas' => '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];
if (
!$request->has('PayID') or
$request->has('cancel') or
$order->status->equals(OrderStatus::failed)
) {
return view('club.pages.shop.info')
->with('title', trans('Commande annulée'))
->with('message', trans("Le paiement n'a pu aboutir. Veuillez réessayer")
);
}
$ref = $formDatas['transactionReference'];
$responseCode = $formDatas['responseCode'];
return compact('ref', 'responseCode', 'returnDatas');
if ($order->status->equals(OrderStatus::payed)) {
return view('club.pages.shop.validated');
}
$aPayment = new AxeptaPayment(
config('axepta.endpoint'),
config('axepta.merchantId'),
config('axepta.apiKey'),
config('axepta.hmac')
);
$response = $aPayment->getPaymentStatusById($request->PayID);
if (is_null($response)) {
return view('club.pages.shop.info')
->with('title', trans('Commande annulée'))
->with('message', trans("Le paiement n'a pu aboutir. Veuillez réessayer")
);
}
switch (intval($response[0])) {
case PaymentResponse::ok->value:
return view('club.pages.shop.validated');
case PaymentResponse::transient->value:
case PaymentResponse::emvinfo->value:
return view('club.pages.shop.info')
->with('title', 'Paiement en cours')
->with('message', 'Paiement en attente de traitement par la banque');
case PaymentResponse::error->value:
case PaymentResponse::fatalerror->value:
default:
return view('club.pages.shop.info')
->with('title', trans('Commande annulée'))
->with('message', trans("Le paiement n'a pu aboutir. Veuillez réessayer")
);
}
}
}

View file

@ -87,7 +87,7 @@ class SecureHeaders
'https://use.fontawesome.com',
'https://fonts.googleapis.com',
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
config('app.debug') ? "https://{$host}:4443" : ''
]) . ';',
// ! If you dont use ckeditor use this
// "style-src 'self' 'nonce-$nonce';",

View file

@ -29,7 +29,6 @@ class ShopBuyRequest extends FormRequest
return [
'ref' => 'required|string|uuid|unique:orders,ref',
'coords' => 'required|array',
'coords.civility' => ['required', new Enum(ContactFormCivility::class)],
'coords.firstname' => 'required|string|between:3,255',
'coords.lastname' => 'required|string|between:3,255',
'coords.address' => 'required|string|between:3,255',
@ -38,7 +37,6 @@ class ShopBuyRequest extends FormRequest
'coords.phone' => 'nullable|phone:FR|max:255',
'coords.email' => 'required|email:rfc,strict,dns,spoof,filter',
'shipping' => 'sometimes|array',
'shipping.civility' => ['required_with:shipping', new Enum(ContactFormCivility::class)],
'shipping.firstname' => 'required_with:shipping|string|between:3,255',
'shipping.lastname' => 'required_with:shipping|string|between:3,255',
'shipping.address' => 'required_with:shipping|string|between:3,255',
@ -62,7 +60,6 @@ class ShopBuyRequest extends FormRequest
{
return [
'coords' => trans('vos coordonnées'),
'coords.civility' => trans('civilité dans vos coordonnées'),
'coords.firstname' => trans('prénom dans vos coordonnées'),
'coords.lastname' => trans('nom dans vos coordonnées'),
'coords.address' => trans('adresse dans vos coordonnées'),
@ -71,7 +68,6 @@ class ShopBuyRequest extends FormRequest
'coords.city' => trans('ville dans vos coordonnées'),
'coords.email' => trans('e-mail dans vos coordonnées'),
'shipping' => trans('votre adresse de livraison'),
'shipping.civility' => trans('civilité dans votre adresse de livraison'),
'shipping.firstname' => trans('prénom dans votre adresse de livraison'),
'shipping.lastname' => trans('nom dans votre adresse de livraison'),
'shipping.address' => trans('adresse dans votre adresse de livraison'),

View file

@ -28,7 +28,7 @@ class DeleteOldMessages implements ShouldQueue
{
Log::debug(sprintf(
'Deleted %d old messages that are more than 30 days',
Message::query()->getMessagesOlderThan(30)->delete()
Message::getMessagesOlderThan(30)->delete()
));
Log::debug(sprintf(

View file

@ -3,6 +3,7 @@
namespace App\Lib\Arcadia;
use App\Enums\Arcadia\ArcadiaOrderType;
use App\Enums\Shop\OrderType;
use App\Models\Order;
use App\Models\SellItem;
use Illuminate\Contracts\Support\Arrayable;
@ -103,10 +104,7 @@ class HasOrderedAnItemData implements Jsonable, Arrayable
'label_type_support' => $sellItem->name,
'order_date' => $order->created_at,
'order_num' => $order->getKey(),
'state_order' => ArcadiaOrderType::check->value(),
// FIXME: uncomment when mercanet cb is ready.
// 'state_order' => $order->payementType->equals(OrderType::cb) ?
// ArcadiaOrderType::cb->value() : ArcadiaOrderType::check->value(),
'state_order' => $order->payementType->equals(OrderType::cb) ? ArcadiaOrderType::cb->value() : ArcadiaOrderType::check->value(),
'package_unit_weight' => $sellItem->arcadia_weight,
'email' => $order->email,
'firstname' => $firstname,

View file

@ -0,0 +1,301 @@
<?php
namespace App\Lib\Axepta;
use App\Lib\Axepta\Objects\AxeptaOrder;
use Carbon\Carbon;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class AxeptaPayment
{
/**
* Guzzle Client instance
* @var GuzzleHttp\Client
*/
private \GuzzleHttp\Client $client;
/**
* Additionnal or Update Axepta configuration
* @var array
*/
private array $axeptaCheckoutConfig;
/**
* AxeptaBearerToken cache key
* @var string
*/
private string $cacheKey = "AxeptaBearerToken";
/**
* Construct method
*
* @param string $endpoint Axepta Endpoint
* @param string $merchantId Axepta Merchant ID
* @param string $apiKey Axepta Secret API Key
* @param string $hmac Axepta HMAC
* @param array $axeptaConfig Additionnal or Update Axepta configuration
* @return void
*/
public function __construct(private string $endpoint, private string $merchantId, private string $apiKey, private string $hmac, private array $axeptaConfig = [])
{
$this->endpoint = $endpoint;
$this->merchantId = $merchantId;
$this->apiKey = $apiKey;
$this->hmac = $hmac;
$this->client = new Client([
'base_uri' => $this->endpoint,
'timeout' => 5.0,
]);
$this->axeptaCheckoutConfig = [
...[
"language" => "fr",
"amount" => [
"currency" => "EUR",
],
"captureMethod" => [
"type" => "AUTOMATIC"
],
"allowedPaymentMethods" => [
"CARD"
],
"paymentMethods" => [
"card" => [
"subType" => ["VISA", "MASTERCARD"],
],
]
],
...$axeptaConfig
];
}
/**
* getAccessToken method
* https://axeptabnpparibas-docs.redocly.app/authentication/authorizationservice
* @return string
* @throws \RuntimeException
*/
private function getAccessToken(): string
{
if (Cache::has($this->cacheKey)) {
return Cache::get($this->cacheKey);
}
try {
$response = $this->client->post('/authorization/oauth/token', [
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => $this->merchantId,
'client_secret' => $this->apiKey
]
]);
if ($response->getStatusCode() !== 200) {
throw new \RuntimeException('Token request failed');
}
$responseBody = json_decode($response->getBody()->getContents());
if (json_last_error() !== JSON_ERROR_NONE || !isset($responseBody->access_token) || !isset($responseBody->expires_in)) {
throw new \RuntimeException('Invalid token response');
}
Cache::put($this->cacheKey, $responseBody->access_token, $responseBody->expires_in);
return $responseBody->access_token;
} catch (RequestException $e) {
$errorBody = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : '{}';
$errorData = json_decode($errorBody, true);
$message = $errorData['error'] ?? $errorData['responseDescription'] ?? $e->getMessage();
throw new \RuntimeException("Axepta token error: {$message}", 0, $e);
}
}
/**
* getCheckoutUrl method
*
* @param \App\Lib\Axepta\Objects\AxeptaOrder $order
* @param string $returnUrl
* @param string $cancelUrl
* @return string
* @throws \RuntimeException
*/
public function getCheckoutUrl(AxeptaOrder $order, string $returnUrl, string $cancelUrl, string $webhookUrl): string
{
$payload = [
"transId" => $this->generateTransId(),
"urls" => [
"return" => $returnUrl,
"cancel" => $cancelUrl,
"webhook" => $webhookUrl
],
"amount" => [
"value" => $order->amount
],
"order" => [
"merchantReference" => $order->reference,
"creationDate" => gmdate('c'), // ISO 8601 now
],
"customerInfo" => [
"email" => $order->customerEmail,
]
];
try {
$response = $this->client->post('/api/v2/payments/sessions', [
'headers' => [
'Authorization' => sprintf(
"Bearer %s",
$this->getAccessToken()
),
'Content-Type' => 'application/json',
'Idempotency-Key' => (string) Str::uuid()
],
'json' => array_merge_recursive($payload, $this->axeptaCheckoutConfig) // Automatically JSON encodes and sets Content-Type
]);
if ($response->getStatusCode() !== 202) {
throw new \RuntimeException('Session creation failed');
}
$responseBody = json_decode($response->getBody()->getContents());
if (!isset($responseBody->_links->redirect->href)) {
throw new \RuntimeException('Missing redirect URL in response');
}
return $responseBody->_links->redirect->href;
} catch (RequestException $e) {
throw new \RuntimeException("Payment session error: " . $e->getMessage(), 0, $e);
}
}
/**
* generateTransId method
*
* @param int $length
* @return string
*/
private function generateTransId(int $length = 64): string
{
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{}|;:,.<>?';
$bytes = random_bytes($length);
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $chars[ord($bytes[$i]) % strlen($chars)];
}
return $result;
}
public function getPaymentStatusById(string $paymentId): string|null
{
try {
$response = $this->client->get(
sprintf(
'/api/v2/payments/getByPayId/%s',
$paymentId
),
[
'headers' => [
'Authorization' => sprintf(
"Bearer %s",
$this->getAccessToken()
),
'Content-Type' => 'application/json',
]
]
);
$responseBody = json_decode($response->getBody()->getContents());
return $responseBody->responseCode;
} catch (RequestException $e) {
if ($e->hasResponse()) {
$response = $e->getResponse();
$responseBody = json_decode($response->getBody()->getContents());
return $responseBody->responseCode;
} else {
return null;
}
}
}
/**
* Check signature version
*
* https://axepta.computop.com/display/DOCBNP/Gestion+des+callbacks+et+notification+de+paiement
*
* @param string|null $version
* @return bool
*/
private function isValidSignatureVersion(?string $version): bool
{
return $version === 'v1';
}
/**
* Check signature
*
* https://axepta.computop.com/display/DOCBNP/Gestion+des+callbacks+et+notification+de+paiement
*
* @param string $signature
* @param string $timestamp
* @param string $rawPayload
* @return bool
*/
private function isValidSignature(string $signature, string $timestamp, string $rawPayload): bool
{
$providedSignature = $this->extractSignature($signature);
if (!$providedSignature) {
return false;
}
$signedPayload = "{$timestamp}.{$rawPayload}";
$expectedSignature = hash_hmac('sha256', $signedPayload, $this->hmac);
return hash_equals($expectedSignature, $providedSignature);
}
/**
* Extract the hex value after "v1=" from the signature header.
*
* https://axepta.computop.com/display/DOCBNP/Gestion+des+callbacks+et+notification+de+paiement
*
* @param string $signature
* @return string|null
*/
private function extractSignature(string $signature): ?string
{
if (!str_starts_with($signature, 'v1=')) {
return null;
}
$hex = substr($signature, 3);
return strlen($hex) === 64 ? $hex : null; // SHA256 hex = 64 chars
}
/**
* Check if is valid webhook access
*
* From Axepta 2.0 Doc
* https://axepta.computop.com/display/DOCBNP/Gestion+des+callbacks+et+notification+de+paiement
*
* X-Paygate-Signature-Version Signature format version (currently v1)
* X-Paygate-Timestamp Unix epoch timestamp (seconds since 1970-01-01T00:00:00Z, UTC), the timestamp is within ±5 minutes of your local clock.
* X-Paygate-Signature v1=<hex-hmac>
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
public function isValidWebhookAccess(\Illuminate\Http\Request $request): bool
{
$signatureVersion = $request->header('x-paygate-signature-version');
$timestamp = $request->header('x-paygate-timestamp', '0');
$signature = $request->header('x-paygate-signature');
return $this->isValidSignatureVersion($signatureVersion) and
Carbon::createFromTimestampUTC($timestamp)->diffInMinutes(Carbon::now('UTC')) <= 5 and
$this->isValidSignature($signature, $timestamp, $request->getContent());
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Lib\Axepta\Enums;
use App\Traits\Enums\BaseEnum;
enum PaymentResponse: int
{
use BaseEnum;
case ok = 0;
case error = 2;
case fatalerror = 4;
case transient = 6;
case emvinfo = 7;
/**
* Optionnal labels definition.
*
* @phpcs:disable Generic.Files.LineLength.TooLong
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::ok->name => 'Transaction successful, other digits may indicate further information',
self::error->name => 'Transaction failed, other digits may indicate further information',
self::fatalerror->name => 'Transaction failed, other digits may indicate further information, e.g. mandatory parameter is missing',
self::transient->name => 'Transaction pending. Final status will be sent asynchronously when available',
self::emvinfo->name => 'Intermediate states in the EMV 3-D Secure sequence'
];
//phpcs:enable
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Lib\Axepta\Objects;
class AxeptaOrder
{
/**
* Construct method
*
* @param string $reference Internal system reference order
* @param int $amount Order amount ex: 10000 = 100$
* @param string $customerEmail Customer email
* @return void
*/
public function __construct(
public string $reference,
public int $amount,
public string $customerEmail
) {}
}

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ use App\Models\Notification;
use App\Interfaces\Notifiable as INotifiable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class Message extends Model implements INotifiable
{
@ -79,9 +80,9 @@ class Message extends Model implements INotifiable
* Get older messages.
*
* @param integer $days
* @return \Illuminate\Contracts\Database\Eloquent\Builder
* @return Builder
*/
public static function getMessagesOlderThan(int $days): \Illuminate\Contracts\Database\Eloquent\Builder
public static function getMessagesOlderThan(int $days): Builder
{
return self::query()->where('updated_at', '<', now()->subDays($days));
}

View file

@ -67,7 +67,6 @@ class Order extends Model
{
return [
'ref' => $this->ref,
'civility' => $this->civility->value(),
'lastname' => $this->lastname,
'firstname' => $this->firstname,
'address' => $this->address,

8
config/axepta.php Normal file
View file

@ -0,0 +1,8 @@
<?php
return [
'endpoint' => env('AXEPTA_ENDPOINT'),
'merchantId' => env('AXEPTA_MERCHANT_ID'),
'apiKey' => env('AXEPTA_APIKEY'),
'hmac' => env('AXEPTA_HMAC')
];

View file

@ -1,8 +0,0 @@
<?php
return [
'secret' => env('MERCANET_SECRET'),
'merchantId' => env('MERCANET_MERCHAND_ID'),
// Ex: DEVELOPMENT, PRODUCTION.
'env' => env('MERCANET_ENV', 'DEVELOPMENT'),
];

View file

@ -58,6 +58,10 @@ data:
REDIS_PREFIX: "${APP_REDIS_PREFIX}"
REDIS_SCHEME: "${APP_REDIS_SCHEME}"
UWIL_ADMIN_SECURE_CIDR_LIST: "${APP_GLOBAL_UWIL_ADMIN_SECURE_CIDR_LIST}"
AXEPTA_ENDPOINT: "${APP_AXEPTA_ENDPOINT}"
AXEPTA_MERCHANT_ID: "${APP_AXEPTA_MERCHANT_ID}"
AXEPTA_APIKEY: "${APP_AXEPTA_APIKEY}"
AXEPTA_HMAC: "${APP_AXEPTA_HMAC}"
---
apiVersion: v1
kind: PersistentVolumeClaim

View file

@ -144,7 +144,9 @@
<tr>
<td style="width:550px" class="mj-full-width-mobile"> <a
href="https://www.legaulois.fr/?mtm_campaign=le_gaulois_mailing_consentement_newsletter_nov2025_club_des_gaulois&mtm_source=mailing_consentement_nl_le_gaulois&mtm_medium=mailing_consentement_le_gaulois"
target="_blank"> <img alt="Le Club Le Gaulois"
target="_blank"> <img
alt="Bienvenue sur le club des Gaulois"
title="Bienvenue sur le club des Gaulois"
src="https://www.legaulois.fr/newsletters/251201/assets/header.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="550" height="auto"> </a> </td>
@ -337,7 +339,8 @@
<tr>
<td style="width:180px" class="mj-full-width-mobile"> <a
href="https://www.legaulois.fr/?mtm_campaign=le_gaulois_mailing_consentement_newsletter_nov2025_club_des_gaulois&mtm_source=mailing_consentement_nl_le_gaulois&mtm_medium=mailing_consentement_le_gaulois"
target="_blank"> <img alt="Logo Le Gaulois"
target="_blank"> <img alt="Logo marque Le Gaulois"
title="Logo marque Le Gaulois"
src="https://www.legaulois.fr/newsletters/251201/assets/logo.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="180" height="auto"> </a> </td>
@ -391,6 +394,7 @@
href="https://www.legaulois.fr/club?mtm_campaign=le_gaulois_mailing_consentement_newsletter_oct2025_club_des_gaulois&mtm_source=mailing_consentement_nl_le_gaulois&mtm_medium=mailing_consentement_le_Gaulois"
target="_blank"> <img
alt="Page internet Club des Gaulois"
title="Page internet Club des Gaulois"
src="https://www.legaulois.fr/newsletters/ref/picto_club_lgl.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="60" height="auto"> </a> </td>
@ -420,6 +424,7 @@
href="https://www.linkedin.com/company/legaulois"
target="_blank"> <img
alt="Logo Linkedin Le Gaulois"
title="Logo Linkedin Le Gaulois"
src="https://www.legaulois.fr/newsletters/ref/picto_in.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="60" height="auto"> </a> </td>
@ -449,6 +454,7 @@
href="https://www.instagram.com/legaulois_officiel/"
target="_blank"> <img
alt="Logo Instagram Le Gaulois"
title="Logo Instagram Le Gaulois"
src="https://www.legaulois.fr/newsletters/ref/picto_insta.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="60" height="auto"> </a> </td>
@ -478,7 +484,8 @@
href="https://www.tiktok.com/@legaulois_off"
target="_blank"> <img
alt="Logo Tiktok Le Gaulois"
src="https://www.legaulois.fr/newsletters/ref/tiktok.png"
title="Logo Tiktok Le Gaulois"
src="https://www.legaulois.fr/newsletters/ref/picto_tiktok.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="60" height="auto"> </a> </td>
</tr>
@ -507,6 +514,7 @@
href="https://www.youtube.com/channel/UCiTpBkZrwH9_xUpTv1UTuLA"
target="_blank">
<img alt="Logo Youtube Le Gaulois"
title="Logo Youtube Le Gaulois"
src="https://www.legaulois.fr/newsletters/ref/picto_yt.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="60" height="auto"> </a> </td>
@ -536,6 +544,7 @@
href="https://www.facebook.com/LeGauloisFrance/"
target="_blank"> <img
alt="Logo Facebook Le Gaulois"
title="Logo Facebook Le Gaulois"
src="https://www.legaulois.fr/newsletters/ref/picto_fb.png"
style="border:0;display:block;font-size:13px;height:auto;outline:none;text-decoration:none;width:100%"
width="60" height="auto"> </a> </td>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -12,63 +12,35 @@
</h3>
<!-- CRM ADDRESS -->
<div class="row">
<div class="col-sm-12 col-md-6">
<fieldset class="row py-2">
<div class="col-12 col-sm-3 d-flex justify-content-start align-items-center">
<label
for="userCivility"
class="pb-2"
>{{ trans.methods.__("Civilité") }}&nbsp;*</label>
</div>
<div class="col-12 col-sm-9">
<select
id="userCivility"
class="form-select"
:aria-label="trans.methods.__('Choisir la civilité dans la liste déroulante')"
@change="setCivility($event)"
>
<option
:value="null"
disabled
selected
>
{{ trans.methods.__("Choisissez une civilité parmi la liste déroulante") }}
</option>
<option
v-for="civility in currentShop.civility"
:key="civility.value"
:value="civility.value"
>
{{ civility.label }}
</option>
</select>
</div>
</fieldset>
</div>
<div
<template
v-for="(input, inputIndex) in formInputs"
:key="inputIndex"
class="col-12 col-md-6"
>
<fieldset class="row py-2">
<div class="col-12 col-sm-3 d-flex justify-content-start align-items-center">
<label
:for="`user${input.name}`"
class="pb-2"
>{{ trans.methods.__(input.label) }}&nbsp;*</label>
</div>
<div class="col-12 col-sm-9">
<input
:id="`user${input.name}`"
type="text"
class="form-control"
:placeholder="trans.methods.__(input.label) + '*'"
v-model="input.coords"
required
>
</div>
</fieldset>
</div>
<div class="col-12 col-md-6">
<fieldset class="row py-2">
<div class="col-12 col-sm-3 d-flex align-items-center">
<label
:for="`user${input.name}`"
class="pb-2"
>{{ trans.methods.__(input.label) }}&nbsp;*</label>
</div>
<div class="col-12 col-sm-9">
<input
:id="`user${input.name}`"
type="text"
class="form-control"
:placeholder="trans.methods.__(input.label) + '*'"
v-model="input.coords"
required
>
</div>
</fieldset>
</div>
<div
v-if="input.name === 'address'"
class="d-none d-md-block col-md-6"
></div>
</template>
<div class="col-12">
<div class="form-check my-4">
<input
@ -96,39 +68,6 @@
{{ trans.methods.__('Adresse de livraison') }}
</h3>
</div>
<div class="col-sm-12 col-md-6">
<fieldset class="row py-2">
<div class="col-12 col-sm-3 d-flex justify-content-start align-items-center">
<label
for="userCivility"
class="pb-2"
>{{ trans.methods.__("Civilité") }}&nbsp;*</label>
</div>
<div class="col-12 col-sm-9">
<select
id="userCivility"
class="form-select"
:aria-label="trans.methods.__('Choisir la civilité dans la liste déroulante')"
@change="setShippingCivility($event)"
>
<option
:value="null"
disabled
selected
>
{{ trans.methods.__("Choisissez une civilité parmi la liste déroulante") }}
</option>
<option
v-for="civility in currentShop.civility"
:key="civility.value"
:value="civility.value"
>
{{ civility.label }}
</option>
</select>
</div>
</fieldset>
</div>
<template
v-for="(input, inputIndex) in formInputs"
:key="inputIndex"
@ -276,6 +215,7 @@
>{{ payementMethod.label }}</label>
</li>
</ul>
<!-- A supprimer à la mise en place de la CB -->
<p class="fst-italic m-0">
{{ trans.methods.__('(CB prochainement)') }}
</p>
@ -407,7 +347,6 @@ const form = ref<{
total: number,
payementMethod: number|null,
coords: {
civility: number|null,
firstname: string,
lastname: string,
address: string,
@ -417,7 +356,6 @@ const form = ref<{
email: string,
},
shipping?: {
civility: number|null,
firstname: string,
lastname: string,
address: string,
@ -431,7 +369,6 @@ const form = ref<{
total: 0,
payementMethod: null,
coords: {
civility: null,
firstname: "",
lastname: "",
address: "",
@ -441,7 +378,6 @@ const form = ref<{
email: ""
},
shipping: {
civility: null,
firstname: "",
lastname: "",
address: "",
@ -466,10 +402,10 @@ onMounted(() => {
form.value.coords.phone = currentShop.value.user.phone ? Number(currentShop.value.user.phone) : null;
form.value.coords.email = String(currentShop.value.user.email);
formInputs.value = [
{"name": "firstname", "label": "Prénom", "coords": form.value.coords.firstname, "shipping": form.value.shipping!.firstname},
{"name": "lastname", "label": "Nom", "coords": form.value.coords.lastname, "shipping": form.value.shipping!.lastname},
{"name": "email", "label": "E-mail", "coords": form.value.coords.email, "shipping": null},
{"name": "firstname", "label": "Prénom", "coords": form.value.coords.firstname, "shipping": form.value.shipping!.firstname},
{"name": "phone", "label": "Téléphone", "coords": form.value.coords.phone, "shipping": form.value.shipping!.phone},
{"name": "email", "label": "E-mail", "coords": form.value.coords.email, "shipping": null},
{"name": "address", "label": "Adresse", "coords": form.value.coords.address, "shipping": form.value.shipping!.address},
{"name": "postal_code", "label": "Code postal", "coords": form.value.coords.postcode, "shipping": form.value.shipping!.postcode},
{"name": "city", "label": "Ville", "coords": form.value.coords.city, "shipping": form.value.shipping!.city},
@ -492,29 +428,6 @@ function selectPayement(payment: number): void {
form.value.payementMethod = payment;
}
/**
* Add specific item to the customer cart.
* e.target.value = "{label: string, value: number}"
*
* @param e Event
* @return void
*/
function setCivility(e: Event): void {
const input = e.target as HTMLOptionElement;
form.value.coords.civility = parseInt(input.value);
}
/**
* Add specific item to the customer cart.
* e.target.value = "{label: string, value: number}"
*
* @param e Event
* @return void
*/
function setShippingCivility(e: Event): void {
const input = e.target as HTMLOptionElement;
form.value.shipping!.civility = parseInt(input.value);
}
/**
* Return destroy route for a game.

View file

@ -17,11 +17,12 @@
<span class="header-icon position-relative mb-3 mb-sm-0 me-sm-3">
@include('club.icons.bell')
</span>
<h1 class="text-uppercase m-0">{{ __('Commande annulée') }}</h1>
<h1 class="text-uppercase m-0">{{ $title }}</h1>
</div>
<div class="col-lg-6">
<p class="text-center">
{{ __('La commande que vous venez de passer n\'a pu aboutir !') }}
{{ $message }}
</p>
</div>
</section>

View file

@ -1,29 +0,0 @@
@extends('club.layout')
@section('title', __('Redirection vers Mercanet'))
@section('description', __('Page de redirection vers le service de BNP Paribas nommé Mercanet.'))
@section('breadcrumb', request()->route()->getName())
@section('container-fluid', true)
@section('no-bottom-space', true)
@section('page-class', 'shop')
@section('metaIndex', 'noindex,nofollow')
@section('content')
<section class="container mb-5 pb-5">
<form name="redirectForm" method="POST" action="{{ $url }}">
<input type="hidden" name="redirectionVersion" value="{{ $version }}">
<input type="hidden" name="redirectionData" value="{{ $data }}">
<h2 class="text-center">{{ __('Redirection en cours') }}</h2>
<noscript>
<input type="submit" name="Go" value="{{ __('Cliquez ici pour accéder à la page de paiement') }}" />
</noscript>
</form>
</section>
@endsection
@push('scripts')
<script nonce="{{ $nonce }}">
document.redirectForm.submit();
</script>
@endpush

View file

@ -7,9 +7,9 @@
@if ($orderType->equals(\App\Enums\Arcadia\ArcadiaOrderType::check))
<Etat_Commande_CB>Null</Etat_Commande_CB>
<Etat_Commande_Cheque>1</Etat_Commande_Cheque>
{{-- @elseif($orderType->equals(\App\Enums\Arcadia\ArcadiaOrderType::cb))
@elseif($orderType->equals(\App\Enums\Arcadia\ArcadiaOrderType::cb))
<Etat_Commande_CB>1</Etat_Commande_CB>
<Etat_Commande_Cheque>Null</Etat_Commande_Cheque> --}}
<Etat_Commande_Cheque>Null</Etat_Commande_Cheque>
@endif
<Email_conso>
<![CDATA[{{ $email }}]]>

View file

@ -6,6 +6,10 @@
title="{{ __('Go to page :page :appname', ['page' => 'Instagram', 'appname' => config('app.name')]) }}" target="_blank">
<img class="img-fluid" src="{{ Vite::asset('resources/assets/images/footer/socials/instagram.png') }}" alt="Instagram" role="img">
</a>
<a class="mx-sm-1 btn btn-link btn-social text-decoration-none mx-2" href="https://www.tiktok.com/@legaulois_off"
title="{{ __('Go to page :page :appname', ['page' => 'Tiktok', 'appname' => config('app.name')]) }}" target="_blank">
<img class="img-fluid" src="{{ Vite::asset('resources/assets/images/footer/socials/tiktok.png') }}" alt="Tiktok" role="img">
</a>
<a class="mx-sm-1 btn btn-link btn-social text-decoration-none mx-2" href="https://www.linkedin.com/company/legaulois"
title="{{ __('Go to page :page :appname', ['page' => 'LinkedIn', 'appname' => config('app.name')]) }}" target="_blank">
<img class="img-fluid" src="{{ Vite::asset('resources/assets/images/footer/socials/linkedin.png') }}" alt="LinkedIn" role="img">

View file

@ -102,7 +102,7 @@ Route::name('club.')->prefix('club')->group(function () {
Route::get('{order}/reglement', [ShopController::class, 'payWithCb'])->name('payementCB');
});
Route::prefix('/reglement')->name('reglement.')->group(function () {
Route::post('/{order}/confirmServ', [ShopController::class, 'confirmCbFromMercanet'])
Route::post('/{order}/confirmServ', [ShopController::class, 'confirmCbFromAxepta'])
->name('payementConfirmCBServ')
->withoutMiddleware([
'auth:club',
@ -115,19 +115,8 @@ Route::name('club.')->prefix('club')->group(function () {
XssSanitizer::class,
EnsureEmailIsVerified::class,
]);
Route::post('/{order}/confirm', [ShopController::class, 'confirmCb'])
->name('payementConfirmCB')
->withoutMiddleware([
'auth:club',
Authenticate::class,
AddQueuedCookiesToResponse::class,
EncryptCookies::class,
VerifyCsrfToken::class,
StartSession::class,
ShareErrorsFromSession::class,
XssSanitizer::class,
EnsureEmailIsVerified::class,
]);
Route::get('/{order}/confirm', [ShopController::class, 'confirmCb'])
->name('payementConfirmCB');
});
});

View file

@ -13,8 +13,5 @@ spec:
services:
- name: ${CI_PROJECT_PATH_SLUG}-service
port: 80
middlewares:
- name: admin-ldc-whitelist
namespace: common-services
tls:
secretName: ${CRT_TLS_NAME}