Created
May 26, 2015 15:58
-
-
Save jsqu99/f0735b88156dd52436df to your computer and use it in GitHub Desktop.
Revisions
-
jsqu99 created this gist
May 26, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,227 @@ require 'active_support/core_ext/date/calculations' require 'active_support/core_ext/numeric/time' require 'base64' #Unirest.timeout(240) # generous timeout class ShipstationClient class ResponseError < StandardError; end class << self def request(method, path, options) response = Unirest.send method, "https://ssapi.shipstation.com/#{path}", options return response if response.code == 200 raise ResponseError, "#{response.code}, API error: #{response.body.inspect}" end end end class ShipStationApp def initialize(config) @config = config end # ShipStation appears to be recording their timestamps in local (PST) time but storing that timestamp # as UTC (so it's basically 7-8 hours off the correct time (depending on daylight savings). To compensate # for this the timestamp we use for "since" should be adjusted accordingly. ZONE = "Pacific Time (US & Canada)" # REST API doc https://www.mashape.com/shipstation/shipstation def add_shipment(shipment) # Shipstation wants orders and then it creates shipments. This integration assumes the # shipments are already split and will just create "orders" that are identical to the # storefront concept of a shipment. order = populate_order(shipment) options = { headers: ship_headers.merge("content-type" => "application/json"), parameters: order.to_json } response = ShipstationClient.request :post, "Orders/CreateOrder", options puts "result 200 - Shipment transmitted to ShipStation: #{response.body['orderId']}" end private def map_carrier(carrier_name) response = ShipstationClient.request :get, "Carriers", headers: ship_headers response.body.each do |carrier| return carrier["code"] if carrier["name"] == carrier_name end raise "There is no carrier named '#{carrier_name}' configured with this ShipStation account" end def map_service(carrier_code, service_name) response = ShipstationClient.request :get, "Carriers/ListServices?carrierCode=#{carrier_code}", headers: ship_headers response.body.each do |service| return service["code"] if service["name"] == service_name end raise "There is no service named '#{service_name}' associated with the carrier_code of '#{carrier_code}'" end def map_package(carrier_code, package_name) response = ShipstationClient.request :get, "Carriers/ListPackages?carrierCode=#{carrier_code}", headers: ship_headers response.body.each do |package| return package["code"] if package["name"] == package_name end raise "There is no package named '#{package_name}' associated with the carrier_code of '#{carrier_code}'" end def populate_order(shipment) order = { "customerEmail" => shipment[:email], "customerUsername" => shipment[:email], "orderNumber" => shipment[:id], #required "orderDate" => shipment[:created_at] || Time.now, "paymentDate" => shipment[:created_at] || Time.now, "orderStatus" => map_status(shipment[:status]), #required: hold, canceled, awaiting_shipment "shipTo" => populate_address(shipment[:shipping_address]), #required (see populate_address for details) "billTo" => populate_address(shipment[:billing_address]) || populate_address(shipment[:shipping_address]), "customerNotes" => shipment[:delivery_instructions], "internalNotes" => shipment[:internal_notes], "gift" => shipment[:is_gift], "packageCode" => 'package', "advancedOptions" => populate_advanced(shipment), "items" => populate_items(shipment[:items]) } if shipment[:amount_paid] order["amountPaid"] = shipment[:amount_paid].to_f.to_s end if shipment[:shipping_amount] order["shippingAmount"] = shipment[:shipping_amount].to_f.to_s end if shipment[:tax_amount] order["taxAmount"] = shipment[:tax_amount].to_f.to_s end if shipment[:gift_message] order["giftMessage"] = shipment[:gift_message] end if shipment[:confirmation] order["confirmation"] = map_confirmation(shipment[:confirmation]) end if shipment[:requested_shipping_service] order["requestedShippingService"] = shipment[:requested_shipping_service] elsif shipment[:shipping_carrier] carrier_code = map_carrier(shipment[:shipping_carrier]) order["carrierCode"] = carrier_code order["serviceCode"] = map_service(carrier_code, shipment[:shipping_method]) order["packageCode"] = map_package(carrier_code, shipment[:package]) if shipment[:package] end order end def populate_advanced(shipment) advanced = { "storeId" => @config[:shipstation_store_id], "customfield1" => shipment[:custom_field_1], "customfield2" => shipment[:custom_field_2], "customfield3" => shipment[:custom_field_3], } if shipment[:contains_alcohol] advanced["containsAlcohol"] = shipment[:contains_alcohol] end if shipment[:saturday_delivery] advanced["saturdayDelivery"] = shipment[:saturday_delivery] end if shipment[:non_machinable] advanced["nonMachinable"] = shipment[:non_machinable] end advanced end def populate_items(line_items) return if line_items.nil? line_items.map do |item| { "lineItemKey" => nil, "sku" => item[:product_id], "name" => item[:name], "imageUrl" => item[:image_url], "quantity" => item[:quantity], "unitPrice" => item[:price] } end end def populate_address(address) return if address.nil? || address.empty? address_hash = { :name => "#{address[:firstname]} #{address[:lastname]}", #required :street1 => address[:address1], #required :street2 => address[:address2], :street3 => address[:address3], :city => address[:city], #required :state => address[:state], #required :postalCode => address[:zipcode], #required :country => address[:country], #required :phone => address[:phone], :company => address[:company] } if address[:is_residential].present? address_hash[:residential] = address[:is_residential] end address_hash end def map_status(status) case status when 'hold' 'on_hold' when 'canceled', 'cancelled' 'cancelled' else 'awaiting_shipment' end end def map_confirmation(confirmation) case confirmation when 'Delivery' 'delivery' when 'Signature' 'signature' when 'Adult Signature' 'adult_signature' end end def ship_headers # the parameter authorization is for old customers - legacy support (Mashape) # new customers should use key & secret if !@config[:key].to_s.empty? && !@config[:secret].to_s.empty? authorization = Base64.strict_encode64("#{@config[:key]}:#{@config[:secret]}") else authorization = @config[:authorization] end headers = { 'Authorization' => "Basic #{authorization}" } unless @config[:x_partner].to_s.empty? headers['x-partner'] = @config[:x_partner] end headers end end