diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68729d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index e69de29..7263c85 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,68 @@ +# Craftgate Payment Orchestration Module for PrestaShop + +## Description + +The Craftgate Payment Orchestration module for PrestaShop provides a seamless integration with Craftgate's payment services. It allows your customers to make payments using various methods supported by Craftgate, including credit/debit cards and alternative payment methods. + +## Features + +- Easy integration with Craftgate payment services +- Support for credit/debit card payments +- Support for alternative payment methods +- Customizable payment option title and description +- Sandbox mode for testing +- One-page checkout option + +## Compatibility + +This module is compatible with PrestaShop versions 1.7.x and 8.x. + +## Requirements + +- PHP 7.2 or higher +- PrestaShop 1.7.x or 8.x + +## Installation + +There are two ways to install the Craftgate Payment Orchestration module: + +### Method 1: Manual Installation + +1. Download the latest release of the module from the GitHub repository. +2. Unzip the downloaded file. +3. Upload the `craftgate_payment_orchestration` folder to your PrestaShop's `/modules` directory. +4. Log in to your PrestaShop admin panel. +5. Navigate to "Modules" > "Module Manager". +6. Search for "Craftgate Payment Orchestration" in the module list. +7. Click on the "Install" button next to the module. +8. After installation, click on "Configure" to set up your Craftgate API credentials and other settings. + +### Method 2: ZIP File Installation + +1. Download the ZIP file from the Github repository. +2. Log in to your PrestaShop admin panel. +3. Navigate to "Modules" > "Module Manager". +4. Click on the "Upload a module" button at the top of the page. +5. Select the downloaded ZIP file and upload it. +6. PrestaShop will automatically install the module. +7. After installation, click on "Configure" to set up your Craftgate API credentials and other settings. + +## Configuration + +1. In the module configuration page, enter your Craftgate API credentials (API Key and Secret Key) for both live and sandbox environments. +2. Choose whether to enable sandbox mode for testing. +3. Customize the payment option title and description if desired. +4. Configure the one-page checkout option if you want to use it. +5. Save your settings. + +## Usage + +Once installed and configured, the Craftgate payment option will appear on the checkout page for your customers. They can select this option to pay using Craftgate's services. + +## Support + +For any issues or questions, please contact Craftgate support via support@craftgate.io + +## License + +This module is released under the [MIT License](LICENSE). \ No newline at end of file diff --git a/classes/CraftgateClient.php b/classes/CraftgateClient.php new file mode 100755 index 0000000..65a8786 --- /dev/null +++ b/classes/CraftgateClient.php @@ -0,0 +1,46 @@ +craftgate = new \Craftgate\Craftgate(array( + 'apiKey' => $api_key, + 'secretKey' => $secret_key, + 'baseUrl' => $api_url, + )); + } + + public function initCheckoutForm($request) + { + $response = $this->craftgate->payment()->initCheckoutPayment($request); + return $this->buildResponse($response); + } + + public function retrieveCheckoutFormResult($token) + { + $response = $this->craftgate->payment()->retrieveCheckoutPayment($token); + return $this->buildResponse($response); + } + + private function buildResponse($response) + { + $response_json = json_decode($response); + return $response_json->data ?? $response_json; + } +} \ No newline at end of file diff --git a/classes/CraftgatePayment.php b/classes/CraftgatePayment.php new file mode 100644 index 0000000..ac8afb4 --- /dev/null +++ b/classes/CraftgatePayment.php @@ -0,0 +1,96 @@ + 'craftgate_payments', + 'primary' => 'id_craftgate_payment', + 'fields' => [ + 'payment_id' => ['type' => self::TYPE_INT, 'required' => true], + 'checkout_token' => ['type' => self::TYPE_STRING, 'validate' => 'isString', 'size' => 255], + 'meta_data' => ['type' => self::TYPE_STRING], + 'id_order' => ['type' => self::TYPE_INT, 'required' => true], + ], + ]; + + public static function getByOrderId(int $orderId) + { + $sql = 'SELECT `id_craftgate_payment` + FROM `' . _DB_PREFIX_ . 'craftgate_payments` + WHERE `id_order` = ' . $orderId; + + $result = Db::getInstance()->getValue($sql); + return !empty($result) ? new self($result) : false; + } + + public static function getByCheckoutToken(string $checkoutToken): false|CraftgatePayment + { + $sql = 'SELECT `id_craftgate_payment` + FROM `' . _DB_PREFIX_ . 'craftgate_payments` + WHERE `checkout_token` = \'' . pSQL($checkoutToken) . '\''; + + $result = Db::getInstance()->getValue($sql); + return !empty($result) ? new self($result) : false; + } + + public function getIdOrder() + { + return $this->id_order; + } + + public function setIdOrder($id_order): void + { + $this->id_order = $id_order; + } + + public function getPaymentId() + { + return $this->payment_id; + } + + public function setPaymentId($payment_id): void + { + $this->payment_id = $payment_id; + } + + public function getCheckoutToken() + { + return $this->checkout_token; + } + + public function setCheckoutToken($checkout_token): void + { + $this->checkout_token = $checkout_token; + } + + public function getIdCraftgatePayment(): string + { + return $this->id_craftgate_payment; + } + + public function setIdCraftgatePayment(string $id_craftgate_payment): void + { + $this->id_craftgate_payment = $id_craftgate_payment; + } + + public function getMetaData() + { + return json_decode($this->meta_data); + } + + public function setMetaData($data): void + { + $this->meta_data = json_encode($data); + } +} diff --git a/classes/CraftgatePaymentDBManager.php b/classes/CraftgatePaymentDBManager.php new file mode 100644 index 0000000..a29dd68 --- /dev/null +++ b/classes/CraftgatePaymentDBManager.php @@ -0,0 +1,24 @@ +execute($sql); + } +} + + diff --git a/classes/CraftgatePaymentErrorException.php b/classes/CraftgatePaymentErrorException.php new file mode 100644 index 0000000..586f27f --- /dev/null +++ b/classes/CraftgatePaymentErrorException.php @@ -0,0 +1,10 @@ +module = $module; + } + + public function handleCheckoutPaymentCallbackResult(Cart $cart, Customer $customer, $checkoutToken): void + { + if (!$this->checkIfContextIsValid($cart, $customer)) { + throw new Exception("Context is not valid!"); + } + + $craftgatePayment = $this->retrieveCraftgatePayment($checkoutToken); + if ($craftgatePayment) { + $this->redirectOrderConfirmationPage($craftgatePayment, $customer); + exit(); + } + + $checkoutFormResult = self::craftgateClient()->retrieveCheckoutFormResult($checkoutToken); + $checkoutFormResult->checkoutToken = $checkoutToken; + + if (!isset($checkoutFormResult->conversationId) || $checkoutFormResult->conversationId != $cart->id) { + PrestaShopLogger::addLog("Conversation ID is not matched cart id " . $cart->id . $checkoutFormResult->conversationId); + throw new Exception("Unknown error occurred!"); + } + + if (!isset($checkoutFormResult->paymentError) && $checkoutFormResult->paymentStatus === 'SUCCESS') { + $this->createOrder($cart, $customer, $checkoutFormResult); + } else { + throw new CraftgatePaymentErrorException($checkoutFormResult->paymentError->errorDescription); + } + } + + public function handleCheckoutPaymentWebhookResult($checkoutToken): void + { + if ($this->retrieveCraftgatePayment($checkoutToken)) + return; + + + $checkoutFormResult = self::craftgateClient()->retrieveCheckoutFormResult($checkoutToken); + $checkoutFormResult->checkoutToken = $checkoutToken; + + $cartId = $checkoutFormResult->conversationId; + $cart = new Cart($cartId); + $customer = new Customer($cart->id_customer); + + if ($this->checkIfContextIsValid($cart, $customer) && !isset($checkoutFormResult->paymentError) && $checkoutFormResult->paymentStatus === 'SUCCESS') { + $this->createOrder($cart, $customer, $checkoutFormResult); + } + } + + private function createOrder(Cart $cart, Customer $customer, $checkoutFormResult): void + { + Context::getContext()->cookie->__unset(self::INSTALLMENT_FEE_COOKIE_NAME); + Context::getContext()->cookie->write(); + + if ($checkoutFormResult->installment > 1) { + Context::getContext()->cookie->__set(self::INSTALLMENT_FEE_COOKIE_NAME, $checkoutFormResult->paidPrice - $checkoutFormResult->price); + Context::getContext()->cookie->write(); + } + + $this->module->validateOrder( + (int)$cart->id, + (int)Configuration::get('PS_OS_PAYMENT'), + (float)$checkoutFormResult->price, + $this->module->displayName, + null, + ['transaction_id' => $checkoutFormResult->id], + null, + false, + $customer->secure_key, + ); + + $orderCore = Order::getByCartId($cart->id); + $craftgatePayment = new CraftgatePayment(); + $craftgatePayment->setPaymentId($checkoutFormResult->id); + $craftgatePayment->setCheckoutToken($checkoutFormResult->checkoutToken); + $craftgatePayment->setIdOrder($orderCore->id); + $craftgatePayment->setMetaData([ + 'paidPrice' => $checkoutFormResult->paidPrice, + 'installment' => $checkoutFormResult->installment, + 'installmentFee' => $checkoutFormResult->paidPrice - $checkoutFormResult->price, + 'orderId' => $checkoutFormResult->orderId, + 'environment' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IS_SANDBOX_ACTIVE) ? "Sandbox" : "Production" + ]); + + $craftgatePayment->save(); + + } + + public function addInstallmentFee(Order $order): void + { + $installmentFee = Context::getContext()->cookie->__get(self::INSTALLMENT_FEE_COOKIE_NAME); + if (!$installmentFee) { + return; + } + + Context::getContext()->cookie->__unset(self::INSTALLMENT_FEE_COOKIE_NAME); + Context::getContext()->cookie->write(); + + $installmentFee = (float)$installmentFee; + + $orderDetail = new OrderDetail(); + $orderDetail->id_order = $order->id; + $orderDetail->product_name = $this->module->l('Installment Fee'); + $orderDetail->product_quantity = 1; + $orderDetail->product_price = $installmentFee; + $orderDetail->unit_price_tax_excl = $installmentFee; + $orderDetail->unit_price_tax_incl = $installmentFee; + $orderDetail->total_price_tax_incl = $installmentFee; + $orderDetail->total_price_tax_excl = $installmentFee; + $orderDetail->id_warehouse = 0; + $orderDetail->id_shop = $order->id_shop; + $orderDetail->add(); + + $orderPayment = $order->getOrderPayments()[0]; + $orderPayment->amount += $installmentFee; + $orderPayment->save(); + + $order->total_paid_real += $installmentFee; + $order->total_paid += $installmentFee; + $order->total_paid_tax_incl += $installmentFee; + $order->total_paid_tax_excl += $installmentFee; + $order->update(); + } + + private function retrieveCraftgatePayment($checkoutToken): false|CraftgatePayment + { + return CraftgatePayment::getByCheckoutToken($checkoutToken); + } + + private function checkIfContextIsValid($cart, $customer): bool + { + return true === Validate::isLoadedObject($cart) + && true === Validate::isLoadedObject($customer); + } + + public static function craftgateClient(): CraftgateClient + { + $isSandboxActive = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IS_SANDBOX_ACTIVE); + if ($isSandboxActive) { + $apiKey = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_SANDBOX_API_KEY); + $secretKey = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_SANDBOX_SECRET_KEY); + return CraftgateClient::getInstance($apiKey, $secretKey, "https://sandbox-api.craftgate.io"); + } + + $apiKey = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_LIVE_API_KEY); + $secretKey = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_LIVE_SECRET_KEY); + return CraftgateClient::getInstance($apiKey, $secretKey, "https://api.craftgate.io"); + } + + + public function redirectOrderConfirmationPage(CraftgatePayment $craftgatePayment, Customer $customer): void + { + $id_order = $craftgatePayment->getIdOrder(); + $order = new Order($id_order); + $link = new Link(); + $redirect_url = $link->getPageLink('order-confirmation', true, null, [ + 'id_cart' => $order->id_cart, + 'id_module' => $this->module->id, + 'id_order' => $id_order, + 'key' => $customer->secure_key, + ]); + Tools::redirect($redirect_url); + } +} \ No newline at end of file diff --git a/classes/CraftgateUtil.php b/classes/CraftgateUtil.php new file mode 100644 index 0000000..43e0894 --- /dev/null +++ b/classes/CraftgateUtil.php @@ -0,0 +1,11 @@ + + + craftgate_payment_orchestration + + + + + + 1 + 1 + \ No newline at end of file diff --git a/controllers/admin/AdminConfigureCraftgatePaymentOrchestrationController.php b/controllers/admin/AdminConfigureCraftgatePaymentOrchestrationController.php new file mode 100755 index 0000000..f8286e8 --- /dev/null +++ b/controllers/admin/AdminConfigureCraftgatePaymentOrchestrationController.php @@ -0,0 +1,64 @@ +page_header_toolbar_title = 'Craftgate Payment Orchestration Module'; + $this->bootstrap = true; + $this->className = 'Configuration'; + $this->table = 'configuration'; + parent::__construct(); + } + + public function renderForm() + { + $this->context->smarty->assign([ + 'CRAFTGATE_MODULE_ENABLED' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IS_MODULE_ENABLED), + 'CRAFTGATE_PAYMENT_OPTION_TITLE' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_TITLE), + 'CRAFTGATE_PAYMENT_OPTION_DESCRIPTION' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_DESCRIPTION), + 'CRAFTGATE_LIVE_API_KEY' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_LIVE_API_KEY), + 'CRAFTGATE_LIVE_SECRET_KEY' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_LIVE_SECRET_KEY), + 'CRAFTGATE_SANDBOX_API_KEY' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_SANDBOX_API_KEY), + 'CRAFTGATE_SANDBOX_SECRET_KEY' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_SANDBOX_SECRET_KEY), + 'CRAFTGATE_IS_SANDBOX_ACTIVE' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IS_SANDBOX_ACTIVE), + 'CRAFTGATE_IFRAME_OPTIONS' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IFRAME_OPTIONS), + 'CRAFTGATE_WEBHOOK_URL' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_WEBHOOK_URL), + 'CRAFTGATE_IS_ONE_PAGE_CHECKOUT_ACTIVE' => Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IS_ONE_PAGE_CHECKOUT_ACTIVE), + ]); + + return $this->context->smarty->fetch($this->getTemplatePath() . 'configure.tpl'); + } + + public function postProcess() + { + if (Tools::isSubmit('submitCraftgateModuleConfiguration')) { + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_IS_MODULE_ENABLED, Tools::getValue('CRAFTGATE_MODULE_ENABLED')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_TITLE, Tools::getValue('CRAFTGATE_PAYMENT_OPTION_TITLE')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_DESCRIPTION, Tools::getValue('CRAFTGATE_PAYMENT_OPTION_DESCRIPTION')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_IS_ONE_PAGE_CHECKOUT_ACTIVE, Tools::getValue('CRAFTGATE_IS_ONE_PAGE_CHECKOUT_ACTIVE')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_LIVE_API_KEY, Tools::getValue('CRAFTGATE_LIVE_API_KEY')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_LIVE_SECRET_KEY, Tools::getValue('CRAFTGATE_LIVE_SECRET_KEY')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_SANDBOX_API_KEY, Tools::getValue('CRAFTGATE_SANDBOX_API_KEY')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_SANDBOX_SECRET_KEY, Tools::getValue('CRAFTGATE_SANDBOX_SECRET_KEY')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_IS_SANDBOX_ACTIVE, Tools::getValue('CRAFTGATE_IS_SANDBOX_ACTIVE')); + Configuration::updateValue(Craftgate_Payment_Orchestration::CONFIG_IFRAME_OPTIONS, Tools::getValue('CRAFTGATE_IFRAME_OPTIONS')); + + $this->confirmations[] = $this->l('Settings updated successfully.'); + } + + return parent::postProcess(); + } + + public function initContent() + { + parent::initContent(); + $this->content .= $this->renderForm(); + $this->context->smarty->assign('content', $this->content); + } + + public function getTemplatePath() + { + return _PS_MODULE_DIR_ . $this->module->name . '/views/templates/admin/'; + } +} diff --git a/controllers/admin/index.php b/controllers/admin/index.php new file mode 100755 index 0000000..3b58b73 --- /dev/null +++ b/controllers/admin/index.php @@ -0,0 +1,10 @@ +context->cart->id_customer); + if (false === $this->checkIfContextIsValid() || false === $this->checkIfPaymentOptionIsAvailable() || false === Validate::isLoadedObject($customer)) { + $this->redirectToCheckoutPage(); + } + } + + public function initContent(): void + { + parent::initContent(); + if (Tools::isSubmit("isOnePageCheckout")) { + $this->handleOnePageCheckout(); + } else { + $this->handleCheckout(); + } + } + + public function handleOnePageCheckout(): void + { + header('Content-Type: application/json'); + try { + $checkoutFormUrl = $this->initCheckoutForm(); + $language = $this->context->language->iso_code; + $iframeOptions = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IFRAME_OPTIONS); + $response = ["checkoutFormUrl" => $checkoutFormUrl . "&iframe=true&lang=" . $language . "&" . $iframeOptions . "&hideSubmitButton=true"]; + http_response_code(201); + exit(json_encode($response)); + } catch (Exception $exception) { + http_response_code(422); + $response = ["errorMessage" => $this->module->l("We're currently unable to process this payment option. Please choose another method or try again later.")]; + PrestaShopLogger::addLog("Error occurred while initializing checkout form. Error: " . $exception->getMessage(), 3); + exit(json_encode($response)); + } + } + + public function handleCheckout(): void + { + try { + $checkoutFormUrl = $this->initCheckoutForm(); + + $language = $this->context->language->iso_code; + $iframeOptions = Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IFRAME_OPTIONS); + + $this->context->smarty->assign([ + 'checkoutFormUrl' => $checkoutFormUrl . "&iframe=true&lang=" . $language . "&" . $iframeOptions + ]); + } catch (Exception $exception) { + PrestaShopLogger::addLog("Error occurred while initializing checkout form. Error: " . $exception->getMessage(), 3); + $this->context->smarty->assign([ + 'error' => $this->module->l("We're currently unable to process this payment option. Please choose another method or try again later.") + ]); + } + + $this->setTemplate('module:' . $this->module->name . '/views/templates/front/checkoutForm.tpl'); + } + + private function initCheckoutForm() + { + $request = $this->buildInitCheckoutFormRequest(); + $response = CraftgatePaymentService::craftgateClient()->initCheckoutForm($request); + + if (isset($response->pageUrl)) { + return $response->pageUrl; + } elseif (isset($response->errors)) { + throw new Exception($response->errors->errorDescription, $response->errors->errorCode); + } + } + + private function buildInitCheckoutFormRequest(): array + { + $cartId = $this->context->cart->id; + $orderTotal = $this->context->cart->getOrderTotal(); + + return [ + 'price' => CraftgateUtil::format_price($orderTotal), + 'paidPrice' => CraftgateUtil::format_price($orderTotal), + 'currency' => $this->context->currency->iso_code, + 'paymentGroup' => \Craftgate\Model\PaymentGroup::LISTING_OR_SUBSCRIPTION, + 'conversationId' => $cartId, + 'callbackUrl' => $this->context->link->getModuleLink($this->module->name, 'checkoutFormCallbackHandler', ['cartId' => $cartId], true), + 'items' => $this->buildItems($this->context->cart) + ]; + } + + private function buildItems($cart) + { + $cart_products = $cart->getProducts(); + $items = []; + + if ($cart_products) { + foreach ($cart_products as $product) { + $items[] = [ + 'externalId' => $product['id_product'], + 'name' => $product['name'], + 'price' => CraftgateUtil::format_price($product['total_wt']), + ]; + } + } + + $shipping_cost = (float)$cart->getOrderTotal(true, Cart::ONLY_SHIPPING); + if ($shipping_cost > 0) { + $items[] = [ + 'externalId' => 'shipping-total', + 'name' => 'Shipping Total', + 'price' => CraftgateUtil::format_price($shipping_cost), + ]; + } + + return $items; + } + + private function checkIfContextIsValid() + { + return true === Validate::isLoadedObject($this->context->cart) + && true === Validate::isUnsignedInt($this->context->cart->id_customer) + && true === Validate::isUnsignedInt($this->context->cart->id_address_delivery) + && true === Validate::isUnsignedInt($this->context->cart->id_address_invoice); + } + + + private function checkIfPaymentOptionIsAvailable(): bool + { + if (!Configuration::get(Craftgate_Payment_Orchestration::CONFIG_IS_MODULE_ENABLED)) { + return false; + } + + $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; + } + + public function redirectToCheckoutPage(): void + { + Tools::redirect($this->context->link->getPageLink('order', true, $this->context->language->id, ['step' => 1])); + } +} diff --git a/controllers/front/checkoutFormCallbackHandler.php b/controllers/front/checkoutFormCallbackHandler.php new file mode 100755 index 0000000..afbb8b0 --- /dev/null +++ b/controllers/front/checkoutFormCallbackHandler.php @@ -0,0 +1,80 @@ +auth = false; + $this->craftgatePaymentService = new CraftgatePaymentService($this->module); + } + + public function postProcess(): void + { + $checkoutToken = Tools::getValue("token"); + + if (!$checkoutToken) { + Tools::displayError("Error occurred"); + exit(); + } + + if (Tools::getValue('source') != 'prestashop') { + $this->context->smarty->assign([ + "checkoutToken" => $checkoutToken, + "cartId" => Tools::getValue("cartId") + ]); + + $this->setTemplate('module:' . $this->module->name . '/views/templates/front/checkoutFormCallback.tpl'); + } else { + $this->handleResult($checkoutToken); + } + } + + public function handleResult(mixed $checkoutToken): void + { + $cartId = Tools::getValue("cartId"); + $cart = new Cart($cartId); + $customer = new Customer($cart->id_customer); + + try { + $this->craftgatePaymentService->handleCheckoutPaymentCallbackResult($cart, $customer, $checkoutToken); + } catch (CraftgatePaymentErrorException $e) { + PrestaShopLogger::addLog("Payment error occurred while creating order for cart $cartId" . $e->getMessage(), 2); + $this->redirectOrderCheckout($e->getMessage()); + } catch (Exception $e) { + PrestaShopLogger::addLog("Unknown error occurred while creating order" . serialize($e), 3); + $this->redirectOrderCheckout(); + } + + $orderConfirmationPageLink = $this->buildOrderConfirmationLink($cart, $customer); + Tools::redirect($orderConfirmationPageLink); + } + + public function redirectOrderCheckout($message = null): void + { + $redirectUrl = $this->context->link->getPageLink('order', true, $this->context->language->id, ['step' => 1]); + if (!isset($message)) { + Tools::redirect($redirectUrl); + } else { + $this->context->controller->errors[] = $message; + $this->context->controller->redirectWithNotifications($redirectUrl); + } + } + + public function buildOrderConfirmationLink(Cart $cart, Customer $customer): string + { + return $this->context->link->getPageLink('order-confirmation', true, $this->context->language->id, + [ + 'id_cart' => (int)$cart->id, + 'id_module' => (int)$this->module->id, + 'id_order' => (int)$this->module->currentOrder, + 'key' => $customer->secure_key, + ] + ); + } +} diff --git a/controllers/front/checkoutFormWebhookHandler.php b/controllers/front/checkoutFormWebhookHandler.php new file mode 100644 index 0000000..1d5997c --- /dev/null +++ b/controllers/front/checkoutFormWebhookHandler.php @@ -0,0 +1,48 @@ +auth = false; + $this->craftgatePaymentService = new CraftgatePaymentService($this->module); + } + + public function postProcess(): void + { + $webhook_data = json_decode(file_get_contents('php://input'), true); + if (!isset($webhook_data) || !$this->shouldProcessWebhookRequest($webhook_data)) { + exit(); + } + + try { + $this->craftgatePaymentService->handleCheckoutPaymentWebhookResult($webhook_data["payloadId"]); + exit(); + } catch (Exception $e) { + Tools::displayError($e); + PrestaShopLogger::addLog("Unexpected error occurred while creating order" . serialize($e)); + } + } + + private function shouldProcessWebhookRequest($webhook_data): bool + { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + return false; + } + + $event_type = $webhook_data['eventType']; + $status = $webhook_data['status']; + $checkout_token = $webhook_data['payloadId']; + + if ($event_type !== 'CHECKOUTFORM_AUTH' || $status !== "SUCCESS" || !isset($checkout_token)) { + return false; + } + return true; + } +} diff --git a/controllers/front/index.php b/controllers/front/index.php new file mode 100755 index 0000000..3b58b73 --- /dev/null +++ b/controllers/front/index.php @@ -0,0 +1,10 @@ +name = 'craftgate_payment_orchestration'; + $this->tab = 'payments_gateways'; + $this->version = '1.0.0'; + $this->author = 'Craftgate'; + $this->currencies = true; + $this->currencies_mode = 'checkbox'; + $this->ps_versions_compliancy = [ + 'min' => '1.7', + 'max' => _PS_VERSION_, + ]; + + $this->controllers = [ + 'cancel', + 'external', + 'validation', + ]; + + parent::__construct(); + + $this->displayName = $this->l('Craftgate Payment Orchestration'); + $this->description = $this->l("Simple, Flexible, Accessible 'One-Stop Shop' Payment Orchestration"); + $this->craftgatePaymentService = new CraftgatePaymentService($this); + } + + public function install(): bool + { + return parent::install() + && CraftgatePaymentDBManager::install() + && $this->registerHook(static::HOOKS) + && $this->updateDefaultConfigurations(); + } + + public function uninstall(): bool + { + return parent::uninstall() + && Configuration::deleteByName(static::CONFIG_IS_MODULE_ENABLED); + } + + public function getContent(): void + { + Tools::redirectAdmin($this->context->link->getAdminLink(static::MODULE_ADMIN_CONTROLLER)); + } + + public function hookActionValidateOrder($params) + { + $order = $params['order']; + $this->craftgatePaymentService->addInstallmentFee($order); + } + + public function hookPaymentOptions(array $params): array + { + $cart = $params['cart']; + + if (false === Validate::isLoadedObject($cart) || false === $this->isCurrencySupported($cart)) { + return []; + } + + $paymentOptions = []; + + if (Configuration::get(static::CONFIG_IS_MODULE_ENABLED)) { + $paymentOptions[] = Configuration::get(static::CONFIG_IS_ONE_PAGE_CHECKOUT_ACTIVE) + ? $this->getCraftgateOnePageCheckoutFormPaymentOption() + : $this->getCraftgateCheckoutFormPaymentOption(); + } + + return $paymentOptions; + } + + public function hookDisplayHeader() + { + $this->context->controller->addCSS('modules/' . $this->name . '/views/css/style.css'); + } + + public function hookDisplayPaymentByBinaries(array $params) + { + + $cart = $params['cart']; + + if (false === Validate::isLoadedObject($cart) || false === $this->isCurrencySupported($cart) || false === Configuration::get(static::CONFIG_IS_ONE_PAGE_CHECKOUT_ACTIVE)) { + return ''; + } + + return $this->context->smarty->fetch('module:craftgate_payment_orchestration/views/templates/hook/embeddedCheckoutFormBinary.tpl'); + } + + public function hookDisplayAdminOrderLeft(array $params): string + { + return $this->hookDisplayAdminOrderMainBottom($params); + } + + + public function hookDisplayAdminOrderMainBottom(array $params): string + { + if (empty($params['id_order'])) { + return ''; + } + + $orderId = $params['id_order']; + $order = new Order((int)$orderId); + if (false === Validate::isLoadedObject($order) || $order->module !== $this->name) { + return ''; + } + + $craftgatePayment = CraftgatePayment::getByOrderId($orderId); + + $this->context->smarty->assign([ + 'currency' => $order->id_currency, + 'craftgatePayment' => $craftgatePayment, + 'moduleName' => $this->name, + 'moduleDisplayName' => $this->displayName, + 'moduleLogoSrc' => $this->getPathUri() . 'logo.png', + ]); + + return $this->context->smarty->fetch('module:craftgate_payment_orchestration/views/templates/hook/displayAdminOrderMainBottom.tpl'); + } + + + private function isCurrencySupported(Cart $cart): bool + { + $cartCurrency = new Currency($cart->id_currency); + $moduleCurrencies = $this->getCurrency($cart->id_currency); + + if (empty($moduleCurrencies)) { + return false; + } + + foreach ($moduleCurrencies as $currency_module) { + if ($cartCurrency->id == $currency_module['id_currency']) { + return true; + } + } + + return false; + } + + private function getCraftgateCheckoutFormPaymentOption(): PaymentOption + { + $paymentOption = new PaymentOption(); + $paymentOption->setModuleName($this->name); + $paymentOptionTitle = !empty(Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_TITLE)) + ? Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_TITLE) + : $this->l('Pay with Debit/Credit Card and Alternative Payment Methods'); + + $paymentOptionDescription = !empty(Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_DESCRIPTION)) + ? Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_DESCRIPTION) + : $this->l('You can pay with Debit and Credit Card'); + + $paymentOption->setCallToActionText($paymentOptionTitle); + $paymentOption->setAction($this->context->link->getModuleLink($this->name, 'checkoutForm', [], true)); + + $this->context->smarty->clearCompiledTemplate('module:craftgate_payment_orchestration/views/templates/front/checkoutFormPaymentOption.tpl'); + $this->context->smarty->assign([ + "description" => $paymentOptionDescription + ]); + $paymentOption->setAdditionalInformation($this->context->smarty->fetch('module:craftgate_payment_orchestration/views/templates/front/checkoutFormPaymentOption.tpl', 1, 2)); + return $paymentOption; + } + + private function getCraftgateOnePageCheckoutFormPaymentOption(): PaymentOption + { + $paymentOption = new PaymentOption(); + $paymentOption->setModuleName($this->name); + $paymentOptionTitle = !empty(Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_TITLE)) + ? Configuration::get(Craftgate_Payment_Orchestration::CONFIG_PAYMENT_OPTION_TITLE) + : $this->l('Pay with Debit/Credit Card and Alternative Payment Methods'); + + $paymentOption->setCallToActionText($paymentOptionTitle); + $paymentOption->setAction($this->context->link->getModuleLink($this->name, 'checkoutForm', [], true)); + + $this->context->smarty->clearCompiledTemplate('module:craftgate_payment_orchestration/views/templates/front/onePageCheckoutFormPaymentOption.tpl'); + $paymentOption->setBinary(true); + $paymentOption->setAdditionalInformation($this->context->smarty->fetch('module:craftgate_payment_orchestration/views/templates/front/onePageCheckoutFormPaymentOption.tpl', 1, 2)); + return $paymentOption; + } + + private function updateDefaultConfigurations(): bool + { + $webhookUrl = $this->context->link->getModuleLink($this->name, 'checkoutFormWebhookHandler', [], true); + return Configuration::updateGlobalValue(Craftgate_Payment_Orchestration::CONFIG_IS_ONE_PAGE_CHECKOUT_ACTIVE, true) + && Configuration::updateGlobalValue(Craftgate_Payment_Orchestration::CONFIG_WEBHOOK_URL, $webhookUrl); + } +} diff --git a/index.php b/index.php new file mode 100755 index 0000000..61ffa71 --- /dev/null +++ b/index.php @@ -0,0 +1,10 @@ +options = $options; + } + + protected function httpGet($path) + { + $url = $this->prepareUrl($path); + $headers = $this->prepareHeaders($path); + + return Curl::get($url, $headers); + } + + protected function httpPost($path, $request) + { + $url = $this->prepareUrl($path); + $headers = $this->prepareHeaders($path, $request); + + return Curl::post($url, $headers, $request); + } + + protected function httpPut($path, $request) + { + $url = $this->prepareUrl($path); + $headers = $this->prepareHeaders($path, $request); + + return Curl::put($url, $headers, $request); + } + + protected function httpDelete($path) + { + $url = $this->prepareUrl($path); + $headers = $this->prepareHeaders($path); + + return Curl::delete($url, $headers); + } + + private function prepareHeaders($path, $request = null) + { + $headers = array( + 'accept: application/json', + 'content-type: application/json' + ); + + if (isset($GLOBALS["cg-lang-header"])) { + $headers[] = 'lang: ' . $GLOBALS["cg-lang-header"]; + } + + $headers[] = 'x-api-key: ' . $this->options->getApiKey(); + $headers[] = 'x-rnd-key: ' . ($randomString = Guid::generate()); + $headers[] = 'x-auth-version: v1'; + $headers[] = 'x-signature: ' . Signature::generate( + $this->options, $path, $randomString, $request + ); + + return $headers; + } + + private function prepareUrl($path) + { + return $this->options->getBaseUrl() . '/' . trim($path, '/'); + } +} diff --git a/lib/craftgate/src/Adapter/PaymentAdapter.php b/lib/craftgate/src/Adapter/PaymentAdapter.php new file mode 100755 index 0000000..31dd331 --- /dev/null +++ b/lib/craftgate/src/Adapter/PaymentAdapter.php @@ -0,0 +1,18 @@ +httpPost($path, $request); + } + + public function retrieveCheckoutPayment($token) + { + $path = "/payment/v1/checkout-payments/" . $token; + return $this->httpGet($path); + } +} diff --git a/lib/craftgate/src/Craftgate.php b/lib/craftgate/src/Craftgate.php new file mode 100755 index 0000000..236bc10 --- /dev/null +++ b/lib/craftgate/src/Craftgate.php @@ -0,0 +1,43 @@ +setOptions($options); + } + + public function setOptions($options) + { + if (is_array($options)) { + $options = new CraftgateOptions($options); + } + + if (!$options instanceof CraftgateOptions) { + throw new \Exception(sprintf( + 'Argument $options must be either instance of %s or an array, %s given', + 'Craftgate\CraftgateOptions', gettype($options) + )); + } + + $this->options = $options; + + return $this; + } + + public function getOptions() + { + return $this->options; + } + + public function payment() + { + return new PaymentAdapter($this->options); + } +} diff --git a/lib/craftgate/src/CraftgateOptions.php b/lib/craftgate/src/CraftgateOptions.php new file mode 100755 index 0000000..9796d04 --- /dev/null +++ b/lib/craftgate/src/CraftgateOptions.php @@ -0,0 +1,67 @@ +apiKey = $options['apiKey']; + isset($options['secretKey']) && $this->secretKey = $options['secretKey']; + isset($options['baseUrl']) && $this->baseUrl = $options['baseUrl']; + } + } + + public function __debugInfo() + { + // Hides sensitive data (properties) while dump calls, + // so toArray() method should be called for debugging. + } + + public function setApiKey($apiKey) + { + $this->apiKey = $apiKey; + } + + public function getApiKey() + { + return $this->apiKey; + } + + public function setSecretKey($secretKey) + { + $this->secretKey = $secretKey; + } + + public function getSecretKey() + { + return $this->secretKey; + } + + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + } + + public function getBaseUrl() + { + return $this->baseUrl; + } + + public function toArray() + { + return array( + 'apiKey' => $this->apiKey, + 'secretKey' => $this->secretKey, + 'baseUrl' => $this->baseUrl, + ); + } +} diff --git a/lib/craftgate/src/Model/Currency.php b/lib/craftgate/src/Model/Currency.php new file mode 100755 index 0000000..8316a14 --- /dev/null +++ b/lib/craftgate/src/Model/Currency.php @@ -0,0 +1,11 @@ + 'GET', + CURLOPT_CONNECTTIMEOUT => self::CONNECT_TIMEOUT, + CURLOPT_TIMEOUT => self::READ_TIMEOUT, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $headers, + )); + } + + public static function post($url, array $headers, $content) + { + return self::request($url, array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_CONNECTTIMEOUT => self::CONNECT_TIMEOUT, + CURLOPT_TIMEOUT => self::READ_TIMEOUT, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => json_encode($content), + )); + } + + public static function put($url, array $headers, $content) + { + return self::request($url, array( + CURLOPT_CUSTOMREQUEST => 'PUT', + CURLOPT_CONNECTTIMEOUT => self::CONNECT_TIMEOUT, + CURLOPT_TIMEOUT => self::READ_TIMEOUT, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => json_encode($content), + )); + } + + public static function delete($url, array $headers) + { + return self::request($url, array( + CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_CONNECTTIMEOUT => self::CONNECT_TIMEOUT, + CURLOPT_TIMEOUT => self::READ_TIMEOUT, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $headers, + )); + } + + private static function request($url, $options) + { + $request = curl_init($url); + + curl_setopt_array($request, $options); + + $response = curl_exec($request); + + if ($response === false) { + throw new \Exception(curl_error($request), curl_errno($request)); + } + + curl_close($request); + unset($request); // PHP 8.0 + + return $response; + } +} diff --git a/lib/craftgate/src/Util/Guid.php b/lib/craftgate/src/Util/Guid.php new file mode 100755 index 0000000..15f188b --- /dev/null +++ b/lib/craftgate/src/Util/Guid.php @@ -0,0 +1,21 @@ +getBaseUrl() . urldecode($path) + . $options->getApiKey() . $options->getSecretKey() + . $randomString . ($request ? json_encode($request) : ''); + + return base64_encode(hash('sha256', $hash, true)); + } +} diff --git a/logo.png b/logo.png new file mode 100755 index 0000000..986abfd Binary files /dev/null and b/logo.png differ diff --git a/translations/en.php b/translations/en.php new file mode 100644 index 0000000..e817c26 --- /dev/null +++ b/translations/en.php @@ -0,0 +1,39 @@ +craftgate_payment_orchestration_3275c30b6355fca65139ddd0778ac7ec'] = 'Craftgate Payment Orchestration'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgate_payment_orchestration_e083809d29c251972afd5b382a87ef37'] = 'Pay with Debit/Credit Card and Alternative Payment Methods'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgate_payment_orchestration_b2c6367d0ae462370977f8fade784fe6'] = 'You can pay with Debit and Credit Card'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_e566fe9aef1502d69ccdbe28e1957535'] = 'Enable/Disable'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_93cba07454f06a4a960172bbd6e2a435'] = 'Yes'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_bafd7322c6e97d25b6299b5d6fe8920b'] = 'No'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_4c3128be0910f49baecd0497f1ddfdf8'] = 'Enable or disable the Craftgate payment option on the checkout page.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0fc904bf3841ae6d54ced36050fde67e'] = 'One Page Checkout'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c4ec25a4cde1fdd1b2a9e3edc43f3b77'] = 'Enable or disable one-page checkout for the Craftgate payment option.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_b78a3223503896721cca1303f776159b'] = 'Title'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_78aef61495e242bb4477a033f4cd6b00'] = 'This is the title of the payment option shown to the customer during checkout. To use default, keep this field empty.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_b5a7adde1af5c87d7fd797b6245c2a39'] = 'Description'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c5d26bb5054d169f7122d3868e62857a'] = 'Provide a brief description for the payment option that will appear on the checkout page. To use default, keep this field empty.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_3d855a86c289a1fea2ee9b992ed58e2f'] = 'Live API Key'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0393670cb9bd296b3528935e44d0d90e'] = 'Enter the live API key provided by Craftgate.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_df2223649d627882b90c2db3ec8d4f2b'] = 'Live Secret Key'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_af4e8396a3198cc824517152a2414ab8'] = 'Enter the live secret key provided by Craftgate.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0f069af40ceaacc090c657429bf1397a'] = 'Sandbox API Key'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_a17404634f898e154928b3835408c3c3'] = 'Enter the sandbox API key for testing.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_8c9952079f4bd6acb39f6bdf0425aa38'] = 'Sandbox Secret Key'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_da8d9d678dbf7f2c6af42aea161c8dfd'] = 'Enter the sandbox secret key for testing.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_8eaf9dd80ecfbd6de7c83cf3b707232b'] = 'Sandbox Mode'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_f5bc7ee15452cb2038d174084618ad78'] = 'Enable or disable sandbox mode for testing the integration.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0763bcfe13fb05503dada612e8990fe4'] = 'Iframe Options'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_9dc258f2243861d1cf2bea3629675138'] = 'Example: hideFooter=true&hideHeader=true'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c9cc8cce247e49bae79f15173ce97354'] = 'Save'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_1e63d1811556fc2dad106dcbd4217a69'] = 'Payment ID'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_f8510606e25047908d1f3e4633145576'] = 'Craftgate Order ID'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_f97a7b6286078d057df359177e532b72'] = 'Paid Price'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_459f93c30d7bc374abe95e0d8a1c66b0'] = 'Installment'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_ab05c71727b84389efacb94cfe65a299'] = 'Installment Fee'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_eb8abdd76e82b300cba4096d4c120122'] = 'Payment Detail URL'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>embeddedcheckoutformbinary_a128f329c5aeea633ced6bb9d7c56c74'] = 'Place Order'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>adminconfigurecraftgatepaymentorchestrationcontroller_8dd2f915acf4ec98006d11c9a4b0945b'] = 'Settings updated successfully.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgatepaymentservice_ab05c71727b84389efacb94cfe65a299'] = 'Installment Fee'; diff --git a/translations/es.php b/translations/es.php new file mode 100644 index 0000000..00dcf09 --- /dev/null +++ b/translations/es.php @@ -0,0 +1,41 @@ +craftgate_payment_orchestration_3275c30b6355fca65139ddd0778ac7ec'] = 'Orquestación de Pagos de Craftgate'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgate_payment_orchestration_e083809d29c251972afd5b382a87ef37'] = 'Pague con Tarjeta de Débito/Crédito y Métodos de Pago Alternativos'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgate_payment_orchestration_b2c6367d0ae462370977f8fade784fe6'] = 'Puede pagar con Tarjeta de Débito y Crédito'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_e566fe9aef1502d69ccdbe28e1957535'] = 'Activar/Desactivar'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_93cba07454f06a4a960172bbd6e2a435'] = 'Sí'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_bafd7322c6e97d25b6299b5d6fe8920b'] = 'No'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_4c3128be0910f49baecd0497f1ddfdf8'] = 'Activar o desactivar la opción de pago de Craftgate en la página de pago.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0fc904bf3841ae6d54ced36050fde67e'] = 'Pago en Una Página'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c4ec25a4cde1fdd1b2a9e3edc43f3b77'] = 'Activar o desactivar el pago en una página para la opción de pago de Craftgate.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_b78a3223503896721cca1303f776159b'] = 'Título'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_78aef61495e242bb4477a033f4cd6b00'] = 'Este es el título de la opción de pago mostrado al cliente durante el proceso de pago. Para usar el predeterminado, deje este campo vacío.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_b5a7adde1af5c87d7fd797b6245c2a39'] = 'Descripción'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c5d26bb5054d169f7122d3868e62857a'] = 'Proporcione una breve descripción para la opción de pago que aparecerá en la página de pago. Para usar el predeterminado, deje este campo vacío.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_3d855a86c289a1fea2ee9b992ed58e2f'] = 'Clave API en Vivo'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0393670cb9bd296b3528935e44d0d90e'] = 'Ingrese la clave API en vivo proporcionada por Craftgate.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_df2223649d627882b90c2db3ec8d4f2b'] = 'Clave Secreta en Vivo'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_af4e8396a3198cc824517152a2414ab8'] = 'Ingrese la clave secreta en vivo proporcionada por Craftgate.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0f069af40ceaacc090c657429bf1397a'] = 'Clave API de Sandbox'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_a17404634f898e154928b3835408c3c3'] = 'Ingrese la clave API de sandbox para realizar pruebas.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_8c9952079f4bd6acb39f6bdf0425aa38'] = 'Clave Secreta de Sandbox'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_da8d9d678dbf7f2c6af42aea161c8dfd'] = 'Ingrese la clave secreta de sandbox para realizar pruebas.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_8eaf9dd80ecfbd6de7c83cf3b707232b'] = 'Modo Sandbox'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_f5bc7ee15452cb2038d174084618ad78'] = 'Activar o desactivar el modo sandbox para probar la integración.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0763bcfe13fb05503dada612e8990fe4'] = 'Opciones de Iframe'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_9dc258f2243861d1cf2bea3629675138'] = 'Ejemplo: hideFooter=true&hideHeader=true'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c9cc8cce247e49bae79f15173ce97354'] = 'Guardar'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_accea76d09b0193c332cf51c646a205c'] = 'Detalles del Pago'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_1e63d1811556fc2dad106dcbd4217a69'] = 'ID del Pago'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_f8510606e25047908d1f3e4633145576'] = 'ID del Pedido de Craftgate'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_f97a7b6286078d057df359177e532b72'] = 'Importe del Pago'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_459f93c30d7bc374abe95e0d8a1c66b0'] = 'Cuota'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_ab05c71727b84389efacb94cfe65a299'] = 'Cuota de Cuota'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_eb8abdd76e82b300cba4096d4c120122'] = 'URL de los Detalles del Pago'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>embeddedcheckoutformbinary_a128f329c5aeea633ced6bb9d7c56c74'] = 'Realizar Pedido'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>adminconfigurecraftgatepaymentorchestrationcontroller_edfe300ca312067edea71abb9770e302'] = 'Módulo de Orquestación de Pagos Craftgate'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>adminconfigurecraftgatepaymentorchestrationcontroller_8dd2f915acf4ec98006d11c9a4b0945b'] = 'Ajustes actualizados correctamente.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgatepaymentservice_ab05c71727b84389efacb94cfe65a299'] = 'Cuota de Cuota'; diff --git a/translations/tr.php b/translations/tr.php new file mode 100644 index 0000000..6baa29d --- /dev/null +++ b/translations/tr.php @@ -0,0 +1,40 @@ +craftgate_payment_orchestration_3275c30b6355fca65139ddd0778ac7ec'] = 'Craftgate Ödeme Orkestrasyonu'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgate_payment_orchestration_e083809d29c251972afd5b382a87ef37'] = 'Kredi/Banka Kartı veya Alternatif Ödeme Yöntemleri ile Öde'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgate_payment_orchestration_b2c6367d0ae462370977f8fade784fe6'] = 'Kredi/Banka Kartı ile ödeme yapabilirsiniz'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_e566fe9aef1502d69ccdbe28e1957535'] = 'Açık / Kapalı'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_93cba07454f06a4a960172bbd6e2a435'] = 'Evet'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_bafd7322c6e97d25b6299b5d6fe8920b'] = 'Hayır'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_4c3128be0910f49baecd0497f1ddfdf8'] = 'Craftgate ödeme seçeneğini açın veya kapatın'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0fc904bf3841ae6d54ced36050fde67e'] = 'One Page Checkout'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c4ec25a4cde1fdd1b2a9e3edc43f3b77'] = 'One Page Checkout özelliğini açın veya kapatın'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_b78a3223503896721cca1303f776159b'] = 'Başlık'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_78aef61495e242bb4477a033f4cd6b00'] = 'Ödeme seçeneğinin ödeme sırasında müşteriye gösterilen başlığıdır. Varsayılanı kullanmak için bu alanı boş bırakın.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_b5a7adde1af5c87d7fd797b6245c2a39'] = 'Açıklama'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c5d26bb5054d169f7122d3868e62857a'] = 'Ödeme sayfasında görünecek olan ödeme seçeneği için kısa bir açıklama girin. Varsayılanı kullanmak için bu alanı boş bırakın.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_3d855a86c289a1fea2ee9b992ed58e2f'] = 'Canlı API Anahtarı'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0393670cb9bd296b3528935e44d0d90e'] = 'Craftgate tarafından sağlanan canlı API anahtarını girin.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_df2223649d627882b90c2db3ec8d4f2b'] = 'Canlı Gizli Anahtarı'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_af4e8396a3198cc824517152a2414ab8'] = 'Craftgate tarafından sağlanan canlı gizli anahtarı girin.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0f069af40ceaacc090c657429bf1397a'] = 'Sandbox API Anahtarı'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_a17404634f898e154928b3835408c3c3'] = 'Craftgate tarafından sağlanan sandbox API anahtarını girin.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_8c9952079f4bd6acb39f6bdf0425aa38'] = 'Sandbox Gizli Anahtarı'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_da8d9d678dbf7f2c6af42aea161c8dfd'] = 'Test için sandbox gizli anahtarını girin.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_8eaf9dd80ecfbd6de7c83cf3b707232b'] = 'Sandbox Mod'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_f5bc7ee15452cb2038d174084618ad78'] = 'Entegrasyonu test etmek için sandbox modunu etkinleştirin veya devre dışı bırakın.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_0763bcfe13fb05503dada612e8990fe4'] = 'Iframe Seçenekleri'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_9dc258f2243861d1cf2bea3629675138'] = 'Örnek: hideFooter=true&hideHeader=true'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>configure_c9cc8cce247e49bae79f15173ce97354'] = 'Kaydet'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_accea76d09b0193c332cf51c646a205c'] = 'Ödeme Detayları'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_1e63d1811556fc2dad106dcbd4217a69'] = 'Ödeme ID'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_f8510606e25047908d1f3e4633145576'] = 'Craftgate Sipariş Numarası'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_f97a7b6286078d057df359177e532b72'] = 'Toplam Tahsil Edilen Tutar'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_459f93c30d7bc374abe95e0d8a1c66b0'] = 'Taksit'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_ab05c71727b84389efacb94cfe65a299'] = 'Taksit Vade Farkı'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>displayadminordermainbottom_eb8abdd76e82b300cba4096d4c120122'] = 'Ödeme Detay Sayfası'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>embeddedcheckoutformbinary_a128f329c5aeea633ced6bb9d7c56c74'] = 'Sipariş Oluştur'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>adminconfigurecraftgatepaymentorchestrationcontroller_8dd2f915acf4ec98006d11c9a4b0945b'] = 'Ayarlar başarılı bir şekilde güncellendi.'; +$_MODULE['<{craftgate_payment_orchestration}prestashop>craftgatepaymentservice_ab05c71727b84389efacb94cfe65a299'] = 'Taksit Vade Farkı '; diff --git a/vendor/.htaccess b/vendor/.htaccess new file mode 100755 index 0000000..3de9e40 --- /dev/null +++ b/vendor/.htaccess @@ -0,0 +1,10 @@ +# Apache 2.2 + + Order deny,allow + Deny from all + + +# Apache 2.4 + + Require all denied + diff --git a/views/css/style.css b/views/css/style.css new file mode 100755 index 0000000..ca282e5 --- /dev/null +++ b/views/css/style.css @@ -0,0 +1,89 @@ +#craftgate-checkout-form-iframe-container { + min-height: 858px; +} + +#craftgate-checkout-form-iframe-container iframe { + border: 0; + width: 100%; + height: 100%; +} + +#craftgate-one-page-checkout-form-iframe-container { + min-height: 640px; +} + +#craftgate-one-page-checkout-form-iframe-container iframe { + border: 0; + width: 100%; + height: 100%; +} + +.disabled-by-craftgate { + cursor: not-allowed; + opacity: .6; +} + +.disabled-by-craftgate .btn { + cursor: not-allowed; + opacity: .65; +} + +[type=reset], +#loading-spinner { + text-align: center; +} + +.display-none { + display: none; +} + +.lds-facebook { + color: #333fdd; +} + +.lds-facebook, +.lds-facebook div { + box-sizing: border-box; +} + +.lds-facebook { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} + +.lds-facebook div { + display: inline-block; + position: absolute; + left: 8px; + width: 16px; + background: currentColor; + animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; +} + +.lds-facebook div:nth-child(1) { + left: 8px; + animation-delay: -0.24s; +} + +.lds-facebook div:nth-child(2) { + left: 32px; + animation-delay: -0.12s; +} + +.lds-facebook div:nth-child(3) { + left: 56px; + animation-delay: 0s; +} + +@keyframes lds-facebook { + 0% { + top: 8px; + height: 64px; + } + 50%, 100% { + top: 24px; + height: 32px; + } +} diff --git a/views/img/cards/amex.svg b/views/img/cards/amex.svg new file mode 100644 index 0000000..3fef956 --- /dev/null +++ b/views/img/cards/amex.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/views/img/cards/maestro.svg b/views/img/cards/maestro.svg new file mode 100644 index 0000000..316d396 --- /dev/null +++ b/views/img/cards/maestro.svg @@ -0,0 +1,33 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/views/img/cards/mastercard.svg b/views/img/cards/mastercard.svg new file mode 100644 index 0000000..623f1fb --- /dev/null +++ b/views/img/cards/mastercard.svg @@ -0,0 +1,91 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/views/img/cards/troy.svg b/views/img/cards/troy.svg new file mode 100644 index 0000000..6fe84fb --- /dev/null +++ b/views/img/cards/troy.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/views/img/cards/visa-electron.svg b/views/img/cards/visa-electron.svg new file mode 100644 index 0000000..9f31f62 --- /dev/null +++ b/views/img/cards/visa-electron.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/views/img/cards/visa.svg b/views/img/cards/visa.svg new file mode 100644 index 0000000..e521bf8 --- /dev/null +++ b/views/img/cards/visa.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/views/img/index.php b/views/img/index.php new file mode 100755 index 0000000..ad177a4 --- /dev/null +++ b/views/img/index.php @@ -0,0 +1,10 @@ + +
+
+
+ +
+ + + + + + + +

