Jump to content

"Cart cannot be loaded or an order has already been placed using this cart" error with custom payment module


Recommended Posts

I try to create very simple payment module which writes a couple strings in payment detailes and administartor manually should make and send pay invoice to customer. I generated module here https://validator.prestashop.com/generator# and added some modifications.

manualpayments.php

<?php
/**
* 2007-2025 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
*  @author    PrestaShop SA <contact@prestashop.com>
*  @copyright 2007-2025 PrestaShop SA
*  @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
*  International Registered Trademark & Property of PrestaShop SA
*/

if (!defined('_PS_VERSION_')) {
    exit;
}

class ManualPayment extends PaymentModule
{
    protected $config_form = false;

    public function __construct()
    {
        $this->name = 'manualpayment';
        $this->tab = 'payments_gateways';
        $this->version = '1.0.0';
        $this->author = 'artem78';
        $this->need_instance = 0;

        /**
         * Set $this->bootstrap to true if your module is compliant with bootstrap (PrestaShop 1.6)
         */
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Manual Payment');
        $this->description = $this->l('');

        /*$this->limited_countries = array('FR');*/

        /*$this->limited_currencies = array('EUR');*/

        $this->ps_versions_compliancy = array('min' => '8.1', 'max' => _PS_VERSION_);
    }

    /**
     * Don't forget to create update methods if needed:
     * http://doc.prestashop.com/display/PS16/Enabling+the+Auto-Update
     */
    public function install()
    {
        if (extension_loaded('curl') == false)
        {
            $this->_errors[] = $this->l('You have to enable the cURL extension on your server to install this module');
            return false;
        }

        $iso_code = Country::getIsoById(Configuration::get('PS_COUNTRY_DEFAULT'));

        /*if (in_array($iso_code, $this->limited_countries) == false)
        {
            $this->_errors[] = $this->l('This module is not available in your country');
            return false;
        }*/

        Configuration::updateValue('MANUALPAYMENT_LIVE_MODE', false);

        return parent::install() &&
            $this->registerHook('header') &&
            $this->registerHook('displayBackOfficeHeader') &&
            $this->registerHook('paymentOptions');
    }

    public function uninstall()
    {
        Configuration::deleteByName('MANUALPAYMENT_LIVE_MODE');

        return parent::uninstall();
    }

    /**
     * Load the configuration form
     */
    public function getContent()
    {
        /**
         * If values have been submitted in the form, process.
         */
        if (((bool)Tools::isSubmit('submitManualPaymentModule')) == true) {
            $this->postProcess();
        }

        $this->context->smarty->assign('module_dir', $this->_path);

        $output = $this->context->smarty->fetch($this->local_path.'views/templates/admin/configure.tpl');

        return $output.$this->renderForm();
    }

    /**
     * Create the form that will be displayed in the configuration of your module.
     */
    protected function renderForm()
    {
        $helper = new HelperForm();

        $helper->show_toolbar = false;
        $helper->table = $this->table;
        $helper->module = $this;
        $helper->default_form_language = $this->context->language->id;
        $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0);

