mws 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.3.0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.0"
12
+ gem "rcov", ">= 0"
13
+ end
14
+
15
+ gem "httparty"
16
+ gem "awesome_print"
17
+ gem "barby"
18
+ gem "chunky_png"
19
+ gem "prawn"
20
+ gem "aws-s3", :require => "aws/s3"
21
+ gem "tzinfo"
22
+
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ Ascii85 (1.0.1)
5
+ awesome_print (0.4.0)
6
+ aws-s3 (0.6.2)
7
+ builder
8
+ mime-types
9
+ xml-simple
10
+ barby (0.4.3)
11
+ builder (3.0.0)
12
+ chunky_png (1.2.0)
13
+ crack (0.1.8)
14
+ diff-lcs (1.1.2)
15
+ git (1.2.5)
16
+ httparty (0.7.8)
17
+ crack (= 0.1.8)
18
+ jeweler (1.6.2)
19
+ bundler (~> 1.0)
20
+ git (>= 1.2.5)
21
+ rake
22
+ mime-types (1.16)
23
+ pdf-reader (0.9.2)
24
+ Ascii85 (>= 0.9)
25
+ prawn (0.11.1)
26
+ pdf-reader (>= 0.9.0)
27
+ ttfunk (~> 1.0.0)
28
+ rake (0.9.2)
29
+ rcov (0.9.9)
30
+ rspec (2.3.0)
31
+ rspec-core (~> 2.3.0)
32
+ rspec-expectations (~> 2.3.0)
33
+ rspec-mocks (~> 2.3.0)
34
+ rspec-core (2.3.1)
35
+ rspec-expectations (2.3.0)
36
+ diff-lcs (~> 1.1.2)
37
+ rspec-mocks (2.3.0)
38
+ ttfunk (1.0.1)
39
+ tzinfo (0.3.29)
40
+ xml-simple (1.1.0)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ awesome_print
47
+ aws-s3
48
+ barby
49
+ bundler (~> 1.0.0)
50
+ chunky_png
51
+ httparty
52
+ jeweler (~> 1.6.0)
53
+ prawn
54
+ rcov
55
+ rspec (~> 2.3.0)
56
+ tzinfo
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Anton Zolotov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,69 @@
1
+ # mws
2
+
3
+ ## Usage
4
+
5
+ #### 1. Initialize Client
6
+
7
+ ``` ruby
8
+ client = MWS::Client.new({
9
+ :access_key_id => "",
10
+ :secret_access_key => "",
11
+ :merchant_id => "",
12
+ :marketplace_id => ""
13
+ })
14
+ ```
15
+
16
+ #### 2. Set Address
17
+
18
+ ``` ruby
19
+ client.from_address = {
20
+ :name => "",
21
+ :address_line_1 => "",
22
+ :address_line_2 => "",
23
+ :city => "",
24
+ :state_or_province_code => "",
25
+ :country_code => "",
26
+ :postal_code => ""
27
+ }
28
+ ```
29
+
30
+ #### 3. Add Items
31
+
32
+ ``` ruby
33
+ client.items << {
34
+ :condition => "",
35
+ :seller_sku => "",
36
+ :asin => "",
37
+ :quantity => ""
38
+ }
39
+ ```
40
+
41
+ #### 4. Create Inbound Shipment Plan
42
+
43
+ ``` ruby
44
+ inbound_shipment_plan = client.create_inbound_shipment_plan
45
+ ```
46
+
47
+ #### 5. Create Inbound Shipment
48
+
49
+ ``` ruby
50
+ inbound_shipment = client.create_inbound_shipment
51
+ ```
52
+
53
+ #### 6. Generate Label & Packing Slip
54
+
55
+ # Contributing to mws
56
+
57
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
58
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
59
+ * Fork the project
60
+ * Start a feature/bugfix branch
61
+ * Commit and push until you are happy with your contribution
62
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
63
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
64
+
65
+ ## Copyright
66
+
67
+ Copyright (c) 2011 Anton Zolotov. See LICENSE.txt for
68
+ further details.
69
+
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "mws"
18
+ gem.homepage = "http://github.com/azolotov/mws"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Wrapper for the Amazon MWS API}
21
+ gem.description = %Q{A simple wrapper for the Amazon MWS API.}
22
+ gem.email = "anton@zolotov.eu"
23
+ gem.authors = ["Anton Zolotov"]
24
+ gem.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "mws #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/mws.rb ADDED
@@ -0,0 +1,224 @@
1
+ require 'httparty'
2
+ require 'awesome_print'
3
+ require 'tzinfo'
4
+
5
+ # required to sign requests
6
+ require 'openssl'
7
+ require 'base64'
8
+
9
+ # include mws modules
10
+ require 'mws/address'
11
+ require 'mws/request'
12
+ require 'mws/packing_slip'
13
+ require 'mws/fba_label'
14
+
15
+ # barcodes
16
+ require 'barby'
17
+ require 'barby/outputter/png_outputter'
18
+
19
+ ##
20
+ # MWS
21
+ #
22
+ module MWS
23
+
24
+ ##
25
+ # MWS::Client
26
+ #
27
+ class Client
28
+ def initialize(args={})
29
+ args.each { |k, v| send("#{k}=", v) }
30
+ @items = []
31
+ end
32
+
33
+ # Amazon Access Key ID
34
+ attr_accessor :access_key_id
35
+
36
+ # Amazon Secret Access Key
37
+ attr_accessor :secret_access_key
38
+
39
+ # Amazon Merchant ID (also Seller ID)
40
+ attr_accessor :merchant_id
41
+
42
+ # Amazon Marketplace
43
+ attr_accessor :marketplace_id
44
+
45
+ # From Address
46
+ attr_accessor :from_address
47
+
48
+ # Label Prep Preference
49
+ attr_accessor :label_prep_preference
50
+
51
+ # Items
52
+ attr_accessor :items
53
+
54
+ # Inbound Shipment
55
+ attr_accessor :inbound_shipment, :inbound_shipment_plan
56
+
57
+ # Shipment Name
58
+ attr_accessor :shipment_name
59
+
60
+ # Shipment ID (assigned by amazon)
61
+ attr_accessor :shipment_id
62
+
63
+ attr_accessor :s3_access_key_id, :s3_secret_access_key
64
+
65
+ attr_accessor :shipment_status
66
+
67
+ attr_accessor :fulfillment_center_id
68
+
69
+ ##
70
+ # GetServiceStatus
71
+ #
72
+ def get_service_status
73
+ Request.new({
74
+ 'Action' => 'GetServiceStatus',
75
+ 'Marketplace' => @marketplace_id,
76
+ 'SellerId' => @merchant_id,
77
+ 'AWSAccessKeyId' => @access_key_id
78
+ },@secret_access_key).post
79
+ end
80
+
81
+ ##
82
+ # CreateInboundShipmentPlan
83
+ #
84
+ def create_inbound_shipment_plan
85
+ request = Request.new({
86
+ 'Action' => 'CreateInboundShipmentPlan',
87
+ 'Marketplace' => @marketplace_id,
88
+ 'SellerId' => @merchant_id,
89
+ 'AWSAccessKeyId' => @access_key_id,
90
+ 'ShipFromAddress.Name' => @from_address[:name],
91
+ 'ShipFromAddress.AddressLine1' => @from_address[:address_line_1],
92
+ 'ShipFromAddress.AddressLine2' => @from_address[:address_line_2],
93
+ 'ShipFromAddress.City' => @from_address[:city],
94
+ 'ShipFromAddress.StateOrProvinceCode' => @from_address[:state_or_province_code],
95
+ 'ShipFromAddress.PostalCode' => @from_address[:postal_code],
96
+ 'ShipFromAddress.CountryCode' => @from_address[:country_code],
97
+ 'LabelPrepPreference' => @label_prep_preference
98
+ },@secret_access_key)
99
+
100
+ @items.flatten!.each_with_index do |item, index|
101
+ request << {
102
+ "InboundShipmentPlanRequestItems.member.#{index+1}.Condition" => item[:condition],
103
+ "InboundShipmentPlanRequestItems.member.#{index+1}.SellerSKU" => item[:seller_sku],
104
+ "InboundShipmentPlanRequestItems.member.#{index+1}.ASIN" => item[:asin],
105
+ "InboundShipmentPlanRequestItems.member.#{index+1}.Quantity" => item[:quantity]
106
+ }
107
+ end
108
+
109
+ @inbound_shipment_plan = request.post["CreateInboundShipmentPlanResponse"]["CreateInboundShipmentPlanResult"]["InboundShipmentPlans"]
110
+ @fulfillment_center_id = @inbound_shipment_plan["member"]["DestinationFulfillmentCenterId"]
111
+ @inbound_shipment_plan
112
+ end
113
+
114
+ ##
115
+ # CreateInboundShipment
116
+ #
117
+ def create_inbound_shipment
118
+ puts "creating new inbound shipment..."
119
+
120
+ request = Request.new({
121
+ 'Action' => 'CreateInboundShipment',
122
+ 'Marketplace' => @marketplace_id,
123
+ 'SellerId' => @merchant_id,
124
+ 'AWSAccessKeyId' => @access_key_id,
125
+ 'InboundShipmentHeader.ShipmentName' => @shipment_name,
126
+ 'InboundShipmentHeader.ShipFromAddress.Name' => @from_address[:name],
127
+ 'InboundShipmentHeader.ShipFromAddress.AddressLine1' => @from_address[:address_line_1],
128
+ 'InboundShipmentHeader.ShipFromAddress.AddressLine2' => @from_address[:address_line_2],
129
+ 'InboundShipmentHeader.ShipFromAddress.City' => @from_address[:city],
130
+ 'InboundShipmentHeader.ShipFromAddress.StateOrProvinceCode' => @from_address[:state_or_province_code],
131
+ 'InboundShipmentHeader.ShipFromAddress.PostalCode' => @from_address[:postal_code],
132
+ 'InboundShipmentHeader.ShipFromAddress.CountryCode' => @from_address[:country_code],
133
+ 'InboundShipmentHeader.DestinationFulfillmentCenterId' => @inbound_shipment_plan["member"]["DestinationFulfillmentCenterId"]
134
+ },@secret_access_key)
135
+
136
+ puts "created request"
137
+
138
+ @inbound_shipment_plan["member"]["Items"]["member"].each_with_index do |item, index|
139
+ request << {
140
+ "InboundShipmentItems.member.#{index+1}.QuantityShipped" => item["Quantity"],
141
+ "InboundShipmentItems.member.#{index+1}.SellerSKU" => item["SellerSKU"]
142
+ }
143
+ end
144
+
145
+ puts "added items"
146
+ response = request.post.to_hash
147
+ ap response
148
+ @shipment_id = request.post["CreateInboundShipmentResponse"]["CreateInboundShipmentResult"]["ShipmentId"]
149
+ end
150
+
151
+ def format_from_address
152
+ [
153
+ @from_address[:name],
154
+ [@from_address[:address_line_1], @from_address[:address_line_2]].join,
155
+ "#{@from_address[:city]}, #{@from_address[:state_or_province_code]} #{@from_address[:postal_code]}",
156
+ TZInfo::Country.get(@from_address[:country_code]).name
157
+ ].join("\n")
158
+ end
159
+
160
+ def format_to_address
161
+ ship_to_address = @inbound_shipment_plan["member"]["ShipToAddress"]
162
+
163
+ [
164
+ ship_to_address["Name"],
165
+ [ship_to_address["AddressLine1"], (ship_to_address["AddressLine2"] || [])].join,
166
+ "#{ship_to_address["City"]}, #{ship_to_address["StateOrProvinceCode"]} #{ship_to_address["PostalCode"]}",
167
+ TZInfo::Country.get(ship_to_address["CountryCode"]).name
168
+ ].join("\n")
169
+ end
170
+
171
+ def fba_label
172
+ FBA::ShippingLabel.new({
173
+ :shipment_id => @shipment_id,
174
+ :ship_from_address => format_from_address,
175
+ :ship_to_address => format_to_address,
176
+ :access_key_id => @s3_access_key_id,
177
+ :secret_access_key => @s3_secret_access_key,
178
+ :template_url => "http://bsgnpo.s3.amazonaws.com/template.fba.label.pdf",
179
+ :bucket => 'bsgnpo'
180
+ })
181
+ end
182
+
183
+ def fba_packing_slip
184
+ MWS::PackingSlip.new({
185
+ :shipment_id => @shipment_id,
186
+ :ship_from_address => format_from_address,
187
+ :ship_to_address => format_to_address,
188
+ :access_key_id => @s3_access_key_id,
189
+ :secret_access_key => @s3_secret_access_key,
190
+ :template_url => "http://bsgnpo.s3.amazonaws.com/template.fba.packing.slip.pdf",
191
+ :bucket => 'bsgnpo'
192
+ })
193
+ end
194
+
195
+ def set_shipment_status(status)
196
+ request = Request.new({
197
+ 'Action' => 'CreateInboundShipment',
198
+ 'Marketplace' => @marketplace_id,
199
+ 'SellerId' => @merchant_id,
200
+ 'AWSAccessKeyId' => @access_key_id,
201
+ 'InboundShipmentHeader.ShipmentName' => @shipment_name,
202
+ 'InboundShipmentHeader.ShipmentStatus' => status,
203
+ 'InboundShipmentHeader.ShipFromAddress.Name' => @from_address[:name],
204
+ 'InboundShipmentHeader.ShipFromAddress.AddressLine1' => @from_address[:address_line_1],
205
+ 'InboundShipmentHeader.ShipFromAddress.AddressLine2' => @from_address[:address_line_2],
206
+ 'InboundShipmentHeader.ShipFromAddress.City' => @from_address[:city],
207
+ 'InboundShipmentHeader.ShipFromAddress.StateOrProvinceCode' => @from_address[:state_or_province_code],
208
+ 'InboundShipmentHeader.ShipFromAddress.PostalCode' => @from_address[:postal_code],
209
+ 'InboundShipmentHeader.ShipFromAddress.CountryCode' => @from_address[:country_code],
210
+ 'InboundShipmentHeader.DestinationFulfillmentCenterId' => @fulfillment_center_id
211
+ },@secret_access_key)
212
+
213
+ response = request.post.to_hash
214
+ shipment_id = response["CreateInboundShipmentResponse"]["CreateInboundShipmentResult"]["ShipmentId"]
215
+
216
+ if shipment_id
217
+ @shipment_status = status
218
+ end
219
+ shipment_id
220
+ end
221
+
222
+ end
223
+
224
+ end
@@ -0,0 +1,25 @@
1
+ ##
2
+ # MWS::Address
3
+ #
4
+ module MWS
5
+ class Address
6
+ def initialize(args={})
7
+ args.each { |k, v| send("#{k}=", v) }
8
+ end
9
+
10
+ attr_accessor :name, :address_line_1, :address_line_2, :city, :state_or_province_code, :country_code, :postal_code
11
+
12
+ def to_hash
13
+ {
14
+ :name => @name,
15
+ :address_line_1 => @address_line_1,
16
+ :address_line_2 => @address_line_2,
17
+ :city => @city,
18
+ :state_or_province_code => @state_or_province_code,
19
+ :country_code => @country_code,
20
+ :postal_code => @postal_code
21
+ }
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,86 @@
1
+ require 'prawn'
2
+ require 'prawn/security'
3
+ require "prawn/layout"
4
+ require 'barby'
5
+ require 'barby/outputter/png_outputter'
6
+ require 'barby/outputter/prawn_outputter'
7
+ require 'aws/s3'
8
+ require 'open-uri'
9
+
10
+ # fba_label = MWS::FBA::ShippingLabel.new({
11
+ # :shipment_id => "FBA6PPQV5",
12
+ # :ship_from_address => "Anton Zolotov\n1255 25th Street NW APT 405\nWashington, DC 20037\nUnited States",
13
+ # :ship_to_address => "Amazon.com\n1221 24th Street NW, APT 228\nWashington, DC 20037\nUnited States",
14
+ # :access_key_id => 'AKIAIDRBUMU6MCLORZ6Q',
15
+ # :secret_access_key => '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',
16
+ # :template_url => "http://bsgnpo.s3.amazonaws.com/template.fba.label.pdf",
17
+ # :bucket => 'bsgnpo'
18
+ # })
19
+ # fba_label.build_pdf
20
+
21
+ module MWS
22
+ module FBA
23
+ class ShippingLabel
24
+
25
+ attr_accessor :url, :shipment_id, :ship_from_address, :ship_to_address, :access_key_id, :secret_access_key, :template_url, :bucket
26
+
27
+ def initialize(args={})
28
+ args.each { |k, v| send("#{k}=", v) }
29
+
30
+ AWS::S3::Base.establish_connection!(
31
+ :access_key_id => @access_key_id,
32
+ :secret_access_key => @secret_access_key
33
+ )
34
+ upload
35
+ end
36
+
37
+ ##
38
+ # Build PDF document and return Prawn::Document, ready to be saved locally or uploaded to S3
39
+ #
40
+ def build_pdf
41
+ Prawn::Document.new(:template => open(@template_url)) do |pdf|
42
+
43
+ common_options = {
44
+ :width => 200,
45
+ :height => 50,
46
+ :align => :left,
47
+ :valign => :top,
48
+ :overflow => :shrink_to_fit,
49
+ }
50
+ # print from address
51
+ pdf.font "Helvetica", :size => 8
52
+ pdf.text_box(@ship_from_address, common_options.merge({:at => [375,498]}))
53
+
54
+ # print to address
55
+ pdf.font "Helvetica", :style => :bold, :size => 11
56
+ pdf.text_box(@ship_to_address, common_options.merge({:at => [540,498], :height => 40}))
57
+
58
+ # print shipment ID
59
+ pdf.font "Helvetica", :style => :bold, :size => 11
60
+ puts pdf.text_box(@shipment_id, common_options.merge({
61
+ :width => 170,
62
+ :height => 16,
63
+ :align => :center,
64
+ :at => [455,374],
65
+ }))
66
+
67
+ # Generate barcode, store on s3, insert into pdf from S3
68
+ barcode_png = Barby::Code128A.new(@shipment_id).to_png(:height => 50, :margin => 0)
69
+ barcode_filename = "barcode_#{@shipment_id}.png"
70
+ AWS::S3::S3Object.store(barcode_filename, barcode_png, @bucket)
71
+ barcode_url = AWS::S3::S3Object.url_for(barcode_filename, @bucket, :expires_in => 3600)
72
+ pdf.image open(barcode_url), :at => [470,430]
73
+ end
74
+
75
+ end
76
+
77
+ def upload
78
+ filename = "FBA_label_#{@shipment_id}.pdf"
79
+ AWS::S3::S3Object.store(filename, build_pdf.render, @bucket)
80
+ @url = AWS::S3::S3Object.url_for(filename, @bucket, :expires_in => 3600)
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+
@@ -0,0 +1,85 @@
1
+ require 'prawn'
2
+ require 'prawn/security'
3
+ require "prawn/layout"
4
+ require 'barby'
5
+ require 'barby/outputter/png_outputter'
6
+ require 'barby/outputter/prawn_outputter'
7
+ require 'aws/s3'
8
+ require 'open-uri'
9
+
10
+ ##
11
+ # packing_slip = MWS::PackingSlip.new({
12
+ # :shipment_id => "FBA6PPQV5",
13
+ # :ship_from_address => "Anton Zolotov\n1255 25th Street NW APT 405\nWashington, DC 20037\nUnited States",
14
+ # :ship_to_address => "Amazon.com\n1221 24th Street NW, APT 228\nWashington, DC 20037\nUnited States",
15
+ # :access_key_id => 'AKIAIDRBUMU6MCLORZ6Q',
16
+ # :secret_access_key => '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',
17
+ # :template_url => "http://bsgnpo.s3.amazonaws.com/template.fba.packing.slip.pdf",
18
+ # :bucket => 'bsgnpo'
19
+ # })
20
+ # puts packing_slip.url
21
+ #
22
+ module MWS
23
+ class PackingSlip
24
+ attr_accessor :url, :shipment_id, :ship_from_address, :ship_to_address, :access_key_id, :secret_access_key, :template_url, :bucket
25
+
26
+ def initialize(args={})
27
+ args.each { |k, v| send("#{k}=", v) }
28
+
29
+ AWS::S3::Base.establish_connection!(
30
+ :access_key_id => @access_key_id,
31
+ :secret_access_key => @secret_access_key
32
+ )
33
+
34
+ upload
35
+ end
36
+
37
+ ##
38
+ # Build PDF document and return Prawn::Document, ready to be saved locally or uploaded to S3
39
+ #
40
+ def build_pdf
41
+ Prawn::Document.new(:template => open(@template_url)) do |pdf|
42
+ common_options = {
43
+ :width => 200,
44
+ :height => 50,
45
+ :align => :left,
46
+ :valign => :top,
47
+ :overflow => :shrink_to_fit,
48
+ }
49
+ # print from address
50
+ puts pdf.text_box(@ship_from_address, common_options.merge({:at => [95,392]}))
51
+
52
+ # print to address
53
+ pdf.text_box(@ship_to_address, common_options.merge({:at => [308,392]}))
54
+
55
+ # print shipment ID
56
+ pdf.font "Helvetica", :style => :bold, :size => 16
57
+ pdf.text_box(@shipment_id, common_options.merge({
58
+ :width => 170,
59
+ :height => 16,
60
+ :align => :center,
61
+ :at => [185,250],
62
+ }))
63
+
64
+ # Generate barcode, store on s3, insert into pdf from S3
65
+ barcode_png = Barby::Code128A.new(@shipment_id).to_png(:height => 60, :width => 142, :margin => 0)
66
+ barcode_filename = "barcode_#{@shipment_id}.png"
67
+ AWS::S3::S3Object.store(barcode_filename, barcode_png, @bucket)
68
+ barcode_url = AWS::S3::S3Object.url_for(barcode_filename, @bucket, :expires_in => 3600)
69
+ pdf.image open(barcode_url), :at => [200,315]
70
+ end
71
+
72
+ end
73
+
74
+ ##
75
+ # Upload packing slip to S3
76
+ #
77
+ def upload
78
+ filename = "packing_slip_#{@shipment_id}.pdf"
79
+ AWS::S3::S3Object.store(filename, build_pdf.render, @bucket)
80
+ @url = AWS::S3::S3Object.url_for(filename, @bucket, :expires_in => 3600)
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,66 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'httparty'
4
+
5
+ module MWS
6
+
7
+ class Request
8
+ include HTTParty
9
+ base_uri 'https://mws.amazonservices.com'
10
+ debug_output $stdout
11
+
12
+ DEFAULTS = {
13
+ "Version" => "2010-10-01",
14
+ "SignatureVersion" => "2",
15
+ "SignatureMethod" => "HmacSHA256",
16
+ }
17
+
18
+ attr_accessor :parameters
19
+
20
+ def initialize(parameters, secret_access_key)
21
+ @parameters = parameters.merge!(DEFAULTS)
22
+ @secret_access_key = secret_access_key
23
+ end
24
+
25
+ def <<(hash)
26
+ @parameters.merge!(hash)
27
+ end
28
+
29
+ def post
30
+ self.class.post('/FulfillmentInboundShipment/2010-10-01?' + sign(build_query_string))
31
+ end
32
+
33
+ private
34
+
35
+ def timestamp
36
+ Time.now.utc.strftime '%Y-%m-%dT%H:%M:%SZ'
37
+ end
38
+
39
+ def escape(value)
40
+ value.gsub(/([^a-zA-Z0-9_.~-]+)/) do
41
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
42
+ end
43
+ end
44
+
45
+ def normalize
46
+ @parameters.inject({}) do |hash, (k, v)|
47
+ v = v.is_a?(Array) ? v.join(',') : v.to_s
48
+ k = k.to_s.split('_').map {|w| w[0, 1] = w[0, 1].upcase; w }.join
49
+ hash[k] = v
50
+ hash
51
+ end
52
+ end
53
+
54
+ def build_query_string
55
+ normalize.merge('Timestamp' => timestamp).sort.map { |k, v| "#{k}=" + escape(v) }.join('&')
56
+ end
57
+
58
+ def sign(url)
59
+ digest = OpenSSL::Digest::Digest.new('sha256')
60
+ url_to_sign = [ "POST", "mws.amazonservices.com", '/FulfillmentInboundShipment/2010-10-01', url].join("\n")
61
+ hmac = OpenSSL::HMAC.digest(digest, @secret_access_key, url_to_sign)
62
+ url + "&Signature=" + escape([hmac].pack('m').chomp)
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module MWS
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mws
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Anton Zolotov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-07 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: httparty
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: awesome_print
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: barby
39
+ requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: chunky_png
50
+ requirement: &id004 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: prawn
61
+ requirement: &id005 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: *id005
70
+ - !ruby/object:Gem::Dependency
71
+ name: aws-s3
72
+ requirement: &id006 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: *id006
81
+ - !ruby/object:Gem::Dependency
82
+ name: tzinfo
83
+ requirement: &id007 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: *id007
92
+ - !ruby/object:Gem::Dependency
93
+ name: rspec
94
+ requirement: &id008 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ version: 2.3.0
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: *id008
103
+ - !ruby/object:Gem::Dependency
104
+ name: bundler
105
+ requirement: &id009 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.0
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: *id009
114
+ - !ruby/object:Gem::Dependency
115
+ name: jeweler
116
+ requirement: &id010 !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ~>
120
+ - !ruby/object:Gem::Version
121
+ version: 1.6.0
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: *id010
125
+ - !ruby/object:Gem::Dependency
126
+ name: rcov
127
+ requirement: &id011 !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: "0"
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: *id011
136
+ description: A simple wrapper for the Amazon MWS API.
137
+ email: anton@zolotov.eu
138
+ executables: []
139
+
140
+ extensions: []
141
+
142
+ extra_rdoc_files:
143
+ - LICENSE.txt
144
+ - README.markdown
145
+ files:
146
+ - Gemfile
147
+ - Gemfile.lock
148
+ - LICENSE.txt
149
+ - README.markdown
150
+ - Rakefile
151
+ - VERSION
152
+ - lib/mws.rb
153
+ - lib/mws/address.rb
154
+ - lib/mws/fba_label.rb
155
+ - lib/mws/packing_slip.rb
156
+ - lib/mws/request.rb
157
+ - lib/mws/version.rb
158
+ homepage: http://github.com/azolotov/mws
159
+ licenses:
160
+ - MIT
161
+ post_install_message:
162
+ rdoc_options: []
163
+
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ hash: 3251270039587644097
172
+ segments:
173
+ - 0
174
+ version: "0"
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ none: false
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: "0"
181
+ requirements: []
182
+
183
+ rubyforge_project:
184
+ rubygems_version: 1.7.2
185
+ signing_key:
186
+ specification_version: 3
187
+ summary: Wrapper for the Amazon MWS API
188
+ test_files: []
189
+