{l s='Enable or disable the Craftgate payment option on the checkout page.' mod='craftgate_payment_orchestration'}

+
+ + +
+ + + + + + + +

{l s='Enable or disable one-page checkout for the Craftgate payment option.' mod='craftgate_payment_orchestration'}

+
+ + +
+ +

{l s='This is the title of the payment option shown to the customer during checkout. To use default, keep this field empty.' mod='craftgate_payment_orchestration'}

+
+ + +
+ +

{l s='Provide a brief description for the payment option that will appear on the checkout page. To use default, keep this field empty.' mod='craftgate_payment_orchestration'}

+
+
+ +
+ +
+ +

{l s='Enter the live API key provided by Craftgate.' mod='craftgate_payment_orchestration'}

+
+ + +
+ +

{l s='Enter the live secret key provided by Craftgate.' mod='craftgate_payment_orchestration'}

+
+
+ +
+ +
+ +

{l s='Enter the sandbox API key for testing.' mod='craftgate_payment_orchestration'}

+
+ + +
+ +

{l s='Enter the sandbox secret key for testing.' mod='craftgate_payment_orchestration'}

+
+ +
+ +
+ + + + + + + +

{l s='Enable or disable sandbox mode for testing the integration.' mod='craftgate_payment_orchestration'}

+
+
+
+ +
+ +
+ +

