From 136db42e9e9aed90b3eac869d3489c72b83b9ef9 Mon Sep 17 00:00:00 2001 From: JKacicM Date: Wed, 17 Jul 2024 13:18:41 -0400 Subject: [PATCH] =?UTF-8?q?Actualizaci=C3=B3n=20a=20nueva=20API=203.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controller/Payment/Callback.php | 136 ++++++++++++++++++--- Model/Simplified.php | 205 ++++++++------------------------ README.md | 4 +- composer.json | 5 +- etc/adminhtml/system.xml | 12 +- etc/di.xml | 5 + etc/module.xml | 2 +- 7 files changed, 190 insertions(+), 179 deletions(-) diff --git a/Controller/Payment/Callback.php b/Controller/Payment/Callback.php index 82c2d08..e288011 100644 --- a/Controller/Payment/Callback.php +++ b/Controller/Payment/Callback.php @@ -8,22 +8,33 @@ use Magento\Framework\App\CsrfAwareActionInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Sales\Model\Order\Email\Sender\OrderSender; class Callback extends Action implements CsrfAwareActionInterface { protected $order; protected $khipuPayment; + protected $resultJsonFactory; + protected $scopeConfig; + protected $orderSender; public function __construct( Context $context, Order $order, - Simplified $khipuPayment + Simplified $khipuPayment, + JsonFactory $resultJsonFactory, + ScopeConfigInterface $scopeConfig, + OrderSender $orderSender ) { parent::__construct($context); - $this->order = $order; $this->khipuPayment = $khipuPayment; + $this->resultJsonFactory = $resultJsonFactory; + $this->scopeConfig = $scopeConfig; + $this->orderSender = $orderSender; } public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException @@ -36,22 +47,119 @@ public function validateForCsrf(RequestInterface $request): ?bool return true; } - /** - * Default customer account page - * - * @return void - */ public function execute() { - $order = $this->order->loadByIncrementId($this->getRequest()->getParam('order_id')); + $secret = $this->scopeConfig->getValue('payment/simplified/merchant_secret'); + + if (!$secret) { + throw new \Exception('Missing secret in configuration'); + } + $raw_post = file_get_contents('php://input'); + $signature = $_SERVER['HTTP_X_KHIPU_SIGNATURE'] ?? ''; + + if (!$signature) { + return $this->resultJsonFactory->create()->setData(['error' => 'Missing signature header'])->setStatusHeader(400); + } + + $notificationData = json_decode($raw_post, true); + try { - $this->khipuPayment->validateKhipuCallback($order, $this->getRequest()->getPost()['notification_token'], - $this->getRequest()->getPost()['api_version']); + $this->verifyNotification($raw_post, $signature, $secret); } catch (\Exception $e) { - $this->getResponse()->setStatusCode(\Magento\Framework\App\Response\Http::STATUS_CODE_400); - $this->getResponse()->setContent($e->getMessage()); - return; + return $this->resultJsonFactory->create()->setData(['error' => $e->getMessage()])->setStatusHeader(400); + } + + if (isset($notificationData['payment_id'])) { + $order = $this->order->loadByIncrementId($notificationData['transaction_id']); + if ($order->getId()) { + try { + $this->validateKhipuCallback($order, $notificationData, '3.0'); + } catch (\Exception $e) { + return $this->resultJsonFactory->create()->setData(['error' => $e->getMessage()])->setStatusHeader(400); + } + return $this->resultJsonFactory->create()->setData(['success' => true])->setStatusHeader(200); + } else { + return $this->resultJsonFactory->create()->setData(['error' => 'Order not found'])->setStatusHeader(404); + } + } else { + return $this->resultJsonFactory->create()->setData(['error' => 'Invalid notification data'])->setStatusHeader(400); } - $this->getResponse()->setBody('OK'); + } + + private function verifyNotification($notificationData, $signatureHeader, $secret) + { + $signature_parts = explode(',', $signatureHeader); + $t_value = ''; + $s_value = ''; + foreach ($signature_parts as $part) { + [$key, $value] = explode('=', $part); + if ($key === 't') { + $t_value = $value; + } elseif ($key === 's') { + $s_value = $value; + } + } + + $to_hash = $t_value . '.' . $notificationData; + $hmac_signature = hash_hmac('sha256', $to_hash, $secret, true); + $hmac_base64 = base64_encode($hmac_signature); + + return hash_equals($hmac_base64, $s_value); + } + + + public function validateKhipuCallback(Order $order, $notificationData, $apiVersion) + { + if (!$order || !$order->getIncrementId()) { + throw new \Exception('Order #' . $_REQUEST['order_id'] . ' does not exist'); + } + + if ($apiVersion != '3.0') { + throw new \Exception('Invalid notification API version.'); + } + + if ($notificationData['receiver_id'] != $this->scopeConfig->getValue('payment/simplified/merchant_id')) { + throw new \Exception('Invalid receiver ID'); + } + + if ($notificationData['custom'] != $order->getPayment()->getAdditionalInformation('khipu_order_token')) { + throw new \Exception('Invalid transaction ID'); + } + + if ($notificationData['amount'] != number_format($order->getGrandTotal(), + $this->khipuPayment->getDecimalPlaces($order->getOrderCurrencyCode()), '.', '') + ) { + throw new \Exception('Amount mismatch'); + } + + if ($notificationData['currency'] != $order->getOrderCurrencyCode()) { + throw new \Exception('Currency mismatch'); + } + + $responseTxt = 'Pago Khipu Aceptado
'; + $responseTxt .= 'TransactionId: ' . $notificationData['transaction_id'] . '
'; + $responseTxt .= 'PaymentId: ' . $notificationData['payment_id'] . '
'; + $responseTxt .= 'Subject: ' . $notificationData['subject'] . '
'; + $responseTxt .= 'Amount: ' . $notificationData['amount'] .' '.$notificationData['currency'] .'
'; + $responseTxt .= 'Body: ' . $notificationData['body'] . '
'; + $responseTxt .= 'Bank: ' . $notificationData['bank'] . '
'; + $responseTxt .= 'Bank Account Number: ' . $notificationData['bank_account_number'] . '
'; + $responseTxt .= 'Payer Name: ' . $notificationData['payer_name'] . '
'; + $responseTxt .= 'Payer Email: ' . $notificationData['payer_email'] . '
'; + $responseTxt .= 'Personal Identifier: ' . $notificationData['personal_identifier'] . '
'; + + $invoice = $order->prepareInvoice(); + $invoice->register(); + $invoice->save(); + + $paymentCompleteStatus = $this->scopeConfig->getValue('payment/simplified/payment_complete_status'); + + $order->setState($paymentCompleteStatus, false, "Pago Realizado con Khipu", true); + $order->setStatus($order->getConfig()->getStateDefaultStatus($paymentCompleteStatus)); + $order->setIsCustomerNotified(true); + $order->addStatusToHistory($paymentCompleteStatus, $responseTxt); + $order->save(); + + $this->orderSender->send($order); } } diff --git a/Model/Simplified.php b/Model/Simplified.php index adaa62f..f1b0e4b 100755 --- a/Model/Simplified.php +++ b/Model/Simplified.php @@ -21,7 +21,6 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Sales\Model\Order\Email\Sender\OrderSender; - class Simplified extends \Magento\Payment\Model\Method\AbstractMethod { const KHIPU_MAGENTO_VERSION = "2.4.10"; @@ -35,24 +34,6 @@ class Simplified extends \Magento\Payment\Model\Method\AbstractMethod protected $_canUseCheckout = true; protected $_canFetchTransactionInfo = true; - /** - * @param Context $context - * @param Registry $registry - * @param ExtensionAttributesFactory $extensionFactory - * @param AttributeValueFactory $customAttributeFactory - * @param Data $paymentData - * @param ScopeConfigInterface $scopeConfig - * @param Logger $logger - * @param UrlInterface $urlBuilder - * @param StoreManagerInterface $storeManager - * @param AbstractResource|null $resource - * @param AbstractDb|null $resourceCollection - * @param array $data - * @internal param ModuleListInterface $moduleList - * @internal param TimezoneInterface $localeDate - * @internal param CountryFactory $countryFactory - * @internal param Http $response - */ public function __construct( Context $context, Registry $registry, @@ -85,16 +66,10 @@ public function __construct( $this->urlBuilder = $urlBuilder; $this->storeManager = $storeManager; $this->orderSender = $orderSender; - } - /** - * @param Order $order - * @return array - */ public function getKhipuRequest(Order $order) { - $token = substr(md5(rand()), 0, 32); $payment = $order->getPayment(); @@ -106,58 +81,60 @@ public function getKhipuRequest(Order $order) $description[] = number_format($item->getQtyOrdered(), 0) . ' × ' . $item->getName(); } - $configuration = new \Khipu\Configuration(); - $configuration->setSecret($this->getConfigData('merchant_secret')); - $configuration->setReceiverId($this->getConfigData('merchant_id')); - $configuration->setPlatform('magento2-khipu', Simplified::KHIPU_MAGENTO_VERSION); - - $client = new \Khipu\ApiClient($configuration); - $payments = new \Khipu\Client\PaymentsApi($client); - - error_log($this->getConfigData('merchant_secret')); - - error_log($this->getConfigData('merchant_id')); - - try { - - $opts = array( - "transaction_id" => $order->getIncrementId(), - "body" => join(', ',$description), - "custom" => $payment->getAdditionalInformation('khipu_order_token'), - "return_url" => $this->urlBuilder->getUrl('checkout/onepage/success'), - "cancel_url" => $this->urlBuilder->getUrl('checkout/onepage/failure'), - "notify_url" => ($this->urlBuilder->getUrl('khipupayment/payment/callback', array("order_id" => $order->getIncrementId()))), - "notify_api_version" => "1.3", - "payer_email" => $order->getCustomerEmail() - ); - - $createPaymentResponse = $payments->paymentsPost( - $this->storeManager->getWebsite()->getName() . ' Carro #' . $order->getIncrementId() - , $order->getOrderCurrencyCode() - , number_format($order->getGrandTotal(), $this->getDecimalPlaces($order->getOrderCurrencyCode()), '.', '') - , $opts - ); - } catch (\Khipu\ApiException $e) { - $error = $e->getResponseObject(); - $msg = "Error de comunicación con khipu.\n"; - $msg .= "Código: " . $error->getStatus() . "\n"; - $msg .= "Mensaje: " . $error->getMessage() . "\n"; - if (method_exists($error, 'getErrors')) { - $msg .= "Errores:"; - foreach ($error->getErrors() as $errorItem) { - $msg .= "\n" . $errorItem->getField() . ": " . $errorItem->getMessage(); - } - } - return array( - 'reason' => $msg, - 'status' => false - ); + $apiKey = $this->getConfigData('api_key'); + $notifyUrl = $this->urlBuilder->getUrl('khipupayment/payment/callback', array("order_id" => $order->getIncrementId())); + $payerEmail = $order->getCustomerEmail(); + + $paymentData = [ + 'amount' => (float)number_format($order->getGrandTotal(), $this->getDecimalPlaces($order->getOrderCurrencyCode()), '.', ''), + 'currency' => $order->getOrderCurrencyCode(), + 'subject' => $this->storeManager->getWebsite()->getName() . ' Carro #' . $order->getIncrementId(), + 'transaction_id' => $order->getIncrementId(), + 'body' => join(', ', $description), + 'custom' => $payment->getAdditionalInformation('khipu_order_token'), + 'return_url' => $this->urlBuilder->getUrl('checkout/onepage/success'), + 'cancel_url' => $this->urlBuilder->getUrl('checkout/onepage/failure'), + 'notify_url' => $notifyUrl, + 'notify_api_version' => '3.0', + 'payer_email' => $payerEmail + ]; + + $ch = curl_init('https://payment-api.khipu.com/v3/payments'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'x-api-key: ' . $apiKey, + ]); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($paymentData)); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Timeout in seconds + curl_setopt($ch, CURLOPT_FAILONERROR, true); // Fail on HTTP error + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL peer verification + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Disable SSL host verification + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlError = curl_error($ch); + curl_close($ch); + + if ($curlError) { + $msg = "Error de comunicación con Khipu: " . $curlError; + return ['reason' => $msg, 'status' => false]; } - return array( - 'status' => true, - 'payment_url' => $createPaymentResponse->getSimplifiedTransferUrl() - ); + $responseData = json_decode($response, true); + if (isset($responseData['payment_id'])) { + $order->setKhipuPaymentId($responseData['payment_id']); + $order->save(); + return ['status' => true, 'payment_url' => $responseData['simplified_transfer_url']]; + } else { + $msg = "Error de comunicación con Khipu.\n"; + if (isset($responseData['message'])) { + $msg .= "Mensaje: " . $responseData['message'] . "\n"; + } + + return ['reason' => $msg, 'status' => false]; + } } public function getDecimalPlaces($currencyCode) @@ -167,82 +144,4 @@ public function getDecimalPlaces($currencyCode) } return 2; } - - /** - * @param Order $order - */ - public function validateKhipuCallback(Order $order, $notificationToken, $apiVersion) - { - if (!$order || !$order->getIncrementId()) { - throw new \Exception('Order #' . $_REQUEST['order_id'] . ' does not exists'); - } - - $payment = $order->getPayment(); - - if ($apiVersion != '1.3') { - throw new \Exception('Invalid notification api version.'); - } - $configuration = new \Khipu\Configuration(); - $configuration->setSecret($this->getConfigData('merchant_secret')); - $configuration->setReceiverId($this->getConfigData('merchant_id')); - $configuration->setPlatform('magento2-khipu', Simplified::KHIPU_MAGENTO_VERSION); - - $client = new \Khipu\ApiClient($configuration); - $payments = new \Khipu\Client\PaymentsApi($client); - - try { - $paymentResponse = $payments->paymentsGet($notificationToken); - } catch (\Khipu\ApiException $exception) { - throw new \Exception(print_r($exception->getResponseObject(), TRUE)); - } - - if ($paymentResponse->getReceiverId() != $this->getConfigData('merchant_id')) { - throw new \Exception('Invalid receiver id'); - } - - if ($paymentResponse->getCustom() != $payment->getAdditionalInformation('khipu_order_token')) { - throw new \Exception('Invalid transaction id'); - } - - if ($paymentResponse->getStatus() != 'done') { - throw new \Exception('Payment not done'); - } - - if ($paymentResponse->getAmount() != number_format($order->getGrandTotal(), - $this->getDecimalPlaces($order->getOrderCurrencyCode()), '.', '') - ) { - throw new \Exception('Amount mismatch'); - } - - if ($paymentResponse->getCurrency() != $order->getOrderCurrencyCode()) { - throw new \Exception('Currency mismatch'); - } - - $responseTxt = 'Pago Khipu Aceptado
'; - $responseTxt .= 'TransactionId: ' . $paymentResponse->getTransactionId() . '
'; - $responseTxt .= 'PaymentId: ' . $paymentResponse->getPaymentId() . '
'; - $responseTxt .= 'Subject: ' . $paymentResponse->getSubject() . '
'; - $responseTxt .= 'Amount: ' . $paymentResponse->getAmount() .' '.$paymentResponse->getCurrency() .'
'; - $responseTxt .= 'Status: ' . $paymentResponse->getStatus() .' - ' . $paymentResponse->getStatusDetail() .'
'; - $responseTxt .= 'Body: ' . $paymentResponse->getBody() . '
'; - $responseTxt .= 'Bank: ' . $paymentResponse->getBank() . '
'; - $responseTxt .= 'Bank Account Number: ' . $paymentResponse->getBankAccountNumber() . '
'; - $responseTxt .= 'Payer Name: ' . $paymentResponse->getPayerName() . '
'; - $responseTxt .= 'Payer Email: ' . $paymentResponse->getPayerEmail() . '
'; - $responseTxt .= 'Personal Identifier: ' . $paymentResponse->getPersonalIdentifier() . '
'; - - $invoice = $order->prepareInvoice(); - $invoice->register(); - $invoice->save(); - - $paymentCompleteStatus = $this->getConfigData('payment_complete_status'); - - $order->setState($paymentCompleteStatus, false, "Pago Realizado con Khipu", true); - $order->setStatus($order->getConfig()->getStateDefaultStatus($paymentCompleteStatus)); - $order->setIsCustomerNotified(true); - $order->addStatusToHistory($paymentCompleteStatus, $responseTxt); - $order->save(); - - $this->orderSender->send($order); - } } diff --git a/README.md b/README.md index a1b5aec..17381fc 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Magento 2 Khipu Plugin -khipu payment gateway Magento 2.4.10 plugin. +khipu payment gateway Magento 2.5 plugin. This version is compatible with Magento 2.3 to 2.6 @@ -8,7 +8,7 @@ You can sign up for khipu account at ## Install via Composer -You can install Magento 2.4.10 khipu plugin via [Composer](http://getcomposer.org/). Run the following command in your terminal: +You can install Magento 2.5 khipu plugin via [Composer](http://getcomposer.org/). Run the following command in your terminal: 1. Go to your Magento 2 root folder. diff --git a/composer.json b/composer.json index 4f00174..c3ff47b 100755 --- a/composer.json +++ b/composer.json @@ -2,14 +2,11 @@ "name": "khipu/magento2-khipu", "type": "magento2-module", "description": "khipu integration for Magento 2", - "version": "2.4.10", + "version": "2.5", "homepage": "https://khipu.com", "license": [ "OSL-3.0" ], - "require": { - "khipu/khipu-api-client": "3.0.0.x-dev" - }, "autoload": { "files": [ "registration.php" ], "psr-4": { diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 39d66f4..1f90ac4 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -15,10 +15,14 @@ - + + + + - + + @@ -35,6 +39,4 @@ - - - + \ No newline at end of file diff --git a/etc/di.xml b/etc/di.xml index 2974214..b6d67b5 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -8,4 +8,9 @@ Magento\Sales\Model\Order\Email\Sender\OrderSender + + + Magento\Sales\Model\Order\Email\Sender\OrderSender + + \ No newline at end of file diff --git a/etc/module.xml b/etc/module.xml index 1475be1..9e4e3a5 100755 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - +