File Coverage

lib/Net/Amazon/RemoteCart.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Net::Amazon::RemoteCart;
2            
3 1     1   34500 use 5.006;
  1         5  
  1         46  
4              
5              
6 1     1   5 use strict;
  1         1  
  1         37  
7 1     1   5 use warnings;
  1         7  
  1         38  
8             #use diagnostics;
9              
10 1     1   498 use Net::Amazon;
  0            
  0            
11             use Net::Amazon::Response;
12             use Net::Amazon::Request;
13             ###use Data::Dumper;
14             use Log::Log4perl qw(:easy);
15              
16             our $VERSION = '0.02';
17              
18             use base qw( Net::Amazon );
19              
20              
21             sub new {
22             my($class, %options) = @_;
23              
24             if(! exists $options{token}) {
25             die "Mandatory parameter 'token' not defined";
26             }
27              
28             if(! exists $options{affiliate_id}) {
29             $options{affiliate_id} = "webservices-20";
30             }
31              
32             my $self = {
33             token => $options{token},
34             affiliate_id => $options{affiliate_id},
35             };
36              
37             # Optional attributes
38             $self->{locale} = $options{locale} if exists $options{locale};
39             $self->{cart_id} = $options{cart_id} if exists $options{cart_id};
40             $self->{hmac} = $options{hmac} if $options{hmac};
41             $self->{purchase_url} = $options{purchase_url} if $options{purchase_url};
42            
43             #Set items only if passed as a hash ref
44             $self->{items} = $options{items} if ref $options{items} eq 'HASH';
45              
46             # Set similar_products only if passed as array ref
47             $self->{similar_products} = $options{similar_products} if ref $options{similar_products} eq 'ARRAY';
48              
49             # Set items only if passed as a hash ref
50             # will hold the raw xmlrefreturned by the latest request
51             #$self->{xmlref} = $options{xmlref} if ref $options{xmlref} eq 'HASH';
52              
53             $self->{f} = 'xml'; # Always the same
54             $self->{sims} = 'true'; # Always the same
55              
56              
57             $class->SUPER::help_xml_simple_choose_a_parser();
58              
59             bless $self, ref($class) || $class;
60              
61             # Make accessors for various params
62             for my $attr ( qw( locale cart_id hmac purchase_url similar_products ) ) {
63             $class->SUPER::make_accessor($attr);
64             }
65              
66             return $self;
67             }
68              
69              
70              
71             #==========================================================
72             # synchronize the local instance with Amazon's remote cart
73             #==========================================================
74             sub sync {
75             ## my $self = shift;
76             my ($self, %params) = @_;
77              
78             if ( exists $params{cart_id} ) {
79             $self->{cart_id} = $params{cart_id};
80             }
81              
82             if ( exists $params{hmac} ) {
83             $self->{hmac} = $params{hmac};
84             }
85              
86             if(! exists $self->{cart_id}) {
87             die 'Mandatory parameter "cart_id" not defined. $cart->cart_id(mycartid)';
88             }
89              
90             if(! exists $self->{hmac}) {
91             die 'Mandatory parameter "hmac" not defined. $cart->hmac(myhmac)';
92             }
93              
94             my %req_params;
95             $req_params{"Shopping-Cart"} = 'get';
96              
97             my $res = $self->_request(\%req_params);
98             return $res;
99             }
100              
101              
102             #==========================================================
103             # remove items from cart
104             #==========================================================
105             sub remove {
106             my ($self, @items) = @_;
107              
108             if(! exists $self->{cart_id}) {
109             die 'Mandatory parameter "cart_id" not defined. Use $cart->cart_id(mycartid)';
110             }
111              
112             if(! exists $self->{hmac}) {
113             die 'Mandatory parameter "hmac" not defined. Use $cart->hmac(myhmac)';
114             }
115              
116             my (%params, $remove);
117              
118             foreach my $asin (@items ){
119             my $item_id = $self->{items}{$asin}{item_id};
120             next unless $item_id;
121             $params{"Item.$item_id"} = 0;
122             }
123              
124             $params{ShoppingCart} = 'remove';
125             my $res = $self->_request(\%params);
126              
127             return $res;
128             }
129              
130              
131             #==========================================================
132             # empty the cart
133             #==========================================================
134             sub clear {
135             my $self = shift;
136              
137             if(! exists $self->{cart_id}) {
138             die 'Mandatory parameter "cart_id" not defined. Use $cart->cart_id(mycartid)';
139             }
140              
141             if(! exists $self->{hmac}) {
142             die 'Mandatory parameter "hmac" not defined. Use $cart->hmac(myhmac)';
143             }
144              
145             my (%params, $remove);
146              
147             $params{ShoppingCart} = 'clear';
148             my $res = $self->_request(\%params);
149            
150             return $res;
151             }
152              
153              
154              
155              
156             #==========================================================
157             # change quantities
158             #==========================================================
159             sub modify {
160             my ($self, %items) = @_;
161              
162             unless ( exists $self->{cart_id} && exists $self->{hmac} ){
163             die "Cannot modify non-existent cart (i.e. that has no cart_id or hmac)";
164             }
165              
166             my (@remove_items, %params, $modify, $remove, $res);
167              
168              
169             foreach my $asin (keys %items ){
170             # If the quantity is set to 0, we'll remove the item
171             if ($items{$asin} == 0) {
172             push @remove_items, $asin;
173             $remove = 1;
174            
175             } elsif (exists $self->{items}{$asin} ){
176             my $item_id = $self->{items}{$asin}{item_id};
177             $params{"Item.$item_id"} = $items{$asin};
178             $modify = 1;
179             }
180             }
181              
182             if ( $remove ){
183             $res = $self->remove(@remove_items);
184             return $res unless $res->status;
185             }
186              
187             if ( $modify ){
188             $params{ShoppingCart} = 'modify';
189             $res = $self->_request(\%params);
190             #return $res unless $res->status;
191             #$res->status || die "Failed to modify items in cart: ", join(", ", @{$res->messages}), "\n";
192             }
193             return $res;
194              
195             }
196              
197              
198              
199             #===========================================================
200             # put stuff in the cart
201             #===========================================================
202             sub add {
203             my ($self, %items) = @_;
204              
205             my (%modify_items, %params, $modify, $add, $res);
206            
207              
208             foreach my $asin (keys %items ){
209             # If the item was already in the cart, increase the quantity by the requested num
210             if ($self->{items}{$asin} ){
211             $modify_items{$asin} = $items{$asin} + $self->{items}{$asin}{quantity};
212             $modify = 1;
213             } else {
214             $params{"Asin.$asin"} = $items{$asin};
215             $add = 1;
216             }
217             }
218              
219             if ( $modify ){
220             $res = $self->modify(%modify_items);
221             return $res unless $res->status;
222             }
223              
224             if ( $add ){
225              
226             $params{ShoppingCart} = 'add';
227             $res = $self->_request(\%params);
228             return $res unless $res->status;
229             #$res->status || die "Failed to add items to cart: ", join(", ", @{$res->messages}), "\n";
230             }
231            
232             return $res;
233             }
234              
235              
236              
237              
238             #=================================================
239             # return an array ref of hashrefs containing
240             # data about the cart items
241             #=================================================
242             sub get_items {
243             my $self = shift;
244             my @items = values %{$self->{items}};
245             return \@items;
246             }
247              
248              
249              
250             #===============================================
251             # First update the cart data from Amzn
252             # return an array ref of hashrefs containing
253             # data about the cart items
254             #===============================================
255             sub get_items_online {
256             my $self = shift;
257             $self->sync;
258             my @items = values %{$self->{items}};
259             return \@items;
260             }
261              
262              
263              
264             #=================================================
265             # Get a hashref of data for a single item
266             # based on its ASIN
267             #=================================================
268             sub get_item {
269             my ($self, $asin) = @_;
270             return $self->{items}{$asin};
271             }
272              
273              
274              
275              
276              
277             #===========================================================
278             # calculate the total cost of the items in the cart
279             #===========================================================
280             sub total_cost {
281             my $self = shift;
282              
283             return $self->{total_cost} if exists $self->{total_cost};
284              
285             my $items = $self->get_items();
286             # Assume it's impossible to have more than one currency in the same cart
287             my $currency ;
288             my $total = 0;
289             foreach ( @{ $items } ){
290             my $val;
291             ($currency, $val) = split /\s+/, $_->{our_price};
292             $total += $_->{quantity} * $val;
293             }
294            
295             # format the total val based on currency
296             unless ($currency eq 'JPY'){
297             $total = sprintf ("%.2f", $total);
298             }
299             return "$currency $total";
300             }
301              
302              
303              
304             #===========================================================
305             # Get the total cost of the items in the cart, formatted
306             # for HTML display
307             #===========================================================
308             sub total_cost_fmt {
309             my $self = shift;
310              
311             return $self->{total_cost_fmt} if exists $self->{total_cost_fmt};
312              
313             return _price_format($self->total_cost());
314             }
315              
316              
317              
318             #==========================================================
319             # fixes decimals based on currency and changes curency to
320             # HTML compatible symbol
321             #==========================================================
322             sub _price_format {
323             my $str = shift;
324              
325             return unless defined $str;
326              
327             my ($currency, $val) = split /\s+/, $str;
328              
329             # format the total val based on currency
330             unless ($currency eq 'JPY'){
331             $val = sprintf ("%.2f", $val);
332             }
333              
334             # Format the currency
335             if ($currency eq 'USD') {
336             $currency = '$';
337             } elsif ($currency eq 'GBP'){
338             $currency = '£';
339             } elsif ($currency eq 'EUR'){
340             $currency = '&euro';
341             } elsif ($currency eq 'JPY'){
342             $currency = '¥';
343             } else {
344             $currency = "$currency ";
345             }
346              
347             return "$currency$val";
348              
349             }
350              
351              
352              
353              
354             ##################################################
355             # Slightly altered version of the request method in Amazon.pm
356             sub _request {
357             ##################################################
358             my($self, $params) = @_;
359              
360             my $url = URI->new($self->intl_url(Net::Amazon::Request::amzn_xml_url()));
361              
362             my $ref;
363             my $res = Net::Amazon::Response->new();
364              
365             {
366             #$params->{locale} = $self->{locale} if $self->{locale};
367              
368             # Add the cart_id and hmac if they exist, meaning this isn't the first
369             # request for this cart object
370             $params->{CartId} = $self->{cart_id} if exists $self->{cart_id};
371             $params->{Hmac} = $self->{hmac} if exists $self->{hmac};
372              
373             # The f and sims params are always req'd so let's put em in here
374             $params->{f} = 'xml';
375             $params->{sims} = 'true';
376              
377             $url->query_form(
378             'dev-t' => $self->{token},
379             't' => $self->{affiliate_id},
380             %$params,
381             );
382              
383             my $urlstr = $url->as_string;
384              
385             DEBUG(sub { "URL string [ " . $urlstr . "]" });
386              
387             my $xml = $self->fetch_url($urlstr, $res);
388              
389             if(!defined $xml) {
390             return $res;
391             }
392              
393             DEBUG(sub { "Received [ " . $xml . "]" });
394              
395             my $xs = XML::Simple->new();
396             $ref = $xs->XMLin($xml);
397              
398             DEBUG(sub { Data::Dumper::Dumper($ref) });
399              
400             if(! defined $ref) {
401             ERROR("Invalid XML");
402             $res->messages( [ "Invalid XML" ]);
403             $res->status("");
404             return $res;
405             }
406              
407             if(exists $ref->{ErrorMsg}) {
408              
409             if (ref($ref->{ErrorMsg}) eq "ARRAY") {
410             # multiple errors, set arrary ref
411             $res->messages( $ref->{ErrorMsg} );
412             } else {
413             # single error, create array
414             $res->messages( [ $ref->{ErrorMsg} ] );
415             }
416             ERROR("Fetch Error: " . $res->message );
417             $res->status("");
418             return $res;
419             }
420              
421             # Update the cart data based on the returned xml
422             $self->_update_cart_data($ref);
423              
424             # We're gonna fall out of this loop here.
425             }
426              
427             $res->status(1);
428             return $res;
429             }
430              
431              
432              
433             #===========================================================
434             # reload the data returned from an Amazon request into
435             # the cart obj
436             #===========================================================
437             sub _update_cart_data {
438             my ($self, $xmlref) = @_;
439              
440             $self->{xmlref} = $xmlref;
441             $self->{cart_id} = $xmlref->{ShoppingCart}{CartId};
442             $self->{hmac} = $xmlref->{ShoppingCart}{HMAC};
443             $self->{purchase_url} = $xmlref->{ShoppingCart}{PurchaseUrl};
444             $self->{similar_products} = $xmlref->{ShoppingCart}{SimilarProducts}{Product}; #yeah, really
445              
446             my $items = {};
447             my @items;
448              
449             # If there is only one item, {ShoppingCart}{Items}{Item} will be
450             # a hash. If more than one, it will be an array ref. We always want
451             # an array.
452             if ( ref $xmlref->{ShoppingCart}{Items}{Item} eq 'ARRAY' ){
453             @items = @{ $xmlref->{ShoppingCart}{Items}{Item} };
454             }else{
455             $items[0] = $xmlref->{ShoppingCart}{Items}{Item};
456             }
457            
458             foreach my $item (@items) {
459             my $asin = $item->{Asin} or next;
460            
461             if ( exists $items->{$asin} ) {
462             $items->{$asin}{quantity} += $item->{Quantity};
463             } else {
464             $items->{$asin} = {
465             asin => $item->{Asin},
466             quantity => $item->{Quantity},
467             item_id => $item->{ItemId},
468             product_name => $item->{ProductName},
469             merchant_sku => $item->{MerchantSku}, # Haven't yet seen this param for real
470             list_price => $item->{ListPrice},
471             list_price_fmt => &_price_format($item->{ListPrice}),
472             our_price => $item->{OurPrice},
473             our_price_fmt => &_price_format($item->{OurPrice}),
474             };
475             }
476             }
477            
478             $self->{items} = $items;
479             $self->{total_cost} = $self->total_cost();
480             $self->{total_cost_fmt} = &_price_format($self->{total_cost});
481             }
482              
483              
484              
485             1;
486              
487             __END__