719-286-0751 [email protected]

Import Actual Shipping Cost from ShipStation to Magento

Import Actual Shipping Cost from ShipStation to Magento

We recently finished a customization for the ShipStation Magento extension which allows our client to pull in the actual shipping cost from ShipStation into Magento for reporting purposes. This article will show you how you can do this yourself if you’re a developer. If you’re not a developer please Contact Us for a free estimate on implementing this or other customizations for your store.  

There will be two parts in this article, if you just want to learn how to extend the ShipStation module to get Shipping Cost, you can skip to “Extending ShipStation Module”. 

Installing Ngrok

  1. To start we need to install an application call ngrok (https://ngrok.com). Ngrok allows you to expose your local development server to the internet, and it will also provide a web interface for you to inspect the traffic coming through via localhost:4040.
  2. After ngrok is finished installing you will need to update your local server’s VirtualHost configuration so it will forward traffic from ngrok to your local server. Below is an example configuration I used for my apache web server. I am mapping a wildcard subdomain to the project folder’s name.
    
    <VirtualHost *:80>
    # Official name is cadence-labs.com
    ServerName ngrok.io# Any subdomain *.cadence-labs.com also goes here
    ServerAlias *.ngrok.io<Directory "/Users/jay/vms/cadence/www/*">
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
    DirectoryIndex index.html index.php
    
    <FilesMatch "\.php$">
    SetHandler "proxy:fcgi://127.0.0.1:9070"
    </FilesMatch>
    </Directory>
    
    UseCanonicalName Off
    VirtualDocumentRoot /var/html/www/%1
    
    </VirtualHost>
    
  3. After you update your VirtualHost configuration, you can start ngrok by issuing the following command in terminal:

    ngrok http 80

    If you are using the basic plan ($5/month) you can create custom subdomain by passing in the parameter:

    -subdomain=subdomain

    I opt-ed in for this option since I all my project’s folder name is mapped to the subdomain name in my ngrok configuration. that way I don’t need to update my configuration each time ngrok generates a random subdomain for me.

  4. After ngrok starts you should be able to access localhost:4040 and see something like this:

  5. If you see that page that means ngrok is successfully installed and running, now we need to update our Magento’s secure and unsecure url to match our ngrok url.

    update core_config_data set value = ‘[NGROK_URL]’ where path = ‘web/unsecure/base_url’;

    update core_config_data set value = ‘[NGROK_URL]’ where path = ‘web/secure/base_url’;

  6. We also need to update our shipstation setting to point to our ngrok url:

  7. once this is done, click test connection, if the connection was successful you should already be see some request in your ngrok inspect panel:

  8. Now we can try to ship a package in shipstation and inspect the request they send to our test store, below is a sample response we see from the API:
    <?xml version="1.0" encoding="UTF-8"?>
    <ShipNotice>
        <OrderNumber>1111111</OrderNumber>
        <OrderID>11111111</OrderID>
        <CustomerCode>[email protected]</CustomerCode>
        <CustomerNotes>
            <![CDATA[]]>
        </CustomerNotes>
        <InternalNotes>
            <![CDATA[2017-03-22 19:24:27 ]]>
        </InternalNotes>
        <NotesToCustomer>
            <![CDATA[]]>
        </NotesToCustomer>
        <NotifyCustomer>true</NotifyCustomer>
        <LabelCreateDate>03/23/2017 23:13</LabelCreateDate>
        <ShipDate>03/23/2017</ShipDate>
        <Carrier>other</Carrier>
        <Service />
        <TrackingNumber>121312323313213213</TrackingNumber>
        <ShippingCost>0.00</ShippingCost>
        <Recipient>
            <Name>John Doe</Name>
            <Company />
            <Address1>123 Main St.</Address1>
            <Address2></Address2>
            <City>San Jose</City>
            <State>CA</State>
            <PostalCode>95122</PostalCode>
            <Country>US</Country>
        </Recipient>
    </ShipNotice>
  9. As we can see from that request, there is a <ShippigCost/> node in the XML request for us to use, in the next section we will learn how to use this information to create a custom Magento module to pull in the actual shipping cost. 

Extending ShipStation Module

  1. I created the module Cadence_Api which extends Auctane_Api module. To do that first create the config.xml and place them in app/code/local/Cadence/Api/etc:
    <?xml version="1.0"?>
    <config>
        <modules>
            <Cadence_Api>
                <version>0.1.0</version>
            </Cadence_Api>
        </modules>
        <global>
            <models>
                <cadence_api>
                    <class>Cadence_Api_Model</class>
                    <resourceModel>cadence_api_resource</resourceModel>
                </cadence_api>
                <cadence_api_resource>
                    <class>Cadence_Api_Model_Resource</class>
                </cadence_api_resource>
                <auctaneapi>
                    <rewrite>
                        <action_shipnotify>Cadence_Api_Model_Auctane_Action_Shipnotify</action_shipnotify>
                    </rewrite>
                </auctaneapi>
            </models>
        </global>
    </config>
  2. From the first part of this tutorial we know that when we ship an order on ship notify, there is a ShippingCost node that they send to our store. So in our rewrite we just need to pull that value and set it in our order. To do that we will create a model that will extend Auctane_Api_Model_Action_Shipnotify. This file will go under app/code/local/Cadence/Api/Model/Action/Shipnotify with the following content:
    <?php
    class Cadence_Api_Model_Auctane_Action_Shipnotify
    extends Auctane_Api_Model_Action_Shipnotify
    {
        /**
         * Perform a notify using POSTed data.
         * See Auctane API specification.
         *
         * @param Mage_Core_Controller_Request_Http $request
         * @throws Exception
         */
        public function process(Mage_Core_Controller_Request_Http $request)
        {
            // Raw XML is POSTed to this stream
            $xml = simplexml_load_file('php://input');
            // load some objects
            $order = $this->_getOrder($xml->OrderNumber);
            $qtys = $this->_getOrderItemQtys($xml->Items, $order);
            $shipment = $this->_getOrderShipment($order, $qtys);
    
            // this is where tracking is actually added
            $track = Mage::getModel('sales/order_shipment_track')
                ->setNumber($xml->TrackingNumber)
                ->setCarrierCode($xml->Carrier)
                ->setTitle(strtoupper($xml->Carrier));
    
            // Add shipping cost to order
            $order->setData('shipping_cost', $xml->ShippingCost);
    
    
            $shipment->addTrack($track);
    
            // 'NotifyCustomer' must be "true" or "yes" to trigger an email
            $notify = filter_var($xml->NotifyCustomer, FILTER_VALIDATE_BOOLEAN);
    
            $capture = filter_var($request->getParam('capture'), FILTER_VALIDATE_BOOLEAN);
            if ($capture && $order->canInvoice()) {
                $invoice = $order->prepareInvoice($qtys);
                $invoice->setRequestedCaptureCase($invoice->canCapture() ? 'online' : 'offline')
                    ->register() // captures & updates order totals
                    ->addComment($this->_getInvoiceComment(), $notify)
                    ->sendEmail($notify); // always send to store manager, and optionally notify customer too
                $order->setIsInProcess(true); // updates status on save
            }
    
            // Internal notes are only visible to admin
            if ($xml->InternalNotes) {
                $shipment->addComment($xml->InternalNotes);
            }
            // Customer notes have 'Visible On Frontend' set
            if ($notify) {
                // if no NotesToCustomer then comment is empty string
                $shipment->sendEmail(true, (string) $xml->NotesToCustomer)
                    ->setEmailSent(true);
            }
            if ($xml->NotesToCustomer) {
                $shipment->addComment($xml->NotesToCustomer, $notify, true);
            }
    
            $transaction = Mage::getModel('core/resource_transaction');
            $transaction->addObject($shipment)
                ->addObject($track)
                ->addObject($order);
            if (isset($invoice)) {
                // order has been captured, therefore has been modified
                $transaction->addObject($invoice)
                    ->addObject($order);
            }
            $transaction->save();
    
            if ($order->canInvoice() && !$order->canShip()) {
                $invoice = $order->prepareInvoice();
                $invoice->setRequestedCaptureCase($invoice->canCapture() ? 'online' : 'offline')
                    ->register() // captures & updates order totals
                    ->addComment($this->_getInvoiceComment(), false)
                    ->sendEmail(false); // always send to store manager, and optionally notify customer too
                $order->setIsInProcess(true); // updates status on save
    
                $transaction = Mage::getModel('core/resource_transaction');
                if (isset($invoice)) {
                    // order has been captured, therefore has been modified
                    $transaction->addObject($invoice)
                        ->addObject($order);
                }
                $transaction->save();
            }
        }
    }
  3. The last part file we need to create is the app/etc/moudles/Cadence_Api.xml. This will setup the dependency of this module and activate the module:
    <?xml version="1.0"?>
    <config>
        <modules>
            <Cadence_Api>
                <active>true</active>
                <codePool>local</codePool>
                <depends>
                    <Auctane_Api/>
                </depends>
            </Cadence_Api>
        </modules>
    </config>
  4. Flush Magento’s cache and this module should start saving the shipping cost to orders each time an order is shipped from ShipStation.

Let us know if you have any questions or comments below! 

1 Comment

  1. Brian Saminathen

    Very nice article! Thanks for sharing this as Ngrok is an awesome tool.

    Reply

Submit a Comment

Your email address will not be published. Required fields are marked *

Install our webapp on your iPhone! Tap and then Add to homescreen.
Share This