active_fulfillment 2.1.9 → 3.0.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|