{l s='Example: hideFooter=true&hideHeader=true' mod='craftgate_payment_orchestration'}

+
+
+
+ +
+ +

{l s='The URL that payment results will be sent to on the server-side. You should enter this webhook address to Craftgate Merchant Panel to get webhook request.' mod='craftgate_payment_orchestration'}

+
+
+ + +
+
+ diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php new file mode 100755 index 0000000..ad177a4 --- /dev/null +++ b/views/templates/admin/index.php @@ -0,0 +1,10 @@ + +

+ {$error|escape:'htmlall':'UTF-8'} +

+
+
+ +
+
+ {/if} +{/block} + +{block name="footer"} + +{/block} \ No newline at end of file diff --git a/views/templates/front/checkoutFormCallback.tpl b/views/templates/front/checkoutFormCallback.tpl new file mode 100755 index 0000000..f1b475c --- /dev/null +++ b/views/templates/front/checkoutFormCallback.tpl @@ -0,0 +1,77 @@ + +
+
+
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/views/templates/front/checkoutFormPaymentOption.tpl b/views/templates/front/checkoutFormPaymentOption.tpl new file mode 100755 index 0000000..dca8674 --- /dev/null +++ b/views/templates/front/checkoutFormPaymentOption.tpl @@ -0,0 +1,39 @@ + +
+

