package Google::Checkout::General::Util; =head1 NAME Google::Checkout::General::Util =head1 SYNOPSIS use Google::Checkout::General::Util qw/is_gco_error compute_hmac_sha1 compute_base64 date_time_string make_xml_safe is_merchant_item is_shipping_method is_tax_table format_tax_rate get_valid_carrier is_valid_buyer_info/; =head1 DESCRIPTION Library contains a group of useful utility functions =over 4 =item is_gco_error OBJECT Returns true if OBJECT is an object (or sub-class) of C. Returns false otherwise. Many functions reteurns C to indicate error conditions so it's important to use this function to check for them. =item is_merchant_item OBJECT Returns true if OBJECT is an object (or sub-class) of C. Returns false otherwise. This function is used internally. =item is_shipping_method OBJECT Returns true if OBJECT is an object (or sub-class) of C. Returns false otherwise. This function is used internally. =item is_tax_table OBJECT Returns true if OBJECT is an object (or sub-class) of C. Returns false otherwise. This function is used internally. =item compute_hmac_sha1 DATA, B64 Compute HMAC SHA1 for DATA. If B64 is true, also encode it in Base64 before returning. =item compute_base64 DATA Compute Base64 for DATA. =item date_time_string DATE_TIME_STRING Given a valid date/time string, return it in ISO 8601 UTC format. If string is not a valid date/time string, C is returned. =item make_xml_safe XML_DATA Make XML_DATE safe to be used in a XML document. '&' is turned into '&', '>' is turned into '>' and '<' is turned into '<'. =item format_tax_rate RATE Make sure tax rate is in the right format. If RATE is less than 1, it's already in the right form so it's returned without any change. If RATE is equal to or greater than 1, it's assumed to be in percent format (for example, 25 means 25%) in which case it's turned into RATE / 100 (25 / 100 = 0.25). =item get_valid_carrier CARRIER Returns a valid carrier if CARRIER is valid. Returns C otherwise. This function is used internally. =item is_valid_buyer_info BUYER_INFO This function is used internally. =back =cut =head1 COPYRIGHT Copyright 2006 Google. All rights reserved. =cut use strict; use warnings; use Exporter; our @ISA = qw/Exporter/; our @EXPORT_OK = qw/is_gco_error compute_hmac_sha1 compute_base64 date_time_string make_xml_safe is_merchant_item is_shipping_method is_tax_table format_tax_rate get_valid_carrier is_valid_buyer_info/; use UNIVERSAL qw/isa/; use Date::Manip; use MIME::Base64; use Digest::HMAC_SHA1; use Google::Checkout::XML::Constants; #-- #-- A small hash to hold all possible valid buyer info #-- my %valid_buyer_info = ( Google::Checkout::XML::Constants::BUYER_CONTACT_NAME , 1, Google::Checkout::XML::Constants::BUYER_COMPANY_NAME , 1, Google::Checkout::XML::Constants::BUYER_EMAIL , 1, Google::Checkout::XML::Constants::BUYER_PHONE , 1, Google::Checkout::XML::Constants::BUYER_FAX , 1, Google::Checkout::XML::Constants::BUYER_ADDRESS1 , 1, Google::Checkout::XML::Constants::BUYER_ADDRESS2 , 1, Google::Checkout::XML::Constants::BUYER_CITY , 1, Google::Checkout::XML::Constants::BUYER_CITY , 1, Google::Checkout::XML::Constants::BUYER_REGION , 1, Google::Checkout::XML::Constants::BUYER_POSTAL_CODE , 1, Google::Checkout::XML::Constants::BUYER_COUNTRY_CODE , 1); sub is_gco_error { my ($obj) = @_; return is_object($obj, "Google::Checkout::General::Error"); } sub is_merchant_item { my ($obj) = @_; return is_object($obj, "Google::Checkout::General::MerchantItem"); } sub is_shipping_method { my ($obj) = @_; return is_object($obj, "Google::Checkout::General::Shipping"); } sub is_tax_table { my ($obj) = @_; return is_object($obj, "Google::Checkout::General::TaxTable"); } sub is_object { my ($obj, $name) = @_; return isa $obj, $name; } #-- #-- Given data ($data) and key ($key), compute the HMAC SHA1 #-- value. If $b64 is true, apply Base64 at the same time #-- sub compute_hmac_sha1 { #-- #-- $b64 = Apply Base64 at the same time? #-- my ($key, $data, $b64) = @_; my $hmac = Digest::HMAC_SHA1->new($key); $hmac->add($data); my $secure = $b64 ? $hmac->b64digest : $hmac->digest; #-- #-- pad with = if not multiple of 4 #-- while (length($secure) % 4) { $secure .= '='; } return $secure; } #-- #-- Given data ($data), compute the Base64 encoding #-- sub compute_base64 { my ($data) = @_; return encode_base64($data, ""); } #-- #-- Returns a ISO 8601 UTC date string #-- sub date_time_string { my ($date_time) = @_; my $date = ParseDateString($date_time); return Google::Checkout::General::Error->new( $Google::Checkout::General::Error::ERRORS{INVALID_DATE_STRING}->[0], $Google::Checkout::General::Error::ERRORS{INVALID_DATE_STRING}->[1] . ": $date_time") if (!$date); #-- #-- Return in ISO 8601 UTC format: YYYY-MM-DDThh:mm:ssZ #-- return UnixDate($date, "%Y-%m-%dT%H:%M:%SZ"); } #-- #-- GCO requires special characters to be in hex #-- form but XML::Writer generate the entity form #-- so I encode them here myself #-- sub make_xml_safe { my ($data) = @_; if ($data) { $data =~ s/&/&/g; $data =~ s/>/>/g; $data =~ s/new( $Google::Checkout::General::Error::ERRORS{INVALID_CARRIER}->[0], $Google::Checkout::General::Error::ERRORS{INVALID_CARRIER}->[1] . ": $carrier"); } sub is_valid_buyer_info { my ($info) = @_; return 0 unless defined $info; return exists $valid_buyer_info{$info} && $valid_buyer_info{$info}; } 1;