Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,167 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Webhook\WebhookException;
use PsCheckout\Core\WebhookDispatcher\Action\CheckPSLSignatureAction;
use PsCheckout\Core\WebhookDispatcher\Processor\DispatchWebhookProcessor;
use PsCheckout\Core\WebhookDispatcher\Validator\BodyValuesValidator;
use PsCheckout\Core\WebhookDispatcher\Validator\HeaderValuesValidator;
use PsCheckout\Core\WebhookDispatcher\Validator\WebhookShopIdValidator;
use PsCheckout\Core\WebhookDispatcher\ValueObject\DispatchWebhookRequest;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use Psr\Log\LoggerInterface;
class ps_checkoutDispatchWebHookModuleFrontController extends AbstractFrontController
{
const PS_CHECKOUT_PAYPAL_ID_LABEL = 'PS_CHECKOUT_PAYPAL_ID_MERCHANT';
/**
* @var bool If set to true, will be redirected to authentication page
*/
public $auth = false;
/**
* @return bool
*/
public function display(): bool
{
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
$logger->info('Webhook dispatch initiated');
try {
/** @var HeaderValuesValidator $headerValuesValidator */
$headerValuesValidator = $this->module->getService(HeaderValuesValidator::class);
$headerValues = $headerValuesValidator->validate();
$logger->info('Headers validated', $headerValues);
/** @var BodyValuesValidator $bodyValuesValidator */
$bodyValuesValidator = $this->module->getService(BodyValuesValidator::class);
$bodyValues = $bodyValuesValidator->validate();
$logger->info('Body validated', $bodyValues);
$dispatchWebhookRequest = DispatchWebhookRequest::createFromRequest($bodyValues, $headerValues);
/** @var CheckPSLSignatureAction $checkPSLSignatureAction */
$checkPSLSignatureAction = $this->module->getService(CheckPSLSignatureAction::class);
$checkPSLSignatureAction->execute($bodyValues);
$logger->info('PSL Signature validated', $bodyValues);
/** @var WebhookShopIdValidator $webhookShopIdValidator */
$webhookShopIdValidator = $this->module->getService(WebhookShopIdValidator::class);
$webhookShopIdValidator->validate($dispatchWebhookRequest->getShopId());
$logger->info('Webhook dispatch started');
/** @var DispatchWebhookProcessor $dispatchWebHookProcessor */
$dispatchWebHookProcessor = $this->module->getService(DispatchWebhookProcessor::class);
$processed = $dispatchWebHookProcessor->process($dispatchWebhookRequest);
if ($processed) {
$logger->info('Webhook dispatch completed successfully');
} else {
$logger->warning('Webhook dispatch completed with no processing');
}
return $processed;
} catch (WebhookException $e) {
// Handle the exception
$logger->error('Webhook Dispatcher error: ' . $e->getMessage());
http_response_code($e->getCode());
echo json_encode(['error' => $e->getMessage()]);
} catch (Throwable $e) {
$logger->error(
sprintf(
'DispatchWebHookController - Exception %s : %s',
$e->getCode(),
$e->getMessage()
),
['exception' => $e]
);
http_response_code(500);
echo json_encode(['error' => 'An unexpected error occurred.']);
}
return false;
}
/**
* Override displayMaintenancePage to prevent the maintenance page to be displayed
*
* @see FrontController::displayMaintenancePage()
*/
protected function displayMaintenancePage()
{
return;
}
/**
* Override displayRestrictedCountryPage to prevent page country is not allowed
*
* @see FrontController::displayRestrictedCountryPage()
*/
protected function displayRestrictedCountryPage()
{
return;
}
/**
* Override geolocationManagement to prevent country GEOIP blocking
*
* @see FrontController::geolocationManagement()
*
* @param Country $defaultCountry
*
* @return false
*/
protected function geolocationManagement($defaultCountry)
{
return false;
}
/**
* Override sslRedirection to prevent redirection
*
* @see FrontController::sslRedirection()
*/
protected function sslRedirection()
{
return;
}
/**
* Override canonicalRedirection to prevent redirection
*
* @see FrontController::canonicalRedirection()
*
* @param string $canonical_url
*/
protected function canonicalRedirection($canonical_url = '')
{
return;
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Customer\Action\ExpressCheckoutAction;
use PsCheckout\Core\Customer\Request\ValueObject\ExpressCheckoutRequest;
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PayPal\Order\Entity\PayPalOrder;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Infrastructure\Repository\PayPalOrderRepository;
use PsCheckout\Infrastructure\Validator\FrontControllerValidator;
use PsCheckout\Utility\Common\InputStreamUtility;
/**
* This controller receive ajax call when customer click on an express checkout button
* We retrieve data from PayPal in payload and save it in PrestaShop to prefill order page
* Then customer must be redirected to order page to choose shipping method
*/
class ps_checkoutExpressCheckoutModuleFrontController extends AbstractFrontController
{
/**
* {@inheritdoc}
*/
public function postProcess()
{
/** @var FrontControllerValidator $frontControllerValidator */
$frontControllerValidator = $this->module->getService(FrontControllerValidator::class);
if (!$frontControllerValidator->isExpressCheckoutEnabled()) {
$this->exitWithResponse([
'httpCode' => 403,
'body' => 'Forbidden',
]);
}
// We receive data in a payload not in GET/POST
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (empty($bodyContent)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
$requestData = json_decode($bodyContent, true);
if (empty($requestData)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
$expressCheckoutRequest = new ExpressCheckoutRequest($requestData);
if (!$expressCheckoutRequest->getOrderId()) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
try {
/** @var PayPalOrderRepository $payPalOrderReposistory */
$payPalOrderReposistory = $this->module->getService(PayPalOrderRepository::class);
/** @var PayPalOrder|null $psCheckoutCart */
$payPalOrder = $payPalOrderReposistory->getOneBy(['id' => $expressCheckoutRequest->getOrderId()]);
if (!$payPalOrder || $payPalOrder->getIdCart() !== $this->context->cart->id) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
if ($payPalOrder) {
$payPalOrder->setFundingSource($expressCheckoutRequest->getFundingSource())
->setIsExpressCheckout(true)
->setIsCardFields(false);
$payPalOrderReposistory->save($payPalOrder);
}
/** @var ExpressCheckoutAction $expressCheckoutAction */
$expressCheckoutAction = $this->module->getService(ExpressCheckoutAction::class);
$expressCheckoutAction->execute($expressCheckoutRequest);
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while processing the express checkout.',
PsCheckoutException::UNKNOWN,
$exception
));
}
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => $expressCheckoutRequest->getPayload(),
'exceptionCode' => null,
'exceptionMessage' => null,
]);
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PayPal\ApplePay\Builder\ApplePayPaymentRequestDataBuilder;
use PsCheckout\Core\Settings\Configuration\PayPalConfiguration;
use PsCheckout\Infrastructure\Adapter\Configuration;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Utility\Common\InputStreamUtility;
/**
* This controller receives AJAX calls when a customer clicks on a payment button.
*/
class Ps_CheckoutApplepayModuleFrontController extends AbstractFrontController
{
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
try {
$action = $this->getActionFromRequest();
if (!$action) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Invalid request',
]);
}
switch ($action) {
case 'getPaymentRequest':
$this->getPaymentRequest();
break;
case 'getDomainAssociation':
$this->handleDomainAssociation();
break;
default:
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Invalid request',
]);
}
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while processing the Apple Pay request.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
/**
* Extracts action from the request body or GET parameters.
*
* @return string|null
*/
private function getActionFromRequest()
{
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (!empty($bodyContent)) {
$bodyValues = json_decode($bodyContent, true);
if (!empty($bodyValues['action'])) {
return $bodyValues['action'];
}
}
$action = Tools::getValue('action');
return $action === 'getDomainAssociation' ? $action : null;
}
/**
* Handles the domain association file retrieval for Apple Pay validation.
*
* @return void
*/
private function handleDomainAssociation()
{
/** @var Configuration $configuration */
$configuration = $this->module->getService(Configuration::class);
$environment = $configuration->get(PayPalConfiguration::PS_CHECKOUT_PAYMENT_MODE);
$associationFile = _PS_MODULE_DIR_ . $this->module->name . "/.well-known/apple-$environment-merchantid-domain-association";
if (!file_exists($associationFile)) {
$this->exitWithResponse([
'httpCode' => 404,
'body' => 'File not found',
]);
}
if (!headers_sent()) {
ob_end_clean();
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('X-Robots-Tag: noindex, nofollow');
header_remove('Last-Modified');
header('Content-Type: text/plain', true, 200);
}
echo file_get_contents($associationFile);
exit;
}
/**
* Handles the payment request for Apple Pay.
*
* @return void
*/
private function getPaymentRequest()
{
/** @var ApplePayPaymentRequestDataBuilder $applePayPaymentRequestDataBuilder */
$applePayPaymentRequestDataBuilder = $this->module->getService(ApplePayPaymentRequestDataBuilder::class);
$paymentRequest = $applePayPaymentRequestDataBuilder->build();
$this->exitWithResponse([
'httpCode' => 200,
'body' => $paymentRequest->toArray(),
]);
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PayPal\Order\Action\CancelPayPalOrderAction;
use PsCheckout\Core\PayPal\Order\Request\ValueObject\CancelPayPalOrderRequest;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Utility\Common\InputStreamUtility;
use Psr\Log\LoggerInterface;
/**
* This controller receive ajax call on customer canceled payment
*/
class Ps_CheckoutCancelModuleFrontController extends AbstractFrontController
{
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
try {
if (!Validate::isLoadedObject($this->context->cart)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'No cart found.',
]);
}
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (empty($bodyContent)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
$bodyValues = json_decode($bodyContent, true);
if (empty($bodyValues)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
$orderCancelRequest = new CancelPayPalOrderRequest($bodyValues, $this->context->cart->id);
if ($orderCancelRequest->getOrderId()) {
/** @var CancelPayPalOrderAction $cancelPayPalOrderAction */
$cancelPayPalOrderAction = $this->module->getService(CancelPayPalOrderAction::class);
$cancelPayPalOrderAction->execute($orderCancelRequest);
}
$logger->log(
$orderCancelRequest->getError() ? 400 : 200,
'Customer canceled payment',
[
'PayPalOrderId' => $orderCancelRequest->getOrderId(),
'FundingSource' => $orderCancelRequest->getFundingSource(),
'id_cart' => $this->context->cart->id,
'isExpressCheckout' => $orderCancelRequest->isExpressCheckout(),
'isHostedFields' => $orderCancelRequest->isHostedFields(),
'reason' => $orderCancelRequest->getReason(),
'error' => $orderCancelRequest->getError(),
]
);
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => $bodyValues,
'exceptionCode' => null,
'exceptionMessage' => null,
]);
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while canceling the PayPal order.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PayPal\Order\Action\CancelPayPalOrderAction;
use PsCheckout\Core\PayPal\Order\Processor\UpdateExternalPayPalOrderProcessor;
use PsCheckout\Core\PayPal\Order\Request\ValueObject\CancelPayPalOrderRequest;
use PsCheckout\Core\PayPal\Order\Request\ValueObject\CheckPayPalOrderRequest;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Utility\Common\InputStreamUtility;
use Psr\Log\LoggerInterface;
/**
* This controller receive ajax call on customer click on a payment button
*/
class Ps_CheckoutCheckModuleFrontController extends AbstractFrontController
{
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
try {
if (!Validate::isLoadedObject($this->context->cart)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'No cart found.',
]);
}
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
$bodyValues = json_decode($bodyContent, true);
if (empty($bodyContent) || empty($bodyValues)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
$checkOrderRequest = new CheckPayPalOrderRequest($this->context->cart->id, $bodyValues);
if (!$checkOrderRequest->getOrderId()) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Missing PayPal Order Id',
]);
}
try {
/** @var UpdateExternalPayPalOrderProcessor $updateExternalPayPalOrderProcessor */
$updateExternalPayPalOrderProcessor = $this->module->getService(UpdateExternalPayPalOrderProcessor::class);
$updateExternalPayPalOrderProcessor->execute($checkOrderRequest);
} catch (Exception $exception) {
$logger->error(
'Failed to patch PayPal Order',
[
'PayPalOrderId' => $checkOrderRequest->getOrderId(),
'FundingSource' => $checkOrderRequest->getFundingSource(),
'isExpressCheckout' => $checkOrderRequest->isExpressCheckout(),
'isHostedFields' => $checkOrderRequest->isHostedFields(),
'id_cart' => (int) $this->context->cart->id,
]
);
/** @var CancelPayPalOrderAction $cancelPayPalOrderAction */
$cancelPayPalOrderAction = $this->module->getService(CancelPayPalOrderAction::class);
$cancelPayPalOrderAction->execute(new CancelPayPalOrderRequest($bodyValues, $this->context->cart->id));
}
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => $bodyValues,
'exceptionCode' => null,
'exceptionMessage' => null,
]);
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while checking the PayPal order.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
}

