active_fulfillment 2.1.9 → 3.0.0.pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/lib/active_fulfillment.rb +5 -5
- data/lib/active_fulfillment/base.rb +10 -0
- data/lib/active_fulfillment/response.rb +26 -0
- data/lib/active_fulfillment/service.rb +56 -0
- data/lib/active_fulfillment/services.rb +5 -0
- data/lib/active_fulfillment/services/amazon_mws.rb +473 -0
- data/lib/active_fulfillment/services/james_and_james.rb +122 -0
- data/lib/active_fulfillment/services/shipwire.rb +266 -0
- data/lib/active_fulfillment/services/shopify_api.rb +125 -0
- data/lib/active_fulfillment/services/webgistix.rb +334 -0
- data/lib/active_fulfillment/version.rb +4 -0
- data/test/remote/amazon_mws_test.rb +20 -17
- data/test/remote/james_and_james_test.rb +77 -0
- data/test/remote/shipwire_test.rb +25 -25
- data/test/remote/webgistix_test.rb +21 -21
- data/test/test_helper.rb +27 -52
- data/test/unit/base_test.rb +4 -4
- data/test/unit/services/amazon_mws_test.rb +56 -26
- data/test/unit/services/james_and_james_test.rb +90 -0
- data/test/unit/services/shipwire_test.rb +18 -18
- data/test/unit/services/shopify_api_test.rb +7 -20
- data/test/unit/services/webgistix_test.rb +35 -35
- metadata +32 -114
- data/CHANGELOG +0 -62
- data/lib/active_fulfillment/fulfillment/base.rb +0 -12
- data/lib/active_fulfillment/fulfillment/response.rb +0 -28
- data/lib/active_fulfillment/fulfillment/service.rb +0 -58
- data/lib/active_fulfillment/fulfillment/services.rb +0 -5
- data/lib/active_fulfillment/fulfillment/services/amazon.rb +0 -389
- data/lib/active_fulfillment/fulfillment/services/amazon_mws.rb +0 -454
- data/lib/active_fulfillment/fulfillment/services/shipwire.rb +0 -268
- data/lib/active_fulfillment/fulfillment/services/shopify_api.rb +0 -125
- data/lib/active_fulfillment/fulfillment/services/webgistix.rb +0 -338
- data/lib/active_fulfillment/fulfillment/version.rb +0 -6
- data/test/fixtures.yml +0 -16
- data/test/fixtures/xml/amazon/inventory_get_response.xml +0 -17
- data/test/fixtures/xml/amazon/inventory_list_response.xml +0 -29
- data/test/fixtures/xml/amazon/inventory_list_response_with_next_1.xml +0 -30
- data/test/fixtures/xml/amazon/inventory_list_response_with_next_2.xml +0 -29
- data/test/fixtures/xml/amazon/tracking_response_1.xml +0 -56
- data/test/fixtures/xml/amazon/tracking_response_2.xml +0 -38
- data/test/fixtures/xml/amazon/tracking_response_error.xml +0 -13
- data/test/fixtures/xml/amazon/tracking_response_not_found.xml +0 -13
- data/test/fixtures/xml/amazon_mws/fulfillment_get_fulfillment_order.xml +0 -114
- data/test/fixtures/xml/amazon_mws/fulfillment_get_fulfillment_order_2.xml +0 -90
- data/test/fixtures/xml/amazon_mws/fulfillment_get_fullfillment_order_with_multiple_tracking_numbers.xml +0 -121
- data/test/fixtures/xml/amazon_mws/fulfillment_list_all_fulfillment_orders.xml +0 -70
- data/test/fixtures/xml/amazon_mws/inventory_list_inventory_item_supply.xml +0 -32
- data/test/fixtures/xml/amazon_mws/inventory_list_inventory_supply.xml +0 -75
- data/test/fixtures/xml/amazon_mws/inventory_list_inventory_supply_by_next_token.xml +0 -38
- data/test/fixtures/xml/amazon_mws/tracking_response_error.xml +0 -9
- data/test/fixtures/xml/amazon_mws/tracking_response_not_found.xml +0 -9
- data/test/fixtures/xml/shipwire/fulfillment_failure_response.xml +0 -7
- data/test/fixtures/xml/shipwire/invalid_login_response.xml +0 -7
- data/test/fixtures/xml/shipwire/inventory_get_response.xml +0 -44
- data/test/fixtures/xml/shipwire/successful_empty_tracking_response.xml +0 -8
- data/test/fixtures/xml/shipwire/successful_live_tracking_response.xml +0 -53
- data/test/fixtures/xml/shipwire/successful_tracking_response.xml +0 -16
- data/test/fixtures/xml/shipwire/successful_tracking_response_with_tracking_urls.xml +0 -31
- data/test/fixtures/xml/webgistix/multiple_tracking_response.xml +0 -21
- data/test/fixtures/xml/webgistix/tracking_response.xml +0 -14
- data/test/remote/amazon_test.rb +0 -124
- data/test/unit/services/amazon_test.rb +0 -271
data/CHANGELOG
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
= ActiveFulfillment CHANGELOG
|
2
|
-
|
3
|
-
== Version 2.1.7
|
4
|
-
|
5
|
-
* Add shopify_api service
|
6
|
-
* Drop Ruby 1.9.3 support
|
7
|
-
|
8
|
-
== Version 2.1.0
|
9
|
-
|
10
|
-
* Added fetch_tracking_data methods which returns tracking_companies and tracking_urls if available in addition to tracking_numbers for each service
|
11
|
-
|
12
|
-
== Version 2.0.0 (Jan 5, 2013)
|
13
|
-
|
14
|
-
* API Change on tracking numbers, returns array instead of single string [csaunders]
|
15
|
-
|
16
|
-
== Version 1.0.3 (Jan 21, 2010)
|
17
|
-
|
18
|
-
* Include "pending" counts in stock levels for Shipwire [wisq]
|
19
|
-
* Add option to include pending stock in Shipwire inventory calculations [jessehk]
|
20
|
-
|
21
|
-
== Version 1.0.2 (Jan 12, 2010)
|
22
|
-
|
23
|
-
* Include "pending" counts in stock levels for Shipwire [wisq]
|
24
|
-
|
25
|
-
== Version 1.0.1 (Dec 13, 2010)
|
26
|
-
|
27
|
-
* Updated common files with changes from activemerchant [Dennis Theisen]
|
28
|
-
* Updated Webgistix USPS shipping methods (4 added, 1 removed) [Dennis Theisen]
|
29
|
-
* Changed Webgistix to treat a duplicate response as success instead of failure and to retry failed connection errors. [Dennis Theisen]
|
30
|
-
|
31
|
-
== Version 1.0.0 (July 12, 2010)
|
32
|
-
|
33
|
-
* Add inventory support to Amazon and Webgistix [wisq]
|
34
|
-
|
35
|
-
== Version 0.10.0 (July 6, 2010)
|
36
|
-
|
37
|
-
* Remove DHL from Webgistix shipping methods [Dennis Thiesen]
|
38
|
-
* Update Amazon FBA to use AWS credentials [John Tajima]
|
39
|
-
* Use new connection code from ActiveMerchant [cody]
|
40
|
-
* Add #valid_credentials? support to all fulfillment services [cody]
|
41
|
-
* Return 'Access Denied' message when Webgistix credenentials are invalid [cody]
|
42
|
-
* Update Shipwire endpoint hostname [cody]
|
43
|
-
* Add missing ISO countries [Edward Ocampo-Gooding]
|
44
|
-
* Add support for Guernsey to country.rb [cody]
|
45
|
-
* Use a Rails 2.3 compatible OrderedHash [cody]
|
46
|
-
* Use :words_connector instead of connector in RequiresParameters [cody]
|
47
|
-
* Provide Webgistix with a valid test sku to keep remote tests passing
|
48
|
-
* Update PostsData to support get requests
|
49
|
-
* Update Shipwire to latest version of dtd.
|
50
|
-
* Use real addresses for Shipwire remote fulfillment tests
|
51
|
-
* Pass Shipwire the ISO country code instead of the previous name and country combo. Always add the country element to the document
|
52
|
-
* Update Shipwire warehouses and don't send unneeded Content-Type header
|
53
|
-
* Add configurable timeouts from Active Merchant
|
54
|
-
* Shipwire: Send the company in address1 if present. Otherwise send address1 in address1.
|
55
|
-
* Always send address to Shipwire
|
56
|
-
* Map company to address1 with Shipwire
|
57
|
-
* Sync posts_data.rb with ActiveMerchant
|
58
|
-
* Add support for fetching tracking numbers to Shipwire
|
59
|
-
* Move email to the options hash. Refactor Shipwire commit method.
|
60
|
-
* Package for initial upload to Google Code
|
61
|
-
* Fix remote Webgistix test
|
62
|
-
* Add support for Fulfillment by Amazon Basic Fulfillment
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module ActiveMerchant
|
2
|
-
module Fulfillment
|
3
|
-
class Response
|
4
|
-
attr_reader :params
|
5
|
-
attr_reader :message
|
6
|
-
attr_reader :test
|
7
|
-
|
8
|
-
def success?
|
9
|
-
@success
|
10
|
-
end
|
11
|
-
|
12
|
-
def test?
|
13
|
-
@test
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(success, message, params = {}, options = {})
|
17
|
-
@success, @message, @params = success, message, params.stringify_keys
|
18
|
-
@test = options[:test] || false
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
def method_missing(method, *args)
|
23
|
-
@params[method.to_s] || super
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
module ActiveMerchant
|
2
|
-
module Fulfillment
|
3
|
-
class Service
|
4
|
-
|
5
|
-
include RequiresParameters
|
6
|
-
include PostsData
|
7
|
-
|
8
|
-
cattr_accessor :logger
|
9
|
-
|
10
|
-
def initialize(options = {})
|
11
|
-
check_test_mode(options)
|
12
|
-
|
13
|
-
@options = {}
|
14
|
-
@options.update(options)
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_mode?
|
18
|
-
false
|
19
|
-
end
|
20
|
-
|
21
|
-
def test?
|
22
|
-
@options[:test] || Base.mode == :test
|
23
|
-
end
|
24
|
-
|
25
|
-
def valid_credentials?
|
26
|
-
true
|
27
|
-
end
|
28
|
-
|
29
|
-
# API Requirements for Implementors
|
30
|
-
def fulfill(order_id, shipping_address, line_items, options = {})
|
31
|
-
raise NotImplementedError.new("Subclasses must implement")
|
32
|
-
end
|
33
|
-
|
34
|
-
def fetch_stock_levels(options = {})
|
35
|
-
raise NotImplementedError.new("Subclasses must implement")
|
36
|
-
end
|
37
|
-
|
38
|
-
def fetch_tracking_numbers(order_ids, options = {})
|
39
|
-
response = fetch_tracking_data(order_ids, options)
|
40
|
-
response.params.delete('tracking_companies')
|
41
|
-
response.params.delete('tracking_urls')
|
42
|
-
response
|
43
|
-
end
|
44
|
-
|
45
|
-
def fetch_tracking_data(order_ids, options = {})
|
46
|
-
raise NotImplementedError.new("Subclasses must implement")
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def check_test_mode(options)
|
52
|
-
if options[:test] and not test_mode?
|
53
|
-
raise ArgumentError, 'Test mode is not supported by this gateway'
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,5 +0,0 @@
|
|
1
|
-
require 'active_fulfillment/fulfillment/services/shopify_api'
|
2
|
-
require 'active_fulfillment/fulfillment/services/shipwire'
|
3
|
-
require 'active_fulfillment/fulfillment/services/webgistix'
|
4
|
-
require 'active_fulfillment/fulfillment/services/amazon'
|
5
|
-
require 'active_fulfillment/fulfillment/services/amazon_mws'
|
@@ -1,389 +0,0 @@
|
|
1
|
-
require 'base64'
|
2
|
-
require 'openssl'
|
3
|
-
|
4
|
-
module ActiveMerchant
|
5
|
-
module Fulfillment
|
6
|
-
class AmazonService < Service
|
7
|
-
SERVICES = {
|
8
|
-
:outbound => {
|
9
|
-
:url => 'https://fba-outbound.amazonaws.com',
|
10
|
-
:xmlns => 'http://fba-outbound.amazonaws.com/doc/2007-08-02/',
|
11
|
-
:version => '2007-08-02'
|
12
|
-
},
|
13
|
-
:inventory => {
|
14
|
-
:url => 'https://fba-inventory.amazonaws.com',
|
15
|
-
:xmlns => 'http://fba-inventory.amazonaws.com/doc/2009-07-31/',
|
16
|
-
:version => '2009-07-31'
|
17
|
-
}
|
18
|
-
}
|
19
|
-
|
20
|
-
SUCCESS, FAILURE, ERROR = 'Accepted', 'Failure', 'Error'
|
21
|
-
MESSAGES = {
|
22
|
-
:status => {
|
23
|
-
'Accepted' => 'Success',
|
24
|
-
'Failure' => 'Failed',
|
25
|
-
'Error' => 'An error occurred'
|
26
|
-
},
|
27
|
-
:create => {
|
28
|
-
'Accepted' => 'Successfully submitted the order',
|
29
|
-
'Failure' => 'Failed to submit the order',
|
30
|
-
'Error' => 'An error occurred while submitting the order'
|
31
|
-
},
|
32
|
-
:list => {
|
33
|
-
'Accepted' => 'Successfully submitted request',
|
34
|
-
'Failure' => 'Failed to submit request',
|
35
|
-
'Error' => 'An error occurred while submitting request'
|
36
|
-
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
ENV_NAMESPACES = { 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
|
41
|
-
'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
42
|
-
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
43
|
-
}
|
44
|
-
|
45
|
-
AWS_SECURITY_ATTRIBUTES = {
|
46
|
-
"env:actor" => "http://schemas.xmlsoap.org/soap/actor/next",
|
47
|
-
"env:mustUnderstand" => "0",
|
48
|
-
"xmlns:aws" => "http://security.amazonaws.com/doc/2007-01-01/"
|
49
|
-
}
|
50
|
-
|
51
|
-
@@digest = OpenSSL::Digest.new("sha1")
|
52
|
-
|
53
|
-
OPERATIONS = {
|
54
|
-
:outbound => {
|
55
|
-
:status => 'GetServiceStatus',
|
56
|
-
:create => 'CreateFulfillmentOrder',
|
57
|
-
:list => 'ListAllFulfillmentOrders',
|
58
|
-
:tracking => 'GetFulfillmentOrder'
|
59
|
-
},
|
60
|
-
:inventory => {
|
61
|
-
:get => 'GetInventorySupply',
|
62
|
-
:list => 'ListUpdatedInventorySupply',
|
63
|
-
:list_next => 'ListUpdatedInventorySupplyByNextToken'
|
64
|
-
}
|
65
|
-
}
|
66
|
-
|
67
|
-
# The first is the label, and the last is the code
|
68
|
-
# Standard: 3-5 business days
|
69
|
-
# Expedited: 2 business days
|
70
|
-
# Priority: 1 business day
|
71
|
-
def self.shipping_methods
|
72
|
-
[
|
73
|
-
[ 'Standard Shipping', 'Standard' ],
|
74
|
-
[ 'Expedited Shipping', 'Expedited' ],
|
75
|
-
[ 'Priority Shipping', 'Priority' ]
|
76
|
-
].inject(ActiveSupport::OrderedHash.new){|h, (k,v)| h[k] = v; h}
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.sign(aws_secret_access_key, auth_string)
|
80
|
-
Base64.encode64(OpenSSL::HMAC.digest(@@digest, aws_secret_access_key, auth_string)).strip
|
81
|
-
end
|
82
|
-
|
83
|
-
def initialize(options = {})
|
84
|
-
requires!(options, :login, :password)
|
85
|
-
super
|
86
|
-
end
|
87
|
-
|
88
|
-
def status
|
89
|
-
commit :outbound, :status, build_status_request
|
90
|
-
end
|
91
|
-
|
92
|
-
def fulfill(order_id, shipping_address, line_items, options = {})
|
93
|
-
requires!(options, :order_date, :comment, :shipping_method)
|
94
|
-
commit :outbound, :create, build_fulfillment_request(order_id, shipping_address, line_items, options)
|
95
|
-
end
|
96
|
-
|
97
|
-
def fetch_current_orders
|
98
|
-
commit :outbound, :list, build_get_current_fulfillment_orders_request
|
99
|
-
end
|
100
|
-
|
101
|
-
def fetch_stock_levels(options = {})
|
102
|
-
if options[:sku]
|
103
|
-
commit :inventory, :get, build_inventory_get_request(options)
|
104
|
-
else
|
105
|
-
response = commit :inventory, :list, build_inventory_list_request(options)
|
106
|
-
|
107
|
-
while token = response.params['next_token'] do
|
108
|
-
next_page = commit :inventory, :list_next, build_next_inventory_list_request(token)
|
109
|
-
|
110
|
-
next_page.stock_levels.merge!(response.stock_levels)
|
111
|
-
response = next_page
|
112
|
-
end
|
113
|
-
|
114
|
-
response
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def fetch_tracking_data(order_ids, options = {})
|
119
|
-
order_ids.inject(nil) do |previous, o_id|
|
120
|
-
response = commit :outbound, :tracking, build_tracking_request(o_id, options)
|
121
|
-
return response unless response.success?
|
122
|
-
|
123
|
-
if previous
|
124
|
-
response.tracking_numbers.merge!(previous.tracking_numbers)
|
125
|
-
response.tracking_companies.merge!(previous.tracking_companies)
|
126
|
-
response.tracking_urls.merge!(previous.tracking_urls)
|
127
|
-
end
|
128
|
-
|
129
|
-
response
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def valid_credentials?
|
134
|
-
status.success?
|
135
|
-
end
|
136
|
-
|
137
|
-
def test_mode?
|
138
|
-
false
|
139
|
-
end
|
140
|
-
|
141
|
-
private
|
142
|
-
def soap_request(request)
|
143
|
-
xml = Builder::XmlMarkup.new :indent => 2
|
144
|
-
xml.instruct!
|
145
|
-
xml.tag! "env:Envelope", ENV_NAMESPACES do
|
146
|
-
xml.tag! "env:Header" do
|
147
|
-
add_credentials(xml, request)
|
148
|
-
end
|
149
|
-
xml.tag! "env:Body" do
|
150
|
-
yield xml
|
151
|
-
end
|
152
|
-
end
|
153
|
-
xml.target!
|
154
|
-
end
|
155
|
-
|
156
|
-
def build_status_request
|
157
|
-
request = OPERATIONS[:outbound][:status]
|
158
|
-
soap_request(request) do |xml|
|
159
|
-
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] }
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def build_get_current_fulfillment_orders_request
|
164
|
-
request = OPERATIONS[:outbound][:list]
|
165
|
-
soap_request(request) do |xml|
|
166
|
-
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] } do
|
167
|
-
xml.tag! "NumberOfResultsRequested", 5
|
168
|
-
xml.tag! "QueryStartDateTime", Time.now.utc.yesterday.strftime("%Y-%m-%dT%H:%M:%SZ")
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
def build_fulfillment_request(order_id, shipping_address, line_items, options)
|
174
|
-
request = OPERATIONS[:outbound][:create]
|
175
|
-
soap_request(request) do |xml|
|
176
|
-
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] } do
|
177
|
-
xml.tag! "MerchantFulfillmentOrderId", order_id
|
178
|
-
xml.tag! "DisplayableOrderId", order_id
|
179
|
-
xml.tag! "DisplayableOrderDateTime", options[:order_date].strftime("%Y-%m-%dT%H:%M:%SZ")
|
180
|
-
xml.tag! "DisplayableOrderComment", options[:comment]
|
181
|
-
xml.tag! "ShippingSpeedCategory", options[:shipping_method]
|
182
|
-
|
183
|
-
add_address(xml, shipping_address)
|
184
|
-
add_items(xml, line_items)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def build_inventory_get_request(options)
|
190
|
-
request = OPERATIONS[:inventory][:get]
|
191
|
-
soap_request(request) do |xml|
|
192
|
-
xml.tag! request, { 'xmlns' => SERVICES[:inventory][:xmlns] } do
|
193
|
-
xml.tag! "MerchantSKU", options[:sku]
|
194
|
-
xml.tag! "ResponseGroup", "Basic"
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def build_inventory_list_request(options)
|
200
|
-
start_time = options[:start_time] || 1.day.ago
|
201
|
-
|
202
|
-
request = OPERATIONS[:inventory][:list]
|
203
|
-
soap_request(request) do |xml|
|
204
|
-
xml.tag! request, { 'xmlns' => SERVICES[:inventory][:xmlns] } do
|
205
|
-
xml.tag! "NumberOfResultsRequested", 50
|
206
|
-
xml.tag! "QueryStartDateTime", start_time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
207
|
-
xml.tag! "ResponseGroup", "Basic"
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def build_next_inventory_list_request(token)
|
213
|
-
request = OPERATIONS[:inventory][:list_next]
|
214
|
-
soap_request(request) do |xml|
|
215
|
-
xml.tag! request, { 'xmlns' => SERVICES[:inventory][:xmlns] } do
|
216
|
-
xml.tag! "NextToken", token
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def build_tracking_request(order_id, options)
|
222
|
-
request = OPERATIONS[:outbound][:tracking]
|
223
|
-
soap_request(request) do |xml|
|
224
|
-
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] } do
|
225
|
-
xml.tag! "MerchantFulfillmentOrderId", order_id
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def add_credentials(xml, request)
|
231
|
-
login = @options[:login]
|
232
|
-
timestamp = "#{Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S")}Z"
|
233
|
-
signature = self.class.sign(@options[:password], "#{request}#{timestamp}")
|
234
|
-
|
235
|
-
xml.tag! 'aws:AWSAccessKeyId', login, AWS_SECURITY_ATTRIBUTES
|
236
|
-
xml.tag! 'aws:Signature', signature, AWS_SECURITY_ATTRIBUTES
|
237
|
-
xml.tag! 'aws:Timestamp', timestamp, AWS_SECURITY_ATTRIBUTES
|
238
|
-
end
|
239
|
-
|
240
|
-
def add_items(xml, line_items)
|
241
|
-
Array(line_items).each_with_index do |item, index|
|
242
|
-
xml.tag! 'Item' do
|
243
|
-
xml.tag! 'MerchantSKU', item[:sku]
|
244
|
-
xml.tag! "MerchantFulfillmentOrderItemId", index
|
245
|
-
xml.tag! "Quantity", item[:quantity]
|
246
|
-
xml.tag! "GiftMessage", item[:gift_message] unless item[:gift_message].blank?
|
247
|
-
xml.tag! "DisplayableComment", item[:comment] unless item[:comment].blank?
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def add_address(xml, address)
|
253
|
-
xml.tag! 'DestinationAddress' do
|
254
|
-
xml.tag! 'Name', address[:name]
|
255
|
-
xml.tag! 'Line1', address[:address1]
|
256
|
-
xml.tag! 'Line2', address[:address2] unless address[:address2].blank?
|
257
|
-
xml.tag! 'Line3', address[:address3] unless address[:address3].blank?
|
258
|
-
xml.tag! 'City', address[:city]
|
259
|
-
xml.tag! 'StateOrProvinceCode', address[:state]
|
260
|
-
xml.tag! 'CountryCode', address[:country]
|
261
|
-
xml.tag! 'PostalCode', address[:zip]
|
262
|
-
xml.tag! 'PhoneNumber', address[:phone] unless address[:phone].blank?
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
def commit(service, op, body)
|
267
|
-
data = ssl_post(SERVICES[service][:url], body, 'Content-Type' => 'application/soap+xml; charset=utf-8')
|
268
|
-
response = parse_response(service, op, data)
|
269
|
-
Response.new(success?(response), message_from(response), response)
|
270
|
-
rescue ActiveMerchant::ResponseError => e
|
271
|
-
handle_error(e)
|
272
|
-
end
|
273
|
-
|
274
|
-
def handle_error(e)
|
275
|
-
response = parse_error(e.response)
|
276
|
-
if response.fetch(:faultstring, "") =~ /Reason: requested order not found./
|
277
|
-
Response.new(true, nil, {:status => SUCCESS, :tracking_numbers => {}, :tracking_companies => {}, :tracking_urls => {}})
|
278
|
-
else
|
279
|
-
Response.new(false, message_from(response), response)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
def success?(response)
|
284
|
-
response[:response_status] == SUCCESS
|
285
|
-
end
|
286
|
-
|
287
|
-
def message_from(response)
|
288
|
-
response[:response_comment]
|
289
|
-
end
|
290
|
-
|
291
|
-
def parse_response(service, op, xml)
|
292
|
-
begin
|
293
|
-
document = REXML::Document.new(xml)
|
294
|
-
rescue REXML::ParseException
|
295
|
-
return {:success => FAILURE}
|
296
|
-
end
|
297
|
-
|
298
|
-
case service
|
299
|
-
when :outbound
|
300
|
-
case op
|
301
|
-
when :tracking
|
302
|
-
parse_tracking_response(document)
|
303
|
-
else
|
304
|
-
parse_fulfillment_response(op, document)
|
305
|
-
end
|
306
|
-
when :inventory
|
307
|
-
parse_inventory_response(document)
|
308
|
-
else
|
309
|
-
raise ArgumentError, "Unknown service #{service}"
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
def parse_fulfillment_response(op, document)
|
314
|
-
response = {}
|
315
|
-
action = OPERATIONS[:outbound][op]
|
316
|
-
node = REXML::XPath.first(document, "//ns1:#{action}Response")
|
317
|
-
|
318
|
-
response[:response_status] = SUCCESS
|
319
|
-
response[:response_comment] = MESSAGES[op][SUCCESS]
|
320
|
-
response
|
321
|
-
end
|
322
|
-
|
323
|
-
def parse_inventory_response(document)
|
324
|
-
response = {}
|
325
|
-
response[:stock_levels] = {}
|
326
|
-
|
327
|
-
document.each_element('//ns1:MerchantSKUSupply') do |node|
|
328
|
-
# {MerchantSKU => 'SOME-ID', InStockSupplyQuantity => '101', ...}
|
329
|
-
params = node.elements.to_a.each_with_object({}) {|elem, hash| hash[elem.name] = elem.text}
|
330
|
-
|
331
|
-
response[:stock_levels][params['MerchantSKU']] = params['InStockSupplyQuantity'].to_i
|
332
|
-
end
|
333
|
-
|
334
|
-
next_token = REXML::XPath.first(document, '//ns1:NextToken')
|
335
|
-
response[:next_token] = (next_token ? next_token.text : nil)
|
336
|
-
|
337
|
-
response[:response_status] = SUCCESS
|
338
|
-
response
|
339
|
-
end
|
340
|
-
|
341
|
-
def parse_tracking_response(document)
|
342
|
-
response = {}
|
343
|
-
response[:tracking_numbers] = {}
|
344
|
-
response[:tracking_companies] = {}
|
345
|
-
response[:tracking_urls] = {}
|
346
|
-
|
347
|
-
track_node = REXML::XPath.first(document, '//ns1:FulfillmentShipmentPackage/ns1:TrackingNumber')
|
348
|
-
if track_node
|
349
|
-
id_node = REXML::XPath.first(document, '//ns1:MerchantFulfillmentOrderId')
|
350
|
-
response[:tracking_numbers][id_node.text] = [track_node.text]
|
351
|
-
end
|
352
|
-
|
353
|
-
company_node = REXML::XPath.first(document, '//ns1:FulfillmentShipmentPackage/ns1:CarrierCode')
|
354
|
-
if company_node
|
355
|
-
id_node = REXML::XPath.first(document, '//ns1:MerchantFulfillmentOrderId')
|
356
|
-
response[:tracking_companies][id_node.text] = [company_node.text]
|
357
|
-
end
|
358
|
-
|
359
|
-
response[:response_status] = SUCCESS
|
360
|
-
response
|
361
|
-
end
|
362
|
-
|
363
|
-
def parse_error(http_response)
|
364
|
-
response = {}
|
365
|
-
response[:http_code] = http_response.code
|
366
|
-
response[:http_message] = http_response.message
|
367
|
-
|
368
|
-
document = REXML::Document.new(http_response.body)
|
369
|
-
|
370
|
-
node = REXML::XPath.first(document, "//env:Fault")
|
371
|
-
|
372
|
-
failed_node = node.find_first_recursive {|sib| sib.name == "Fault" }
|
373
|
-
faultcode_node = node.find_first_recursive {|sib| sib.name == "faultcode" }
|
374
|
-
faultstring_node = node.find_first_recursive {|sib| sib.name == "faultstring" }
|
375
|
-
|
376
|
-
response[:response_status] = FAILURE
|
377
|
-
response[:faultcode] = faultcode_node ? faultcode_node.text : ""
|
378
|
-
response[:faultstring] = faultstring_node ? faultstring_node.text : ""
|
379
|
-
response[:response_comment] = "#{response[:faultcode]} #{response[:faultstring]}"
|
380
|
-
response
|
381
|
-
rescue REXML::ParseException => e
|
382
|
-
response[:http_body] = http_response.body
|
383
|
-
response[:response_status] = FAILURE
|
384
|
-
response[:response_comment] = "#{response[:http_code]}: #{response[:http_message]}"
|
385
|
-
response
|
386
|
-
end
|
387
|
-
end
|
388
|
-
end
|
389
|
-
end
|