walmart_open 0.0.1
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 +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
|