The SUMO Heavy Industries Blog

Design / eCommerce / Business / Culture / Life

Capturing Payment on Shipment Creation in Magento

June 21, 2011 by Bob Brodie

Every shop owner – online and offline – knows that dealing with credit cards is a very complex subject. There are rules about storing cards, how long you can store them (if you need to at all), authorizations, captures, rules regarding those, and how to handle certain situations.

One easy step you can take to handle regulations is to only authorize at checkout. This is an easy change in Magento. Log into your admin, and go into the settings for your payment processor. You’ll notice something called “Payment Action”. For most, there’s “Authorize Only” and “Authorize and Capture”. PayPal is a little different; they use “Authorization” and “Sale” (same concept though). This is great. Set that to Authorize and you’re good to go. But wait – how do you actually get your money?

This is the not-so-fun part. When you ship every order, you’ll need to log into Magento, invoice the order by capturing online (Available in Magento 1.4.2.0 and higher), and then you’ll need to create shipments – either manually or via 3rd party integration.

A developer who does a lot of work with us, Kamil Durski, figured out a solution to this. Amazingly, it’s only 2 files. That’s right – 2. Why this functionality isn’t there is beyond me.

First, you’ll create app/code/local/SUMOHeavy/ShippingInvoice/etc/config.xml:

<?xml version="1.0"?>
<config>
	<modules>
		<SUMOHeavy_ShippingInvoice>
			<version>1.0.0</version>
		</SUMOHeavy_ShippingInvoice>
	</modules>
	<global>
		<models>
			<shippinginvoice>
				<class>SUMOHeavy_ShippingInvoice_Model</class>
			</shippinginvoice>
		</models>
		<events>
			<sales_order_shipment_save_after>
				<observers>
					<sumo_shippinginvoice>
						<type>singleton</type>
						<class>shippinginvoice/observer</class>
						<method>createInvoiceAndCapturePayment</method>
					</sumo_shippinginvoice>
				</observers>
			</sales_order_shipment_save_after>
		</events>
	</global>
</config>

Then you’ll need to create app/code/local/SUMOHeavy/ShippingInvoice/Model/Observer.php:

<?php
 
class SUMOHeavy_ShippingInvoice_Model_Observer extends Mage_Core_Model_Abstract {
 
	public function createInvoiceAndCapturePayment($observer)
	{
		try {
			$shipment = $observer->getEvent()->getShipment();
			$order = $shipment->getOrder();
 
			if (!$order->canInvoice()) {
				return $this;
			}
 
			$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($savedQtys);
			if (!$invoice->getTotalQty()) {
				return $this;
			}
 
 
			$invoice->setRequestedCaptureCase('online');
			$invoice->register();
			$invoice->getOrder()->setIsInProcess(true);
	        $transactionSave = Mage::getModel('core/resource_transaction')
	            ->addObject($invoice)
	            ->addObject($invoice->getOrder());
 
	        $transactionSave->save();
 
			// Mage::getSingleton('adminhtml/session')->addSuccess($this->__('The invoice has been created.'));		
		} catch(Exception $e) {
			die($e->getMessage());
		}
	}
 
}

In the first file, we’re setting up or observer to watch the sales_order_shipment_save_after event. This means that after a shipment is committed to the database, we’ll capture the payment. In the observer file, we’re doing a few things:

1) Look up the shipping and order info:

$shipment = $observer->getEvent()->getShipment();
$order = $shipment->getOrder();

2) Check to see if the order can be invoiced

if (!$order->canInvoice()) {
	return $this;
}

3) Load all necessary info we need to process the transaction:

$invoice = Mage::getModel('sales/service_order', $order)->prepareInvoice($savedQtys);
if (!$invoice->getTotalQty()) {
	return $this;
}

4) Capture the funds online and save the order:

$invoice->setRequestedCaptureCase('online');
$invoice->register();
$invoice->getOrder()->setIsInProcess(true);
$transactionSave = Mage::getModel('core/resource_transaction')
	->addObject($invoice)
	->addObject($invoice->getOrder());
 
$transactionSave->save();

5) If we can’t capture, it will fail and present an error:

catch(Exception $e) {
	die($e->getMessage());
}

