Creating a Magento Payment Module – Part 3 – Model, Controllers and Events

There are many tutorials out there which show parts of implementing a payment module. None completely build the module. They all stop short of showing how they implemented their nameless payment method.

This is part 3 of the tutorial. Here's part 1 and part 2 in case you missed them.

This post will walk through the model, action controllers and how they interact with the mockpay api.

My/Mockpay/Model/Standard.php

This model extends Mage_Payment_Model_Method_Abstract and if you look at the code of the base model, you'll see it has a lot of protected boolean attributes. These attributes are probed by the various consumers of these payment methods to figure out which features of the payment API our module supports.

Most of the modules I looked at for examples do this by overriding the properties instead of just setting them in the constructor. Our model defines four properties:

  • $_canUseForMultiShipping which enables/disables this method to be used for orders split up and shipped to many addresses,
  • $_canUseInternal which enables/disables using this method from the admin order screen,
  • $_code which is the unique payment method code for this module. It should match the code defined in the config.xml file. Its just text.
  • $_isInitializeNeeded, which enables a call to the initialize method when payment is captured.

The model::initialize() method is called when the order is placed. (Last step of the checkout process when the "place order" button is clicked.)
Looking at other modules is a great way to figure out how this code should work and if you look at the paypal or authorize.net modules you'll find a bunch more options are configured. These modules do the full authorize/capture process. Authorize occurs when the payment method is selected and the capture method is called when you click the "place order" button is clicked.

My module is very simple, needed only one method to be called at the very end of the payment process.
I opted to use initialize() instead of capture/authorizenbsp;because it always occurs at the same point in the process and I observed that initialize is only called in one place. I didn't find that to be true of the other methods.

In my initialize method, I'm setting the order state to "pending payment". This provides a clue to the folks working with the order in the backend that no payment has been collected for this order yet. I'm also connecting to the mockpay api and sending the order details. Mockpay will respond by providing a session token which will be unique to this order. The token is used to associate the customer on our site to the order details on the mockpay site with the order info and amount we're requesting. This is also where we'll inform mockpay about the cancel, error and success urls for this order.

Once payment is completed or canceled. The customer will be redirected back to our site. At that point we have a little more work to do with the order to either make an invoice and change the order state if successful or cancel it if the order fails or is canceled.

We'll use action controllers to handle these requests.

These are in the controllers/StandardController.php file. This is also extending an existing magento resource called "Mage_Core_Controller_Front_Action".

The successAction() will handle the success condition of the api where payment was completed successfully.
We'll get a token back from the mockpay api and we'll be able to use the token to query the api to get more details about the payment.

If everything checks out, we can change the state of the order from STATE_PENDING_PAYMENT to STATE_NEW, so its ready for someone on the store to ship and complete. This is also where we'll convert the quote into an invoice and mark it as paid. I skipped this step in the first version and it means someone on the backend has to do the extra step of creating an invoice and marking it as paid in magento.

Once all these steps are completed, I can then redirect the customer to the order complete page.

The other possible conditions are cancel, the person doesn't want to pay or failure where payment can't be completed because of lack of funds. I have these two conditions routed to the same action, the cancelAction().

Cancel will make the quote inactive and call the order objects cancel method.

With these actions in place, we have a functional payment module integrated with the mock pay api.

I've tried to comment as much as possible in the code so hopefully it will be instructive as you attempt to create your own payment method.

The full code for this payment module can be found on github.