From 3c75f59236b9e4bd46352087d0752b605c0601f2 Mon Sep 17 00:00:00 2001 From: Wojciech Gorczyca Date: Thu, 21 Mar 2024 11:18:14 +0100 Subject: [PATCH] Initial commit --- .gitignore | 6 + .php-cs-fixer.dist.php | 25 + LICENSE | 21 + Makefile | 7 + README.md | 1 + composer.json | 35 + config.xml | 12 + config/admin/index.php | 11 + config/admin/services.yml | 36 + config/front/index.php | 11 + config/front/services.yml | 12 + config/index.php | 11 + config/routes.yml | 8 + controllers/front/failed.php | 15 + controllers/front/index.php | 11 + controllers/front/notify.php | 101 ++ controllers/front/validate.php | 175 ++++ controllers/index.php | 11 + docker-compose.yaml | 38 + index.php | 11 + logo.png | Bin 0 -> 862 bytes mails/en/awaiting_simpay_payment.html | 884 ++++++++++++++++++ mails/en/awaiting_simpay_payment.txt | 23 + mails/en/index.php | 11 + mails/index.php | 11 + phpstan.neon | 15 + simpay.php | 333 +++++++ .../SimpayConfigurationAdminController.php | 42 + src/Controller/index.php | 11 + src/Form/SimpayDataConfiguration.php | 65 ++ src/Form/SimpayFormDataProvider.php | 33 + src/Form/SimpayFormType.php | 63 ++ src/Form/index.php | 11 + src/PaymentClientFactory.php | 32 + src/index.php | 11 + views/img/option/index.php | 11 + views/img/option/simpay.png | Bin 0 -> 2031 bytes .../img/orderstate/PS_OS_SIMPAY_AWAITING.png | Bin 0 -> 4356 bytes views/img/orderstate/index.php | 11 + views/index.php | 11 + views/templates/admin/configuration.html.twig | 28 + views/templates/admin/index.php | 11 + views/templates/front/failed.tpl | 30 + views/templates/front/index.php | 11 + views/templates/front/validate.tpl | 22 + 45 files changed, 2238 insertions(+) create mode 100644 .gitignore create mode 100644 .php-cs-fixer.dist.php create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 composer.json create mode 100644 config.xml create mode 100644 config/admin/index.php create mode 100644 config/admin/services.yml create mode 100644 config/front/index.php create mode 100644 config/front/services.yml create mode 100644 config/index.php create mode 100644 config/routes.yml create mode 100644 controllers/front/failed.php create mode 100644 controllers/front/index.php create mode 100644 controllers/front/notify.php create mode 100644 controllers/front/validate.php create mode 100644 controllers/index.php create mode 100644 docker-compose.yaml create mode 100644 index.php create mode 100644 logo.png create mode 100644 mails/en/awaiting_simpay_payment.html create mode 100644 mails/en/awaiting_simpay_payment.txt create mode 100644 mails/en/index.php create mode 100644 mails/index.php create mode 100644 phpstan.neon create mode 100644 simpay.php create mode 100644 src/Controller/SimpayConfigurationAdminController.php create mode 100644 src/Controller/index.php create mode 100644 src/Form/SimpayDataConfiguration.php create mode 100644 src/Form/SimpayFormDataProvider.php create mode 100644 src/Form/SimpayFormType.php create mode 100644 src/Form/index.php create mode 100644 src/PaymentClientFactory.php create mode 100644 src/index.php create mode 100644 views/img/option/index.php create mode 100644 views/img/option/simpay.png create mode 100644 views/img/orderstate/PS_OS_SIMPAY_AWAITING.png create mode 100644 views/img/orderstate/index.php create mode 100644 views/index.php create mode 100644 views/templates/admin/configuration.html.twig create mode 100644 views/templates/admin/index.php create mode 100644 views/templates/front/failed.tpl create mode 100644 views/templates/front/index.php create mode 100644 views/templates/front/validate.tpl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0b68a78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +ps_root_dir/ +vendor/ + +composer.lock +.php-cs-fixer.php +.php-cs-fixer.cache \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..819ca8c --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,25 @@ +in([ + __DIR__, + ]) + ->exclude([ + 'vendor', + 'ps_root_dir', + ]); + +$config = new PhpCsFixer\Config(); + +return $config + ->setRules([ + '@PER-CS2.0' => true, + 'declare_strict_types' => true, + 'strict_param' => true, + 'concat_space' => ['spacing' => 'one'], + 'protected_to_private' => true, + 'nullable_type_declaration_for_default_null_value' => true, + 'final_class' => true, + ]) + ->setRiskyAllowed(true) + ->setFinder($finder); \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f6f9be4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SimPay.pl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eeed923 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +fixer: + vendor/bin/php-cs-fixer fix + +phpstan: + vendor/bin/phpstan analyse + +sa: fixer phpstan \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7d1e8b0 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Simpay Prestashop diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..cabe8a7 --- /dev/null +++ b/composer.json @@ -0,0 +1,35 @@ +{ + "license": "MIT", + "name": "simpaypl/prestashop", + "type": "prestashop-module", + "description": "Module for integration with SimPay payment gateway", + "keywords": [ + "simpay", + "prestashop", + "payment", + "gateway" + ], + "authors": [ + { + "name": "SimPay Core Team" + } + ], + "autoload": { + "psr-4": { + "SimPaypl\\PrestaShop\\": "src/" + } + }, + "require": { + "php": "^8.1", + "simpaypl/simpay": "^3.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "prestashop/php-dev-tools": "^4.3" + }, + "config": { + "preferred-install": "dist", + "prepend-autoloader": false, + "sort-packages": true + } +} diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..a1f1c7d --- /dev/null +++ b/config.xml @@ -0,0 +1,12 @@ + + + simpay + + + + + + + 1 + 1 + \ No newline at end of file diff --git a/config/admin/index.php b/config/admin/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/config/admin/index.php @@ -0,0 +1,11 @@ +setTemplate('module:simpay/views/templates/front/failed.tpl'); + } +} diff --git a/controllers/front/index.php b/controllers/front/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/controllers/front/index.php @@ -0,0 +1,11 @@ + 'error', 'message' => 'Invalid payload, expected JSON'])); + } + + /** @var array{ + * id: string, + * service_id: string, + * status: string, + * amount: array{ + * value: float, + * currency: string, + * commission: float, + * }, + * control: string, + * channel: string, + * environment: string, + * signature: string + * }|bool|null $payload + */ + $payload = json_decode($jsonPayload, true); + + if (!is_array($payload)) { + http_response_code(400); + header('Content-Type: application/json'); + die(json_encode(['status' => 'error', 'message' => 'Invalid payload, expected JSON'])); + } + + $signatureValues = []; + array_walk_recursive($payload, function (mixed $item, string $key) use (&$signatureValues) { + if ($key === 'signature') { + return; + } + + $signatureValues[] = (string) $item; + }); + + $signatureValues[] = Configuration::get('SIMPAY_SERVICE_IPN_SIGNATURE_KEY'); + $signatureValues = implode('|', $signatureValues); + $signatureHash = hash('sha256', $signatureValues); + + if ($payload['signature'] !== $signatureHash) { + http_response_code(400); + header('Content-Type: application/json'); + die(json_encode(['status' => 'error', 'message' => 'Invalid signature'])); + } + + $order = Order::getByCartId((int) $payload['control']); + + if (null === $order) { + http_response_code(400); + header('Content-Type: application/json'); + die(json_encode(['status' => 'error', 'message' => 'Order not found'])); + } + + if ((int) Configuration::get(Simpay::CONFIG_OS_AWAITING) === $order->getCurrentState()) { + + $orderStatusId = (int) match($payload['status']) { + 'transaction_paid' => Configuration::get('PS_OS_PAYMENT'), + default => Configuration::get('PS_OS_ERROR'), + }; + + $orderHistory = new OrderHistory(); + $orderHistory->id_order = (int) $order->id; + $orderHistory->changeIdOrderState($orderStatusId, (int) $order->id, true); + $orderHistory->save(); + } + + + http_response_code(200); + header('Content-Type: application/json'); + die(json_encode(['status' => 'ok'])); + } +} diff --git a/controllers/front/validate.php b/controllers/front/validate.php new file mode 100644 index 0000000..d776e3d --- /dev/null +++ b/controllers/front/validate.php @@ -0,0 +1,175 @@ +assertModuleIsActive(); + + if (!$this->checkIfContextIsValid()) { + $this->redirectToOrder(); + + return; + } + + if (!$this->checkIfPaymentOptionIsAvailable()) { + $this->redirectToOrder(); + + return; + } + + /** @var Cart $cart */ + $cart = $this->context->cart; + /** @var Currency $currency */ + $currency = $this->context->currency; + + $customer = new Customer($cart->id_customer); + if (false === Validate::isLoadedObject($customer)) { + $this->redirectToOrder(); + + return; + } + + /** @var PaymentInterface $paymentClient */ + $paymentClient = $this->get('prestashop.module.simpay.front.payment_client'); + + /** @var string $serviceIdString */ + $serviceIdString = Configuration::get(SimpayDataConfiguration::SERVICE_ID); + $response = $paymentClient->paymentTransactionCreate( + new ServiceId($serviceIdString), + $this->createPaymentRequest($cart, $customer->secure_key), + ); + + $this->module->validateOrder( + (int) $cart->id, + (int) Configuration::get(Simpay::CONFIG_OS_AWAITING), + $cart->getOrderTotal(), + $this->trans('SimPay', [], 'Modules.Simpay.Shop'), + null, + [ + 'transaction_id' => $response->transactionId, + ], + $currency->id, + false, + $customer->secure_key, + ); + + $this->setTemplate('module:simpay/views/templates/front/validate.tpl'); + $this->context->smarty?->assign([ + 'action' => $response->redirectUrl, + ]); + + } + private function assertModuleIsActive(): void + { + if (!Module::isEnabled($this->module->name)) { + die($this->trans('This payment method is not available.', [], 'Modules.Simpay.Shop')); + } + + if (!$this->module->active) { + die($this->trans('SimPay module module isn\'t active.', [], 'Modules.Simpay.Shop')); + } + } + + private function checkIfContextIsValid(): bool + { + if (null === $this->context->cart) { + return false; + } + + if (null === $this->context->currency) { + return false; + } + + return Validate::isLoadedObject($this->context->cart) + && Validate::isUnsignedInt($this->context->cart->id_customer) + && Validate::isUnsignedInt($this->context->cart->id_address_delivery) + && Validate::isUnsignedInt($this->context->cart->id_address_invoice); + } + + private function checkIfPaymentOptionIsAvailable(): bool + { + $modules = Module::getPaymentModules(); + + if (empty($modules)) { + return false; + } + + foreach ($modules as $module) { + if (isset($module['name']) && $this->module->name === $module['name']) { + return true; + } + } + + return false; + } + + private function redirectToOrder(): void + { + /** @var Link $link */ + $link = $this->context->link; + + Tools::redirect($link->getPageLink( + 'order', + true, + (int) $this->context->language?->id, + [ + 'step' => 1, + ] + )); + } + + private function createPaymentRequest(Cart $cart, string $customerSecureKey): CreatePayment + { + $amount = (float) $cart->getOrderTotal(); + + /** @var Link $link */ + $link = $this->context->link; + + $successReturnUrl = $link->getPageLink( + 'order-confirmation', + true, + $this->context->language?->id, + [ + 'id_cart' => (int) $cart->id, + 'id_module' => (int) $this->module->id, + 'id_order' => $this->module->currentOrder, + 'key' => $customerSecureKey + ] + ); + + $failureReturnUrl = $link->getModuleLink( + 'simpay', + 'failed', + [], + true, + $this->context->language?->id, + ); + + return new CreatePayment( + new Amount($amount), + [], + null, + new SimpayCurrency('PLN'), + null, + new Control((string) $cart->id), + null, + null, + null, + new CallbackReturnUrl($successReturnUrl, $failureReturnUrl), + ); + } +} diff --git a/controllers/index.php b/controllers/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/controllers/index.php @@ -0,0 +1,11 @@ +EX>4Tx04R}tkv&MmKpe$i(@Kj}9PA+C5TQERMMZS0RVYG*P%E_RU~>J0CJjl7 zi=*ILaPVWX>fqw6tAnc`2!4P#J2)x2NQwVT3N2zhIPS;0dyl(!fY7Wm)eOV|RkMtA zG9hMjt77mKegqK0EJkEz>T{Bmg6H_UhmWs!F`ngp?$6PeFPIGQiNtZH8y4{f@${yp zbKWP8u(F&dJ|`YG=z_$LTvuFv<6LrB;F%F4lb$Dz5R0WQR=Sv#4V8F`IGR^A$``UO z=Q(e2R;zW^z9)ZSxS*{pbDicel32tNB#2N@M+H?_h|#K%Vj@lZ2@n5}t;i~_-3pw+PL?_=9;odEu4;7aTGYfWJ0lk`SM ziyi^}+rY(jM^pBI%N=0wNtX@Tk^D4;Vi9;hqi@Oq1Ghl$n%i4@AEysMhPq1K00)P_ zSc$UNJ>DJa?(N?*?f!lMqq1_kM+iwe00006VoOIv0RI600RN!9r;`8x010qNS#tmY z4c7nw4c7reD4Tcy000McNliru=mP`^0uCfzs=@#O0b5B#K~z}7?bf|&RZ$Rz;b(Rh z2azmRA>*q%@=E>tS#= + + + {payment} + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+ + + + +
+ + +
+ +
+ + +
+ + +
+ + +
+
+
+ +
+
+ + + + + +
+ + +
+ +
+ + +
+ + + + + + + + +
+
+ Hi {firstname} {lastname}, +
+
+
+ Thank you for shopping with {shop_name}! +
+
+
+
+ +
+
+ + + +
+ + +
+ +
+ + +
+

+

+ +
+
+ +
+
+ + + + +
+ + +
+ +
+ + +
+ + +
+
+ Order ID {order_name} - Pending payment +
+
+
+
+ +
+
+ + + + +
+ + +
+ +
+ + +
+ + +
+
+ Your order with the reference {order_name} has been placed successfully. You can expect delivery as soon as your payment is received. +
+
+
+
+ +
+
+ + + + +
+ + +
+ +
+ + +
+ + +
+
+ Payment method: {payment} +
+
+
+
+ +
+
+ + + + +
+ + +
+ +
+ + +
+ + + + + + + +
+
+ You have decided to pay via SimPay. +
+
+
+ Amount: + {total_paid} +
+
+
+
+ +
+
+ + + + +
+ + +
+ +
+ + +
+
+ Follow your order and download your invoice on our shop, go to the Order history and details section of your customer account. +
+
+
+ +
+
+ + + + +
+ + +
+ +
+ + +
+
+ If you have a guest account, you can follow your order via the Guest Tracking section on our shop. +
+
+
+ +
+
+ + + + + + + +
+
+ + + + +
+ + +
+ +
+ + + + + + + +
+ +
+
Powered by PrestaShop +
+
+
+ +
+
+ + + + +
+ + \ No newline at end of file diff --git a/mails/en/awaiting_simpay_payment.txt b/mails/en/awaiting_simpay_payment.txt new file mode 100644 index 0000000..aa7ab6a --- /dev/null +++ b/mails/en/awaiting_simpay_payment.txt @@ -0,0 +1,23 @@ +{shop_url} + +Hi {firstname} {lastname}, + +Thank you for shopping with {shop_name}! + +Order ID {order_name} - Pending payment + +Your order with the reference {order_name} has been placed successfully. You can expect delivery as soon as your payment is received. + +Payment method: {payment} + +You have decided to pay via SimPay. + +Amount: {total_paid} + +Follow your order and download your invoice on our shop, go to the [Order history and details]({history_url}) section of your customer account. + +If you have a guest account, you can follow your order via the [Guest Tracking]({guest_tracking_url}) section on our shop. + +[{shop_name}]({shop_url}) + +Powered by [PrestaShop](https://www.prestashop.com/?utm_source=marchandprestashop&utm_medium=e-mail=utm_campaign=footer_1-7) \ No newline at end of file diff --git a/mails/en/index.php b/mails/en/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/mails/en/index.php @@ -0,0 +1,11 @@ + 'SimpayConfigurationAdminParentController', + 'name' => 'SimPay Module', + 'parent_class_name' => 'AdminParentModulesSf', + 'visible' => false, + ], + [ + 'class_name' => 'SimpayConfigurationAdminController', + 'route_name' => 'simpay_configuration', + 'name' => 'Configuration', + 'parent_class_name' => 'SimpayConfigurationAdminParentController', + 'visible' => false, + ], + ]; + + private const HOOKS = [ + 'paymentOptions', + ]; + + public function __construct() + { + $this->name = 'simpay'; + $this->tab = 'payments_gateways'; + $this->version = '0.1.0'; + $this->author = 'Payments Solution Sp. z o.o.'; + $this->ps_versions_compliancy = [ + 'min' => '8.0.0', + 'max' => '8.99.99', + ]; + $this->controllers = ['failed', 'notify', 'validate']; + + $this->bootstrap = true; + parent::__construct(); + + $this->displayName = $this->trans('SimPay', [], 'Modules.Simpay.Admin'); + $this->description = $this->trans('Connect your shop directly with SimPay gateway', [], 'Modules.Simpay.Admin'); + $this->confirmUninstall = $this->trans('Are you sure you want to uninstall?', [], 'Modules.Simpay.Admin'); + } + + public function install(): bool + { + if (!parent::install()) { + return false; + } + + if (!$this->registerHook(self::HOOKS)) { + return false; + } + + if (!$this->createOrderState( + self::CONFIG_OS_AWAITING, + [ + 'en' => 'Awaiting SimPay payment', + ], + '#34209e', + true, + false, + false, + false, + false, + false, + false, + false, + 'awaiting_simpay_payment' + )) { + return false; + } + + if (!$this->installTabs()) { + return false; + } + + return true; + } + + public function uninstall(): bool + { + if (!parent::uninstall()) { + return false; + } + + if (!$this->deleteOrderState()) { + return false; + } + + if (!$this->uninstallTabs()) { + return false; + } + + return true; + } + + /** + * @param array{cart: Cart} $params + * @return array + */ + public function hookPaymentOptions(array $params): array + { + $cart = $params['cart']; + + if (false === Validate::isLoadedObject($cart)) { + return []; + } + + if (false === $this->checkCurrency($cart)) { + return []; + } + + if ($cart->isVirtualCart()) { + return []; + } + + if (!isset($this->context->link)) { + return []; + } + + $logoPath = Media::getMediaPath(_PS_MODULE_DIR_ . $this->name . '/views/img/option/simpay.png'); + + $simpayPaymentOption = (new PaymentOption()) + ->setModuleName((string) $this->name) + ->setCallToActionText('Pay with Simpay') + ->setAction($this->context->link->getModuleLink((string) $this->name, 'validate', [], true)) + ->setInputs([ + 'token' => [ + 'name' => 'token', + 'type' => 'hidden', + 'value' => '[5cbfniD+(gEV<59lYbG/,3VmHiEsetLogo($logoPath); + } + + return [$simpayPaymentOption]; + } + + public function getContent(): void + { + /** @var Router $router */ + $router = $this->get('router'); + Tools::redirectAdmin($router->generate('simpay_configuration')); + } + + private function installTabs(): bool + { + foreach (self::MODULE_ADMIN_CONTROLLERS as $controller) { + if (Tab::getIdFromClassName($controller['class_name'])) { + continue; + } + + $tab = new Tab(); + $tab->class_name = $controller['class_name']; + $tab->active = $controller['visible']; + + /** @var array, + * }> $languages + */ + $languages = Language::getLanguages(false); + foreach ($languages as $lang) { + $tab->name[$lang['id_lang']] = $this->trans($controller['name'], [], 'Modules.Simpay.Admin', $lang['locale']); + } + $tab->id_parent = Tab::getIdFromClassName($controller['parent_class_name']); + $tab->module = 'simpay'; + if (!$tab->add()) { + return false; + } + } + + return true; + } + + private function uninstallTabs(): bool + { + foreach (self::MODULE_ADMIN_CONTROLLERS as $controller) { + $id_tab = (int) Tab::getIdFromClassName($controller['class_name']); + $tab = new Tab($id_tab); + if (Validate::isLoadedObject($tab)) { + if (!$tab->delete()) { + return false; + } + } + } + + return true; + } + + /** @param array $nameByLangIsoCode */ + private function createOrderState( + string $configurationKey, + array $nameByLangIsoCode, + string $color, + bool $isLogable = false, + bool $isPaid = false, + bool $isInvoice = false, + bool $isShipped = false, + bool $isDelivery = false, + bool $isPdfDelivery = false, + bool $isPdfInvoice = false, + bool $isSendEmail = false, + string $template = '', + bool $isHidden = false, + bool $isUnremovable = true, + bool $isDeleted = false, + ): bool { + $tabNameByLangId = []; + + foreach ($nameByLangIsoCode as $langIsoCode => $name) { + /** @var array> $languages */ + $languages = Language::getLanguages(false); + foreach ($languages as $language) { + if (Tools::strtolower($language['iso_code']) === $langIsoCode) { + $tabNameByLangId[(int) $language['id_lang']] = $name; + } elseif (isset($nameByLangIsoCode['en'])) { + $tabNameByLangId[(int) $language['id_lang']] = $nameByLangIsoCode['en']; + } + } + } + + $orderState = new OrderState(); + $orderState->module_name = $this->name; + $orderState->name = $tabNameByLangId; + $orderState->color = $color; + $orderState->logable = $isLogable; + $orderState->paid = $isPaid; + $orderState->invoice = $isInvoice; + $orderState->shipped = $isShipped; + $orderState->delivery = $isDelivery; + $orderState->pdf_delivery = $isPdfDelivery; + $orderState->pdf_invoice = $isPdfInvoice; + $orderState->send_email = $isSendEmail; + $orderState->hidden = $isHidden; + $orderState->unremovable = $isUnremovable; + $orderState->template = $template; + $orderState->deleted = $isDeleted; + + if (false === $orderState->add()) { + $this->_errors[] = sprintf( + 'Failed to create OrderState %s', + $configurationKey + ); + + return false; + } + + if (false === Configuration::updateGlobalValue($configurationKey, (int) $orderState->id)) { + $this->_errors[] = sprintf( + 'Failed to save OrderState %s to Configuration', + $configurationKey + ); + + return false; + } + + $orderStateImgPath = $this->getLocalPath() . 'views/img/orderstate/' . $configurationKey . '.png'; + if (false === Tools::file_exists_cache($orderStateImgPath)) { + $this->_errors[] = sprintf( + 'Failed to find icon file of OrderState %s', + $configurationKey + ); + + return false; + } + + if (false === Tools::copy($orderStateImgPath, _PS_ORDER_STATE_IMG_DIR_ . $orderState->id . '.gif')) { + $this->_errors[] = sprintf( + 'Failed to copy icon of OrderState %s', + $configurationKey + ); + + return false; + } + + return true; + } + + private function deleteOrderState(): bool + { + $result = true; + + $orderStateCollection = new PrestaShopCollection('OrderState'); + $orderStateCollection->where('module_name', '=', $this->name); + /** @var OrderState[] $orderStates */ + $orderStates = $orderStateCollection->getAll(); + + foreach ($orderStates as $orderState) { + $orderState->deleted = true; + if (!$orderState->save()) { + $result = false; + } + } + + return $result; + } + + private function checkCurrency(Cart $cart): bool + { + $currencyOrder = new Currency($cart->id_currency); + + return 'PLN' === $currencyOrder->iso_code; + } +} diff --git a/src/Controller/SimpayConfigurationAdminController.php b/src/Controller/SimpayConfigurationAdminController.php new file mode 100644 index 0000000..ddaa42d --- /dev/null +++ b/src/Controller/SimpayConfigurationAdminController.php @@ -0,0 +1,42 @@ +simpayFormDataHandler = $simpayFormDataHandler; + } + public function configureAction(Request $request): Response + { + $configurationForm = $this->simpayFormDataHandler->getForm(); + $configurationForm->handleRequest($request); + + if ($configurationForm->isSubmitted() && $configurationForm->isValid()) { + /** @var array $data */ + $data = $configurationForm->getData(); + $errors = $this->simpayFormDataHandler->save($data); + + if (empty($errors)) { + $this->addFlash('success', $this->trans('Successful update.', 'Admin.Notifications.Success')); + + return $this->redirectToRoute('simpay_configuration'); + } + + $this->flashErrors($errors); + } + + return $this->render('@Modules/simpay/views/templates/admin/configuration.html.twig', [ + 'configurationForm' => $configurationForm->createView(), + ]); + } +} diff --git a/src/Controller/index.php b/src/Controller/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/src/Controller/index.php @@ -0,0 +1,11 @@ +configuration = $configuration; + } + + /** @return array{api_key: string, api_password: string, service_id: string, service_ipn_signature_key: string} */ + public function getConfiguration(): array + { + return [ + 'api_key' => (string) $this->configuration->get(self::API_KEY), + 'api_password' => (string) $this->configuration->get(self::API_PASSWORD), + 'service_id' => (string) $this->configuration->get(self::SERVICE_ID), + 'service_ipn_signature_key' => (string) $this->configuration->get(self::SERVICE_IPN_SIGNATURE_KEY), + ]; + } + + /** + * @param array{api_key: string, api_password: string, service_id: string, service_ipn_signature_key: string} $configuration + * @return array + */ + public function updateConfiguration(array $configuration) + { + if (!$this->validateConfiguration($configuration)) { + return ['Invalid configuration']; + } + + $this->configuration->set(self::API_KEY, $configuration['api_key']); + $this->configuration->set(self::API_PASSWORD, $configuration['api_password']); + $this->configuration->set(self::SERVICE_ID, $configuration['service_id']); + $this->configuration->set(self::SERVICE_IPN_SIGNATURE_KEY, $configuration['service_ipn_signature_key']); + + + + return []; + } + + /** + * @param array $configuration + */ + public function validateConfiguration(array $configuration): bool + { + return isset($configuration['api_key']) + && isset($configuration['api_password']) + && isset($configuration['service_id']) + && isset($configuration['service_ipn_signature_key']); + } +} diff --git a/src/Form/SimpayFormDataProvider.php b/src/Form/SimpayFormDataProvider.php new file mode 100644 index 0000000..19795a4 --- /dev/null +++ b/src/Form/SimpayFormDataProvider.php @@ -0,0 +1,33 @@ +simpayDataConfiguration = $simpayDataConfiguration; + } + + /** @return array{api_key: string, api_password: string, service_id: string, service_ipn_signature_key: string} */ + public function getData(): array + { + return $this->simpayDataConfiguration->getConfiguration(); + + } + + /** + * @param array{api_key: string, api_password: string, service_id: string, service_ipn_signature_key: string} $data + * @return array + */ + public function setData(array $data): array + { + return $this->simpayDataConfiguration->updateConfiguration($data); + } +} diff --git a/src/Form/SimpayFormType.php b/src/Form/SimpayFormType.php new file mode 100644 index 0000000..86f809f --- /dev/null +++ b/src/Form/SimpayFormType.php @@ -0,0 +1,63 @@ +add('api_key', TextType::class, [ + 'label' => $this->trans('API Key', 'Modules.Simpay.Admin'), + 'help' => $this->trans('Konto Klienta -> API -> Szczegoly -> Klucz', 'Modules.Simpay.Admin'), + 'constraints' => [ + new NotBlank(), + ], + ]) + + ->add('api_password', TextType::class, [ + 'label' => $this->trans('API Password', 'Modules.Simpay.Admin'), + 'help' => $this->trans('Konto Klienta -> API -> Szczegoly -> Hasło / Bearer Token ', 'Modules.Simpay.Admin'), + 'constraints' => [ + new NotBlank(), + ], + ]) + + ->add('service_id', TextType::class, [ + 'label' => $this->trans('Service ID', 'Modules.Simpay.Admin'), + 'help' => $this->trans('Platnosci online -> Uslugi -> Szczegoly -> ID', 'Modules.Simpay.Admin'), + 'constraints' => [ + new NotBlank(), + ], + ]) + + ->add('service_ipn_signature_key', TextType::class, [ + 'label' => $this->trans('Service IPN Signature Key', 'Modules.Simpay.Admin'), + 'help' => $this->trans('Platnosci online -> Uslugi -> Szczegoly -> Ustawienia -> Klucz do sygnatury IPN', 'Modules.Simpay.Admin'), + 'constraints' => [ + new NotBlank(), + ], + ]) + + ->add('service_ipn_notify_url', UrlType::class, [ + 'label' => $this->trans('Service IPN Notify URL', 'Modules.Simpay.Admin'), + 'help' => $this->trans('Platnosci online -> Uslugi -> Szczegoly -> Ustawienia -> Adres url do powiadomień IPN', 'Modules.Simpay.Admin'), + 'required' => false, + 'mapped' => false, + 'disabled' => true, + 'data' => (new Link())->getModuleLink('simpay', 'notify', [], true), + ]) + ; + } + +} diff --git a/src/Form/index.php b/src/Form/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/src/Form/index.php @@ -0,0 +1,11 @@ +configuration = $configuration; + } + public function __invoke(): PaymentInterface + { + return new PaymentApi( + new HttpClientFactory( + new SimpayConfiguration( + $this->configuration->get('SIMPAY_API_KEY'), + $this->configuration->get('SIMPAY_API_PASSWORD'), + 'en', + ) + ) + ); + } +} diff --git a/src/index.php b/src/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/src/index.php @@ -0,0 +1,11 @@ +EX>4Tx04R}tkv&MmKpe$iQ>7vmhjtKg$WV2$ixqLKRVYG*P%E_RU~=gfG%+M8 zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0YbCJG^-~GXu54? z(;^|G> z;Ji;9WffT^J|~_q>4LKlt6PRZ2{HN#PXG`{Fnsqd;gEXf+(?``B?>CqVESxY9fRjV3VjNqVEB z#g2f%ZQ$a%qbYm9A(Ki)<$Su&n>h;#%$LRx*qpmVHz`-Ff zR;KJVpLh3l_xA6Zc7H#{x^l>@QRtEY000JJOGiWi{{a60|De66lK=n!32;bRa{vGh z*8l(w*8xH(n|J^K00(qQO+^Rj0}}@=5DOjc-~a#x?MXyIRA}DqnSYE_)fLA-=gy8S zR5xmbl%{rPc4m#SvQtXiXkwFED=}0U2qmo;MX?Qvl3*exwXxPFDpVV#B*sXK(H8n6 zf(%FuB}GV$p#ts>rWTo*omqY~#ika}WnFgWe*I(KF2iOAk$rB^>^FItH)rmB=e+a1 z=bU@b7@q{$^km>V;0j<2XNLl*%+DFckZ3rgQE$x}i}x#ln}Nn*>f8y85^~mrWWV2l z_s;`!M%{HzL`ZZRXx2y@*4&a9?_^QQA1#$SyGIEbfaafZY8xh6;z?IAU2n3-5nmA) z<|@tcFR+oBohsibplYswWXRft@PG1pAaP)42|rBb0Xh?c#4 zE%T>=@4Ke|k*NJ2Sc>U~z&bk85wJ-(-Hw=uu^H)Q%%zczffX^PfO52DwhTP0BOS*v z2hmF^vwZl3NY92$$ZlZ$VBvq>-BXUmlYaxI3inELJh{oC*THt?^SOTm{^TO@^TDQi z@7~5#03N`&1knpzfWTG!B5&?FLw5sF1j6paSO)y`v=)A2qsXTB2RfUo$=#knx(5Y5 z6LY}cLAnfMFVZ5!3g9S(*%L^tEe8pD5#nCVo(ijmgbmd-#<@FyZNTLQ_X}GuI24N~ zbItMO{7aLGXq{(x;5=Zr%w~aB0ABzW14U$Wfu9BA?r%;czdXeJnqoh5A~3J&AKOkg zeJ#>Gz!A_-Fxo&1fQy4P=NS{-4jcq7qPs6$J-^-91pFBI5b1^R4u&Iy&ceQfu~^~; zGbVa--!tFW!>eDlrX67&fn1%IyG%#go&4t4yQ< z=k;$h?k1bQt!B-P2_oYVXbo^D5CL0EDzm!(xJ*a7597B0T>fI2Y|~w?rJFc zY>nd&1ZjBOpt<9K@io^t;4jF&M=G-)m;m&F@~75U?FGswz3Kl990sn&xcKB@+U^av zbut3m-dHR-0pnJQ!(FBP?@t!>edXp@>uYA}1)8kkG&#@u%U%S`4-}l*f(SO01y5rQ z4}u)vX&ZJEDu=Dw#evd*Xu}4}ILAL?RKXRLuG4d6oUSxBX|PJJ>$638%xsLiiS` z%)i2JorH-Kz)NQ`uuv#efXZjCUF)@y;5ep7DDx{m&zQWSFX#w-ip@olIm^?AfOiqQfLBRv$TNzM#YZ9FH@SbPg|h$v N002ovPDHLkV1h8V$hiOj literal 0 HcmV?d00001 diff --git a/views/img/orderstate/PS_OS_SIMPAY_AWAITING.png b/views/img/orderstate/PS_OS_SIMPAY_AWAITING.png new file mode 100644 index 0000000000000000000000000000000000000000..4196c4e203a2095ff891a76f250dc4aed73b7c18 GIT binary patch literal 4356 zcmeHKe{dAl9p6jDgo7wXVq03udT5K7z1`d0<8Id+A-T(s!yI0dOcIDFd%JHh+vIL9 zx0~FOLJq_mFL(3qefRr5-_QHL&-=cY>;vHyRR#8Wb_~M`g4Kaq=;xR%|62H6aCuaK?p4$j zl!L*=SUx-e(sm7IGcnL?6YLyYE|v?vtC$j|NDVpUehIi38#qliI zE|}leKc11v)cE(kt02G4ir*g&`u%t!sme+l!Z4$6{Z3!?$z|U;eQ(`?ipAG8%xQlq zy!x5$?cN_2Z4cl4C;O9p@7-5tgyuaw_Zw9wo*f=O6M6l_>6MLnjb(E_$J#p+wffG& z(MRU4I%Ooz|19;{cT<yzm@-@|g!b+YWkSKe@cLeb;Vj_mw{5{Qg;mrODO*yz*1+ z4`^ew^7l1ed%eqxZ@Q!DgEbAb``z2#?LG9tE>iL}aBBM-i`{P={r(2wmLo@swLR}q z4>z4Eyk%QB?EhBLpSQhss3&rBXk_Ms(H$Q?^3s7Fhi6wlG^2i?tn-WE4WArqK1^}% z+*(n%?6d9*>mI}gR`;(=^<5IrD0{IJ(c$Il$l-!D@AdqvtGRo=ZT*{rr#qVK{(axs z`D;FTc}M6kyEn00%56sJKJ^vb7r$P1?$M#<)&&QzU$*t=^1oqu_Yb!0{MFFca))tFO|)Ch7Kv4pu{SV^go5X4rb;}H~9 z;y%ZvW50FaisW-NxbypJAb*-ouyIMs~a+EH$ml!+{#E>rFMyxHa@rKV~ z@$z7sZqk8U5WUssXo5QOt4V~jPS!~g6^7DDJC@qNO-4x<$8jV@lQc~Lg3#8+b-^Iw+Cq~e%@IJFm{bzFqQ-HPQ;4V?y3gT&aeOSl zSRxe4(#N%o3ZMsR2no{Vq{vu|oEV|$6`eqmN$5)>wEDFPMAjlr?MRBKq7%jSg%c?x zF&m%gNVZwakwg-;p%_3lnAJ6DO7p~JBTNaRN-SYTf!LFfx+0H@H7Pf9#9Ge8L?ArN zI|)6ub_)!sP>2txVuzVtFyM2T^YfA_DiWVI1xAj@B1<5zmnB%4V+qbHEg>X{6(~-W z85%_P9hK`a45ogST;ggRFcS7s>zrD+o{BaC?XT_ zs5M{;&XK1*n{**nQslK?g*FclUIKX7@@hqc`PW*m^ogoN?dhlVW1C_fB^f z$!^w@mNm<+sj{9DlBg^SI)YYEp4ML|e37+Lw@uC^)q>1j0Ko`~AzbvBU`$3ZGP7XP zyfemDTtfbjCM6bN!X*Q7X&+o(a2Jx<%P^xEiWOQWgmZ?LUH&nCWxGfxkOD)KJo&dx8+-Mx2DH@F6ZfwFq<*#obZE`Oo@n}hGX-%4&N zZ@g&VQ{Hmp`My4T$=1~;SN?VLiW~9^7MHWz_w_dS4-egbab)D9>n}Z?({TK`r=3@g z7xsL3b!PWl`<_lcKXBnWext3@*zv@cq2jHBHyztQT5zByT>Ct~c~$h&^;JD1?Qj3_ z7lXgtSULmso+=vYzwWim-DvpM<7Lk(pPnkMS@@$nUc6%eoqFGB(}i5^+~Wsl<$e6U VyZARR=0LJouwq5vsq!_O{tLfv;THe^ literal 0 HcmV?d00001 diff --git a/views/img/orderstate/index.php b/views/img/orderstate/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/views/img/orderstate/index.php @@ -0,0 +1,11 @@ + +
+
+

+ settings {{ 'Settings'|trans({}, 'Modules.Simpay.Admin') }} +

+ +
+
+ {{ form_widget(configurationForm) }} +
+
+ + +
+
+ + {{ form_end(configurationForm) }} +{% endblock %} \ No newline at end of file diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/views/templates/admin/index.php @@ -0,0 +1,11 @@ + + +
+

There was an error during the payment process

+
+ +{/block} + +{block name="link_rewrite"} + simpay-payment-error +{/block} \ No newline at end of file diff --git a/views/templates/front/index.php b/views/templates/front/index.php new file mode 100644 index 0000000..232bfba --- /dev/null +++ b/views/templates/front/index.php @@ -0,0 +1,11 @@ + + {l s='You will be redirected to SimPay payment gateway. Please wait...' mod='simpay'} +

{l s='If you are not redirected automatically' mod='simpay'}

+
+ +
+ +{/block} + +{*{block name='javascript_bottom'}*} +{* {include file="_partials/javascript.tpl" javascript=$javascript.bottom}*} +{* *} +{*{/block}*} \ No newline at end of file