{l s=$description mod='craftgate_payment_orchestration'}

+ +
+ + + + + + +
+
diff --git a/views/templates/front/index.php b/views/templates/front/index.php new file mode 100755 index 0000000..cb284c2 --- /dev/null +++ b/views/templates/front/index.php @@ -0,0 +1,10 @@ + +
+
+
+
+
+ +
+ +
+
+ + + diff --git a/views/templates/hook/displayAdminOrderMainBottom.tpl b/views/templates/hook/displayAdminOrderMainBottom.tpl new file mode 100755 index 0000000..f068c9e --- /dev/null +++ b/views/templates/hook/displayAdminOrderMainBottom.tpl @@ -0,0 +1,38 @@ +
+
+
+

+ {$moduleDisplayName} + {$moduleDisplayName} +

+
+
+
+

{l s='Payment Details' mod='craftgate_payment_orchestration'}

+

{l s='Payment ID' mod='craftgate_payment_orchestration'}: {$craftgatePayment->payment_id}

+

{l s='Craftgate Order ID' mod='craftgate_payment_orchestration'}: {$craftgatePayment->getMetaData()->orderId}

+

{l s='Paid Price' mod='craftgate_payment_orchestration'} + : {displayPrice currency=$currency price=$craftgatePayment->getMetaData()->paidPrice} +

