From 591bbea69c19bc050299e874bca2b0121915ec9c Mon Sep 17 00:00:00 2001 From: Francois-Gomis Date: Tue, 17 Sep 2024 17:22:04 +0200 Subject: [PATCH 1/2] feature : Add hmac validation to IPN controller --- .../Alma/Installments/Helper/Payment.php | 94 +++++++++++++++++++ .../Model/Exceptions/PaymentException.php | 6 ++ .../controllers/PaymentController.php | 17 ++++ 3 files changed, 117 insertions(+) create mode 100644 src/app/code/community/Alma/Installments/Helper/Payment.php create mode 100644 src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php diff --git a/src/app/code/community/Alma/Installments/Helper/Payment.php b/src/app/code/community/Alma/Installments/Helper/Payment.php new file mode 100644 index 0000000..11a2739 --- /dev/null +++ b/src/app/code/community/Alma/Installments/Helper/Payment.php @@ -0,0 +1,94 @@ + + * @copyright 2024 Alma / Nabla SAS + * @license https://opensource.org/licenses/MIT The MIT License + * + */ + +class Alma_Installments_Helper_Payment extends Mage_Core_Helper_Abstract +{ + const HEADER_SIGNATURE_KEY = 'X-Alma-Signature'; + + /** + * @var AlmaLogger + */ + private $logger; + + public function __construct() + { + $this->logger = Mage::helper('alma/Logger')->getLogger(); + } + + /** + * @return string + * @throws Alma_installments_Model_Exceptions_PaymentException + */ + private function getHeaderSignature() + { + $signature = null; + + try { + $signature = $this->_getRequest()->getHeader(self::HEADER_SIGNATURE_KEY); + } catch (Zend_Controller_Request_Exception $e) { + $this->logger->error('Error retrieving header signature: ', [$e->getMessage()]); + } finally { + if (!$signature) { + throw new Alma_installments_Model_Exceptions_PaymentException('Header signature not found'); + }; + return $signature; + } + } + + /** + * @return void + * @throws Alma_installments_Model_Exceptions_PaymentException + */ + public function checkSignature($almaPaymentId) + { + if (!$this->isHmacValidated($almaPaymentId, $this->getApiKey(), $this->getHeaderSignature())) { + throw new Alma_installments_Model_Exceptions_PaymentException('Invalid signature'); + } + } + + private function isHmacValidated($almaPaymentId, $apiKey, $signature) + { + return is_string($almaPaymentId) && + is_string($apiKey) && + hash_hmac('sha256', $almaPaymentId, $apiKey) === $signature; + } + + /** + * @return string + * @throws Alma_installments_Model_Exceptions_PaymentException + */ + private function getApiKey() + { + /** @var Alma_Installments_Helper_Config $configHelper */ + $configHelper = Mage::helper('alma/Config'); + $apiKey = $configHelper->getActiveAPIKey(); + if (!$apiKey) { + throw new Alma_installments_Model_Exceptions_PaymentException('API key not found'); + } + return $apiKey; + } + + +} diff --git a/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php b/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php new file mode 100644 index 0000000..359196a --- /dev/null +++ b/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php @@ -0,0 +1,6 @@ +getLogger(); + + /** @var Alma_Installments_Helper_Payment $paymentHelper */ + $paymentHelper = Mage::helper('alma/Payment'); + $this->getResponse()->clearHeaders()->setHeader('Content-type','application/json',true); $body = Mage::helper('core')->jsonEncode(array('success' => true)); + $pid = $this->getRequest()->getParam('pid'); + + try { + $paymentHelper->checkSignature($pid); + } catch (Alma_installments_Model_Exceptions_PaymentException $e) { + $logger->error('Error with signature validation :', [$e->getMessage()]); + $body = Mage::helper('core')->jsonEncode(array('error' => $e->getMessage())); + $this->getResponse()->setBody($body); + return; + } + try { $this->validatePayment(); } catch (Exception $e) { From c9fd48711416cd36b0285dcf04026ed075eece39 Mon Sep 17 00:00:00 2001 From: Francois-Gomis Date: Thu, 19 Sep 2024 12:01:58 +0200 Subject: [PATCH 2/2] fix : php documentation --- .../Alma/Installments/Helper/Payment.php | 18 ++++++++ .../Model/Exceptions/PaymentException.php | 2 +- .../controllers/PaymentController.php | 41 +++++++++++++++++-- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/app/code/community/Alma/Installments/Helper/Payment.php b/src/app/code/community/Alma/Installments/Helper/Payment.php index 11a2739..8b6204b 100644 --- a/src/app/code/community/Alma/Installments/Helper/Payment.php +++ b/src/app/code/community/Alma/Installments/Helper/Payment.php @@ -23,6 +23,10 @@ * */ +/** + * Alma_Installments_Helper_Payment + * Payment helper class + */ class Alma_Installments_Helper_Payment extends Mage_Core_Helper_Abstract { const HEADER_SIGNATURE_KEY = 'X-Alma-Signature'; @@ -38,6 +42,8 @@ public function __construct() } /** + * Get header signature + * * @return string * @throws Alma_installments_Model_Exceptions_PaymentException */ @@ -58,6 +64,8 @@ private function getHeaderSignature() } /** + * Check signature with payment id and API key with error handling + * * @return void * @throws Alma_installments_Model_Exceptions_PaymentException */ @@ -68,6 +76,14 @@ public function checkSignature($almaPaymentId) } } + /** + * Validate hmac signature + * + * @param string $almaPaymentId + * @param string $apiKey + * @param string $signature + * @return bool + */ private function isHmacValidated($almaPaymentId, $apiKey, $signature) { return is_string($almaPaymentId) && @@ -76,6 +92,8 @@ private function isHmacValidated($almaPaymentId, $apiKey, $signature) } /** + * Get Current API key in config with error handling + * * @return string * @throws Alma_installments_Model_Exceptions_PaymentException */ diff --git a/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php b/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php index 359196a..8cede0d 100644 --- a/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php +++ b/src/app/code/community/Alma/Installments/Model/Exceptions/PaymentException.php @@ -3,4 +3,4 @@ class Alma_installments_Model_Exceptions_PaymentException extends \Mage_Core_Exception { -} \ No newline at end of file +} diff --git a/src/app/code/community/Alma/Installments/controllers/PaymentController.php b/src/app/code/community/Alma/Installments/controllers/PaymentController.php index 13e7921..b5897d3 100644 --- a/src/app/code/community/Alma/Installments/controllers/PaymentController.php +++ b/src/app/code/community/Alma/Installments/controllers/PaymentController.php @@ -27,7 +27,9 @@ use Alma\API\Entities\Instalment; use Alma\API\Entities\Payment; - +/** + * Exception for payment validation errors + */ class AlmaPaymentValidationError extends \Exception { /** * @var string @@ -46,13 +48,26 @@ public function getReturnPath() { } +/** + * Determine front controller actions for Alma payments + */ class Alma_Installments_PaymentController extends Mage_Core_Controller_Front_Action { + /** + * Cancel an order with a message + * + * @param Mage_Sales_Model_Order $order + * @param string $error + * @return void + * @throws Mage_Core_Exception + */ private function cancelOrder($order, $error) { $order->registerCancellation($error)->save(); } /** + * Get checkout session model instance + * * @return Mage_Checkout_Model_Session */ private function getSession() @@ -61,8 +76,11 @@ private function getSession() } /** + * Validate payment and return redirect path + * * @return string * @throws AlmaPaymentValidationError + * @throws Mage_Core_Exception */ private function validatePayment() { @@ -96,7 +114,7 @@ private function validatePayment() if (Alma_Installments_Helper_Functions::priceToCents($payment->getAmountAuthorized()) !== $almaPayment->purchase_amount) { $internalError = $this->__( "Paid amount (%1) does not match due amount (%2) for order %3", - Functions::priceFromCents($almaPayment->purchase_amount), + Alma_Installments_Helper_Functions::priceFromCents($almaPayment->purchase_amount), $payment->getAmountAuthorized(), $order->getIncrementId() ); @@ -180,6 +198,12 @@ private function validatePayment() throw new AlmaPaymentValidationError(null, 'checkout/cart'); } + /** + * Customer return action method name determine url params + * + * @return Mage_Core_Controller_Varien_Action + * @throws Mage_Core_Exception + */ public function returnAction() { try { @@ -192,6 +216,12 @@ public function returnAction() return $this->_redirect($redirect_to, array('_secure' => true)); } + /** + * Ipn action method name determine url params + * + * @return void + * @throws Zend_Controller_Response_Exception + */ public function ipnAction() { /** @var AlmaLogger $logger */ @@ -224,9 +254,14 @@ public function ipnAction() $this->getResponse()->setBody($body); } + /** + * Cancel action method name determine url params + * + * @return Alma_Installments_PaymentController + * @throws Mage_Core_Exception + */ public function cancelAction() { - /** @var Mage_Sales_Model_Order $order */ $order = $this->getSession()->getLastRealOrder(); if ($order) { $this->cancelOrder($order, $this->__("Order canceled by customer"));