View File

@@ -0,0 +1,181 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PayPal\Order\Action\CreatePayPalOrderAction;
use PsCheckout\Core\PayPal\Order\Configuration\PayPalOrderStatus;
use PsCheckout\Core\PayPal\Order\Request\ValueObject\CreatePayPalOrderRequest;
use PsCheckout\Infrastructure\Action\AddProductToCartAction;
use PsCheckout\Infrastructure\Adapter\Context;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Infrastructure\Repository\PayPalOrderRepository;
use PsCheckout\Utility\Common\InputStreamUtility;
/**
* This controller receives ajax call to create a PayPal Order
*/
class Ps_CheckoutCreateModuleFrontController extends AbstractFrontController
{
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
try {
/** @var Context $context */
$context = $this->module->getService(Context::class);
// BEGIN Express Checkout
$requestData = [];
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (!empty($bodyContent)) {
$requestData = json_decode($bodyContent, true);
}
$requestData['isExpressCheckout'] = (isset($requestData['isExpressCheckout']) && $requestData['isExpressCheckout']) || empty($this->context->cart->id_address_delivery);
$createPayPalOrderRequest = new CreatePayPalOrderRequest($requestData);
if ($this->shouldCreateCart($createPayPalOrderRequest)) {
try {
/** @var AddProductToCartAction $addProductToCartAction */
$addProductToCartAction = $this->module->getService(AddProductToCartAction::class);
$addProductToCartAction->execute($createPayPalOrderRequest);
} catch (PsCheckoutException $exception) {
$this->exitWithResponse([
'status' => false,
'httpCode' => 400,
'body' => [
'error' => [
'message' => 'Failed to update cart quantity.',
],
],
]);
}
}
// END Express Checkout
if (!isset($context->getCart()->id)) {
$this->exitWithResponse([
'httpCode' => 404,
'body' => 'Cart not found',
]);
}
// Validate PUI amount limits (5 EUR < amount < 2500 EUR)
if ($createPayPalOrderRequest->getFundingSource() === 'pay_upon_invoice') {
$cart = $context->getCart();
$cartTotal = (float) $cart->getOrderTotal(true, \Cart::BOTH);
if ($cartTotal <= 5.00 || $cartTotal >= 2500.00) {
$this->exitWithResponse([
'status' => false,
'httpCode' => 400,
'body' => [
'error' => [
'message' => $this->module->l('The payment is not valid: the amount is not eligible.', 'create'),
],
],
]);
}
}
if ($createPayPalOrderRequest->isExpressCheckout() || empty($context->getCart()->id_address_delivery)) {
/** @var PayPalOrderRepository $payPalOrderRepository */
$payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class);
$payPalOrder = $payPalOrderRepository->getOneByCartId((int) $context->getCart()->id);
if ($payPalOrder && $payPalOrder->isExpressCheckout() && in_array(
$payPalOrder->getStatus(),
[
PayPalOrderStatus::CREATED,
PayPalOrderStatus::APPROVED,
PayPalOrderStatus::PAYER_ACTION_REQUIRED,
],
true
)) {
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => [
'orderID' => $payPalOrder->getId(),
],
'exceptionCode' => null,
'exceptionMessage' => null,
]);
}
}
/** @var CreatePayPalOrderAction $createPayPalOrderAction */
$createPayPalOrderAction = $this->module->getService(CreatePayPalOrderAction::class);
$createPayPalOrderAction->execute((int) $context->getCart()->id, $createPayPalOrderRequest);
/** @var PayPalOrderRepository $payPalOrderRepository */
$payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class);
$payPalOrder = $payPalOrderRepository->getOneByCartId((int) $context->getCart()->id);
if (!$payPalOrder) {
$this->exitWithResponse([
'httpCode' => 404,
'body' => 'PayPal order not found',
]);
}
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => [
'orderID' => $payPalOrder->getId(),
],
'exceptionCode' => null,
'exceptionMessage' => null,
]);
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while creating the PayPal order.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
/**
* @param CreatePayPalOrderRequest $checkoutRequest
*
* @return bool
*/
private function shouldCreateCart(CreatePayPalOrderRequest $checkoutRequest): bool
{
return $checkoutRequest->getQuantityWanted() !== null
|| $checkoutRequest->getIdProduct() !== null
|| $checkoutRequest->getIdProductAttribute() !== null
|| $checkoutRequest->getIdCustomization() !== null;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PayPal\GooglePay\Builder\GooglePayPaymentRequestDataBuilder;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Utility\Common\InputStreamUtility;
/**
* This controller receive ajax call on customer click on a payment button
*/
class Ps_CheckoutGooglepayModuleFrontController extends AbstractFrontController
{
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
try {
$bodyValues = [];
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (!empty($bodyContent)) {
$bodyValues = json_decode($bodyContent, true);
}
$action = $bodyValues['action'] ?? null;
if ($action === 'getTransactionInfo') {
$this->getTransactionInfo();
} else {
$this->exitWithExceptionMessage(new Exception('Invalid request', 400));
}
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while processing the Google Pay request.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
/**
* @return void
*/
private function getTransactionInfo()
{
/** @var GooglePayPaymentRequestDataBuilder $googlePayPaymentRequestDataBuilder */
$googlePayPaymentRequestDataBuilder = $this->module->getService(GooglePayPaymentRequestDataBuilder::class);
$transactionInfo = $googlePayPaymentRequestDataBuilder->build($this->context->cart->id);
$this->exitWithResponse([
'httpCode' => 200,
'body' => $transactionInfo->toArray(),
]);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,369 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Api\ValueObject\PayPalOrderResponse;
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\Order\Action\CreateOrderAction;
use PsCheckout\Core\PayPal\Card3DSecure\Card3DSecureConfiguration;
use PsCheckout\Core\PayPal\Card3DSecure\Card3DSecureValidator;
use PsCheckout\Core\PayPal\Order\Action\CapturePayPalOrderAction;
use PsCheckout\Core\PayPal\Order\Configuration\PayPalOrderStatus;
use PsCheckout\Core\PayPal\Order\Entity\PayPalOrder;
use PsCheckout\Core\PayPal\Order\Exception\PayPalOrderException;
use PsCheckout\Core\PayPal\Order\Provider\PayPalOrderProvider;
use PsCheckout\Core\Settings\Configuration\PayPalConfiguration;
use PsCheckout\Infrastructure\Adapter\Tools;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Infrastructure\Repository\OrderRepository;
use PsCheckout\Infrastructure\Repository\PaymentTokenRepository;
use PsCheckout\Infrastructure\Repository\PayPalOrderRepository;
use PsCheckout\Module\Presentation\Translator;
use Psr\Log\LoggerInterface;
class Ps_CheckoutPaymentModuleFrontController extends AbstractFrontController
{
public $ssl = true;
public $controller_type = 'front';
public $display_footer = false;
public $display_header = true;
private $orderPageUrl;
private $paypalOrderId;
/**
* @return bool
*/
public function checkAccess(): bool
{
return $this->context->customer && $this->context->cart;
}
/**
* @return void
*
* @throws PrestaShopException
*/
public function initContent()
{
parent::initContent();
$this->setTemplate('module:' . $this->module->name . '/views/templates/front/payment.tpl');
$this->context->smarty->assign([
'css_url' => $this->module->getPathUri() . 'views/css/payment.css',
'order_url' => $this->orderPageUrl,
]);
}
/**
* @return bool|void
*/
public function setMedia()
{
$this->registerStylesheet('ps_checkout_payment', '/modules/' . $this->module->name . '/views/css/payment.css');
parent::setMedia();
}
/**
* @return void
*/
public function postProcess()
{
$this->orderPageUrl = $this->context->link->getPageLink('order');
/** @var Tools $tools */
$tools = $this->module->getService(Tools::class);
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
$orderId = $tools->getValue('orderID');
if (!$orderId) {
$logger->error('No PayPal Order ID found.');
$tools->redirect($this->orderPageUrl);
}
$this->paypalOrderId = $orderId;
$logger->info('Processing PayPal Order ID: ' . $this->paypalOrderId);
try {
/** @var PayPalOrderRepository $payPalOrderRepository */
$payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class);
/** @var PayPalOrderProvider $payPalOrderProvider */
$payPalOrderProvider = $this->module->getService(PayPalOrderProvider::class);
/** @var OrderRepository $orderRepository */
$orderRepository = $this->module->getService(OrderRepository::class);
$payPalOrder = $payPalOrderRepository->getOneBy(['id' => $this->paypalOrderId]);
if (!$payPalOrder) {
$logger->error('PayPal Order not found: ' . $this->paypalOrderId);
$tools->redirect($this->orderPageUrl);
}
$orders = $orderRepository->getAllBy(['id_cart' => $payPalOrder->getIdCart()]);
if (!empty($orders)) {
$this->handleExistingOrder($payPalOrder->getIdCart());
}
if ($payPalOrder->getIdCart() !== $this->context->cart->id) {
$logger->error('Cart Id mismatch for PayPal Order: ' . $this->paypalOrderId);
$tools->redirect($this->orderPageUrl);
}
$payPalOrderResponse = $payPalOrderProvider->getById($this->paypalOrderId);
$this->handleOrderStatus($payPalOrderResponse, $payPalOrder);
} catch (Throwable $exception) {
$logger->error(
sprintf(
'PaymentController - Exception %s : %s',
$exception->getCode(),
$exception->getMessage()
),
['exception' => $exception]
);
/** @var Translator $translator **/
$translator = $this->module->getService(Translator::class);
$this->context->smarty->assign('error', $translator->trans('There was an error during the payment. Please try again or contact the support.'));
}
}
/**
* @param int $cartId
*
* @return void
*/
private function handleExistingOrder(int $cartId)
{
/** @var Tools $tools */
$tools = $this->module->getService(Tools::class);
/** @var PayPalOrderProvider $payPalOrderProvider */
$payPalOrderProvider = $this->module->getService(PayPalOrderProvider::class);
if ($this->context->customer->isLogged()) {
$tools->redirect($this->context->link->getPageLink('history'));
} else {
$payPalOrderResponse = $payPalOrderProvider->getById($this->paypalOrderId);
$this->redirectToOrderConfirmationPage(
$cartId,
$payPalOrderResponse->getCapture()['id'] ?? null,
$payPalOrderResponse->getStatus()
);
}
}
/**
* @param PayPalOrderResponse $payPalOrderResponse
* @param PayPalOrder $payPalOrder
*
* @return void
*
* @throws PayPalOrderException
* @throws PsCheckoutException
*/
private function handleOrderStatus(
PayPalOrderResponse $payPalOrderResponse,
PayPalOrder $payPalOrder
) {
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
/** @var CapturePayPalOrderAction $capturePayPalOrderAction */
$capturePayPalOrderAction = $this->module->getService(CapturePayPalOrderAction::class);
/** @var PayPalOrderProvider $payPalOrderProvider */
$payPalOrderProvider = $this->module->getService(PayPalOrderProvider::class);
switch ($payPalOrderResponse->getStatus()) {
case PayPalOrderStatus::COMPLETED:
$logger->info('PayPal Order Completed: ' . $this->paypalOrderId);
$this->createOrder($payPalOrderResponse, $payPalOrder);
break;
case PayPalOrderStatus::PAYER_ACTION_REQUIRED:
$logger->info('3DS Verification Required for PayPal Order: ' . $this->paypalOrderId);
$this->redirectTo3DSVerification($payPalOrderResponse);
break;
case PayPalOrderStatus::CREATED:
$this->handle3DSecureDecision($payPalOrderResponse, $payPalOrder);
break;
case PayPalOrderStatus::APPROVED:
$logger->info('Capturing PayPal Order: ' . $this->paypalOrderId);
$capturePayPalOrderAction->execute($payPalOrderResponse);
$this->createOrder($payPalOrderProvider->getById($this->paypalOrderId), $payPalOrder);
break;
}
}
/**
* @param PayPalOrderResponse $payPalOrderResponse
* @param PayPalOrder $payPalOrder
*
* @return void
*
* @throws PsCheckoutException
* @throws PayPalOrderException
*/
private function handle3DSecureDecision(
PayPalOrderResponse $payPalOrderResponse,
PayPalOrder $payPalOrder
) {
/** @var Card3DSecureValidator $card3DSecure */
$card3DSecure = $this->module->getService(Card3DSecureValidator::class);
/** @var CapturePayPalOrderAction $capturePayPalOrderAction */
$capturePayPalOrderAction = $this->module->getService(CapturePayPalOrderAction::class);
/** @var PayPalOrderProvider $payPalOrderProvider */
$payPalOrderProvider = $this->module->getService(PayPalOrderProvider::class);
/** @var PayPalConfiguration $payPalConfiguration */
$payPalConfiguration = $this->module->getService(PayPalConfiguration::class);
switch ($card3DSecure->getAuthorizationDecision($payPalOrderResponse)) {
case Card3DSecureConfiguration::DECISION_RETRY:
$this->redirectTo3DSVerification($payPalOrderResponse);
break;
case Card3DSecureConfiguration::DECISION_PROCEED:
$capturePayPalOrderAction->execute($payPalOrderResponse);
$this->createOrder($payPalOrderProvider->getById($this->paypalOrderId), $payPalOrder);
break;
case Card3DSecureConfiguration::DECISION_NO_DECISION:
if ($payPalConfiguration->getCardFieldsContingencies() !== 'SCA_ALWAYS') {
$capturePayPalOrderAction->execute($payPalOrderResponse);
$this->createOrder($payPalOrderProvider->getById($this->paypalOrderId), $payPalOrder);
}
break;
}
}
/**
* @param PayPalOrderResponse $payPalOrderResponse
* @param PayPalOrder $payPalOrder
*
* @return void
*
* @throws PsCheckoutException
*/
private function createOrder(PayPalOrderResponse $payPalOrderResponse, PayPalOrder $payPalOrder)
{
/** @var CreateOrderAction $createOrderAction */
$createOrderAction = $this->module->getService(CreateOrderAction::class);
$createOrderAction->execute($payPalOrderResponse);
if ($payPalOrder->getPaymentTokenId() && $payPalOrder->checkCustomerIntent(PayPalConfiguration::PS_CHECKOUT_CUSTOMER_INTENT_FAVORITE)) {
/** @var PaymentTokenRepository $paymentTokenRepository */
$paymentTokenRepository = $this->module->getService(PaymentTokenRepository::class);
$paymentTokenRepository->setTokenFavorite($payPalOrder->getPaymentTokenId(), $payPalOrderResponse->getCustomerId());
}
$this->redirectToOrderConfirmationPage(
$payPalOrder->getIdCart(),
$payPalOrderResponse->getCapture()['id'] ?? null,
$payPalOrderResponse->getStatus()
);
}
/**
* @param PayPalOrderResponse $paypalOrder
*
* @return void
*/
private function redirectTo3DSVerification(PayPalOrderResponse $payPalOrderResponse)
{
/** @var Tools $tools */
$tools = $this->module->getService(Tools::class);
$payerActionLinks = array_filter($payPalOrderResponse->getLinks(), function ($link) {
return $link['rel'] === 'payer-action';
});
if (!empty($payerActionLinks)) {
$redirectUrl = reset($payerActionLinks)['href'] . '&redirect_uri=' . urlencode(
$this->context->link->getModuleLink($this->module->name, 'payment', ['orderID' => $payPalOrderResponse->getId()])
);
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
$logger->info('Redirecting to 3DS verification: ' . $redirectUrl);
$tools->redirect($redirectUrl);
}
}
/**
* @param int $cartId
* @param string|null $captureId
* @param string $payPalOrderStatus
*
* @return void
*/
private function redirectToOrderConfirmationPage(int $cartId, $captureId, string $payPalOrderStatus)
{
/** @var Tools $tools */
$tools = $this->module->getService(Tools::class);
/** @var OrderRepository $orderRepository */
$orderRepository = $this->module->getService(OrderRepository::class);
$orders = $orderRepository->getAllBy(['id_cart' => $cartId]);
if (empty($orders)) {
return;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$orderConfirmationUrl = $this->context->link->getPageLink(
'order-confirmation',
true,
$orders[0]->id_lang ?? null,
[
'paypal_status' => $payPalOrderStatus,
'paypal_order' => $this->paypalOrderId,
'paypal_transaction' => $captureId,
'id_cart' => $cartId,
'id_module' => (int) $this->module->id,
'id_order' => $orders[0]->id ?? null,
'key' => (new Cart($cartId))->secure_key,
]
);
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
$logger->info('Redirecting to order confirmation: ' . $orderConfirmationUrl);
$tools->redirect($orderConfirmationUrl);
}
}
}

View File

@@ -0,0 +1,202 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\Order\Exception\Handler\OrderCreationExceptionHandler;
use PsCheckout\Core\Order\Processor\CreateOrderProcessor;
use PsCheckout\Core\Order\Request\ValueObject\ValidateOrderRequest;
use PsCheckout\Core\PayPal\Order\Entity\PayPalOrder;
use PsCheckout\Core\PayPal\Order\Validator\CreatedPayPalOrderValidator;
use PsCheckout\Core\PayPal\Order\Validator\CreatedPayPalOrderValidatorInterface;
use PsCheckout\Core\PayPal\Order\Validator\CreatedPayUponInvoiceOrderValidator;
use PsCheckout\Core\PayPal\Order\ValueObject\PayPalOrderCompletionData;
use PsCheckout\Infrastructure\Adapter\Tools;
use PsCheckout\Infrastructure\Adapter\Validate;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Infrastructure\Repository\PayPalOrderRepository;
use PsCheckout\Utility\Common\InputStreamUtility;
/**
* This controller receive ajax call to capture/authorize payment and create a PrestaShop Order
*/
class Ps_CheckoutValidateModuleFrontController extends AbstractFrontController
{
/**
* @var Ps_checkout
*/
public $module;
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
/** @var Tools $tools */
$tools = $this->module->getService(Tools::class);
/** @var Validate $validate */
$validate = $this->module->getService(Validate::class);
$checkoutRequest = null;
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (empty($bodyContent)) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
$bodyValues = json_decode($bodyContent, true);
} else {
$bodyValues = [
'orderID' => $tools->getValue('token'),
'payerID' => $tools->getValue('token')('PayerID'),
];
}
/** @var PayPalOrderRepository $payPalOrderRepository */
$payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class);
/** @var PayPalOrder $payPalOrder */
$payPalOrder = $payPalOrderRepository->getOneBy(['id' => $bodyValues['orderID']]);
if (!$payPalOrder) {
throw new PsCheckoutException('PayPal Order not found', PsCheckoutException::PAYPAL_ORDER_NOT_FOUND);
}
$checkoutRequest = new ValidateOrderRequest($bodyValues, $payPalOrder->getIdCart());
if (empty($checkoutRequest->getOrderId()) || !$validate->isGenericName($checkoutRequest->getOrderId())) {
$this->exitWithResponse([
'httpCode' => 400,
'body' => 'Payload invalid',
]);
}
/** @var CreateOrderProcessor $createOrderProcessor */
$createOrderProcessor = $this->module->getService(CreateOrderProcessor::class);
$createOrderProcessor->run($checkoutRequest);
/** @var CreatedPayPalOrderValidatorInterface $createdOrderValidator */
$createdOrderValidator = $this->getCreatedOrderValidator($checkoutRequest);
$completedPayPalOrderData = $createdOrderValidator->validate($checkoutRequest->getOrderId(), $checkoutRequest->getCartId());
if (empty($completedPayPalOrderData)) {
throw new PsCheckoutException('PayPal Order not found', PsCheckoutException::PAYPAL_ORDER_NOT_FOUND);
}
$this->sendOkResponse($completedPayPalOrderData);
} catch (Exception $exception) {
if (!$checkoutRequest) {
$this->handleOrderCreationException($exception, 'unknown');
}
// NOTE: Retry to get the PayPal Order after the order creation
/** @var CreatedPayPalOrderValidatorInterface $createdOrderValidator */
$createdOrderValidator = $this->getCreatedOrderValidator($checkoutRequest);
$completedPayPalOrderData = $createdOrderValidator->validate($checkoutRequest->getOrderId(), $checkoutRequest->getCartId());
if (!empty($completedPayPalOrderData)) {
$this->sendOkResponse($completedPayPalOrderData);
}
$this->handleOrderCreationException($exception, $checkoutRequest->getOrderId());
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while validating the PayPal order.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
/**
* @param PayPalOrderCompletionData $orderCompletionData
*
* @return void
*/
private function sendOkResponse(PayPalOrderCompletionData $orderCompletionData)
{
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => $orderCompletionData->toArray(),
'exceptionCode' => null,
'exceptionMessage' => null,
]);
}
/**
* @param Exception $exception
* @param string $paypalOrderId
*
* @return void
*/
public function handleOrderCreationException(Exception $exception, string $paypalOrderId)
{
/** @var OrderCreationExceptionHandler $orderCreationExceptionHandler */
$orderCreationExceptionHandler = $this->module->getService(OrderCreationExceptionHandler::class);
$responseData = $orderCreationExceptionHandler->handle($exception, $paypalOrderId);
if ($responseData['httpCode'] === 500) {
$this->exitWithResponse($responseData);
} else {
$this->sendBadRequestError($responseData['body']['error']['message'], $exception);
}
}
/**
* @param string $exceptionMessageForCustomer
* @param Exception $exception
*
* @return void
*/
private function sendBadRequestError(string $exceptionMessageForCustomer, Exception $exception)
{
$this->exitWithResponse([
'status' => false,
'httpCode' => 400,
'body' => [
'error' => [
'code' => $exception->getCode(),
'message' => $exceptionMessageForCustomer,
],
],
]);
}
private function getCreatedOrderValidator(ValidateOrderRequest $checkoutRequest)
{
if ($checkoutRequest->getFundingSource() === 'pay_upon_invoice') {
return $this->module->getService(CreatedPayUponInvoiceOrderValidator::class);
}
return $this->module->getService(CreatedPayPalOrderValidator::class);
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\PaymentToken\Action\DeletePaymentTokenAction;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Infrastructure\Repository\PaymentTokenRepository;
use PsCheckout\Utility\Common\InputStreamUtility;
/**
* This controller receive ajax call to manage the Customer PayPal Payment Method tokens
*/
class Ps_CheckoutVaultModuleFrontController extends AbstractFrontController
{
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
try {
$bodyValues = [];
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$bodyContent = $inputStreamUtility->getBodyContent();
if (!empty($bodyContent)) {
$bodyValues = json_decode($bodyContent, true);
}
$customerId = $this->context->customer->isLogged() ? $this->context->customer->id : null;
if (isset($bodyValues['action'])) {
$action = $bodyValues['action'];
switch ($action) {
case 'deleteToken':
$vaultId = $bodyValues['vaultId'];
/** @var DeletePaymentTokenAction $deletePaymentTokenAction */
$deletePaymentTokenAction = $this->module->getService(DeletePaymentTokenAction::class);
$deletePaymentTokenAction->execute($vaultId, $customerId);
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
]);
break;
default:
$this->exitWithResponse([
'status' => false,
'httpCode' => 400,
]);
}
}
/** @var PaymentTokenRepository $paymentTokenRepository */
$paymentTokenRepository = $this->module->getService(PaymentTokenRepository::class);
$tokens = $paymentTokenRepository->getAllByCustomerId($customerId);
$this->exitWithResponse([
'status' => true,
'httpCode' => 200,
'body' => [
'customerId' => $customerId,
'paymentTokens' => $tokens,
'totalItems' => null,
'totalPages' => null,
],
]);
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while processing the vault request.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
}