        $helper->identifier = $this->identifier;
        $helper->submit_action = 'submitManualPaymentModule';
        $helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false)
            .'&configure='.$this->name.'&tab_module='.$this->tab.'&module_name='.$this->name;
        $helper->token = Tools::getAdminTokenLite('AdminModules');

        $helper->tpl_vars = array(
            'fields_value' => $this->getConfigFormValues(), /* Add values for your inputs */
            'languages' => $this->context->controller->getLanguages(),
            'id_language' => $this->context->language->id,
        );

        return $helper->generateForm(array($this->getConfigForm()));
    }

    /**
     * Create the structure of your form.
     */
    protected function getConfigForm()
    {
        return array(
            'form' => array(
                'legend' => array(
                'title' => $this->l('Settings'),
                'icon' => 'icon-cogs',
                ),
                'input' => array(
                    array(
                        'type' => 'switch',
                        'label' => $this->l('Live mode'),
                        'name' => 'MANUALPAYMENT_LIVE_MODE',
                        'is_bool' => true,
                        'desc' => $this->l('Use this module in live mode'),
                        'values' => array(
                            array(
                                'id' => 'active_on',
                                'value' => true,
                                'label' => $this->l('Enabled')
                            ),
                            array(
                                'id' => 'active_off',
                                'value' => false,
                                'label' => $this->l('Disabled')
                            )
                        ),
                    ),
                    array(
                        'col' => 3,
                        'type' => 'text',
                        'prefix' => '<i class="icon icon-envelope"></i>',
                        'desc' => $this->l('Enter a valid email address'),
                        'name' => 'MANUALPAYMENT_ACCOUNT_EMAIL',
                        'label' => $this->l('Email'),
                    ),
                    array(
                        'type' => 'password',
                        'name' => 'MANUALPAYMENT_ACCOUNT_PASSWORD',
                        'label' => $this->l('Password'),
                    ),
                ),
                'submit' => array(
                    'title' => $this->l('Save'),
                ),
            ),
        );
    }

    /**
     * Set values for the inputs.
     */
    protected function getConfigFormValues()
    {
        return array(
            'MANUALPAYMENT_LIVE_MODE' => Configuration::get('MANUALPAYMENT_LIVE_MODE', true),
            'MANUALPAYMENT_ACCOUNT_EMAIL' => Configuration::get('MANUALPAYMENT_ACCOUNT_EMAIL', 'contact@prestashop.com'),
            'MANUALPAYMENT_ACCOUNT_PASSWORD' => Configuration::get('MANUALPAYMENT_ACCOUNT_PASSWORD', null),
        );
    }

    /**
     * Save form data.
     */
    protected function postProcess()
    {
        $form_values = $this->getConfigFormValues();

        foreach (array_keys($form_values) as $key) {
            Configuration::updateValue($key, Tools::getValue($key));
        }
    }

    /**
    * Add the CSS & JavaScript files you want to be loaded in the BO.
    */
    public function hookDisplayBackOfficeHeader()
    {
        if (Tools::getValue('configure') == $this->name) {
            $this->context->controller->addJS($this->_path.'views/js/back.js');
            $this->context->controller->addCSS($this->_path.'views/css/back.css');
        }
    }

    /**
     * Add the CSS & JavaScript files you want to be added on the FO.
     */
    public function hookHeader()
    {
        $this->context->controller->addJS($this->_path.'/views/js/front.js');
        $this->context->controller->addCSS($this->_path.'/views/css/front.css');
    }

    /**
     * Return payment options available for PS 1.7+
     *
     * @param array Hook parameters
     *
     * @return array|null
     */
    public function hookPaymentOptions($params)
    {
        if (!$this->active) {
            return;
        }
        if (!$this->checkCurrency($params['cart'])) {
            return;
        }
        $option1 = new \PrestaShop\PrestaShop\Core\Payment\PaymentOption();
        $option1->setCallToActionText($this->l('PayPal invoice'))
            ->setAction($this->context->link->getModuleLink($this->name, 'validation', array(), true));
        $option1->setAdditionalInformation('After order checking you will recieve invoice for payment from PayPal. You can pay it with your PayPal account or card.<br/><br/>More info: <a href="https://www.paypal.com/us/cshelp/article/how-do-i-pay-a-money-request-or-invoice-help316" target="_blank">https://www.paypal.com/us/cshelp/article/how-do-i-pay-a-money-request-or-invoice-help316</a>');

        return [
            $option1
        ];
    }

    public function checkCurrency($cart)
    {
        $currency_order = new Currency($cart->id_currency);
        $currencies_module = $this->getCurrency($cart->id_currency);
        if (is_array($currencies_module)) {
            foreach ($currencies_module as $currency_module) {
                if ($currency_order->id == $currency_module['id_currency']) {
                    return true;
                }
            }
        }
        return false;
    }
}

When I press "Place order" button error fails: "Cart cannot be loaded or an order has already been placed using this cart".

 

I think some changes needed to /controllers/front/validation.php, but I don't understand what is it for and how it works. Seems there:

public function postProcess()

        /**
         * Since it is an example, we choose sample data,
         * You'll have to get the correct values :)
         */
        $cart_id = 1;
        $customer_id = 1;
        $amount = 100.00;

I use prestashop v8.2.0.

Can anybody helps me? Thanks.

manualpayment.zipFetching info...

Link to comment
Share on other sites

Hello

As the user selects your module for payment on the order page and then clicks the checkout button, it is redirected to the controller. validation.php

You specify it in the parameter 

$offlineOption->selection($this->context->link->get Module Link($this->name, 'validation', ['option' => 'offline'], true));

Link to the documentation https://devdocs.prestashop-project.org/8/modules/payment/#paymentoption-types-and-scenarios

This controller is used to check and create an order.
 

$this->module->validateOrder(
            (int) $this->context->cart->id,
            (int) $this->getOrderState(),
            (float) $this->context->cart->getOrderTotal(true, Cart::BOTH),
            $this->getOptionName(),
            null,
            [
                'transaction_id' => Tools::passwdGen(), // Should be retrieved from your Payment response
            ],
            (int) $this->context->currency->id,
            false,
            $customer->secure_key
        );

Here is a complete example of the module https://github.com/PrestaShop/paymentexample

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...