I am new as Prestashop developer and I am trying to create a PaymentModule. I have got to show my payment method but I can not proceed with purchase because I do not know very well hot it works.
Does anyone know where I should redirect to run my hoodDisplayPaymentReturn method?
I will be very happy if someone explain me the complete navigation map to make a purchase.
Anyway, where can I find a relation between hooks and pages?
To develop a payment module you should use 2 main hooks: payment and paymentReturn.
In payment hook you must display your payment option with the specific information. Check bankwire module to see a working example.
In paymentReturn you should show the payment confirmation (or error) information.
When a user click on your payment option link (displayed in payment hook) you should do some validation and processing. After a payment is done (successfully or not) you must call to your module function validateOrder (this is a function of PaymentModule parent class of your module). After that you should be redirected to a controller that will execute paymentReturn hook.
That is the basic process. I strongly recommend you that check bankwire and other payment modules to understand better how to do your own payment module, because is not an easy task for beginner.
Good luck.
when I have a new module for the payment I rely on the simplest provided by PrestaShop: bankwire.
inside you can find 3 hooks.
HookPayment:
public function hookPayment($params)
{
if (!$this->active)
return;
if (!$this->checkCurrency($params['cart']))
return;
$this->smarty->assign(array(
'this_path' => $this->_path,
'this_path_bw' => $this->_path,
'this_path_ssl' => Tools::getShopDomainSsl(true, true).__PS_BASE_URI__.'modules/'.$this->name.'/'
));
return $this->display(__FILE__, 'payment.tpl');
}
hookDisplayPaymentEU:
public function hookDisplayPaymentEU($params)
{
if (!$this->active)
return;
if (!$this->checkCurrency($params['cart']))
return;
$payment_options = array(
'cta_text' => $this->l('Pay by Bank Wire'),
'logo' => Media::getMediaPath(_PS_MODULE_DIR_.$this->name.'/bankwire.jpg'),
'action' => $this->context->link->getModuleLink($this->name, 'validation', array(), true)
);
return $payment_options;
}
hookPaymentReturn:
public function hookPaymentReturn($params)
{
if (!$this->active)
return;
$state = $params['objOrder']->getCurrentState();
if (in_array($state, array(Configuration::get('PS_OS_BANKWIRE'), Configuration::get('PS_OS_OUTOFSTOCK'), Configuration::get('PS_OS_OUTOFSTOCK_UNPAID'))))
{
$this->smarty->assign(array(
'total_to_pay' => Tools::displayPrice($params['total_to_pay'], $params['currencyObj'], false),
'bankwireDetails' => Tools::nl2br($this->details),
'bankwireAddress' => Tools::nl2br($this->address),
'bankwireOwner' => $this->owner,
'status' => 'ok',
'id_order' => $params['objOrder']->id
));
if (isset($params['objOrder']->reference) && !empty($params['objOrder']->reference))
$this->smarty->assign('reference', $params['objOrder']->reference);
}
else
$this->smarty->assign('status', 'failed');
return $this->display(__FILE__, 'payment_return.tpl');
}
Related
One User has One Profile(obviously). How do I design the logic behind this?
I have two tables namely "tbl_users" and "tbl_profile".
When I open the profile page, I should be able to see the "Create
Profile" button only if the profile does not exist.
Once the profile has been created by the user, "Create Profile"
button should not appear next time.
Please describe me in detail how the columns of both tables are linked and what changes I have to include in both the models and other changes too.
Thank you.
In your controller you could write something like this:
$user = User::findOne(Yii::$app->user->id);
$profile = $user->profile;
return $this->render('view', ['user' => $user, 'profile' => $profile]);
Then in your view
if(null !== $profile) {
/*show your button here. It's going to work once because...*/
} else {
/*...here you could show form to create your profile*/
}
In your User model use code like this
public function getProfile()
{
return $this->hasOne(Profile::className(), ['id' => 'profile_id']);
}
This is just an example to give you idea.
I'm new to prestashop and I'm having major trouble removing the delivery shipping step because I only sell virtual products. I am using prestashop 1.6.1.
I know I have to modify order-carrier.tpl file and have followed several posts here and there but couldn't get it done right.
Does any of you have any actual idea on how to do this ?
Bonjour, here is what i did
Override AdminOrderPreferencesController and add a boolean configuration field to toggle this functionnality
$this->fields_options = array(
[...]
'PS_ORDER_PROCESS_BYPASS_SHIPPING' => array(
'title' => $this->l('Bypass shipping step'),
'hint' => $this->l('Do not show shipping step in order process.'),
'validation' => 'isBool',
'cast' => 'intval',
'type' => 'bool'
)
);
You can now find a toggle button in Backoffice under Preferences > Orders
Override OrderController and add an if in init() method to set the current step to payment step if the controller inits itself on delivery step
public function init()
{
global $orderTotal;
parent::init();
$this->step = (int)Tools::getValue('step');
// HERE IT IS
if((bool)Configuration::get('PS_ORDER_PROCESS_BYPASS_SHIPPING') && $this->step == self::STEP_DELIVERY){
$this->step = self::STEP_PAYMENT;
}
if (!$this->nbProducts) {
$this->step = -1;
}
Also bypass the CGV checking verification on payment step in initContent() method.
If you don't, CGV will never be checked, it will redirect you on delivery step, you will tell him that he is in fact on payment step, he will check for CGV again, he will do the same redirection ... and you are in an infinite loop
case OrderController::STEP_PAYMENT:
$cgv = Tools::getValue('cgv') || $this->context->cookie->check_cgv;
if (
!(bool)Configuration::get('PS_ORDER_PROCESS_BYPASS_SHIPPING') && // HERE IT IS
$is_advanced_payment_api === false && Configuration::get('PS_CONDITIONS')
&& (!Validate::isBool($cgv) || $cgv == false)
) {
Tools::redirect('index.php?controller=order&step=2');
}
Pass the configuration parameter to the view to modify display
$this->context->smarty->assign('bypass_shipping_step', (bool)Configuration::get('PS_ORDER_PROCESS_BYPASS_SHIPPING'));
And in your views, do you styling stuff with some if
In order-steps.tpl you can add an {if not $bypass_shipping_step}...{/if} around the fourth li to hide it, and do something like :
{if $bypass_shipping_step}
<style>
ul.step li{
width:25%;
}
</style>
{/if}
or import a dedicated stylesheet which would be cleaner.
Hope it helped.
In shopping-cart.tpl, remove call to order-carrier.tpl. If you are not using one pagecheckout, in orderController.php, you have to change all redirection to step 2 (shipping method choosing), to redirection step 3 Tools::redirect('index.php?controller=order&step=2'); to Tools::redirect('index.php?controller=order&step=3');
I'm creating a back-office module for Prestashop and have figured out everything except the best way to display the admin page. Currently I'm using the renderView() method to display the content of view.tpl.
I would like to display a table with values and an option to add a new row. Should I just create it in the view.tpl or is there a better way? I've seen the renderForm() method but haven't figured out how it works yet.
The biggest question I have is, how do I submit content back to my controller into a specific method?
ModuleAdminController is meant for managing some kind of records, which are ObjectModels. Defauly page for this controller is a list, then you can edit each record individually or view it's full data (view).
If you want to have a settings page, the best way is to create a getContent() function for your module. Besides that HelperOptions is better than HelperForm for this module configuration page because it automatically laods values. Define the form in this function and above it add one if (Tools::isSubmit('submit'.$this->name)) - Submit button name, then save your values into configuration table. Configuration::set(...).
Of course it is possible to create some sort of settings page in AdminController, but its not meant for that. If you really want to: got to HookCore.php and find exec method. Then add error_log($hook_name) and you will all hooks that are executed when you open/save/close a page/form. Maybe you'll find your hook this way. Bettter way would be to inspect the parent class AdminControllerCore or even ControllerCore. They often have specific function ready to be overriden, where you should save your stuff. They are already a part of execution process, but empty.
Edit: You should take a look at other AdminController classes, they are wuite simple; You only need to define some properties in order for it to work:
public function __construct()
{
// Define associated model
$this->table = 'eqa_category';
$this->className = 'EQACategory';
// Add some record actions
$this->addRowAction('edit');
$this->addRowAction('delete');
// define list columns
$this->fields_list = array(
'id_eqa_category' => array(
'title' => $this->l('ID'),
'align' => 'center',
),
'title' => array(
'title' => $this->l('Title'),
),
);
// Define fields for edit form
$this->fields_form = array(
'input' => array(
array(
'name' => 'title',
'type' => 'text',
'label' => $this->l('Title'),
'desc' => $this->l('Category title.'),
'required' => true,
'lang' => true
),
'submit' => array(
'title' => $this->l('Save'),
)
);
// Call parent constructor
parent::__construct();
}
Other people like to move list and form definitions to actual functions which render them:
public function renderForm()
{
$this->fields_form = array(...);
return parent::renderForm();
}
You don't actually need to do anything else, the controller matches fields to your models, loads them, saves them etc.
Again, the best way to learn about these controller is to look at other AdminControllers.
First let me explain myself. I'm new to Prestashop Development, I've a background using Wordpress. The thing is, I'm trying to hook 'blockcart.php' to my custom hook 'HOOK_SHOPPINGBAG'. I have tried a lot and after a couple of hours I have decided to just ask the question here.
First I have created the Hook inside ps_hook inside the Prestashop Database. This hook is functioning and Prestashop reads it. After that I have added my new hook to FrontController.php, the code is shown below:
public function initContent()
{
$this->process();
if (!isset($this->context->cart))
$this->context->cart = new Cart();
if (!$this->useMobileTheme())
{
// These hooks aren't used for the mobile theme.
// Needed hooks are called in the tpl files.
$this->context->smarty->assign(array(
'HOOK_HEADER' => Hook::exec('displayHeader'),
'HOOK_TOP' => Hook::exec('displayTop'),
'HOOK_SHOPPINGBAG' => Hook::exec('displayShoppingBag'),
'HOOK_LEFT_COLUMN' => ($this->display_column_left ? Hook::exec('displayLeftColumn') : ''),
'HOOK_RIGHT_COLUMN' => ($this->display_column_right ? Hook::exec('displayRightColumn', array('cart' => $this->context->cart)) : ''),
));
}
else
$this->context->smarty->assign('HOOK_MOBILE_HEADER', Hook::exec('displayMobileHeader'));
}
I also have added the Hook to the second bunch of lines inside FrontController.php:
// Call hook before assign of css_files and js_files in order to include correctly all css and javascript files
$this->context->smarty->assign(array(
'HOOK_HEADER' => $hook_header,
'HOOK_TOP' => Hook::exec('displayTop'),
'HOOK_SHOPPINGBAG' => Hook::exec('displayShoppingBag'),
'HOOK_LEFT_COLUMN' => ($this->display_column_left ? Hook::exec('displayLeftColumn') : ''),
'HOOK_RIGHT_COLUMN' => ($this->display_column_right ? Hook::exec('displayRightColumn', array('cart' => $this->context->cart)) : ''),
'HOOK_FOOTER' => Hook::exec('displayFooter')
));
I want to show the Cart inside header.tpl so I have added the Hook to this file:
<div class="shoppingbag">
{$HOOK_SHOPPINGBAG}
</div>
To make sure Blockcart.php can be hooked to my new hook I have added the following line to blockcart.php:
public function hookShoppingBag($params) {
return $this->hookheader($params, 'displayShoppingBag');
}
I've already tried so many things, but when I try to Hook the module to the new Hook it keeps telling me "This modules can't transplant to this hook!"
Maybe I have just made some stupid mistakes, but anyway I hope you guys can help me. Thanks!
I assume you need a custom hook for blockcart module to be placed somewhere.
This is how to achieve it through Presta's custom hook way.
|| $this->registerHook('shoppingBag') == false
Add this to the module's install method.
public function install()
{
if (
parent::install() == false
|| $this->registerHook('top') == false
|| $this->registerHook('header') == false
|| $this->registerHook('actionCartListOverride') == false
|| Configuration::updateValue('PS_BLOCK_CART_AJAX', 1) == false
|| Configuration::updateValue('PS_BLOCK_CART_XSELL_LIMIT', 12) == false)
|| $this->registerHook('shoppingBag') == false
return false;
return true;
}
Create a public method hookShoppingBag like this.
public function hookShoppingBag($params)
{
return $this->hookProductActions($params);
}
To hook it to any tpl use this.
<div class="shoppingbag">
{hook h='presetWishlist'}
</div>
First I declare simple view like this:
$di->set('viewSimple', function() {
$view = new \Phalcon\Mvc\View\Simple();
$view->setViewsDir('../app/views/');
$view->registerEngines(array(
".volt" => 'volt'
));
return $view;
});
then to generate html email, I use it as following:
public function renderHTMLEmail($template_name, $template_params) {
$content = $this->viewSimple->render("emails/$template_name", $template_params);
return $this->viewSimple->render("emails/master", array( 'content' => $content) );
}
My emails are being generated just fine, but whenever I call my renderHTMLEmail function actual page rendering is somehow corrupted and page appears totally blank (I have to use redirect as workaround). This is a mystery to me as main view renderer is completely different object. Can I prevent this?
Or does anybody have recommended method of generating arbitrary pieces of html outside of main view rendering process which would not interfere with it? There is couple of similar questions on SO, but none of solutions work. They either don't generate my email or they corrupt main view.
Found a solution, when registering the "\Phalcon\Mvc\View\Simple" component into the DI container make sure to use a new volt instance otherwise it will join up with the application's \Phalcon\Mvc\View volt instance somehow. This is what lot (https://stackoverflow.com/users/1672165/lot) was suggesting in the comments.
Code sample:
public function getTemplate($name, $params)
{
$parameters = array_merge(array(
'publicUrl' => $this->getDI()->getApp()->url->host . $this->getDI()->getApp()->url->baseUri
), $params);
$app = $this->getDI()->getApp();
$this->getDI()->set('simpleView', function() use ($app) {
$view = new \Phalcon\Mvc\View\Simple();
$view->setViewsDir(APP_DIR . $app->application->viewsDir);
$view->registerEngines(array(
".volt" => function ($view, $di) {
$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
$volt->setOptions(array(
'compiledPath' => APP_DIR . '/cache/volt/',
'compiledSeparator' => '_',
'compiledExtension' => '.compiled'
));
$compiler = $volt->getCompiler();
$compiler->addFunction('is_a', 'is_a');
return $volt;
}
));
return $view;
});
/* #var $simpleView \Phalcon\Mvc\View\Simple */
$simpleView = $this->getDI()->get('simpleView');
foreach ($parameters as $key => $value) {
$simpleView->{$key} = $value;
}
$html = $simpleView->render('emails/' . $name, $parameters);
return $html;
}
Sample was pulled straight from our working app so may need some re-working but should help solve the original issue.
This works for me using \View as the application DI registered view component which renders controller action volt views and the above \View\Simple with fresh volt registration successfully emails and allows the original view with volt to carry on.
This was asked a while ago however i've not seen a working solution anywhere else.
For completeness I've registered a github issue with the findings: https://github.com/phalcon/cphalcon/issues/3096
I recently had to deal with a similar problem, I'm not certain where you're falling short, but one important thing was to use a separate view from the one used by the application – you're already doing this. See if the following works.
protected function renderView($view, $template, array $data = null)
{
return $view
->reset()
->pick('emails/' . $template)
->setVars($data)
->start()
->render(null, null)
->finish()
->getContent();
}
public function renderHTMLEmail($template_name, $template_params)
{
$content = $this->render($template_name, $template_params);
return $this->render('master', array('content' => $content));
}