+

{l s='Installment' mod='craftgate_payment_orchestration'}: {$craftgatePayment->getMetaData()->installment}

+ + {if $craftgatePayment->getMetaData()->installment != 1} +

{l s='Installment Fee' mod='craftgate_payment_orchestration'} + : {displayPrice price=$craftgatePayment->getMetaData()->installmentFee}

+ {/if} + + {if $craftgatePayment->getMetaData()->environment == 'Sandbox'} +

{l s='Payment Detail URL' mod='craftgate_payment_orchestration'}: https://sandbox-panel.craftgate.io/payments/{$craftgatePayment->payment_id}

+ {else} +

{l s='Payment Detail URL' mod='craftgate_payment_orchestration'}: https://panel.craftgate.io/payments/{$craftgatePayment->payment_id} +

+ {/if} +
+ +
+
+
diff --git a/views/templates/hook/embeddedCheckoutFormBinary.tpl b/views/templates/hook/embeddedCheckoutFormBinary.tpl new file mode 100644 index 0000000..ee2dd1e --- /dev/null +++ b/views/templates/hook/embeddedCheckoutFormBinary.tpl @@ -0,0 +1,33 @@ +
+ +
+ + diff --git a/views/templates/hook/index.php b/views/templates/hook/index.php new file mode 100755 index 0000000..ad177a4 --- /dev/null +++ b/views/templates/hook/index.php @@ -0,0 +1,10 @@ +