walmart_open 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +171 -0
- data/Rakefile +1 -0
- data/lib/walmart_open.rb +6 -0
- data/lib/walmart_open/auth_token.rb +25 -0
- data/lib/walmart_open/client.rb +48 -0
- data/lib/walmart_open/commerce_request.rb +23 -0
- data/lib/walmart_open/config.rb +41 -0
- data/lib/walmart_open/connection_manager.rb +47 -0
- data/lib/walmart_open/errors.rb +13 -0
- data/lib/walmart_open/item.rb +41 -0
- data/lib/walmart_open/order.rb +53 -0
- data/lib/walmart_open/order_item.rb +17 -0
- data/lib/walmart_open/order_results.rb +40 -0
- data/lib/walmart_open/order_xml_builder.rb +62 -0
- data/lib/walmart_open/ordered_item.rb +31 -0
- data/lib/walmart_open/product_request.rb +28 -0
- data/lib/walmart_open/request.rb +64 -0
- data/lib/walmart_open/requests/lookup.rb +27 -0
- data/lib/walmart_open/requests/place_order.rb +58 -0
- data/lib/walmart_open/requests/search.rb +19 -0
- data/lib/walmart_open/requests/taxonomy.rb +17 -0
- data/lib/walmart_open/requests/token.rb +30 -0
- data/lib/walmart_open/search_results.rb +25 -0
- data/lib/walmart_open/shipping_address.rb +36 -0
- data/lib/walmart_open/version.rb +3 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/walmart_open/auth_token_spec.rb +53 -0
- data/spec/walmart_open/client_spec.rb +92 -0
- data/spec/walmart_open/config_spec.rb +35 -0
- data/spec/walmart_open/connection_manager_spec.rb +58 -0
- data/spec/walmart_open/item_spec.rb +52 -0
- data/spec/walmart_open/order_item_spec.rb +18 -0
- data/spec/walmart_open/order_results_spec.rb +83 -0
- data/spec/walmart_open/order_spec.rb +124 -0
- data/spec/walmart_open/request_spec.rb +79 -0
- data/spec/walmart_open/requests/lookup_spec.rb +91 -0
- data/spec/walmart_open/requests/place_order_spec.rb +149 -0
- data/spec/walmart_open/requests/search_spec.rb +86 -0
- data/spec/walmart_open/requests/taxonomy_spec.rb +69 -0
- data/spec/walmart_open/requests/token_spec.rb +49 -0
- data/spec/walmart_open/search_results_spec.rb +60 -0
- data/spec/walmart_open/shipping_address_spec.rb +63 -0
- metadata +204 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
require "walmart_open/order_item"
|
2
|
+
require "walmart_open/item"
|
3
|
+
require "walmart_open/shipping_address"
|
4
|
+
require "securerandom"
|
5
|
+
|
6
|
+
module WalmartOpen
|
7
|
+
class Order
|
8
|
+
|
9
|
+
attr_reader :shipping_address
|
10
|
+
attr_accessor :billing_id, :first_name, :last_name, :partner_order_time,
|
11
|
+
:partner_order_id, :phone, :items
|
12
|
+
|
13
|
+
def initialize(params)
|
14
|
+
@shipping_address = nil
|
15
|
+
@items = []
|
16
|
+
@billing_id = params[:billing_id]
|
17
|
+
@first_name = params[:first_name]
|
18
|
+
@last_name = params[:last_name]
|
19
|
+
@phone = params[:phone]
|
20
|
+
@partner_order_id = params[:partner_order_id] || "Order-#{Digest::SHA1.hexdigest("#{Time.now.to_i}:#{SecureRandom.hex(16)}")[0..19].upcase}"
|
21
|
+
@partner_order_time = params[:partner_order_time] || Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_shipping_address(params)
|
25
|
+
@shipping_address = ShippingAddress.new(params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_item(item_or_item_id, *args)
|
29
|
+
if item_or_item_id.is_a?(Item)
|
30
|
+
# add_item(item, quantity = 1)
|
31
|
+
@items << OrderItem.new(item_or_item_id.id, item_or_item_id.price, item_or_item_id.shipping_rate, args[0] || 1)
|
32
|
+
else
|
33
|
+
# add_item(item_id, item_price, shipping_price = 0, quantity = 1)
|
34
|
+
@items << OrderItem.new(item_or_item_id, args[0], args[1] || 0, args[2] || 1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid?
|
39
|
+
base_values_valid? && !shipping_address.nil? && shipping_address.valid? && items_valid?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def base_values_valid?
|
45
|
+
billing_id && first_name && partner_order_time && partner_order_id
|
46
|
+
end
|
47
|
+
|
48
|
+
def items_valid?
|
49
|
+
items.any? && items.all?(&:valid?)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module WalmartOpen
|
2
|
+
class OrderItem
|
3
|
+
|
4
|
+
attr_accessor :item_id, :quantity, :item_price, :shipping_price
|
5
|
+
|
6
|
+
def initialize(item_id, item_price, shipping_price = 0, quantity = 1)
|
7
|
+
@item_id = item_id
|
8
|
+
@quantity = quantity
|
9
|
+
@item_price = item_price
|
10
|
+
@shipping_price = shipping_price
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
!!(item_id && quantity && item_price && shipping_price)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "walmart_open/ordered_item"
|
2
|
+
|
3
|
+
module WalmartOpen
|
4
|
+
class OrderResults
|
5
|
+
attr_reader :error, :order_id, :partner_order_id, :items, :total, :shipping, :sales_tax, :surcharge, :raw_attributes
|
6
|
+
|
7
|
+
def initialize(attrs)
|
8
|
+
@raw_attributes = attrs
|
9
|
+
|
10
|
+
if attrs["errors"] && attrs["errors"]["error"]
|
11
|
+
@error = {code: attrs["errors"]["error"]["code"], message: attrs["errors"]["error"]["message"]}
|
12
|
+
else
|
13
|
+
response = attrs["response"]
|
14
|
+
@order_id = response["orderId"]
|
15
|
+
@partner_order_id = response["partnerOrderId"]
|
16
|
+
@total = response["itemTotal"]
|
17
|
+
@shipping = response["shipping"]
|
18
|
+
@sales_tax = response["salesTax"]
|
19
|
+
@surcharge = response["surcharge"]
|
20
|
+
|
21
|
+
@items = []
|
22
|
+
if response["items"] && response["items"]["item"]
|
23
|
+
if response["items"]["item"].is_a?(Array)
|
24
|
+
items = response["items"]["item"]
|
25
|
+
else
|
26
|
+
items = [response["items"]["item"]]
|
27
|
+
end
|
28
|
+
items.each do | item |
|
29
|
+
@items << OrderedItem.new(item)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def error?
|
36
|
+
!@error.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "builder"
|
2
|
+
|
3
|
+
module WalmartOpen
|
4
|
+
class OrderXMLBuilder
|
5
|
+
attr_reader :order
|
6
|
+
|
7
|
+
def initialize(order)
|
8
|
+
@order = order
|
9
|
+
end
|
10
|
+
|
11
|
+
def build
|
12
|
+
xml = Builder::XmlMarkup.new
|
13
|
+
xml.instruct!(:xml, version: "1.0", encoding: "UTF-8")
|
14
|
+
xml.order do |xml|
|
15
|
+
payment(xml)
|
16
|
+
shipping_address(xml)
|
17
|
+
xml.partnerOrderId(order.partner_order_id)
|
18
|
+
xml.partnerOrderTime(order.partner_order_time.strftime( "%H:%M:%S"))
|
19
|
+
|
20
|
+
xml.items do |xml|
|
21
|
+
order.items.each do |order_item|
|
22
|
+
item(xml, order_item)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
xml.target!
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def shipping_address(xml)
|
34
|
+
xml.shippingAddress do |xml|
|
35
|
+
xml.firstName(order.first_name)
|
36
|
+
xml.lastName(order.last_name)
|
37
|
+
xml.street1(order.shipping_address.street1)
|
38
|
+
xml.street2(order.shipping_address.street2) if order.shipping_address.street2
|
39
|
+
xml.city(order.shipping_address.city)
|
40
|
+
xml.state(order.shipping_address.state)
|
41
|
+
xml.zip(order.shipping_address.zipcode)
|
42
|
+
xml.country(order.shipping_address.country)
|
43
|
+
xml.phone(order.phone) if order.phone
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def payment(xml)
|
48
|
+
xml.payment do |xml|
|
49
|
+
xml.billingRecordId(order.billing_id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def item(xml, order_item)
|
54
|
+
xml.item do |xml|
|
55
|
+
xml.itemId(order_item.item_id)
|
56
|
+
xml.quantity(order_item.quantity)
|
57
|
+
xml.itemPrice(order_item.item_price)
|
58
|
+
xml.shippingPrice(order_item.shipping_price)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module WalmartOpen
|
2
|
+
class OrderedItem
|
3
|
+
API_ATTRIBUTES_MAPPING = {
|
4
|
+
"itemId" => "id",
|
5
|
+
"quantity" => "quantity",
|
6
|
+
"itemPrice" => "price"
|
7
|
+
}
|
8
|
+
|
9
|
+
API_ATTRIBUTES_MAPPING.each_value do |attr_name|
|
10
|
+
attr_reader attr_name
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :raw_attributes
|
14
|
+
|
15
|
+
def initialize(attrs)
|
16
|
+
@raw_attributes = attrs
|
17
|
+
|
18
|
+
extract_known_attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def extract_known_attributes
|
24
|
+
API_ATTRIBUTES_MAPPING.each do |api_attr, attr|
|
25
|
+
next unless raw_attributes.has_key?(api_attr)
|
26
|
+
|
27
|
+
instance_variable_set("@#{attr}", raw_attributes[api_attr])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "walmart_open/request"
|
2
|
+
|
3
|
+
module WalmartOpen
|
4
|
+
class ProductRequest < Request
|
5
|
+
private
|
6
|
+
|
7
|
+
def build_url(client)
|
8
|
+
url = "https://#{client.config.product_domain}"
|
9
|
+
url << "/#{client.config.product_version}"
|
10
|
+
url << "/#{path}"
|
11
|
+
url << params_to_query_string(build_params(client))
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_params(client)
|
15
|
+
{
|
16
|
+
format: "json",
|
17
|
+
api_key: client.config.product_api_key
|
18
|
+
}.merge(params || {})
|
19
|
+
end
|
20
|
+
|
21
|
+
# Walmart API unofficially supports HTTPS so we rather hit that instead of
|
22
|
+
# HTTP. However, their SSL certificate is unverifiable so we have to tell
|
23
|
+
# HTTParty not to verify (otherwise it will complain).
|
24
|
+
def request_options(client)
|
25
|
+
{ verify: false }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "httparty"
|
2
|
+
require "uri"
|
3
|
+
require "walmart_open/errors"
|
4
|
+
|
5
|
+
module WalmartOpen
|
6
|
+
class Request
|
7
|
+
attr_reader :path, :params
|
8
|
+
|
9
|
+
def submit(client)
|
10
|
+
raise "@path must be specified" unless path
|
11
|
+
|
12
|
+
response = HTTParty.public_send(request_method, build_url(client), request_options(client))
|
13
|
+
verify_response(response)
|
14
|
+
parse_response(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_writer :path, :params
|
20
|
+
|
21
|
+
def request_method
|
22
|
+
:get
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_url(client)
|
26
|
+
raise NotImplementedError, "build_url must be implemented by subclass"
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_params(client)
|
30
|
+
# noop
|
31
|
+
end
|
32
|
+
|
33
|
+
def request_options(client)
|
34
|
+
{}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Subclasses can override this method to return a different response.
|
38
|
+
def parse_response(response)
|
39
|
+
response
|
40
|
+
end
|
41
|
+
|
42
|
+
def verify_response(response)
|
43
|
+
unless response.success?
|
44
|
+
raise WalmartOpen::AuthenticationError, response.parsed_response.inspect
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def params_to_query_string(params_hash)
|
49
|
+
params_hash = convert_param_keys(params_hash || {})
|
50
|
+
query_str = URI.encode_www_form(params_hash)
|
51
|
+
query_str.prepend("?") unless query_str.size.zero?
|
52
|
+
query_str
|
53
|
+
end
|
54
|
+
|
55
|
+
# Converts foo_bar_param to fooBarParam.
|
56
|
+
def convert_param_keys(underscored_params)
|
57
|
+
pairs = underscored_params.map do |key, value|
|
58
|
+
key = key.to_s.gsub(/_([a-z])/i) { $1.upcase }
|
59
|
+
[key, value]
|
60
|
+
end
|
61
|
+
Hash[pairs]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "walmart_open/product_request"
|
2
|
+
require "walmart_open/item"
|
3
|
+
require "walmart_open/errors"
|
4
|
+
|
5
|
+
module WalmartOpen
|
6
|
+
module Requests
|
7
|
+
class Lookup < ProductRequest
|
8
|
+
def initialize(item_id, params = {})
|
9
|
+
self.path = "items/#{item_id}"
|
10
|
+
self.params = params
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def parse_response(response)
|
16
|
+
Item.new(response.parsed_response)
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify_response(response)
|
20
|
+
if response.code == 400
|
21
|
+
raise WalmartOpen::ItemNotFoundError, response.parsed_response.inspect
|
22
|
+
end
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "walmart_open/commerce_request"
|
2
|
+
require "walmart_open/order_xml_builder"
|
3
|
+
require "walmart_open/order_results"
|
4
|
+
require "walmart_open/errors"
|
5
|
+
require "openssl"
|
6
|
+
require "base64"
|
7
|
+
|
8
|
+
|
9
|
+
module WalmartOpen
|
10
|
+
module Requests
|
11
|
+
class PlaceOrder < CommerceRequest
|
12
|
+
attr_accessor :order
|
13
|
+
|
14
|
+
def initialize(order)
|
15
|
+
self.path = "orders/place"
|
16
|
+
@order = order
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse_response(response)
|
22
|
+
OrderResults.new(response.parsed_response)
|
23
|
+
end
|
24
|
+
|
25
|
+
def verify_response(response)
|
26
|
+
if response.code == 400
|
27
|
+
raise WalmartOpen::OrderError, response.parsed_response.inspect
|
28
|
+
end
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def request_options(client)
|
33
|
+
body = build_xsd
|
34
|
+
signature = client.config.debug ? "FAKE_SIGNATURE" : sign(client.config.private_key, body)
|
35
|
+
{
|
36
|
+
headers: {
|
37
|
+
"Authorization" => client.auth_token.authorization_header,
|
38
|
+
"Content-Type" => "text/xml",
|
39
|
+
"X-Walmart-Body-Signature" => signature
|
40
|
+
},
|
41
|
+
body: body
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_params(client)
|
46
|
+
{ disablesigv: true } if client.config.debug
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_xsd
|
50
|
+
OrderXMLBuilder.new(order).build
|
51
|
+
end
|
52
|
+
|
53
|
+
def sign(key, data)
|
54
|
+
Base64.urlsafe_encode64(key.sign(OpenSSL::Digest::SHA256.new, data))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "walmart_open/product_request"
|
2
|
+
require "walmart_open/search_results"
|
3
|
+
|
4
|
+
module WalmartOpen
|
5
|
+
module Requests
|
6
|
+
class Search < ProductRequest
|
7
|
+
def initialize(query, params = {})
|
8
|
+
self.path = "search"
|
9
|
+
self.params = params.merge(query: query)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def parse_response(response)
|
15
|
+
SearchResults.new(response.parsed_response)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "walmart_open/product_request"
|
2
|
+
|
3
|
+
module WalmartOpen
|
4
|
+
module Requests
|
5
|
+
class Taxonomy < ProductRequest
|
6
|
+
def initialize
|
7
|
+
self.path = "taxonomy"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def parse_response(response)
|
13
|
+
response.parsed_response["categories"]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "walmart_open/commerce_request"
|
2
|
+
require "walmart_open/auth_token"
|
3
|
+
|
4
|
+
module WalmartOpen
|
5
|
+
module Requests
|
6
|
+
class Token < CommerceRequest
|
7
|
+
def initialize
|
8
|
+
self.path = "oauth2/token"
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def request_options(client)
|
14
|
+
{
|
15
|
+
basic_auth: {
|
16
|
+
username: client.config.commerce_api_key,
|
17
|
+
password: client.config.commerce_api_secret
|
18
|
+
},
|
19
|
+
body: {
|
20
|
+
grant_type: "client_credentials"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_response(response)
|
26
|
+
AuthToken.new(response.parsed_response, Time.parse(response.headers["date"]))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|