View File

@@ -0,0 +1,213 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PsCheckout\Core\Exception\PsCheckoutException;
use PsCheckout\Core\Webhook\Handler\WebhookHandler;
use PsCheckout\Core\Webhook\Handler\WebhookHandlerInterface;
use PsCheckout\Core\Webhook\WebhookException;
use PsCheckout\Infrastructure\Controller\AbstractFrontController;
use PsCheckout\Utility\Common\InputStreamUtility;
use Psr\Log\LoggerInterface;
/**
* This controller receive webhook from API to performs asynchronous changes
*/
class Ps_CheckoutWebhookModuleFrontController extends AbstractFrontController
{
/**
* @var bool If set to true, will be redirected to authentication page
*/
public $auth = false;
/**
* @see FrontController::postProcess()
*/
public function postProcess()
{
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
try {
/** @var WebhookHandlerInterface $webhookHandler */
$webhookHandler = $this->module->getService(WebhookHandler::class);
if (empty($_SERVER['HTTP_WEBHOOK_SECRET']) || !$webhookHandler->authenticate($_SERVER['HTTP_WEBHOOK_SECRET'])) {
throw new WebhookException('Webhook secret mismatch', WebhookException::WEBHOOK_SECRET_MISMATCH);
}
$payload = $this->getPayload();
$webhookHandler->handle($payload);
$logger->debug(
'Webhook handled successfully',
[
'id' => $payload['id'],
'createTime' => $payload['createTime'],
'eventType' => $payload['eventType'],
'eventVersion' => $payload['eventVersion'],
'summary' => $payload['summary'],
'resourceType' => $payload['resourceType'],
'resource' => $payload['resource'],
]
);
$this->exitWithResponse([
'httpCode' => 200,
]);
exit;
} catch (WebhookException $exception) {
switch ($exception->getCode()) {
case WebhookException::WEBHOOK_SECRET_MISMATCH:
$this->exitWithResponse([
'httpCode' => 401,
'error' => $exception->getMessage(),
]);
break;
default:
$this->exitWithResponse([
'httpCode' => 400,
'error' => $exception->getMessage(),
]);
}
exit;
} catch (Exception $exception) {
$this->exitWithExceptionMessage($exception);
} catch (Throwable $exception) {
$this->exitWithExceptionMessage(new PsCheckoutException(
'An error occurred while processing the webhook.',
PsCheckoutException::UNKNOWN,
$exception
));
}
}
/**
* @return array{id: string, createTime: string, eventType: string, eventVersion: string, summary: string, resourceType: string, resource: array}
*
* @throws PsCheckoutException
*/
private function getPayload(): array
{
/** @var InputStreamUtility $inputStreamUtility */
$inputStreamUtility = $this->module->getService(InputStreamUtility::class);
$content = $inputStreamUtility->getBodyContent();
if (empty($content)) {
throw new WebhookException('Webhook payload is missing.', WebhookException::WEBHOOK_PAYLOAD_INVALID);
}
$payload = json_decode($content, true);
if (null === $payload && JSON_ERROR_NONE !== json_last_error()) {
throw new PsCheckoutException('Webhook payload cannot be decoded: ' . json_last_error_msg(), WebhookException::WEBHOOK_PAYLOAD_INVALID);
}
if (empty($payload['id'])) {
throw new WebhookException('Webhook id is missing', WebhookException::WEBHOOK_PAYLOAD_EVENT_TYPE_MISSING);
}
if (empty($payload['createTime'])) {
throw new WebhookException('Webhook createTime is missing', WebhookException::WEBHOOK_PAYLOAD_EVENT_TYPE_MISSING);
}
if (empty($payload['eventType'])) {
throw new WebhookException('Webhook eventType is missing', WebhookException::WEBHOOK_PAYLOAD_EVENT_TYPE_MISSING);
}
if (empty($payload['eventVersion'])) {
throw new WebhookException('Webhook eventVersion is missing', WebhookException::WEBHOOK_PAYLOAD_EVENT_TYPE_MISSING);
}
if (empty($payload['summary'])) {
throw new WebhookException('Webhook summary is missing', WebhookException::WEBHOOK_PAYLOAD_EVENT_TYPE_MISSING);
}
if (empty($payload['resourceType'])) {
throw new WebhookException('Webhook resourceType is missing', WebhookException::WEBHOOK_PAYLOAD_EVENT_TYPE_MISSING);
}
if (empty($payload['resource'])) {
throw new WebhookException('Webhook resource is missing', WebhookException::WEBHOOK_PAYLOAD_RESOURCE_MISSING);
}
return $payload;
}
/**
* Override displayMaintenancePage to prevent the maintenance page to be displayed
*
* @see FrontController::displayMaintenancePage()
*/
protected function displayMaintenancePage()
{
return;
}
/**
* Override displayRestrictedCountryPage to prevent page country is not allowed
*
* @see FrontController::displayRestrictedCountryPage()
*/
protected function displayRestrictedCountryPage()
{
return;
}
/**
* Override geolocationManagement to prevent country GEOIP blocking
*
* @see FrontController::geolocationManagement()
*
* @param Country $defaultCountry
*
* @return false
*/
protected function geolocationManagement($defaultCountry): bool
{
return false;
}
/**
* Override sslRedirection to prevent redirection
*
* @see FrontController::sslRedirection()
*/
protected function sslRedirection()
{
return;
}
/**
* Override canonicalRedirection to prevent redirection
*
* @see FrontController::canonicalRedirection()
*
* @param string $canonical_url
*/
protected function canonicalRedirection($canonical_url = '')
{
return;
}
}