From c4130e5c7c8be85593f07032c16d298874009c64 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Sun, 10 Nov 2024 16:43:05 -0300 Subject: [PATCH 01/19] refactor: improve code flow --- .../TransactionStatusController.php | 125 +++++++++++------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/plugin/src/Controllers/TransactionStatusController.php b/plugin/src/Controllers/TransactionStatusController.php index aa22a82..4b1188e 100644 --- a/plugin/src/Controllers/TransactionStatusController.php +++ b/plugin/src/Controllers/TransactionStatusController.php @@ -10,69 +10,39 @@ class TransactionStatusController { const HTTP_OK = 200; const HTTP_UNPROCESSABLE_ENTITY = 422; - public function getStatus() + const DEFAULT_ERROR_MESSAGE = 'No se pudo obtener el estado de la transacción'; + const NO_TRANSACTION_ERROR_MESSAGE = 'No hay transacciones webpay aprobadas para esta orden'; + const BUY_ORDER_MISMATCH_ERROR_MESSAGE = 'El buy_order enviado y el buy_order de la transacción no coinciden'; + const TOKEN_MISMATCH_ERROR_MESSAGE = 'El token enviado y el token de la transacción no coinciden'; + public function getStatus(): void { $response = [ 'body' => [ - 'message' => 'No se pudo obtener el estado de la transacción' - ], - 'code' => self::HTTP_UNPROCESSABLE_ENTITY + 'message' => self::DEFAULT_ERROR_MESSAGE + ] ]; // Check for nonce security $nonce = sanitize_text_field($_POST['nonce']); if (!wp_verify_nonce($nonce, 'my-ajax-nonce')) { - wp_send_json($response['body'], $response['code']); + wp_send_json($response['body'], self::HTTP_UNPROCESSABLE_ENTITY); return; } - $orderId = filter_input(INPUT_POST, 'order_id', FILTER_DEFAULT); - $orderId = htmlspecialchars($orderId, ENT_QUOTES, 'UTF-8'); - $buyOrder = filter_input(INPUT_POST, 'buy_order', FILTER_DEFAULT); - $buyOrder = htmlspecialchars($buyOrder, ENT_QUOTES, 'UTF-8'); - $token = filter_input(INPUT_POST, 'token', FILTER_DEFAULT); - $token = htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); + $orderId = $this->getSecureInputValue('order_id'); + $buyOrder = $this->getSecureInputValue('buy_order'); + $token = $this->getSecureInputValue('token'); try { $transaction = Transaction::getApprovedByOrderId($orderId); - if (!$transaction) { - $response = [ - 'body' => [ - 'message' => 'No hay transacciones webpay aprobadas para esta orden' - ], - 'code' => self::HTTP_UNPROCESSABLE_ENTITY - ]; - } - - if ($transaction->product == Transaction::PRODUCT_WEBPAY_ONECLICK) { - if ($transaction->buy_order !== $buyOrder) { - $response = [ - 'body' => [ - 'message' => 'El buy_order enviado y el buy_order de la transacción no coinciden' - ], - 'code' => self::HTTP_UNPROCESSABLE_ENTITY - ]; - } - - $response = [ - 'body' => $this->getStatusForOneclickTransaction($orderId, $buyOrder), - 'code' => self::HTTP_OK - ]; - } - if ($transaction->token !== $token) { - $response = [ - 'body' => [ - 'message' => 'El token enviado y el token de la transacción no coinciden' - ], - 'code' => self::HTTP_UNPROCESSABLE_ENTITY - ]; + if (!$transaction) { + $response['body'] = self::NO_TRANSACTION_ERROR_MESSAGE; + wp_send_json($response['body'], self::HTTP_UNPROCESSABLE_ENTITY); + return; } - $response = [ - 'body' => $this->getStatusForWebpayTransaction($orderId, $token), - 'code' => self::HTTP_OK - ]; + $response = $this->handleGetStatus($transaction, $orderId, $buyOrder, $token); wp_send_json($response['body'], $response['code']); } catch (\Exception $e) { @@ -82,10 +52,59 @@ public function getStatus() } } + private function handleGetStatus(object $transaction, string $orderId, string $buyOrder, string $token): array + { + if ($transaction->product == Transaction::PRODUCT_WEBPAY_ONECLICK) { + return $this->handleOneclickStatus($orderId, $buyOrder, $transaction->buy_order); + } + + return $this->handleWebpayStatus($orderId, $token, $transaction->token); + } + + private function handleOneclickStatus( + string $orderId, + string $requestBuyOrder, + string $transactionBuyOrder + ): array { + if ($transactionBuyOrder !== $requestBuyOrder) { + return [ + 'body' => [ + 'message' => self::BUY_ORDER_MISMATCH_ERROR_MESSAGE + ], + 'code' => self::HTTP_UNPROCESSABLE_ENTITY + ]; + } + + return [ + 'body' => $this->getStatusForOneclickTransaction($orderId, $transactionBuyOrder), + 'code' => self::HTTP_OK + ]; + } + + private function handleWebpayStatus( + string $orderId, + string $requestToken, + string $transactionToken + ): array { + if ($transactionToken !== $requestToken) { + return [ + 'body' => [ + 'message' => self::TOKEN_MISMATCH_ERROR_MESSAGE + ], + 'code' => self::HTTP_UNPROCESSABLE_ENTITY + ]; + } + + return [ + 'body' => $this->getStatusForWebpayTransaction($orderId, $transactionToken), + 'code' => self::HTTP_OK + ]; + } + private function getStatusForWebpayTransaction(string $orderId, string $token) { - $webpayplusTransbankSdk = TbkFactory::createWebpayplusTransbankSdk(); - $resp = $webpayplusTransbankSdk->status($orderId, $token); + $webpayTransbankSDK = TbkFactory::createWebpayplusTransbankSdk(); + $resp = $webpayTransbankSDK->status($orderId, $token); $formattedDate = TbkResponseUtil::transactionDateToLocalDate($resp->getTransactionDate()); $modifiedResponse = clone $resp; $modifiedResponse->setTransactionDate($formattedDate); @@ -99,8 +118,8 @@ private function getStatusForWebpayTransaction(string $orderId, string $token) private function getStatusForOneclickTransaction(string $orderId, string $buyOrder) { - $oneclickTransbankSdk = TbkFactory::createOneclickTransbankSdk(); - $status = $oneclickTransbankSdk->status($orderId, $buyOrder); + $oneclickTransbankSDK = TbkFactory::createOneclickTransbankSdk(); + $status = $oneclickTransbankSDK->status($orderId, $buyOrder); $statusArray = json_decode(json_encode($status), true); $firstDetail = json_decode(json_encode($status->getDetails()[0]), true); @@ -115,4 +134,10 @@ private function getStatusForOneclickTransaction(string $orderId, string $buyOrd 'raw' => $status, ]; } + + private function getSecureInputValue(string $varName): string + { + $tmpValue = filter_input(INPUT_POST, $varName, FILTER_DEFAULT); + return htmlspecialchars($tmpValue, ENT_QUOTES, 'UTF-8'); + } } From fd88140aaa2b94413803db998bc080874ac985c8 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Mon, 11 Nov 2024 17:26:05 -0300 Subject: [PATCH 02/19] refactor: split code for transaction status meta box --- .../admin/order/transaction-status.php} | 5 +- plugin/webpay-rest.php | 143 +++++++++++------- 2 files changed, 93 insertions(+), 55 deletions(-) rename plugin/{views/get-status.php => templates/admin/order/transaction-status.php} (93%) diff --git a/plugin/views/get-status.php b/plugin/templates/admin/order/transaction-status.php similarity index 93% rename from plugin/views/get-status.php rename to plugin/templates/admin/order/transaction-status.php index 84960f4..0ef3bbf 100644 --- a/plugin/views/get-status.php +++ b/plugin/templates/admin/order/transaction-status.php @@ -2,14 +2,13 @@ if (!defined('ABSPATH')) { return; } -if (!$transaction) { +if (empty($viewData)) { echo 'No hay transacciones webpay aprobadas para esta orden'; - return; } ?> -Consultar estado de la transacción +Consultar estado de la transacción

Esta es la respuesta del API (solo disponible por 7 días desde la fecha de transacción)

diff --git a/plugin/webpay-rest.php b/plugin/webpay-rest.php index 0154259..e9f6d01 100644 --- a/plugin/webpay-rest.php +++ b/plugin/webpay-rest.php @@ -1,4 +1,5 @@ checkIfHposExists(); add_action('plugins_loaded', 'registerPaymentGateways', 0); -add_action('wp_loaded', 'woocommerceTransbankInit'); +add_action('wp_loaded', function () use ($hposExists) { + woocommerceTransbankInit($hposExists); +}); add_action('admin_init', 'on_transbank_rest_webpay_plugins_loaded'); add_action('wp_ajax_check_connection', ConnectionCheck::class . '::check'); @@ -60,36 +68,34 @@ ]); }); -add_action('woocommerce_blocks_loaded', function() { - if ( class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) ){ +add_action('woocommerce_blocks_loaded', function () { + if (class_exists('Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType')) { require_once 'src/Blocks/WC_Gateway_Transbank_Webpay_Blocks.php'; require_once 'src/Blocks/WC_Gateway_Transbank_Oneclick_Blocks.php'; add_action( 'woocommerce_blocks_payment_method_type_registration', - function( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_method_registry ) { - $payment_method_registry->register( new WCGatewayTransbankWebpayBlocks() ); - $payment_method_registry->register( new WCGatewayTransbankOneclickBlocks() ); + function (Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_method_registry) { + $payment_method_registry->register(new WCGatewayTransbankWebpayBlocks()); + $payment_method_registry->register(new WCGatewayTransbankOneclickBlocks()); } ); } }); -add_action( 'before_woocommerce_init', function() { - if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { - \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true ); +add_action('before_woocommerce_init', function () { + if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) { + \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('cart_checkout_blocks', __FILE__, true); } -} ); +}); -$hposHelper = new HposHelper(); -$hPosExists = $hposHelper->checkIfHposExists(); -if ($hPosExists) -{ +if ($hposExists) { add_action('before_woocommerce_init', function () { - if (class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { + if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, - true); + true + ); } }); } @@ -104,26 +110,31 @@ function( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment_ } }); -function woocommerceTransbankInit() { +function woocommerceTransbankInit(bool $hposExists) +{ if (!class_exists('WC_Payment_Gateway')) { noticeMissingWoocommerce(); return; } + registerAdminMenu(); registerPluginActionLinks(); + registerMetaBoxes($hposExists); NoticeHelper::registerNoticesDismissHook(); NoticeHelper::handleReviewNotice(); } -function registerPaymentGateways() { - add_filter('woocommerce_payment_gateways', function($methods) { +function registerPaymentGateways() +{ + add_filter('woocommerce_payment_gateways', function ($methods) { $methods[] = WC_Gateway_Transbank_Webpay_Plus_REST::class; $methods[] = WC_Gateway_Transbank_Oneclick_Mall_REST::class; return $methods; }); } -function registerAdminMenu() { +function registerAdminMenu() +{ add_action('admin_menu', function () { add_submenu_page('woocommerce', __('Configuración de Webpay Plus', 'transbank_wc_plugin'), 'Webpay Plus', 'administrator', 'transbank_webpay_plus_rest', function () { $tab = filter_input(INPUT_GET, 'tbk_tab', FILTER_DEFAULT); @@ -132,7 +143,7 @@ function registerAdminMenu() { wp_redirect(admin_url('admin.php?page=wc-settings&tab=checkout§ion=transbank_webpay_plus_rest&tbk_tab=options')); } - include_once __DIR__.'/views/admin/options-tabs.php'; + include_once __DIR__ . '/views/admin/options-tabs.php'; }, null); add_submenu_page('woocommerce', __('Configuración de Webpay Plus', 'transbank_wc_plugin'), 'Webpay Oneclick', 'administrator', 'transbank_webpay_oneclick_rest', function () { @@ -141,8 +152,9 @@ function registerAdminMenu() { }); } -function registerPluginActionLinks() { - add_filter('plugin_action_links_'.plugin_basename(__FILE__), function ($actionLinks) { +function registerPluginActionLinks() +{ + add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ($actionLinks) { $webpaySettingsLink = sprintf( '%s', admin_url('admin.php?page=wc-settings&tab=checkout§ion=transbank_webpay_plus_rest'), @@ -162,52 +174,78 @@ function registerPluginActionLinks() { }); } -function on_transbank_rest_webpay_plugins_loaded() +function registerMetaBoxes(bool $hPosExists) { - DatabaseTableInstaller::createTableIfNeeded(); -} - -function transbank_rest_remove_database() -{ - DatabaseTableInstaller::deleteTable(); + add_action('add_meta_boxes', function () use ($hPosExists) { + addTransbankStatusMetaBox($hPosExists); + }); } -register_uninstall_hook(__FILE__, 'transbank_rest_remove_database'); - -if ($hPosExists) +function addTransbankStatusMetaBox(bool $hPosExists) { - add_action('add_meta_boxes', function () { + if ($hPosExists) { $screen = wc_get_container() - ->get(Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class) + ->get(CustomOrdersTableController::class) ->custom_orders_table_usage_is_enabled() ? wc_get_page_screen_id('shop-order') : 'shop_order'; + add_meta_box( 'transbank_check_payment_status', __('Verificar estado del pago', 'transbank_wc_plugin'), function ($post_or_order_object) { $order = ($post_or_order_object instanceof WP_Post) - ? wc_get_order($post_or_order_object->ID) - : $post_or_order_object; - $transaction = Transaction::getApprovedByOrderId($order->get_id()); - include_once __DIR__.'/views/get-status.php'; + ? wc_get_order($post_or_order_object->ID) + : $post_or_order_object; + + $orderId = $order->get_id(); + renderTransactionStatusMetaBox($orderId); }, $screen, 'side', - 'core'); - }); -} -else -{ - add_action('add_meta_boxes', function () { + 'core' + ); + } else { add_meta_box('transbank_check_payment_status', __('Verificar estado del pago', 'transbank_wc_plugin'), function ($post) { $order = new WC_Order($post->ID); - $transaction = Transaction::getApprovedByOrderId($order->get_id()); - include_once __DIR__.'/views/get-status.php'; + $orderId = $order->get_id(); + renderTransactionStatusMetaBox($orderId); }, 'shop_order', 'side', 'core'); - }); + } +} + +function renderTransactionStatusMetaBox(int $orderId) +{ + $viewData = []; + $transaction = Transaction::getApprovedByOrderId($orderId); + + if ($transaction) { + $viewData = [ + 'viewData' => [ + 'orderId' => $orderId, + 'token' => $transaction->token, + 'buyOrder' => $transaction->buy_order + ] + ]; + } + + (new Template())->render('admin/order/transaction-status.php', $viewData); +} + +function on_transbank_rest_webpay_plugins_loaded() +{ + DatabaseTableInstaller::createTableIfNeeded(); } +function transbank_rest_remove_database() +{ + DatabaseTableInstaller::deleteTable(); +} + +register_uninstall_hook(__FILE__, 'transbank_rest_remove_database'); + + + function transbank_rest_before_cart() { SessionMessageHelper::printMessage(); @@ -225,7 +263,8 @@ function transbank_rest_check_cancelled_checkout() } } -function noticeMissingWoocommerce() { +function noticeMissingWoocommerce() +{ add_action( 'admin_notices', function () { @@ -253,7 +292,7 @@ function () { $isWooInstalled = !empty($allPlugins['woocommerce/woocommerce.php']); } - if(function_exists('is_plugin_active')) { + if (function_exists('is_plugin_active')) { $isWooActivated = is_plugin_active('woocommerce/woocommerce.php'); } @@ -272,7 +311,7 @@ function () { $noticeDescription = "Woocommerce no se encuentra activado."; } - include_once plugin_dir_path(__FILE__) .'views/admin/components/notice-missing-woocommerce.php'; + include_once plugin_dir_path(__FILE__) . 'views/admin/components/notice-missing-woocommerce.php'; } ); } From b23df79b41fea917f83d2f568fb6a0e6d3ee58af Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 10:40:59 -0300 Subject: [PATCH 03/19] feat: update expired query status error message --- plugin/src/WebpayplusTransbankSdk.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/WebpayplusTransbankSdk.php b/plugin/src/WebpayplusTransbankSdk.php index 8103913..48bb4f8 100644 --- a/plugin/src/WebpayplusTransbankSdk.php +++ b/plugin/src/WebpayplusTransbankSdk.php @@ -103,7 +103,7 @@ public function status($orderId, $token) $this->errorExecutionTbkApi($orderId, 'status', $params, 'StatusWebpayException', $e->getMessage(), $errorMessage); throw new StatusWebpayException($errorMessage, $token, $e); } elseif (ErrorUtil::isMaxTimeError($e)) { - $errorMessage = 'Ya pasaron mas de 7 dias desde la creacion de la transacción, ya no es posible consultarla por este medio'; + $errorMessage = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; $this->errorExecutionTbkApi($orderId, 'status', $params, 'StatusWebpayException', $e->getMessage(), $errorMessage); throw new StatusWebpayException($errorMessage, $token, $e); } From 589fa9bd8b4275a0a16e7986fb7bf1bd85c81403 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 10:43:50 -0300 Subject: [PATCH 04/19] refactor: reduce code in catch block for status method --- plugin/src/WebpayplusTransbankSdk.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugin/src/WebpayplusTransbankSdk.php b/plugin/src/WebpayplusTransbankSdk.php index 48bb4f8..96cd8e1 100644 --- a/plugin/src/WebpayplusTransbankSdk.php +++ b/plugin/src/WebpayplusTransbankSdk.php @@ -98,16 +98,16 @@ public function status($orderId, $token) $this->afterExecutionTbkApi($orderId, 'status', $params, $response); return $response; } catch (Exception $e) { + $errorMessage = 'Ocurrió un error al tratar de obtener el status ( token: '.$token.') de la transacción Webpay en Transbank: '.$e->getMessage(); + if (ErrorUtil::isApiMismatchError($e)) { $errorMessage = 'Esta utilizando una version de api distinta a la utilizada para crear la transacción'; - $this->errorExecutionTbkApi($orderId, 'status', $params, 'StatusWebpayException', $e->getMessage(), $errorMessage); - throw new StatusWebpayException($errorMessage, $token, $e); - } elseif (ErrorUtil::isMaxTimeError($e)) { + } + + if (ErrorUtil::isMaxTimeError($e)) { $errorMessage = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; - $this->errorExecutionTbkApi($orderId, 'status', $params, 'StatusWebpayException', $e->getMessage(), $errorMessage); - throw new StatusWebpayException($errorMessage, $token, $e); } - $errorMessage = 'Ocurrió un error al tratar de obtener el status ( token: '.$token.') de la transacción Webpay en Transbank: '.$e->getMessage(); + $this->errorExecutionTbkApi($orderId, 'status', $params, 'StatusWebpayException', $e->getMessage(), $errorMessage); throw new StatusWebpayException($errorMessage, $token, $e); } From a0ad2e60d7d66181d588e19f4043b37d8f8e5d71 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 12:51:55 -0300 Subject: [PATCH 05/19] feat: add specific error message in catch block for oneclick exception --- plugin/src/OneclickTransbankSdk.php | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/plugin/src/OneclickTransbankSdk.php b/plugin/src/OneclickTransbankSdk.php index b9705b6..8fdca6e 100644 --- a/plugin/src/OneclickTransbankSdk.php +++ b/plugin/src/OneclickTransbankSdk.php @@ -28,6 +28,7 @@ use Transbank\Plugin\Exceptions\Oneclick\StatusOneclickException; use Transbank\Plugin\Exceptions\Oneclick\StartOneclickException; use Transbank\Plugin\Exceptions\Oneclick\StartInscriptionOneclickException; +use Transbank\WooCommerce\WebpayRest\Helpers\ErrorUtil; /** * Class OneclickTransbankSdk. @@ -121,14 +122,24 @@ public function status($orderId, $buyOrder) $this->afterExecutionTbkApi($orderId, 'status', $params, $response); return $response; } catch (Exception $e) { - $maskedBuyOrder = $this->dataMasker->maskBuyOrder($buyOrder); - $errorMessage = 'Oneclick: Error al obtener el status ( buyOrder: '.$maskedBuyOrder.') '.$e->getMessage(); - $this->errorExecutionTbkApi($orderId, - 'status', - $params, - 'StatusOneclickException', - $e->getMessage(), - $errorMessage); + $errorMessage = 'Ocurrió un error al tratar de obtener el estado de la transacción.'; + + if(ErrorUtil::isMaxTimeError($e)) { + $errorMessage = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; + } + + if (ErrorUtil::isApiMismatchError($e)) { + $errorMessage = 'La version de API es distinta a la utilizada para crear la transacción.'; + } + + $this->errorExecutionTbkApi( + $orderId, + 'status', + $params, + 'StatusOneclickException', + $e->getMessage(), + $errorMessage + ); throw new StatusOneclickException($errorMessage, $buyOrder, $e); } } From 8c176df732a361b32d68103849950ae2b2972493 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 12:52:29 -0300 Subject: [PATCH 06/19] feat: improve exception message for oneclick status --- plugin/src/WebpayplusTransbankSdk.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugin/src/WebpayplusTransbankSdk.php b/plugin/src/WebpayplusTransbankSdk.php index 96cd8e1..fcc86e6 100644 --- a/plugin/src/WebpayplusTransbankSdk.php +++ b/plugin/src/WebpayplusTransbankSdk.php @@ -98,17 +98,24 @@ public function status($orderId, $token) $this->afterExecutionTbkApi($orderId, 'status', $params, $response); return $response; } catch (Exception $e) { - $errorMessage = 'Ocurrió un error al tratar de obtener el status ( token: '.$token.') de la transacción Webpay en Transbank: '.$e->getMessage(); + $errorMessage = 'Ocurrió un error al tratar de obtener el estado de la transacción.'; if (ErrorUtil::isApiMismatchError($e)) { - $errorMessage = 'Esta utilizando una version de api distinta a la utilizada para crear la transacción'; + $errorMessage = 'La version de API es distinta a la utilizada para crear la transacción.'; } if (ErrorUtil::isMaxTimeError($e)) { $errorMessage = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; } - $this->errorExecutionTbkApi($orderId, 'status', $params, 'StatusWebpayException', $e->getMessage(), $errorMessage); + $this->errorExecutionTbkApi( + $orderId, + 'status', + $params, + 'StatusWebpayException', + $e->getMessage(), + $errorMessage + ); throw new StatusWebpayException($errorMessage, $token, $e); } } From 5e0a6cda4fbb1c59aa5ddb9ac53025cc824b85c5 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 12:58:38 -0300 Subject: [PATCH 07/19] refactor: use constants for exception message --- plugin/src/Helpers/ErrorUtil.php | 4 ++++ plugin/src/OneclickTransbankSdk.php | 6 +++--- plugin/src/WebpayplusTransbankSdk.php | 10 +++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/plugin/src/Helpers/ErrorUtil.php b/plugin/src/Helpers/ErrorUtil.php index 15c4f2d..f67501e 100644 --- a/plugin/src/Helpers/ErrorUtil.php +++ b/plugin/src/Helpers/ErrorUtil.php @@ -12,6 +12,10 @@ class ErrorUtil { status 422 => "error_message": "Invalid value for parameter: transaction not found" */ + const DEFAULT_STATUS_ERROR_MESSAGE = 'Ocurrió un error al tratar de obtener el estado de la transacción.'; + const EXPIRED_TRANSACTION_ERROR_MESSAGE = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; + const API_MISMATCH_ERROR_MESSAGE = 'La version de API es distinta a la utilizada para crear la transacción.'; + public static function isApiMismatchError(\Throwable $e) { $error = $e->getMessage(); diff --git a/plugin/src/OneclickTransbankSdk.php b/plugin/src/OneclickTransbankSdk.php index 8fdca6e..5322f3a 100644 --- a/plugin/src/OneclickTransbankSdk.php +++ b/plugin/src/OneclickTransbankSdk.php @@ -122,14 +122,14 @@ public function status($orderId, $buyOrder) $this->afterExecutionTbkApi($orderId, 'status', $params, $response); return $response; } catch (Exception $e) { - $errorMessage = 'Ocurrió un error al tratar de obtener el estado de la transacción.'; + $errorMessage = ErrorUtil::DEFAULT_STATUS_ERROR_MESSAGE; if(ErrorUtil::isMaxTimeError($e)) { - $errorMessage = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; + $errorMessage = ErrorUtil::EXPIRED_TRANSACTION_ERROR_MESSAGE; } if (ErrorUtil::isApiMismatchError($e)) { - $errorMessage = 'La version de API es distinta a la utilizada para crear la transacción.'; + $errorMessage = ErrorUtil::API_MISMATCH_ERROR_MESSAGE; } $this->errorExecutionTbkApi( diff --git a/plugin/src/WebpayplusTransbankSdk.php b/plugin/src/WebpayplusTransbankSdk.php index fcc86e6..2a10a53 100644 --- a/plugin/src/WebpayplusTransbankSdk.php +++ b/plugin/src/WebpayplusTransbankSdk.php @@ -98,14 +98,14 @@ public function status($orderId, $token) $this->afterExecutionTbkApi($orderId, 'status', $params, $response); return $response; } catch (Exception $e) { - $errorMessage = 'Ocurrió un error al tratar de obtener el estado de la transacción.'; + $errorMessage = ErrorUtil::DEFAULT_STATUS_ERROR_MESSAGE; - if (ErrorUtil::isApiMismatchError($e)) { - $errorMessage = 'La version de API es distinta a la utilizada para crear la transacción.'; + if(ErrorUtil::isMaxTimeError($e)) { + $errorMessage = ErrorUtil::EXPIRED_TRANSACTION_ERROR_MESSAGE; } - if (ErrorUtil::isMaxTimeError($e)) { - $errorMessage = 'La transacción supera los 7 días y ya no es posible consultarla por este medio.'; + if (ErrorUtil::isApiMismatchError($e)) { + $errorMessage = ErrorUtil::API_MISMATCH_ERROR_MESSAGE; } $this->errorExecutionTbkApi( From ca901d826a6affee8d583ef5f9ee5cb565465eea Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 12:59:36 -0300 Subject: [PATCH 08/19] feat: add getCommonFieldsStatusFormatted method --- plugin/src/Helpers/TbkResponseUtil.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/plugin/src/Helpers/TbkResponseUtil.php b/plugin/src/Helpers/TbkResponseUtil.php index 92d558a..9d41fea 100644 --- a/plugin/src/Helpers/TbkResponseUtil.php +++ b/plugin/src/Helpers/TbkResponseUtil.php @@ -163,4 +163,30 @@ public static function getOneclickFormattedResponse(object $transactionResponse) return array_merge($commonFields, $oneclickFields); } + + /** + * Get the common fields formatted for status response. + * + * @param object $statusResponse The status response. + * @return array The formatted common fields for status response. + */ + private static function getCommonFieldsStatusFormatted(object $statusResponse): array + { + $utcDate = new DateTime($statusResponse->transactionDate, new DateTimeZone('UTC')); + $utcDate->setTimeZone(new DateTimeZone(wc_timezone_string())); + + $buyOrder = $statusResponse->buyOrder; + $cardNumber = "**** **** **** {$statusResponse->cardNumber}"; + $transactionDate = $utcDate->format('d-m-Y'); + $transactionTime = $utcDate->format('H:i:s'); + $accountingDate = self::getAccountingDate($statusResponse->accountingDate); + + return [ + 'buyOrder' => $buyOrder, + 'cardNumber' => $cardNumber, + 'transactionDate' => $transactionDate, + 'transactionTime' => $transactionTime, + 'accountingDate' => $accountingDate + ]; + } } From 3f511494950ef2743e7173d5c2a8af8b9d3b4685 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 13:00:06 -0300 Subject: [PATCH 09/19] feat: add getWebpayStatusFormattedResponse method --- plugin/src/Helpers/TbkResponseUtil.php | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/plugin/src/Helpers/TbkResponseUtil.php b/plugin/src/Helpers/TbkResponseUtil.php index 9d41fea..2048eb4 100644 --- a/plugin/src/Helpers/TbkResponseUtil.php +++ b/plugin/src/Helpers/TbkResponseUtil.php @@ -189,4 +189,51 @@ private static function getCommonFieldsStatusFormatted(object $statusResponse): 'accountingDate' => $accountingDate ]; } + + /** + * Get the formatted response for Webpay status transactions. + * + * @param object $statusResponse The response object for Webpay status transactions. + * @return array The formatted response fields. + */ + public static function getWebpayStatusFormattedResponse(object $statusResponse): array + { + $commonFields = self::getCommonFieldsStatusFormatted($statusResponse); + + $status = self::getStatus($statusResponse->status); + $amount = self::getAmountFormatted($statusResponse->amount); + $paymentType = self::getPaymentType($statusResponse->paymentTypeCode); + $installmentType = self::getInstallmentType($statusResponse->paymentTypeCode); + $installmentNumber = $statusResponse->installmentsNumber; + $installmentAmount = 'N/A'; + $balance = 'N/A'; + + if ($installmentNumber > 0) { + $installmentAmount = self::getAmountFormatted($statusResponse->installmentsAmount ?? 0); + } + + if (!is_null($statusResponse->balance)) { + $balance = self::getAmountFormatted($statusResponse->balance); + } + + return [ + 'vci' => $statusResponse->vci, + 'status' => $status, + 'responseCode' => $statusResponse->responseCode, + 'amount' => $amount, + 'authorizationCode' => $statusResponse->authorizationCode, + 'accountingDate' => $commonFields['accountingDate'], + 'paymentType' => $paymentType, + 'installmentType' => $installmentType, + 'installmentNumber' => $installmentNumber, + 'installmentAmount' => $installmentAmount, + 'sessionId' => $statusResponse->sessionId, + 'buyOrder' => $commonFields['buyOrder'], + 'cardNumber' => $commonFields['cardNumber'], + 'transactionDate' => $commonFields['transactionDate'], + 'transactionTime' => $commonFields['transactionTime'], + 'balance' => $balance + ]; + } + } From b063b6835e5abc1fa3a6d006767edb0dbe9b0b0f Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 13:02:24 -0300 Subject: [PATCH 10/19] feat: add getOneclickStatusFormattedResponse method --- plugin/src/Helpers/TbkResponseUtil.php | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/plugin/src/Helpers/TbkResponseUtil.php b/plugin/src/Helpers/TbkResponseUtil.php index 2048eb4..14ce775 100644 --- a/plugin/src/Helpers/TbkResponseUtil.php +++ b/plugin/src/Helpers/TbkResponseUtil.php @@ -236,4 +236,49 @@ public static function getWebpayStatusFormattedResponse(object $statusResponse): ]; } + /** + * Get the formatted response for Oneclick status transactions. + * + * @param object $statusResponse The response object for Oneclick status transactions. + * @return array The formatted response fields. + */ + public static function getOneclickStatusFormattedResponse(object $statusResponse): array + { + $commonFields = self::getCommonFieldsStatusFormatted($statusResponse); + $detail = $statusResponse->details[0]; + + $status = self::getStatus($detail->status); + $amount = self::getAmountFormatted($detail->amount); + $paymentType = self::getPaymentType($detail->paymentTypeCode); + $installmentType = self::getInstallmentType($detail->paymentTypeCode); + $installmentNumber = $detail->installmentsNumber; + $installmentAmount = 'N/A'; + $balance = 'N/A'; + + if ($installmentNumber > 0) { + $installmentAmount = self::getAmountFormatted($detail->installmentsAmount ?? 0); + } + + if (!is_null($detail->balance)) { + $balance = self::getAmountFormatted($detail->balance); + } + + return [ + 'status' => $status, + 'responseCode' => $detail->responseCode, + 'amount' => $amount, + 'authorizationCode' => $detail->authorizationCode, + 'accountingDate' => $commonFields['accountingDate'], + 'paymentType' => $paymentType, + 'installmentType' => $installmentType, + 'installmentNumber' => $installmentNumber, + 'installmentAmount' => $installmentAmount, + 'buyOrderMall' => $commonFields['buyOrder'], + 'buyOrderStore' => $detail->buyOrder, + 'cardNumber' => $commonFields['cardNumber'], + 'transactionDate' => $commonFields['transactionDate'], + 'transactionTime' => $commonFields['transactionTime'], + 'balance' => $balance + ]; + } } From 317b38f5adfbbcfac2e39ef1f2109423c0fe1256 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 13:04:22 -0300 Subject: [PATCH 11/19] feat: add variables for colors in global context --- plugin/css/tbk.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugin/css/tbk.css b/plugin/css/tbk.css index 002ae3f..c66b707 100644 --- a/plugin/css/tbk.css +++ b/plugin/css/tbk.css @@ -1,3 +1,12 @@ +:root { + --tbk-red: #D5006C; + --tbk-red-2: #C00063; + --tbk-success: #28a745; + --tbk-info: #007bff; + --tbk-warning: #fd7e14; + --tbk-error: #dc3545; + --tbk-default: #6c757d: +} .tbk_table_info { width: 600px; @@ -221,8 +230,6 @@ } .tbk-box { - --tbk-red: #D5006C; - --tbk-red-2: #C00063; background: #fff; border-radius: 10px; padding: 15px; From 5b973c23ed2f1b8418557577ef01e84cb9b6aa51 Mon Sep 17 00:00:00 2001 From: Mauricio Astudillo Toledo Date: Wed, 13 Nov 2024 13:10:14 -0300 Subject: [PATCH 12/19] feat: improve information in status meta box --- plugin/css/tbk.css | 92 +++++++++++++ plugin/js/admin.js | 124 ++++++++++++++---- .../TransactionStatusController.php | 34 ++--- .../admin/order/transaction-status.php | 90 +++---------- 4 files changed, 215 insertions(+), 125 deletions(-) diff --git a/plugin/css/tbk.css b/plugin/css/tbk.css index c66b707..16b072a 100644 --- a/plugin/css/tbk.css +++ b/plugin/css/tbk.css @@ -420,3 +420,95 @@ font-size: 18px; font-weight: 500; } + +.tbk-field { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + font-size: 14px; + line-height: 1.4; +} + +.tbk-field-name { + font-weight: bold; + margin-right: 8px; +} + +.tbk-field-value { + text-align: right; + word-break: break-word; +} + +.tbk-badge { + padding: 2px 6px; + border-radius: 4px; + font-size: 12px; + font-weight: bold; + color: white +} + +.tbk-badge-success { + background-color: var(--tbk-success); +} + +.tbk-badge-info { + background-color: var(--tbk-info); +} + +.tbk-badge-warning { + background-color: var(--tbk-warning); +} + +.tbk-badge-error { + background-color: var(--tbk-error); +} + +.tbk-badge-default { + background-color: var(--tbk-default); +} + +.tbk-status { + display: flex; + flex-direction: row; + align-items: center; +} + +.tbk-status i { + width: 100px; + max-width: 24px; + text-align: center; + font-size: x-large; +} + +.tbk-status p { + padding-left: 10px; + margin: 0px; +} + +.tbk-status.tbk-status-info i { + color: var(--tbk-info); +} + +.tbk-status.tbk-status-error i, .tbk-status.tbk-status-error p { + color: var(--tbk-error); + font-weight: bold; +} + +.tbk-status-button { + display: flex; + justify-content: center; + margin-bottom: 10px; +} + +.tbk-status-button .tbk-button-primary { + width: 100%; + text-align: center; +} + +.tbk-separator { + width: 100%; + height: 1px; + background-color: #ccc; + margin: 10px 0; +} diff --git a/plugin/js/admin.js b/plugin/js/admin.js index 287557d..fd1a146 100644 --- a/plugin/js/admin.js +++ b/plugin/js/admin.js @@ -13,7 +13,9 @@ jQuery(function($) { if ($(`.${btnName}`).data('sending') === true) { return false; } - $(`.${btnName}`).data('sending', true).html(msg); + const btn = $(`.${btnName}`); + btn.data('sending', true).html(`${msg} `); + e.preventDefault(); return true; } @@ -72,40 +74,113 @@ jQuery(function($) { }); $('.get-transaction-status').on("click",function(e) { - if (!blockButton(e, 'get-transaction-status', 'Consultando al API REST...')){ + e.preventDefault(); + if (!blockButton(e, 'get-transaction-status', 'Consultando estado')){ return; } + + const container = document.getElementById('transaction_status_admin'); + container.innerHTML = ''; + + const separator = document.createElement('div'); + separator.className = 'tbk-separator'; + separator.style.display = 'none'; + container.appendChild(separator); + post('get_transaction_status', { order_id: $('.get-transaction-status').data('order-id'), buy_order: $('.get-transaction-status').data('buy-order'), token: $('.get-transaction-status').data('token') }, (resp) => { - let $table = $('.transaction-status-response'); - let statusData = resp.status; - if(resp.product == "webpay_plus"){ - $("#tbk_wpp_vci").removeClass("tbk-hide"); - $("#tbk_wpp_session_id").removeClass("tbk-hide"); - }else{ - $("#tbk_wpoc_commerce_code").removeClass("tbk-hide"); + for (const [key, value] of Object.entries(resp)) { + const fieldName = document.createElement('span'); + fieldName.className = 'tbk-field-name'; + fieldName.textContent = getFieldName(key); + + const fieldValue = document.createElement('span'); + fieldValue.className = 'tbk-field-value'; + fieldValue.textContent = value.toString(); + + if(key == 'status') { + fieldValue.classList.add('tbk-badge'); + fieldValue.classList.add(getBadgeColorFromStatus(value.toString())); + } + + if(key == 'cardNumber') { + fieldValue.style.width = '100%'; + } + + const field = document.createElement('div'); + field.className = 'tbk-field'; + field.appendChild(fieldName); + field.appendChild(fieldValue); + + container.appendChild(field); } - const statusDataKeys = Object.keys(statusData); - statusDataKeys.forEach(key => { - let value = statusData[key] ? statusData[key] : '-'; - const tableRow = $table.find('.status-' + key); - tableRow.html(value); - }); - $table.find('.status-product').html(resp.product); - let niceJson = JSON.stringify(resp.raw, null, 2) - $table.find('.status-raw').html(`
${niceJson}
`); - $table.show(); - releaseButton('get-transaction-status','Consultar estado de la transacción'); + separator.style.removeProperty('display'); + + releaseButton('get-transaction-status','Consultar Estado'); }, (error) => { - $('.error-status-raw').html(`

${error.responseJSON.message}

`); - $('.error-transaction-status-response').show(); - releaseButton('get-transaction-status','Consultar estado de la transacción'); + const errorContainer = createErrorContainer(error.responseJSON.message); + container.appendChild(errorContainer); + separator.style.removeProperty('display'); + + releaseButton('get-transaction-status','Consultar Estado'); }); }); + function getBadgeColorFromStatus(status) { + const statusColorsDictionary = { + 'Inicializada': 'tbk-badge-warning', + 'Capturada': 'tbk-badge-success', + 'Autorizada': 'tbk-badge-success', + 'Fallida': 'tbk-badge-error', + 'Anulada': 'tbk-badge-info', + 'Parcialmente anulada': 'tbk-badge-info' + }; + + return statusColorsDictionary[status] ?? 'tbk-badge-default'; + } + + function getFieldName(fieldKey) { + const fieldNameDictionary = { + vci: 'VCI', + status: 'Estado', + responseCode: 'Código de respuesta', + amount: 'Monto', + authorizationCode: 'Código de autorización', + accountingDate: 'Fecha contable', + paymentType: 'Tipo de pago', + installmentType: 'Tipo de cuota', + installmentNumber: 'Número de cuotas', + installmentAmount: 'Monto cuota', + sessionId: 'ID de sesión', + buyOrder: 'Orden de compra', + buyOrderMall: 'Orden de compra mall', + buyOrderStore: 'Orden de compra tienda', + cardNumber: 'Número de tarjeta', + transactionDate: 'Fecha transacción', + transactionTime: 'Hora transacción', + balance: 'Balance' + }; + + return fieldNameDictionary[fieldKey] ?? fieldKey; + } + + function createErrorContainer(errorMessage) + { + const errorContainer = document.createElement('div'); + errorContainer.classList.add('tbk-status', 'tbk-status-error'); + const icon = document.createElement('i'); + icon.classList.add('fa', 'fa-times'); + const paragraph = document.createElement('p'); + paragraph.textContent = errorMessage; + + errorContainer.appendChild(icon); + errorContainer.appendChild(paragraph); + return errorContainer; + } + $('#mainform').on('click', '.notice-dismiss', function() { let noticeId = $(this).closest('.notice').attr('id'); @@ -169,7 +244,4 @@ jQuery(function($) { }); }); - - - }) diff --git a/plugin/src/Controllers/TransactionStatusController.php b/plugin/src/Controllers/TransactionStatusController.php index 4b1188e..4dac919 100644 --- a/plugin/src/Controllers/TransactionStatusController.php +++ b/plugin/src/Controllers/TransactionStatusController.php @@ -75,8 +75,10 @@ private function handleOneclickStatus( ]; } + $statusResponse = $this->getStatusForOneclickTransaction($orderId, $transactionBuyOrder); + return [ - 'body' => $this->getStatusForOneclickTransaction($orderId, $transactionBuyOrder), + 'body' => TbkResponseUtil::getOneclickStatusFormattedResponse($statusResponse), 'code' => self::HTTP_OK ]; } @@ -95,8 +97,10 @@ private function handleWebpayStatus( ]; } + $statusResponse = $this->getStatusForWebpayTransaction($orderId, $transactionToken); + return [ - 'body' => $this->getStatusForWebpayTransaction($orderId, $transactionToken), + 'body' => TbkResponseUtil::getWebpayStatusFormattedResponse($statusResponse), 'code' => self::HTTP_OK ]; } @@ -104,35 +108,13 @@ private function handleWebpayStatus( private function getStatusForWebpayTransaction(string $orderId, string $token) { $webpayTransbankSDK = TbkFactory::createWebpayplusTransbankSdk(); - $resp = $webpayTransbankSDK->status($orderId, $token); - $formattedDate = TbkResponseUtil::transactionDateToLocalDate($resp->getTransactionDate()); - $modifiedResponse = clone $resp; - $modifiedResponse->setTransactionDate($formattedDate); - - return [ - 'product' => Transaction::PRODUCT_WEBPAY_PLUS, - 'status' => $modifiedResponse, - 'raw' => $resp, - ]; + return $webpayTransbankSDK->status($orderId, $token); } private function getStatusForOneclickTransaction(string $orderId, string $buyOrder) { $oneclickTransbankSDK = TbkFactory::createOneclickTransbankSdk(); - $status = $oneclickTransbankSDK->status($orderId, $buyOrder); - $statusArray = json_decode(json_encode($status), true); - $firstDetail = json_decode(json_encode($status->getDetails()[0]), true); - - $response = array_merge($statusArray, $firstDetail); - $formattedDate = TbkResponseUtil::transactionDateToLocalDate($status->getTransactionDate()); - $response['transactionDate'] = $formattedDate; - unset($response['details']); - - return [ - 'product' => Transaction::PRODUCT_WEBPAY_ONECLICK, - 'status' => $response, - 'raw' => $status, - ]; + return $oneclickTransbankSDK->status($orderId, $buyOrder); } private function getSecureInputValue(string $varName): string diff --git a/plugin/templates/admin/order/transaction-status.php b/plugin/templates/admin/order/transaction-status.php index 0ef3bbf..2837d3b 100644 --- a/plugin/templates/admin/order/transaction-status.php +++ b/plugin/templates/admin/order/transaction-status.php @@ -2,84 +2,28 @@ if (!defined('ABSPATH')) { return; } + +$infoMessage = "El estado de la transacción está disponible solo por 7 días desde su creación."; + if (empty($viewData)) { - echo 'No hay transacciones webpay aprobadas para esta orden'; - return; + $infoMessage = 'No hay transacciones Webpay asociadas a esta orden.'; } ?> -Consultar estado de la transacción - -

Esta es la respuesta del API (solo disponible por 7 días desde la fecha de transacción)

+ -