So, horray - I'm attempting to create a new custom Payment Gateway. It's designed to do auth/capture via a third-party API, but does NOT need to redirect to the third-party site.
From my understanding, when an order is placed/finalized in Magento, and the gateway is set to "Authorize and Capture", it should be firing off the 'capture' method from the gateway model. Currently, it's not doing this.
Of course, if I specifically capture from the Admin Order view, it'll attempt to capture, but this needs to happen immediately upon checkout (and again, it's my understanding that it already should).
In my gateway Model, I have the following (truncated for readability):
<?php
class Example_Gateway_Model_Payment extends Mage_Payment_Model_Method_Cc
{
protected $_code = 'example';
protected $_isGateway = true;
protected $_canAuthorize = true;
protected $_canCapture = true;
protected $_canUseInternal = true;
protected $_canUseCheckout = true;
// This is an empty block class that extends Mage_Payment_Block_Form_Cc
protected $_formBlockType = 'example/form_example';
public function authorize(Varien_Object $payment, $amount)
{
Mage::log('Authorizing!');
}
public function capture(Varien_Object $payment, $amount)
{
Mage::log('** Capturing **');
// Third-party API stuff would go here, with exceptions being thrown if the gateway determines they've provided an invalid card, etc.
}
public function assignData($data)
{
Mage::log('Assigning Data');
}
}
This Payment model itself definitely works - I get logging output for assignData() and validate(), as well as __construct() if I add it. But no matter what I do, neither the capture or authorize methods fire when actually placing the order.
My config.xml reads somewhat like this:
<?xml version="1.0"?>
<config>
<modules>
<Example_Gateway>
<version>0.0.5</version>
</Example_Gateway>
</modules>
<global>
<blocks>
<gateway>
<class>Example_Gateway_Block</class>
</gateway>
</blocks>
<models>
<gateway>
<class>Example_Gateway_Model</class>
</gateway>
</models>
<helpers>
<gateway>
<class>Example_Gateway_Helper</class>
</gateway>
</helpers>
</global>
<frontend>
<!-- Snip.. Nothing special here -->
</frontend>
<default>
<payment>
<gateway>
<sort_order>0</sort_order>
<model>gateway/payment</model>
<enabled>1</enabled>
<order_staus>processing</order_status>
<payment_action>authorize_capture</payment_action>
<cctypes>VI,MC,AE,DI</cctypes>
<useccv>1</useccv>
</gateway>
</payment>
</default>
</config>
I don't believe there's a need for a resource model as I don't need any additional tables; my expectation is that it will simply use sales_flat_order_payment and related tables to store any gateway-related/-provided data (txn id, etc)
Similarly, I'm simply extending the default CC block to get the standard payment form.
What am I missing? It has to be something small and simple that I'm overlooking.
Thanks in advance!
UPDATE:
So far, I have implemented a workaround that uses an observer to the checkout_type_onepage_save_order event that calls the capture() method manually - but I'm pretty sure this is not the right way to go.
I'm not wrong in thinking that Magento should automatically call capture() upon the initial order save, if the gateway is set to authorize_capture, right..?
Solved! You need this:
protected $_isInitializeNeeded = false;
I have NO IDEA why this is necessary, but at this point I've given up trying to figure out the why of magento in favor of actually getting things done. I had the exact same problem as you, and when I traced it through the source code I found that Payment.php was not calling _authorize when isInitializeNeeded returned true. So, stick that line in your model, and it will work.
I think the method should be : "authorize_capture" and not "capture" as stated in the config
<payment_action>authorize_capture</payment_action>
like that:
public function authorize_capture(Varien_Object $payment, $amount)
{
Mage::log('** Capturing **');
// Third-party API stuff would go here, with exceptions being thrown if the gateway determines they've provided an invalid card, etc.
}
i had a similar problem that the "authorize" method was not fired at all because "authorize_action" was empty. I was able to solve this by hardcoding it in the method itself. "getConfigPaymentAction" is called to get the authorize method.
public function getConfigPaymentAction() {
return 'authorize';
}
Well, I used an observer to manually call the capture method.
Not the most elegant solution, but it works well enough.
Related
I need help. I am creating a custom payment method for Magento 2. In the plugin, user needs to enter the keys. While saving the config, I want to check if the keys entered by the user are correct or not. If the keys are incorrect, I want to show a message informing user about the issue.
I have googled about it and I thought I have found the event admin_system_config_changed_section_{sectionname}. I implemented it, cleared the cache and recompiled the code but when I save the config data for the payment method plugin, it does not trigger the class. I looked into Magento documentation. I could not find the exact full event name for it. These are the full event name I tried:
admin_system_config_changed_section_payment_methods
admin_system_config_changed_section_paymentmethods
admin_system_config_changed_section_sales
MyPayment/Payment/etc/adminhtml/events.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="admin_system_config_changed_section_payment_methods">
<observer name="my_payment_admin_system_config_changed_section_paymentmethods" instance="MyPayment\Payment\Observer\CustomPaymentMethodConfigObserver" />
</event>
</config>
MyPayment/Payment/Observer/CustomPaymentMethodConfigObserver
<?php
namespace MyPayment\Payment\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
class CustomPaymentMethodConfigObserver implements ObserverInterface
{
public function execute(Observer $observer)
{
// some codes that did not trigger
}
}
Try using a <backend_model>
public function beforeSave()
{
if ($this->getValue() == '') {
throw new \Magento\Framework\Exception\ValidatorException(__($label . ' is required.'));
} else ($this->getValue())) {
//custom validation here
}
$this->setValue($this->getValue());
parent::beforeSave();
}
See
https://github.com/magepal/magento2-preview-checkout-success-page/blob/master/etc/adminhtml/system.xml#L64
https://github.com/magepal/magento2-preview-checkout-success-page/blob/master/Model/Config/Backend/ValidFor.php
I am used magento version 1.8.0.1
I have created sale category but not assign product from backend. I want to display custom collection on that category page with magento default pagination.
I want to display 24 products per page on that category page.actually I have set 16 product per page from admin panel but this setting is for other all categories.
Please help me.
Many Thanks in advance
You need to set page size for a particular category. The Page Size is actually a property of a toolbar block that present in prodouct list block. So basically what you need to do is to set page size to 24 for toolbar block, when your category get loaded.
For this you can use an observer method. In order to implement it, you need to know the id of category. Here I am taking my reference id as 23. Also for this let us set up a custom module, Programmerrkt_PageSizeSetter.
So first let us activate our module
Location : app/etc/modules/Programmerrkt_PageSizeSetter.xml
<config>
<modules>
<Programmerrkt_PageSizeSetter>
<active>true</active>
<codePool>local</codePool>
</Programmerrkt_PageSizeSetter>
</modules>
</config>
Next, Let us configure our module,
Location: app/code/local/Programmerrkt/PageSizeSetter/etc/config.xml
<config>
<modules>
<Programmerrkt_PageSizeSetter>
<version>1.0.0</version>
</Programmerrkt_PageSizeSetter>
</modules>
<frontend>
<events>
<controller_action_layout_generate_blocks_after>
<observers>
<set_page_size_category>
<type>singleton</type>
<class>programmerrkt_pagesizesetter/observer</class>
<method>setPageSizeForCategory</method>
</set_page_size_category>
</observers>
</controller_action_layout_generate_blocks_after>
</events>
</frontend>
<global>
<models>
<programmerrkt_pagesizesetter>
<class>Programmerrkt_PageSizeSetter_Model</class>
</programmerrkt_pagesizesetter>
</models>
</global>
</config>
As you can see, through our module, we are planning to observe an event controller_action_layout_generate_blocks_after. If we need to change some block properties, then this event would be the perfect and coolest event to listen to. Also you can see we are planning to define a custom method setPageSizeForCategory in our observer. At last, we defined model section for our module inside global node. This is because observer is going to define in our model.
So it is the time to define our observer. So let us do that
Location: app/code/local/Programmerrkt/PageSizeSetter/Model/Observer.php
<?php
class Programmerrkt_PageSizeSetter_Model_Observer {
/**
*
* Holds special category Id
*
* #var int
*
*/
protected $_categoryId = 23 ;
/**
*
* Holds page Size
*
* #var int
*
*/
protected $_pageSize = 24 ;
/**
*
* Use to set Page Size
*
* #param Varint Object | $observer
*
*
*/
public function setPageSizeForCategory($observer){
$controller = $observer->getAction();
$fullActionName = $controller->getFullActionName();
$id = (int)$controller->getRequest()->getParam('id');
//check whether current page is correspond to our special category. If not, returns
if($fullActionName == "catalog_category_view" && $id == $this->_categoryId)
{
//check whether toolbar block exist or not
$toolbar = $controller->getLayout()->getBlock('product_list_toolbar');
if($toolbar)
{
//sets page size to corresponding list mode
$listMode = $toolbar->getCurrentMode();
$toolbar = $toolbar->addPagerLimit($listMode , $this->_pageSize);
}
}
return;
}
}
So here in setPageSizeForCategory method, we ensures that we are standing in particular category page. If it is not, the control returns. If the page is the category that we are talking about, we will set page size to the toolbar block of that category.
The method that we use here to set page size is addPagerLimit. It has 3 parameters to pass. First one is list mode. It will be either grid/list. We are passing the current mode of toolbar block to this method, so that we dont need to really worry on list mode confusion. Next parameter is the size of page. Last one is label. Here we dont want to pass any labels. Hence not using it.
You can also see that our observer class has two properties $_categoryId and $_pageSize. First one holds the id of our special category and second one holds page size values. So you need to set these two properties according to your need. This will allow us setPageSizeForCategory untouched.
Drawback :- There is a small drawback for this approach. The event we are observing here will trigger in every page load. Since the scope our module is limitted to a particular category, most of the time our module will return without doing anything. I feel it as awkward and that's why I mentioned it here. But... common ! this is also a solution. Isn't it?
If anyone solve this issue in better way, let me know and please share it. :)
Hope it helps
You can use XML like this:
<reference name="product_list_toolbar">
<action method="setDefaultGridPerPage"><limit>24</limit></action>
</reference>
In either a local.xml file or in the Category Custom Design field.
hope this will sure help you.
How can I tell if the current request is for a backend or frontend page? This check will be done inside an observer, so I do have access to the request object if that helps.
I considered checking Mage::getSingleton('admin/session')->getUser(), but I don't think that's a very reliable method. I'm hoping for a better solution.
This is one of those areas where there's no good answer. Magento itself doesn't provide an explicit method/API for this information, so with any solution you'll need to examine the environment and infer things.
I was using
Mage::app()->getStore()->isAdmin()
for a while, but it turns out there are certain admin pages (the Magento Connect Package manager) where this isn't true. For some reason this page explicitly sets the store id to be 1, which makes isAdmin return as false.
#File: app/code/core/Mage/Connect/controllers/Adminhtml/Extension/CustomController.php
public function indexAction()
{
$this->_title($this->__('System'))
->_title($this->__('Magento Connect'))
->_title($this->__('Package Extensions'));
Mage::app()->getStore()->setStoreId(1);
$this->_forward('edit');
}
There may be other pages with this behavior,
Another good bet is to check the "area" property of the design package.
This seems less likely to be overridden for a page that's in the admin, since the area impacts the path to the admin areas design templates and layout XML files.
Regardless of what you choose to infer from the environment, create new Magento module, and add a helper class to it
class Namespace_Modulename_Helper_Isadmin extends Mage_Core_Helper_Abstract
{
public function isAdmin()
{
if(Mage::app()->getStore()->isAdmin())
{
return true;
}
if(Mage::getDesign()->getArea() == 'adminhtml')
{
return true;
}
return false;
}
}
and then whenever you need to check if you're in the admin, use this helper
if( Mage::helper('modulename/isadmin')->isAdmin() )
{
//do the thing about the admin thing
}
This way, when/if you discover holes in your admin checking logic, you can correct everything in one centralized place.
If you're able to use an observer, you can limit it to the 'adminhtml' event area.
<config>
...
<adminhtml>
<events>
<core_block_abstract_prepare_layout_after>
<observers>
<mynamespace_mymodule_html_before>
<type>singleton</type>
<class>mynamespace_mymodule/observer</class>
<method>adminPrepareLayoutBefore</method>
</mynamespace_mymodule_html_before>
</observers>
</core_block_abstract_prepare_layout_after>
</events>
</adminhtml>
</config>
Have a look at the methods inside Mage/Core/Model/Store.php you'll want to use:
Mage::app()->getStore()->isAdmin()
In conjunction with
Mage::getDesign()->getArea() == 'adminhtml'
To act as a fallback where the store ID isn't set as you expect (Magento connect etc.)
I like beep logic's answer - it makes sense in the context of observers. I also like Alan's point that there is no way to know the admin state in all contexts, which is a function of "the admin" being a state which is entered after the app and front controller are initialized.
Magento's admin state is effectively created from the control dispatching to an admin action controller; see Mage_Adminhtml_Controller_Action::preDispatch(). This is the method which fires the adminhtml_controller_action_predispatch_start event, which is consumed by Mage_Adminhtml_Model_Observer::bindStore(), which is where the admin store is initially "set". In fact, the observer configuration areas (adminhtml vs frontend) "works" because of the main action controller class - see Mage_Core_Controller_Varien_Action::preDispatch(), specifically Mage::app()->loadArea($this->getLayout()->getArea()); - just note that the layout object has its area information set in the adminhtml predispatch.
No matter how you slice it, the admin behavior on which we rely in so many contexts - even something as high-level as the event observer system - relies on the command control structure.
<config>
<!-- ... -->
<adminhtml>
<events>
<core_block_abstract_prepare_layout_after>
<observers>
<mynamespace_mymodule_html_after>
<type>singleton</type>
<class>mynamespace_mymodule/observer</class>
<method>adminPrepareLayoutAfter</method>
</mynamespace_mymodule_html_after>
</observers>
</core_block_abstract_prepare_layout_after>
</events>
</adminhtml>
<frontend>
<events>
<core_block_abstract_prepare_layout_after>
<observers>
<mynamespace_mymodule_html_after>
<type>singleton</type>
<class>mynamespace_mymodule/observer</class>
<method>frontendPrepareLayoutAfter</method>
</mynamespace_mymodule_html_after>
</observers>
</core_block_abstract_prepare_layout_after>
</events>
</frontend>
</config>
In your observer definition:
class Mynamepace_Mymodule_Model_Observer
{
public function adminPrepareLayoutAfter()
{
$this->_prepareLayoutAfter('admin');
}
public function frontendPrepareLayoutAfter()
{
$this->_prepareLayoutAfter('frontend');
}
protected function _prepareLayoutAfter($area)
{
switch($area){
case 'admin':
// do admin things
break;
case 'frontend':
// do frontend things
break;
default:
// i'm a moron
}
}
}
tl;dr: Use an observer, even use the same observer model, but pass in the context by specifying a different calling method.
I have also included some example code using the config from beeplogic's answer as a starting point.
Whether I'm wrong or not (but I've tested it), some events (like controller_front_init_before) can only be overwritten inside global node. As a result, this override will affect both frontend and backend.
Then come Alan's and benmark's solution to the rescue to specify if you want to apply the observer on frontend only or backend only.
I would like to make a portion of a page available for getting via AJAX. I have in mind to use a URL parameter, bare, that would tell Magento to present a page with a different template applied to the root block. The bare template looks like this:
<?php echo $this->getChildHtml('content'); ?>
That's it! The idea is that a JavaScript method could grab just the content of another page and insert it into the DOM where appropriate. (I don't want this to be possible with just any page – only pages that have been marked to do so in layout xml.)
I've read elsewhere that I should avoid conditional layout xml. The only other approach I can think of is to override the Page/Html block itself, creating a modified setTemplate method, like below. Instinctively, I'm concerned about overriding such a core part of Magento.
public function setTemplate($template, $bareTemplate='')
{
$bareMode = Mage::app()->getRequest()->getParam('bare');
$targetTemplate = (!empty($bareTemplate) && $bareMode === '1') ? $bareTemplate : $template;
return parent::setTemplate($targetTemplate);
}
What better approaches haven't I thought of?
The key to getting what you want is removing root as an output block, replacing it with content. Output blocks are just entry points for renderLayout();
To do this in Magento without include-path-hacking Mage_Core_Controller_Varien_Action, observe the controller_action_layout_render_before_$this->getFullActionName() scoped events which are fired in the base action controller class (ref Mage_Core_Controller_Varien_Action::renderLayout() method).
First configure your model class group and frontend event observer. You'll need to determine the full action name of any route that needs this logic. See Mage_Core_Controller_Varien_Action::renderLayout(). Example config below.
<?xml version="1.0"?>
<config>
<global>
<models>
<your_classgroup>
<class>Your_Classgroup_Model</class>
</your_classgroup>
</models>
</global>
<frontend>
<events>
<controller_action_layout_render_before_FULL_ACTION_NAME...>
<observers>
<your_observer_config>
<type>model</type>
<class>your_classgroup/observer</class>
<method>makeContentBlockTheOutputBlock</method>
</your_observer_config>
</observers>
</controller_action_layout_render_before_FULL_ACTION_NAME...>
</events>
</frontend>
</config>
The event observer logic is easy. Do this:
public function makeContentBlockTheOutputBlock($observer)
{
//Edit: action not passed in to this event; passed in generic generate_blocks event
if( Mage::app()->getRequest()->getParam('bare') )
{
Mage::app()->getLayout()->removeOutputBlock('root')->addOutputBlock('content');
}
}
HTH.
I would like to be able to run some functionality with a module that I am building whenever a customer registers an account, but I can't seem to find any event that is fired upon a new customer registration.
Does anybody know of an event that is dispatched for that?
Whenever I'm looking for an event, I'll temporarily edit the Mage.php file to output all the events for a particular request.
File: app/Mage.php
public static function dispatchEvent($name, array $data = array())
{
Mage::log('Event: ' . $name); //not using Mage::log, as
//file_put_contents('/tmp/test.log','Dispatching '. $name. "\n",FILE_APPEND); //poor man's log
Varien_Profiler::start('DISPATCH EVENT:'.$name);
$result = self::app()->dispatchEvent($name, $data);
#$result = self::registry('events')->dispatch($name, $data);
Varien_Profiler::stop('DISPATCH EVENT:'.$name);
return $result;
}
and then perform whatever action it is I'm trying to hook into. Magento events are logically named, so scanning/sorting through the resulting logs usually reveals what I'm after.
customer_register_success is what you are looking for:
<config>
<frontend>
<events>
<customer_register_success>
<observers>
<your_module>
<type>singleton</type>
<class>your_module/observer</class>
<method>yourMethod</method>
</your_module>
</observers>
</customer_register_success>
</events>
</frontend>
</config>
I discovered how to achieve this today. It involves using one of the generic controller events. This node in the config.xml will hook into the right event:
<events>
....
<controller_action_postdispatch_customer_account_createPost>
<observers>
<your_module_here>...etc
The controller_action_postdispatch_REQUESTPATH event is thrown for every controller that extends Mage_Core_Controller_Front_Action (which is basically all of them) which makes it very easy to target. Ditto for controller_action_predispatch_REQUESTPATH.
I'm a bit surprised that none of the answers if solving the case completely.
Customer create can happen
by url customer/account/create
by register in checkout
I solved it by tracking two events:
config.xml
<events>
<controller_action_postdispatch_customer_account_createpost>
<observers>
<myextensionkey_create_account>
<class>myextensionkey/observer</class>
<method>createAccount</method>
<type>singleton</type>
</myextensionkey_create_account>
</observers>
</controller_action_postdispatch_customer_account_createpost>
<checkout_submit_all_after>
<observers>
<myextensionkey_checkout_create_account>
<class>myextensionkey/observer</class>
<method>createAccountCheckout</method>
<type>singleton</type>
</myextensionkey_checkout_create_account>
</observers>
</checkout_submit_all_after>
</events>
and in Observer.php
public function createAccount($observer) { ... } //Nothing special here
public function createAccountCheckout($observer) {
if ($observer->getQuote()->getData('checkout_method') != Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER) {
return;
}
Edit: I changed
<controller_action_predispatch_customer_account_createpost>
into
<controller_action_postdispatch_customer_account_createpost>
because on predispatch the account is not created yet. There can be an error for example if the email already exists in the shop.
There isn't a direct event for this, but you could use the customer_save_commit_after event. This event also guarantees you that the customer is save in the shop's database. The problem with this event is that is triggered twice. Bellow is an hack that allows you to use this event - the observer function is listed:
public function customer_save_commit_after($p_oObserver) {
$l_oCustomer = $p_oObserver->getCustomer();
if ($l_oCustomer->isObjectNew() && !$l_oCustomer->getMyCustomKeyForIsAlreadyProcessed()) {
$l_oCustomer->setMyCustomKeyForIsAlreadyProcessed(true);
// new customer
}
else {
// existing customer
}
return false;
}
Hope this helps someone!
You have to consider also when the user register on-the-fly on checkout: a Register on chekout. Thinking on this case, you can catch the "checkout_type_onepage_save_order_after" event with your own Observer class, and then this code...
if($observer->getEvent()->getQuote()->getCheckoutMethod(true) == Mage_Sales_Model_Quote::CHECKOUT_METHOD_REGISTER){
(...)
}
Anybody may say: Mage_Sales_Model_Quote->getCheckoutMethod() is deprecated since 1.4!!,but:
If we call the ortodox method Mage_Checkout_Model_Type_Onepage->getCheckoutMethod(), waiting for something as "METHOD_REGISTER" this is executed:
if ($this->getCustomerSession()->isLoggedIn()) {
return self::METHOD_CUSTOMER;
}
... "METHOD_CUSTOMER" is the name for a checkout with an already registrated user, not our case.... but yes!, because....
...the registration operation is perfomed before "checkout_type_onepage_save_order_after" event. Then we a have a METHOD_CUSTOMER now. Ups, something inconsistent?
Fortunatly, the Quote remains with the original value: CHECKOUT_METHOD_REGISTER
Any other idea for the registration on checkout?
You can try customer_save_after, the only thing that the registration sends this event twice
Actually there are customer_save_after and customer_save_before (magento 1.5)
If you want to modify on-the-fly some data after form post, pick customer_save_before, change the data you want and that's all (the save action come after, so your change will be taken into account).
$customer->save() just doesn't work in customer_save_after. (fatal error) Use this observer to run a code after customer creation which are NOT related to customer data.
Hope that helps!
customer_register_success
adminhtml_customer_save_after
these two are the default events when a customer is inserted into the database....
first event fires in frontend when a user registers and second event fires in the backend when a customer is created through admin panel...i hope you know how to register an observer for an event...hope this will help you...
I was looking of the same thing. I believe the event is customer_register_success.
You can find a link for all events at:
http://www.nicksays.co.uk/magento_events_cheat_sheet/
I found the event checkout_submit_all_after.
<checkout_submit_all_after>
<observers>
<my_example>
<class>my_example/observer</class>
<method>customerRegistered</method>
</my_example>
</observers>
</checkout_submit_all_after>
In my Observer.php I get the quote object that is passed in.
public function customerRegistered (Varien_Event_Observer $observer) {
$quote = $observer->getQuote();
$checkout_method = $quote->getData();
$checkout_method = $checkout_method['checkout_method'];
if ($checkout_method == Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER) {
}
Do not use $quote->getCheckoutMethod() it gives you login_in instead. Not sure why. Hope this helps.
I've discovered that the customer_login and customer_session_init events are both thrown on account create. You could register a listener for either and then check to see the create date on the account?
The answer to this question is that there isn't an event for that.
You can use the customer_register_success event. It is triggered after the customer is succesfully created. Here is the link of event cheat sheets. Hope it also helps you.
http://www.nicksays.co.uk/magento-events-cheat-sheet-1-7/
customer_register_success is the event dispatched after successful customer registration.
Here's from the code from Mage/Customer/controllers/AccountController.php::454 in magento 1.8:
protected function _dispatchRegisterSuccess($customer)
{
Mage::dispatchEvent('customer_register_success',
array('account_controller' => $this, 'customer' => $customer)
);
}
customer_save_after is the event which gets called after a new customer registration.
Read about all the events here:
http://www.magentocommerce.com/wiki/5_-_modules_and_development/reference/events
event name:customer_registration_is_allowed
I'm not sure if this is you want,you can write a observer to test