From 95d6cff57460d4b1966771a2cfa874c740830c34 Mon Sep 17 00:00:00 2001 From: Andrew Maltsev Date: Mon, 5 Nov 2012 18:45:14 -0800 Subject: [PATCH 01/26] Signature, tracing, endpoint fix The sha1 signature did not work for me. Replaced with SHA256. Also making sure the endpoint ends with '/' -- without it the base string for the signature does not match the original Amazon assumes and signature verification fails. Replaced GET with POST. Not sure why GET does not work, but it does not for me. Added exception catching to the example for Exception::Class newbies like myself. --- .gitignore | 6 +++ MANIFEST.SKIP | 2 + examples/request-FBA-fulfillment-report.pl | 15 +++++++- lib/Amazon/MWS/Client.pm | 44 ++++++++++++++++++---- lib/Amazon/MWS/Enumeration/ReportType.pm | 13 ++++--- 5 files changed, 65 insertions(+), 15 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95cd25a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Build +MANIFEST +MYMETA.json +MYMETA.yml +_build/ +blib/ diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 019b845..b0a066e 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -45,3 +45,5 @@ \B\._ # Avoid archives of this distribution \bAmazon-MWS-[\d\.\_]+ +^MYMETA\.yml$ +^MYMETA\.json$ diff --git a/examples/request-FBA-fulfillment-report.pl b/examples/request-FBA-fulfillment-report.pl index 21eb94a..7102c04 100644 --- a/examples/request-FBA-fulfillment-report.pl +++ b/examples/request-FBA-fulfillment-report.pl @@ -2,6 +2,7 @@ use strict; +use Amazon::MWS::Enumeration::ReportType qw(:all); use Amazon::MWS::Client; use DateTime; @@ -10,12 +11,22 @@ merchant_id => "ZZZ", marketplace_id => "VVV"); -my $req = $mws->RequestReport(ReportType => '_GET_AMAZON_FULFILLED_SHIPMENTS_DATA_', +my $req; + +eval { + $req = $mws->RequestReport(ReportType => (_GET_AMAZON_FULFILLED_SHIPMENTS_DATA_), StartDate => DateTime->now->add(weeks => -1), EndDate => DateTime->now); +}; + +if(my $e = Exception::Class->caught('Amazon::MWS::Client::Exception')) { + die $e->error . "\n" . $e->trace->as_string . "\n"; +} +elsif($@) { + die $@; +} if (my $req_id = $req->{ReportRequestInfo}->[0]->{ReportRequestId}) { open my $req, "> request.${req_id}"; close $req; } - diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 8b66bb0..13cb537 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -11,7 +11,7 @@ use DateTime; use XML::Simple; use URI::Escape; use MIME::Base64; -use Digest::HMAC_SHA1 qw(hmac_sha1); +use Digest::SHA qw(hmac_sha256_base64); use HTTP::Request; use LWP::UserAgent; use Class::InsideOut qw(:std); @@ -51,6 +51,7 @@ readonly access_key_id => my %access_key_id; readonly secret_key => my %secret_key; readonly merchant_id => my %merchant_id; readonly marketplace_id => my %marketplace_id; +readonly debugging => my %debugging; sub force_array { my ($hash, $key) = @_; @@ -129,7 +130,7 @@ sub define_api_method { Marketplace => $self->marketplace_id, Version => '2009-01-01', SignatureVersion => 2, - SignatureMethod => 'HmacSHA1', + SignatureMethod => 'HmacSHA256', Timestamp => to_amazon('datetime', DateTime->now), ); @@ -176,11 +177,21 @@ sub define_api_method { $request->content_type($args->{content_type}); } else { - $request->method('GET'); + $request->method('POST'); } $self->sign_request($request); + + if($debugging{id $self}) { + print STDERR "REQUEST: ".$request->as_string."\n"; + } + my $response = $self->agent->request($request); + + if($debugging{id $self}) { + print STDERR "RESPONSE: ".$response->as_string."\n"; + } + my $content = $response->content; my $xs = XML::Simple->new( KeepRoot => 1 ); @@ -226,14 +237,26 @@ sub sign_request { "$param=$value"; } sort keys %params; + ### print STDERR ">SIGNATURE: canonical=$canonical\n" if $debugging{id $self}; + my $string = $request->method . "\n" . $uri->authority . "\n" . $uri->path . "\n" . $canonical; - $params{Signature} = - encode_base64(hmac_sha1($string, $self->secret_key), ''); + ### print STDERR ">SIGNATURE: string=$string\n" if $debugging{id $self}; + + my $sig=hmac_sha256_base64($string, $self->secret_key); + $sig.='=' while length($sig) % 4; + + ### print STDERR ">SIGNATURE: signature=$sig\n" if $debugging{id $self}; + + $params{Signature} = $sig; + $uri->query_form(\%params); + + ### print STDERR ">SIGNATURE: uri=".$uri->as_string."\n" if $debugging{id $self}; + $request->uri($uri); } @@ -251,7 +274,12 @@ sub new { my $agent_string = "$appname/$version ($attr_str)"; $agent{id $self} = LWP::UserAgent->new(agent => $agent_string); - $endpoint{id $self} = $opts->{endpoint} || 'https://mws.amazonaws.com/'; + + $endpoint{id $self} = $opts->{endpoint} || 'https://mws.amazonservices.com/'; + + # Signature verification depends on the slash + # + $endpoint{id $self}.='/' unless $endpoint{id $self}=~/\/$/; $access_key_id{id $self} = $opts->{access_key_id} or die 'No access key id'; @@ -265,6 +293,8 @@ sub new { $marketplace_id{id $self} = $opts->{marketplace_id} or die 'No marketplace id'; + $debugging{id $self} = $opts->{debug} || $opts->{'debugging'} || 0; + return $self; } @@ -558,7 +588,7 @@ module. =head3 endpoint -Where MWS lives. Defaults to 'https://mws.amazonaws.com/'. +Where MWS lives. Defaults to 'https://mws.amazonservices.com/'. =head3 access_key_id diff --git a/lib/Amazon/MWS/Enumeration/ReportType.pm b/lib/Amazon/MWS/Enumeration/ReportType.pm index 255fc0f..e324faf 100644 --- a/lib/Amazon/MWS/Enumeration/ReportType.pm +++ b/lib/Amazon/MWS/Enumeration/ReportType.pm @@ -6,18 +6,19 @@ use warnings; use base qw(Amazon::MWS::Enumeration); __PACKAGE__->define qw( + _GET_AFN_INVENTORY_DATA_ + _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_ + _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_ + _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ _GET_FLAT_FILE_OPEN_LISTINGS_DATA_ + _GET_FLAT_FILE_ORDER_REPORT_DATA_ + _GET_FLAT_FILE_ORDERS_DATA_ + _GET_MERCHANT_CANCELLED_LISTINGS_DATA_ _GET_MERCHANT_LISTINGS_DATA_ _GET_MERCHANT_LISTINGS_DATA_LITE_ _GET_MERCHANT_LISTINGS_DATA_LITER_ - _GET_MERCHANT_CANCELLED_LISTINGS_DATA_ _GET_NEMO_MERCHANT_LISTINGS_DATA_ - _GET_AFN_INVENTORY_DATA_ - _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ _GET_ORDERS_DATA_ - _GET_FLAT_FILE_ORDER_REPORT_DATA_ - _GET_FLAT_FILE_ORDERS_DATA_ - _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_ ); 1; From 2b6aa491ea349fbc7dba05fef94c59cf9801c592 Mon Sep 17 00:00:00 2001 From: Andrew Maltsev Date: Mon, 5 Nov 2012 20:19:13 -0800 Subject: [PATCH 02/26] Bug fix for ManageReportSchedule It's an incompatible change, but without it it simply does not work. --- lib/Amazon/MWS/Client.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 13cb537..81a31b4 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -504,7 +504,7 @@ define_api_method ManageReportSchedule => }, respond => sub { my $root = shift; - convert($root, ScheduledDate => 'datetime'); + convert_ReportSchedule($root); return $root; }; From ebbcc49c1a64c2c2376e7f207115f14f4d8b5ba8 Mon Sep 17 00:00:00 2001 From: Andrew Maltsev Date: Tue, 6 Nov 2012 14:44:31 -0800 Subject: [PATCH 03/26] Version bump for require --- Build.PL | 2 +- lib/Amazon/MWS/Client.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build.PL b/Build.PL index 5674e8b..b403b46 100644 --- a/Build.PL +++ b/Build.PL @@ -31,7 +31,7 @@ my $build = Module::Build->new( 'MIME::Base64' => 0, 'Digest::MD5' => 0, - 'Digest::HMAC_SHA1' => 0, + 'Digest::SHA' => 0, }, ); diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 81a31b4..61fd629 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -3,7 +3,7 @@ package Amazon::MWS::Client; use warnings; use strict; -our $VERSION = '0.1'; +our $VERSION = '0.2'; use URI; use Readonly; From b6fbc9201e780a0244c7a549d8f7f3eb354c4b2c Mon Sep 17 00:00:00 2001 From: Andrew Maltsev Date: Tue, 6 Nov 2012 16:24:02 -0800 Subject: [PATCH 04/26] Changed report ID to a string Report ids are 64bit integers, they may overflow perl's integers. There is no math on report IDs, so it's better to treat them as strings. --- lib/Amazon/MWS/Client.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 61fd629..b8e5a73 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -491,7 +491,7 @@ define_api_method GetReport => raw_body => 1, parameters => { ReportId => { - type => 'nonNegativeInteger', + type => 'string', required => 1, } }; From f9926a792b6c9817be85903fdbbb15111143a780 Mon Sep 17 00:00:00 2001 From: Andrew Maltsev Date: Tue, 6 Nov 2012 17:12:34 -0800 Subject: [PATCH 05/26] Primitive auto-throttling --- lib/Amazon/MWS/Client.pm | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index b8e5a73..4236ad5 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -21,6 +21,19 @@ use Amazon::MWS::TypeMap qw(:all); my $baseEx; BEGIN { Readonly $baseEx => 'Amazon::MWS::Client::Exception' } +# Data for automatic throttling. First is the maximum request quota, +# second is the restore rate. + +my %throttleconfig=( + '*' => [ 10, 60 ], # conservative default + GetFeedSubmissionList => [ 10, 45 ], + GetFeedSubmissionResult => [ 15, 60 ], + GetReport => [ 15, 60 ], + GetReportList => [ 10, 60 ], + ManageReportSchedule => [ 10, 45 ], + UpdateReportAcknowledgements => [ 10, 45 ], +); + use Exception::Class ( $baseEx, "${baseEx}::MissingArgument" => { @@ -51,6 +64,7 @@ readonly access_key_id => my %access_key_id; readonly secret_key => my %secret_key; readonly merchant_id => my %merchant_id; readonly marketplace_id => my %marketplace_id; +readonly throttling => my %throttling; readonly debugging => my %debugging; sub force_array { @@ -134,6 +148,8 @@ sub define_api_method { Timestamp => to_amazon('datetime', DateTime->now), ); + $self->throttle($method_name); + foreach my $name (keys %$params) { my $param = $params->{$name}; @@ -260,6 +276,28 @@ sub sign_request { $request->uri($uri); } +sub throttle { + my ($self,$action)=@_; + + # TODO: Support bursts! + + my $cf=$throttleconfig{$action} || $throttleconfig{'*'} || return; + + my $td=$throttling{id $self}->{$action} || 0; + + my $now=time; + + my $wtime=$cf->[1] - ($now - $td); + + if($wtime>0) { + print STDERR "..throttling $action for $wtime seconds\n" if $debugging{id $self}; + + sleep $wtime; + } + + $throttling{id $self}->{$action}=$now; +} + sub new { my $class = shift; my $opts = slurp_kwargs(@_); @@ -295,6 +333,8 @@ sub new { $debugging{id $self} = $opts->{debug} || $opts->{'debugging'} || 0; + $throttling{id $self} = { }; + return $self; } From 555f189551d4d085e4b69d8e331388f0f5f1c5d0 Mon Sep 17 00:00:00 2001 From: Andrew Maltsev Date: Tue, 6 Nov 2012 19:17:19 -0800 Subject: [PATCH 06/26] Added payment settlement report types --- lib/Amazon/MWS/Enumeration/ReportType.pm | 41 ++++++++++++++++++------ 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/Amazon/MWS/Enumeration/ReportType.pm b/lib/Amazon/MWS/Enumeration/ReportType.pm index e324faf..e4a1c5a 100644 --- a/lib/Amazon/MWS/Enumeration/ReportType.pm +++ b/lib/Amazon/MWS/Enumeration/ReportType.pm @@ -7,18 +7,25 @@ use base qw(Amazon::MWS::Enumeration); __PACKAGE__->define qw( _GET_AFN_INVENTORY_DATA_ + _GET_ALT_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_ _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_ _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_ _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ + _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_ + _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_ _GET_FLAT_FILE_OPEN_LISTINGS_DATA_ _GET_FLAT_FILE_ORDER_REPORT_DATA_ _GET_FLAT_FILE_ORDERS_DATA_ + _GET_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_ _GET_MERCHANT_CANCELLED_LISTINGS_DATA_ _GET_MERCHANT_LISTINGS_DATA_ _GET_MERCHANT_LISTINGS_DATA_LITE_ _GET_MERCHANT_LISTINGS_DATA_LITER_ _GET_NEMO_MERCHANT_LISTINGS_DATA_ _GET_ORDERS_DATA_ + _GET_PAYMENT_SETTLEMENT_DATA_ + _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_ + _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_ ); 1; @@ -33,29 +40,45 @@ Amazon::MWS::Enumeration::ReportType =over 4 +=item _GET_AFN_INVENTORY_DATA_ + +=item _GET_ALT_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_ + +=item _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_ + +=item _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_ + +=item _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ + +=item _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_ + +=item _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_ + =item _GET_FLAT_FILE_OPEN_LISTINGS_DATA_ -=item _GET_MERCHANT_LISTINGS_DATA_ +=item _GET_FLAT_FILE_ORDER_REPORT_DATA_ -=item _GET_MERCHANT_LISTINGS_DATA_LITE_ +=item _GET_FLAT_FILE_ORDERS_DATA_ -=item _GET_MERCHANT_LISTINGS_DATA_LITER_ +=item _GET_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_ =item _GET_MERCHANT_CANCELLED_LISTINGS_DATA_ -=item _GET_NEMO_MERCHANT_LISTINGS_DATA_ +=item _GET_MERCHANT_LISTINGS_DATA_ -=item _GET_AFN_INVENTORY_DATA_ +=item _GET_MERCHANT_LISTINGS_DATA_LITE_ -=item _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ +=item _GET_MERCHANT_LISTINGS_DATA_LITER_ + +=item _GET_NEMO_MERCHANT_LISTINGS_DATA_ =item _GET_ORDERS_DATA_ -=item _GET_FLAT_FILE_ORDER_REPORT_DATA_ +=item _GET_PAYMENT_SETTLEMENT_DATA_ -=item _GET_FLAT_FILE_ORDERS_DATA_ +=item _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_ -=item _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_ +=item _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_ =back From 15801ca4ab086835576f068d4faeb11ea5431970 Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Wed, 21 Nov 2012 17:52:45 -0600 Subject: [PATCH 07/26] cleaned up some perl packaging and git ignores. --- .gitignore | 7 +++++++ INSTALL | 0 MANIFEST | 19 +++++++++++++++++++ MANIFEST.SKIP | 5 +++++ README | 0 5 files changed, 31 insertions(+) create mode 100644 .gitignore create mode 100644 INSTALL create mode 100644 MANIFEST create mode 100644 README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a76ef58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/.idea +/_build +META.* +MYMETA.* +Build +*.bak +*.tar.gz diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e69de29 diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..0018c21 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,19 @@ +Build.PL +examples/download-FBA-fulfillment-report.pl +examples/request-FBA-fulfillment-report.pl +INSTALL +lib/Amazon/MWS/Client.pm +lib/Amazon/MWS/Enumeration.pm +lib/Amazon/MWS/Enumeration/FeedProcessingStatus.pm +lib/Amazon/MWS/Enumeration/FeedType.pm +lib/Amazon/MWS/Enumeration/ReportProcessingStatus.pm +lib/Amazon/MWS/Enumeration/ReportType.pm +lib/Amazon/MWS/Enumeration/Schedule.pm +lib/Amazon/MWS/TypeMap.pm +MANIFEST This list of files +MANIFEST.SKIP +META.json +META.yml +README +t/enumeration.t +t/SubmitFeed.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 019b845..23bbaa0 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -38,6 +38,9 @@ \.# \.rej$ +# Avoid IDE files/dirs +\B\.idea + # Avoid OS-specific files/dirs # Mac OSX metadata \B\.DS_Store @@ -45,3 +48,5 @@ \B\._ # Avoid archives of this distribution \bAmazon-MWS-[\d\.\_]+ +^MYMETA\.yml$ +^MYMETA\.json$ diff --git a/README b/README new file mode 100644 index 0000000..e69de29 From c29cd1cd4ead9e19e4eaf972725612ca7e65fb83 Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Thu, 22 Nov 2012 21:51:37 -0600 Subject: [PATCH 08/26] add ItemCondition enumeration. change Merchant to SellerId. update API version to 2011-10-01. tack on URI path if called for when defining api method (used for Products). defined GetLowestOfferListingsForSKU method. changed POST back to GET for non-HTTP-BODY calls. --- lib/Amazon/MWS/Client.pm | 36 +++++++++---- lib/Amazon/MWS/Enumeration/ItemCondition.pm | 57 +++++++++++++++++++++ 2 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 lib/Amazon/MWS/Enumeration/ItemCondition.pm diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 4236ad5..94e2a47 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -62,7 +62,7 @@ readonly agent => my %agent; readonly endpoint => my %endpoint; readonly access_key_id => my %access_key_id; readonly secret_key => my %secret_key; -readonly merchant_id => my %merchant_id; +readonly seller_id => my %seller_id; readonly marketplace_id => my %marketplace_id; readonly throttling => my %throttling; readonly debugging => my %debugging; @@ -140,9 +140,9 @@ sub define_api_method { my %form = ( Action => $method_name, AWSAccessKeyId => $self->access_key_id, - Merchant => $self->merchant_id, - Marketplace => $self->marketplace_id, - Version => '2009-01-01', + SellerId => $self->seller_id, + MarketplaceId => $self->marketplace_id, + Version => '2011-10-01', SignatureVersion => 2, SignatureMethod => 'HmacSHA256', Timestamp => to_amazon('datetime', DateTime->now), @@ -181,6 +181,7 @@ sub define_api_method { } my $uri = URI->new($self->endpoint); + $uri->path($spec->{path}.$form{Version}) if ($spec->{path}); $uri->query_form(\%form); my $request = HTTP::Request->new; @@ -193,7 +194,7 @@ sub define_api_method { $request->content_type($args->{content_type}); } else { - $request->method('POST'); + $request->method('GET'); } $self->sign_request($request); @@ -325,8 +326,8 @@ sub new { $secret_key{id $self} = $opts->{secret_key} or die 'No secret key'; - $merchant_id{id $self} = $opts->{merchant_id} - or die 'No merchant id'; + $seller_id{id $self} = $opts->{seller_id} + or die 'No seller id'; $marketplace_id{id $self} = $opts->{marketplace_id} or die 'No marketplace id'; @@ -593,6 +594,21 @@ define_api_method UpdateReportAcknowledgements => return $root; }; +define_api_method GetLowestOfferListingsForSKU => + path => '/Products/', + parameters => { + SellerSKUList => { + type => 'SellerSKUList', + required => 1, + }, + ItemCondition => { type => 'string' }, + ExcludeMe => { type => 'boolean' }, + }, + respond => sub { + my $root = shift; + return $root; + }; + 1; __END__ @@ -638,9 +654,9 @@ Your AWS Access Key Id Your AWS Secret Access Key -=head3 merchant_id +=head3 seller_id -Your Amazon Merchant ID +Your Amazon Seller (Merchant) ID =head3 marketplace_id @@ -744,6 +760,8 @@ The raw body is returned. =head2 UpdateReportAcknowledgements +=head2 GetLowestOfferListingsForSKU + =head1 AUTHOR Paul Driver C<< frodwith@cpan.org >> diff --git a/lib/Amazon/MWS/Enumeration/ItemCondition.pm b/lib/Amazon/MWS/Enumeration/ItemCondition.pm new file mode 100644 index 0000000..10bf372 --- /dev/null +++ b/lib/Amazon/MWS/Enumeration/ItemCondition.pm @@ -0,0 +1,57 @@ +package Amazon::MWS::Enumeration::ItemCondition; + +use strict; +use warnings; + +use base qw(Amazon::MWS::Enumeration); + +__PACKAGE__->define qw( + Any + New + Used + Collectible + Refurbished + Club +); + +1; + +__END__ + +=head1 NAME + +Amazon::MWS::Enumeration::ItemCondition + +=head1 CONSTANTS + +=over 4 + +=item Any + +=item New + +=item Used + +=item Collectible + +=item Refurbished + +=item Club + +=back + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Blayne Puklich C<< blayne@excelcycle.com >> + +=head1 LICENCE AND COPYRIGHT + +Copyright (c) 2012, excelcycle L. +All rights reserved + +This module is free software; you can redistribute it and/or modify it under +the same terms as Perl itself. See L. From c3cb67982b35b47eaf90ebb6311b8e254a84b37d Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Sat, 24 Nov 2012 19:03:31 -0600 Subject: [PATCH 09/26] add eclipse files to gitignore. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9363f14..216881f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ Build *.tar.gz MANIFEST blib/ +/.project +/.includepath +/.settings From 66b4758096f9b67d2903a5765cb8422bf8ce1d24 Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Sat, 24 Nov 2012 19:06:18 -0600 Subject: [PATCH 10/26] bump version 0.3. --- lib/Amazon/MWS/Client.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 94e2a47..c519762 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -3,7 +3,7 @@ package Amazon::MWS::Client; use warnings; use strict; -our $VERSION = '0.2'; +our $VERSION = '0.3'; use URI; use Readonly; From 0383d0e3edde60febd9da50a62905b9dc80ef5e3 Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Sun, 25 Nov 2012 08:01:09 -0600 Subject: [PATCH 11/26] add example code to request lowest Product offers. --- examples/request-lowest-offer.pl | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 examples/request-lowest-offer.pl diff --git a/examples/request-lowest-offer.pl b/examples/request-lowest-offer.pl new file mode 100644 index 0000000..d36bb93 --- /dev/null +++ b/examples/request-lowest-offer.pl @@ -0,0 +1,46 @@ + +use strict; + +use Amazon::MWS::Enumeration::ItemCondition qw(:all); +use Amazon::MWS::Client; +use DateTime; +use Data::Dumper; + +my $mws = Amazon::MWS::Client->new(access_key_id=>"XXX", + secret_key => "YYY", + seller_id => "ZZZ", + marketplace_id => "VVV"); + +my @skus = qw(1234 2345 3456 4567 5678); + +my $req; + +eval { + $req = $mws->GetLowestOfferListingsForSKU(SellerSKUList => \@skus, ItemCondition => New, ExcludeMe => 1); +}; + +if(my $e = Exception::Class->caught('Amazon::MWS::Client::Exception')) { + die $e->error . "\n" . $e->trace->as_string . "\n"; +} +elsif($@) { + die $@; +} + +sub process_product { + my $product = shift; + my $lowest; + foreach my $offer (@{$product->{Product}->{LowestOfferListings}->{LowestOfferListing}}) { + if (!defined($lowest) || $offer->{Price}->{LandedPrice}->{Amount} > 0 && $offer->{Price}->{LandedPrice}->{Amount} < $lowest) { + $lowest = $offer->{Price}->{LandedPrice}->{Amount} ; + } + } + print $product->{SellerSKU}." lowest price ".$lowest."\n"; +} + +if (ref($req) eq 'ARRAY') { + foreach my $product (@$req) { + process_product($product); + } +} else { + process_product($req); +} From d40e74407959317fce2dca4648bbb1e612bdba17 Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Wed, 28 Nov 2012 11:21:28 -0600 Subject: [PATCH 12/26] add proper throttling for GetLowestOfferListingsForSKU. force arrays for response from GetLowestOfferListingsForSKU. --- examples/request-lowest-offer.pl | 8 ++------ lib/Amazon/MWS/Client.pm | 7 +++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/examples/request-lowest-offer.pl b/examples/request-lowest-offer.pl index d36bb93..ed5d355 100644 --- a/examples/request-lowest-offer.pl +++ b/examples/request-lowest-offer.pl @@ -37,10 +37,6 @@ sub process_product { print $product->{SellerSKU}." lowest price ".$lowest."\n"; } -if (ref($req) eq 'ARRAY') { - foreach my $product (@$req) { - process_product($product); - } -} else { - process_product($req); +foreach my $product (@$req) { + process_product($product); } diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index c519762..bdb9968 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -32,6 +32,7 @@ my %throttleconfig=( GetReportList => [ 10, 60 ], ManageReportSchedule => [ 10, 45 ], UpdateReportAcknowledgements => [ 10, 45 ], + GetLowestOfferListingsForSKU => [ 20, 2 ], # restore rate is 10 items every second ); use Exception::Class ( @@ -606,6 +607,12 @@ define_api_method GetLowestOfferListingsForSKU => }, respond => sub { my $root = shift; + if (ref($root) ne 'ARRAY') { + $root = [ $root ]; + } + foreach my $product (@$root) { + force_array($product->{Product}->{LowestOfferListings}, 'LowestOfferListing'); + } return $root; }; From 9ac35dc2ae91d7c137404c453adb60b1285f8e5d Mon Sep 17 00:00:00 2001 From: Blayne Puklich Date: Wed, 28 Nov 2012 12:26:35 -0600 Subject: [PATCH 13/26] Add IdType enumeration, GetMatchingProductForId Products API call (not tested!). --- MANIFEST | 5 +- MANIFEST.SKIP | 5 ++ ...st-lowest-offer.pl => get-lowest-offer.pl} | 0 lib/Amazon/MWS/Client.pm | 17 ++++++ lib/Amazon/MWS/Enumeration/IdType.pm | 57 +++++++++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) rename examples/{request-lowest-offer.pl => get-lowest-offer.pl} (100%) create mode 100644 lib/Amazon/MWS/Enumeration/IdType.pm diff --git a/MANIFEST b/MANIFEST index 0018c21..cc52f83 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,19 +1,20 @@ Build.PL examples/download-FBA-fulfillment-report.pl +examples/get-lowest-offer.pl examples/request-FBA-fulfillment-report.pl INSTALL lib/Amazon/MWS/Client.pm lib/Amazon/MWS/Enumeration.pm lib/Amazon/MWS/Enumeration/FeedProcessingStatus.pm lib/Amazon/MWS/Enumeration/FeedType.pm +lib/Amazon/MWS/Enumeration/IdType.pm +lib/Amazon/MWS/Enumeration/ItemCondition.pm lib/Amazon/MWS/Enumeration/ReportProcessingStatus.pm lib/Amazon/MWS/Enumeration/ReportType.pm lib/Amazon/MWS/Enumeration/Schedule.pm lib/Amazon/MWS/TypeMap.pm MANIFEST This list of files MANIFEST.SKIP -META.json -META.yml README t/enumeration.t t/SubmitFeed.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 23bbaa0..e273af8 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -40,6 +40,9 @@ # Avoid IDE files/dirs \B\.idea +\B\.includepath +\B\.project +\B\.settings # Avoid OS-specific files/dirs # Mac OSX metadata @@ -50,3 +53,5 @@ \bAmazon-MWS-[\d\.\_]+ ^MYMETA\.yml$ ^MYMETA\.json$ +^META\.yml$ +^META\.json$ diff --git a/examples/request-lowest-offer.pl b/examples/get-lowest-offer.pl similarity index 100% rename from examples/request-lowest-offer.pl rename to examples/get-lowest-offer.pl diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index bdb9968..16922a7 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -595,6 +595,23 @@ define_api_method UpdateReportAcknowledgements => return $root; }; +define_api_method GetMatchingProductForId => + path => '/Products/', + parameters => { + IdList => { + type => 'IdList', + required => 1, + }, + IdType => { type => 'string' }, + }, + respond => sub { + my $root = shift; + if (ref($root) ne 'ARRAY') { + $root = [ $root ]; + } + return $root; + }; + define_api_method GetLowestOfferListingsForSKU => path => '/Products/', parameters => { diff --git a/lib/Amazon/MWS/Enumeration/IdType.pm b/lib/Amazon/MWS/Enumeration/IdType.pm new file mode 100644 index 0000000..07c5642 --- /dev/null +++ b/lib/Amazon/MWS/Enumeration/IdType.pm @@ -0,0 +1,57 @@ +package Amazon::MWS::Enumeration::IdType; + +use strict; +use warnings; + +use base qw(Amazon::MWS::Enumeration); + +__PACKAGE__->define qw( + ASIN + SellerSKU + UPC + EAN + ISBN + JAN +); + +1; + +__END__ + +=head1 NAME + +Amazon::MWS::Enumeration::IdType + +=head1 CONSTANTS + +=over 4 + +=item ASIN + +=item SellerSKU + +=item UPC + +=item EAN + +=item ISBN + +=item JAN + +=back + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Blayne Puklich C<< blayne@excelcycle.com >> + +=head1 LICENCE AND COPYRIGHT + +Copyright (c) 2012, excelcycle L. +All rights reserved + +This module is free software; you can redistribute it and/or modify it under +the same terms as Perl itself. See L. From 6e756eb8485ace53409913239ba78aa15095e481 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Fri, 16 Aug 2013 17:12:53 -0500 Subject: [PATCH 14/26] Updated to work with modern MWS API; added methods Modern (as of August 2013) MWS uses POST for everything, and prefers signature calculated via HMAC SHA-256. Also added ListOrders method and GetServiceStatus methods for all methods supporting a 'GetServiceStatus' action. --- Build.PL | 2 +- lib/Amazon/MWS/Client.pm | 148 ++++++++++++++++++++++++++++---------- lib/Amazon/MWS/TypeMap.pm | 15 +++- 3 files changed, 125 insertions(+), 40 deletions(-) diff --git a/Build.PL b/Build.PL index 5674e8b..b403b46 100644 --- a/Build.PL +++ b/Build.PL @@ -31,7 +31,7 @@ my $build = Module::Build->new( 'MIME::Base64' => 0, 'Digest::MD5' => 0, - 'Digest::HMAC_SHA1' => 0, + 'Digest::SHA' => 0, }, ); diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 8b66bb0..8dc7c08 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -3,7 +3,7 @@ package Amazon::MWS::Client; use warnings; use strict; -our $VERSION = '0.1'; +our $VERSION = '0.5-SmartWarehousing'; use URI; use Readonly; @@ -11,7 +11,7 @@ use DateTime; use XML::Simple; use URI::Escape; use MIME::Base64; -use Digest::HMAC_SHA1 qw(hmac_sha1); +use Digest::SHA qw(hmac_sha256); use HTTP::Request; use LWP::UserAgent; use Class::InsideOut qw(:std); @@ -21,6 +21,15 @@ use Amazon::MWS::TypeMap qw(:all); my $baseEx; BEGIN { Readonly $baseEx => 'Amazon::MWS::Client::Exception' } +Readonly my $SERVICE_VERSIONS => { + 'Orders' => q/2011-01-01/, + 'OffAmazonPayments_Sandbox' => q/2013-01-01/, + 'OffAmazonPayments' => q/2013-01-01/, + 'FulfillmentInventory' => q/2010-10-01/, + 'FulfillmentOutbound' => q/2010-10-01/, + 'FulfillmentInbound' => q/2010-10-01/, +}; + use Exception::Class ( $baseEx, "${baseEx}::MissingArgument" => { @@ -50,7 +59,6 @@ readonly endpoint => my %endpoint; readonly access_key_id => my %access_key_id; readonly secret_key => my %secret_key; readonly merchant_id => my %merchant_id; -readonly marketplace_id => my %marketplace_id; sub force_array { my ($hash, $key) = @_; @@ -117,20 +125,21 @@ sub define_api_method { my $method_name = shift; my $spec = slurp_kwargs(@_); my $params = $spec->{parameters}; - + my $service = $spec->{'service'} || ''; + my $version = $spec->{'version'} || $SERVICE_VERSIONS->{$service}; + my $action = $spec->{'action'}; my $method = sub { my $self = shift; my $args = slurp_kwargs(@_); my $body; my %form = ( - Action => $method_name, - AWSAccessKeyId => $self->access_key_id, - Merchant => $self->merchant_id, - Marketplace => $self->marketplace_id, - Version => '2009-01-01', - SignatureVersion => 2, - SignatureMethod => 'HmacSHA1', - Timestamp => to_amazon('datetime', DateTime->now), + 'Action' => $action || $method_name, + 'AWSAccessKeyId' => $self->access_key_id, + 'SellerId' => $self->merchant_id, + 'Version' => $version || '2009-01-01', + 'SignatureVersion' => 2, + 'SignatureMethod' => 'HmacSHA256', + 'Timestamp' => to_amazon('datetime', DateTime->now), ); foreach my $name (keys %$params) { @@ -148,7 +157,10 @@ sub define_api_method { if ($type =~ /(\w+)List/) { my $list_type = $1; my $counter = 1; - foreach my $sub_value (@$value) { + if (!ref $value) { + $value = [$value]; + } + foreach my $sub_value (@{$value}) { my $listKey = "$name.$list_type." . $counter++; $form{$listKey} = $sub_value; } @@ -162,22 +174,26 @@ sub define_api_method { $form{$name} = to_amazon($type, $value); } } - my $uri = URI->new($self->endpoint); + my $path = q{/}; + if ($service && $service ne 'Feeds' && $service ne 'Reports') { + $path = join '/', $service, $form{'Version'}; + } + + $uri->path($path); $uri->query_form(\%form); my $request = HTTP::Request->new; $request->uri($uri); + + $request->method('POST'); if ($body) { - $request->method('POST'); $request->content($body); $request->header('Content-MD5' => md5_base64($body) . '=='); - $request->content_type($args->{content_type}); - } - else { - $request->method('GET'); } + + $request->content_type($args->{content_type} || 'application/x-www-form-urlencoded'); $self->sign_request($request); my $response = $self->agent->request($request); @@ -205,8 +221,8 @@ sub define_api_method { my $hash = $xs->xml_in($content); - my $root = $hash->{$method_name . 'Response'} - ->{$method_name . 'Result'}; + my $root = $hash->{$form{'Action'} . 'Response'} + ->{$form{'Action'} . 'Result'}; return $spec->{respond}->($root); }; @@ -232,7 +248,7 @@ sub sign_request { . $canonical; $params{Signature} = - encode_base64(hmac_sha1($string, $self->secret_key), ''); + encode_base64(hmac_sha256($string, $self->secret_key), ''); $uri->query_form(\%params); $request->uri($uri); } @@ -246,8 +262,8 @@ sub new { $attr->{Language} = 'Perl'; my $attr_str = join ';', map { "$_=$attr->{$_}" } keys %$attr; - my $appname = $opts->{Application} || 'Amazon::MWS::Client'; - my $version = $opts->{Version} || $VERSION; + my $appname = $opts->{Application} || $opts->{'application'} || 'Amazon::MWS::Client'; + my $version = $opts->{Version} || $opts->{'version'} || $VERSION; my $agent_string = "$appname/$version ($attr_str)"; $agent{id $self} = LWP::UserAgent->new(agent => $agent_string); @@ -262,9 +278,6 @@ sub new { $merchant_id{id $self} = $opts->{merchant_id} or die 'No merchant id'; - $marketplace_id{id $self} = $opts->{marketplace_id} - or die 'No marketplace id'; - return $self; } @@ -359,6 +372,7 @@ define_api_method RequestReport => StartDate => { type => 'datetime' }, EndDate => { type => 'datetime' }, }, + 'path' => q{/}, respond => sub { my $root = shift; convert_ReportRequestInfo($root); @@ -523,6 +537,64 @@ define_api_method UpdateReportAcknowledgements => return $root; }; +define_api_method 'ListOrders' => + 'parameters' => { + 'MarketplaceId' => { + 'type' => 'IdList', + 'required' => 1, + }, + 'CreatedAfter' => { + 'type' => 'datetime', + }, + 'CreatedBefore' => { + 'type' => 'datetime', + }, + 'LastUpdatedAfter' => { + 'type' => 'datetime', + }, + 'LastUpdatedBefore' => { + 'type' => 'datetime', + }, + 'OrderStatus' => { + 'type' => 'StatusList', + }, + 'FulfillmentChannel' => { + 'type' => 'ChannelList', + }, + 'SellerOrderID' => { + 'type' => 'string', + }, + 'BuyerEmail' => { + 'type' => 'string', + }, + 'PaymentMethod' => { + 'type' => 'MethodList', + }, + 'TFMShipmentStatus' => { + 'type' => 'StatusList', + }, + 'MaxResultsPerPage' => { + 'type' => 'string', + }, + }, + 'version' => q/2011-01-01/, + 'service' => q/Orders/, + respond => sub { + return @{$_[0]->{'Orders'}{'Order'}} + }; + +for my $service (qw/FulfillmentInbound FulfillmentOutbound FulfillmentInventory Orders Products Recommendations Sellers Subscriptions OffAmazonPayments OffAmazonPayments_Sandbox/) { + my $method = qq{Get${service}ServiceStatus}; + define_api_method $method => + 'parameters' => {}, + 'service' => $service, + 'action' => q/GetServiceStatus/, + 'version' => $SERVICE_VERSIONS->{$service}, + 'respond' => sub { + return $_[0]; + }; +} + 1; __END__ @@ -542,39 +614,39 @@ entire interface can be found at L. Constructs a new client object. Takes the following keyword arguments: -=head3 agent_attributes +=over 4 + +=item agent_attributes An attributes you would like to add (besides language=Perl) to the user agent string, as a hashref. -=head3 application +=item application The name of your application. Defaults to 'Amazon::MWS::Client' -=head3 version +=item version The version of your application. Defaults to the current version of this module. -=head3 endpoint +=item endpoint Where MWS lives. Defaults to 'https://mws.amazonaws.com/'. -=head3 access_key_id +=item access_key_id Your AWS Access Key Id -=head3 secret_key +=item secret_key Your AWS Secret Access Key -=head3 merchant_id +=item merchant_id Your Amazon Merchant ID -=head3 marketplace_id - -The marketplace id for the calls being made by this object. +=back =head1 EXCEPTIONS @@ -683,5 +755,5 @@ Paul Driver C<< frodwith@cpan.org >> Copyright (c) 2009, Plain Black Corporation L. All rights reserved -This module is free software; you can redistribute it and/or modify it under +his module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. diff --git a/lib/Amazon/MWS/TypeMap.pm b/lib/Amazon/MWS/TypeMap.pm index c28fa99..2729529 100644 --- a/lib/Amazon/MWS/TypeMap.pm +++ b/lib/Amazon/MWS/TypeMap.pm @@ -34,7 +34,20 @@ my %to_map = ( $int = 1 unless $int > 0; return $int; }, - 'datetime' => sub { shift->iso8601 } + 'datetime' => sub { + my $arg = shift; + my $is_datetime = 0; + if (ref $arg) { + $is_datetime = eval { + $arg->isa('DateTime'); + }; + # any exception here would indicate that the ref in quetion was not a DateTime, so no need to check it. + } + if ( ! $is_datetime ) { + $arg = DateTime::Format::ISO8601->parse_datetime($arg); + } + return $arg->iso8601 + }, ); sub to_amazon { From 98f3b4851f66fbb616a7c55d382e79dd8e17f40d Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 20 Aug 2013 09:02:28 -0500 Subject: [PATCH 15/26] Added method, added param to another. Added 'ListOrderItems' API method, added required parameter 'content_type' to 'SubmitFeed' API method --- lib/Amazon/MWS/Client.pm | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 8dc7c08..ecd6486 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -294,6 +294,10 @@ define_api_method SubmitFeed => PurgeAndReplace => { type => 'boolean', }, + 'content_type' => { + 'type' => 'string', + 'required' => 1, + }, }, respond => sub { my $root = shift->{FeedSubmissionInfo}; @@ -577,12 +581,23 @@ define_api_method 'ListOrders' => 'type' => 'string', }, }, - 'version' => q/2011-01-01/, 'service' => q/Orders/, respond => sub { return @{$_[0]->{'Orders'}{'Order'}} }; +define_api_method 'ListOrderItems' => + 'parameters' => { + 'AmazonOrderId' => { + 'type' => 'string', + 'required' => 1, + }, + }, + 'service' => q/Orders/, + 'respond' => sub { + return @{$_[0]->{'OrderItems'}{'OrderItem'}}; + }; + for my $service (qw/FulfillmentInbound FulfillmentOutbound FulfillmentInventory Orders Products Recommendations Sellers Subscriptions OffAmazonPayments OffAmazonPayments_Sandbox/) { my $method = qq{Get${service}ServiceStatus}; define_api_method $method => From 04fc8e3bfe6c6f7f44b0689eb90c1ed876c245d1 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 20 Aug 2013 10:33:44 -0500 Subject: [PATCH 16/26] Left a couple of merge artifacts in. oops. --- lib/Amazon/MWS/Client.pm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 7cc2b4d..c86b48e 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -11,11 +11,7 @@ use DateTime; use XML::Simple; use URI::Escape; use MIME::Base64; -<<<<<<< HEAD use Digest::SHA qw(hmac_sha256); -======= -use Digest::SHA qw(hmac_sha256_base64); ->>>>>>> bpuklich/master use HTTP::Request; use LWP::UserAgent; use Class::InsideOut qw(:std); From 502cb9bfd063218d2e7599d349aaa3fddf93b7f6 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 20 Aug 2013 10:36:49 -0500 Subject: [PATCH 17/26] MarketplaceId is now required for all methods --- lib/Amazon/MWS/Client.pm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index c86b48e..8a9d784 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -614,10 +614,6 @@ define_api_method UpdateReportAcknowledgements => define_api_method 'ListOrders' => 'parameters' => { - 'MarketplaceId' => { - 'type' => 'IdList', - 'required' => 1, - }, 'CreatedAfter' => { 'type' => 'datetime', }, From e7d4bd7764b48a838dc07f06cb26246b1bdca9c5 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 20 Aug 2013 11:25:45 -0500 Subject: [PATCH 18/26] Fixed ListOrders ListOrders wasn't working due to incorrect URL syntax. Added throttling for ListOrders and ListOrderItems --- lib/Amazon/MWS/Client.pm | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 8a9d784..20ff67b 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -35,6 +35,8 @@ Readonly my $SERVICE_VERSIONS => { my %throttleconfig=( '*' => [ 10, 60 ], # conservative default + 'ListOrders' => [ 6, 60 ], # max quota of 6, restore rate is one / minute + 'ListOrderItems' => [ 30, 2 ], # max quota of 30, restore rate is one / 2 s GetFeedSubmissionList => [ 10, 45 ], GetFeedSubmissionResult => [ 15, 60 ], GetReport => [ 15, 60 ], @@ -72,7 +74,6 @@ readonly agent => my %agent; readonly endpoint => my %endpoint; readonly access_key_id => my %access_key_id; readonly secret_key => my %secret_key; -readonly merchant_id => my %merchant_id; readonly seller_id => my %seller_id; readonly marketplace_id => my %marketplace_id; readonly throttling => my %throttling; @@ -154,7 +155,7 @@ sub define_api_method { my %form = ( 'Action' => $action || $method_name, 'AWSAccessKeyId' => $self->access_key_id, - 'SellerId' => $self->merchant_id, + 'SellerId' => $self->seller_id, 'MarketplaceId' => $self->marketplace_id, 'Version' => $version || '2009-01-01', 'SignatureVersion' => 2, @@ -162,6 +163,12 @@ sub define_api_method { 'Timestamp' => to_amazon('datetime', DateTime->now), ); + # This is hacky. Some API calls - in particular ListOrders - want marketplace ID as a list. + if ($params->{'MarketplaceId'}) { + delete $form{'MarketplaceId'}; + $args->{'MarketplaceId'} = $self->marketplace_id; + } + $self->throttle($method_name); foreach my $name (keys %$params) { @@ -310,6 +317,7 @@ sub throttle { } $throttling{id $self}->{$action}=$now; + 1; } sub new { @@ -614,6 +622,9 @@ define_api_method UpdateReportAcknowledgements => define_api_method 'ListOrders' => 'parameters' => { + 'MarketplaceId' => { + 'type' => 'IdList', + }, 'CreatedAfter' => { 'type' => 'datetime', }, From c9bfb0de7ea21cd3536d4dbfe9b92537a65ac37c Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 20 Aug 2013 11:54:29 -0500 Subject: [PATCH 19/26] Added CONTRIBUTORS section to pod --- lib/Amazon/MWS/Client.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 20ff67b..a576889 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -893,9 +893,11 @@ The raw body is returned. Paul Driver C<< frodwith@cpan.org >> -Modified by Blayne Puklich +=head1 CONTRIBUTORS + +Blayne Puklich C<< blayne@excelcycle.com >> -Further modified by Kit Peters C<< kitp@smartwarehousing.com >> +Kit Peters C<< kitp@smartwarehousing.com >> =head1 LICENCE AND COPYRIGHT From 3cd1cc5689661cca5c952f079f5ba1dba9011b2b Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Wed, 21 Aug 2013 15:48:09 -0500 Subject: [PATCH 20/26] ListOrders handles single order correctly ListOrders will now handle the case of a single order by returning the order hash in scalar context, or an array containing the order in list context. --- lib/Amazon/MWS/Client.pm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index a576889..07f855d 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -660,8 +660,14 @@ define_api_method 'ListOrders' => }, }, 'service' => q/Orders/, - respond => sub { - return @{$_[0]->{'Orders'}{'Order'}} + 'respond' => sub { + my $root = $_[0]->{'Orders'}{'Order'}; + if (ref $root eq 'ARRAY') { + return @{$root}; + } + else { + return wantarray ? ($root) : $root; + } }; define_api_method 'ListOrderItems' => From 4fa51f7a426d0a9e71dafcd56d99ff454aeaef91 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Wed, 21 Aug 2013 15:54:38 -0500 Subject: [PATCH 21/26] Revert "ListOrders handles single order correctly" This reverts commit 3cd1cc5689661cca5c952f079f5ba1dba9011b2b. --- lib/Amazon/MWS/Client.pm | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 07f855d..a576889 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -660,14 +660,8 @@ define_api_method 'ListOrders' => }, }, 'service' => q/Orders/, - 'respond' => sub { - my $root = $_[0]->{'Orders'}{'Order'}; - if (ref $root eq 'ARRAY') { - return @{$root}; - } - else { - return wantarray ? ($root) : $root; - } + respond => sub { + return @{$_[0]->{'Orders'}{'Order'}} }; define_api_method 'ListOrderItems' => From 155daf35a3735df39dee46c94ff86ab390e8f6c6 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Wed, 21 Aug 2013 15:56:37 -0500 Subject: [PATCH 22/26] ListOrders correctly handles single order In the case of a single order, ListOrders will now return the hash for that order. If there are multiple orders, ListOrders will still return an array. --- lib/Amazon/MWS/Client.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index a576889..8dd5fae 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -661,7 +661,13 @@ define_api_method 'ListOrders' => }, 'service' => q/Orders/, respond => sub { - return @{$_[0]->{'Orders'}{'Order'}} + my $root = $_->[0]->{'Orders'}{'Order'}; + if (ref $root eq 'ARRAY') { + return @{$root}; + } + else { + return $root; + } }; define_api_method 'ListOrderItems' => From 02a954b0b91e0b45f95216a7a1cc51b54d14621d Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Wed, 21 Aug 2013 16:02:00 -0500 Subject: [PATCH 23/26] fixed typo --- lib/Amazon/MWS/Client.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index 8dd5fae..d69f86c 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -661,7 +661,7 @@ define_api_method 'ListOrders' => }, 'service' => q/Orders/, respond => sub { - my $root = $_->[0]->{'Orders'}{'Order'}; + my $root = $_[0]->{'Orders'}{'Order'}; if (ref $root eq 'ARRAY') { return @{$root}; } From d5542d52800510a7ecf307a6ea3c93ea679eada9 Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Thu, 22 Aug 2013 11:09:26 -0500 Subject: [PATCH 24/26] Now correctly handles the case of no orders --- lib/Amazon/MWS/Client.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index d69f86c..acadf6c 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -662,6 +662,9 @@ define_api_method 'ListOrders' => 'service' => q/Orders/, respond => sub { my $root = $_[0]->{'Orders'}{'Order'}; + if (!$root) { + return; + } if (ref $root eq 'ARRAY') { return @{$root}; } From 3546a1804aa09821a97ae141abccb6b070fff0fd Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 3 Sep 2013 10:35:26 -0500 Subject: [PATCH 25/26] Fixed ListOrderItems return ListOrderItems will now return a scalar in the case of one item in an order, and an array in the case of multiple items. This fixes an error where the code would incorrectly try and treat a hashref as an arrayref when an order had only one item. --- lib/Amazon/MWS/Client.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index acadf6c..f83de02 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -682,7 +682,13 @@ define_api_method 'ListOrderItems' => }, 'service' => q/Orders/, 'respond' => sub { - return @{$_[0]->{'OrderItems'}{'OrderItem'}}; + my $items = $_[0]->{'OrderItems'}{'OrderItem'}; + if (ref $items eq 'ARRAY') { + return @{$items}; + } + else { + return $items; + } }; define_api_method GetMatchingProductForId => From 6a76ee9199d95db79874580132309fc7a412f57a Mon Sep 17 00:00:00 2001 From: Kit Peters Date: Tue, 3 Sep 2013 10:41:21 -0500 Subject: [PATCH 26/26] Changed version to just 0.5 --- lib/Amazon/MWS/Client.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Amazon/MWS/Client.pm b/lib/Amazon/MWS/Client.pm index f83de02..62d1c93 100644 --- a/lib/Amazon/MWS/Client.pm +++ b/lib/Amazon/MWS/Client.pm @@ -3,7 +3,7 @@ package Amazon::MWS::Client; use warnings; use strict; -our $VERSION = '0.5-SmartWarehousing'; +our $VERSION = '0.5'; use URI; use Readonly;