This is basic functionality and assumes that all orders are capturing against a live authorization. A great idea would be to extend the payment modules to store authorization tokens so that if you need to re-authorize the order you’d be able to. I’ve been keeping an eye on the subversion repository Magento 1.6.0.0 and one thing is really intriguing:

Added settings Authorization Honor Period and Order Valid Period into EC tab in the backend

I’m not 100% sure what this is going to accomplish in the end but have some ideas. It would be great if there’s some better CC handling methods in 1.6. If this makes it into the stable version, it could be a doorway to even greater opportunities.

Feel free to try this out, or add to it here: https://github.com/sumoheavy/SUMOHeavy_ShippingInvoice

About 

Businesses need to stay on task – that’s where Robert comes in. Developers and artists often need help understanding each other, and Robert will make sure that your functions and pixels come together to deliver exactly what’s on your mind. Mind reading is a tough job though, so keep those thoughts clean, people.

  • http://shop.itmanx.com/ Christian

    How about reversing this so you capture the payment after an invoice is generated, that way you know the payment is good before shipping the items?

    • http://www.bartmroz.com Bart Mroz

      You can not charge anyone unless the item is shipped or you are shipping the item with in 24 hours. 

      • http://shop.itmanx.com/ Christian

        Thank you for that Bart but in the UK a company can take your payment and not ship your order for quite a few days.

        • http://www.bartmroz.com Bart Mroz

          Not in  the US

    • http://twitter.com/bob_brodie Bob Brodie

      Hi Christian,

      Magento already has “Authorize and Capture” built in. That would capture your customers’ money at checkout.

      As for making sure your customer is good for their money – that’s where authorization comes in. It’s a temporary hold on their card. This is why there’s a two-step process.

      This article follows that standard:
      – Customer checks out with $400 in their cart
      – Customer’s card is authorized for $400 (if it cannot reserve the funds, the card is declined)
      – When you ship, the customer’s card is captured, and your money will be at your payment processor

      From there, you just have your daily batch settle and you’ll have your money.

      I hope that clears up a little confusion, which is something the topic of payment is chock full of!

      Thanks,
      Bob Brodie

  • Humayun

    Where the $savedQtys; is coming from in:
    $invoice = Mage::getModel(‘sales/serviceorder’, $order)->prepareInvoice($savedQtys);
    Also how can I get this working from manual script file?  I tried copying the code from createInvoiceAndCapturePayment function and put it in my script file, but it doesn’t seem to load the order attributes.
    I am doing something like this:
                    $orderId = $data[0];                      $order = Mage::getModel(‘sales/order’)->load($orderId);        print
    r($order->getData());Any idea how to get this working from one php file on root?

  • Deepak

    I need to create invoice and shipment through script that is run by cron. When I try to capture invoice online I am getting error like “Online capture requires customer credit card number” . Magento doesn’t store credit card. Any suggestion?

    • http://twitter.com/bob_brodie Bob Brodie

      Hi Deepak,

      This will depend on a few things, such as your Magento version and payment processor.

      Many payment processors will allow you to perform a capture up to the original amount of the authorization as long as you have the original transaction ID and order number.

      Thanks,
      Bob

  • weltlighting

    Is it possible to notify customer with an email when the invoice is created?

  • Desudesu

    “This means that after a shipment is committed to the database, we’ll capture the payment.” I suppose this means there is no way to stop the shipment process from going if capturing payment fails? I’d like to throw an error if something the function tries to do fails and have the order remain unshipped. Any easy way to prevent a shipment from being created for an order if an exception is thrown, or is it too late at this point?

    Big thanks for a very helpful post either way :)

    • bobbrodie

      Hi Desudesu,

      This is intended as an example. You could certainly stop the shipment process if payment fails. In general, we’ll put those orders in held state, so the customers can be contacted. Once settled, the orders are released and shipped.

      Thanks,
      Bob

  • Andrew

    As far as I can tell, Magento still does not include this feature out-of-the-box after 3 years. I am testing this solution out, but am unclear on how this method gets called. What prompts these files to do their thing? How does Magento know they exist?

  • Bryan

    Where are you getting $savedQtys from? I get an undefined variable error?