[ic] Canadian shipping woes.

Honest to Goodness interchange-users@interchange.redhat.com
Wed May 8 15:10:01 2002


>  HG> I set up [ic] to fetch the shipping rates directly from the Canada
>  HG> Post eparcel server when the checkout page is loaded. Inside a 
>  HG> perl
> 
> I posted a repsonse to this on the list, too, but did not see your 
> comments. Would you mind sharing the details how you did this?
> 
First you need an account for the eparcel service from CanPost. This is
easy to get and free. Then you can go to their website at
http://206.191.4.228/. Until you get your ID number from them you use
the development server at 206.191.4.228 with the test id: CPC_HELOISE. A
good sample request is here:
http://206.191.4.228/ServerV3/HTTPInterface.html

Next you need to put this usertag canpo.tag where it can be global and
make sure that interchange.cfg loads it:

UserTag canpo Order inp
UserTag canpo Routine <<EOR
sub {
my @inp = @_;
my $server = shift @inp;
unless ($server =~ /\d{1,3}\.\d{1,3}\.\d{1,3}/){return('error',"Bad
server ip: $server");}
unless ($#inp>=23){return('error',"Badly formed request, only $#inp
lines sent.");}

use IO::Socket::INET;
use Vend::Session;

my $port = 30000;
my $line = '';
my $op1 = 0;

#---------open the socket
my $sock = IO::Socket::INET->new(
  Proto  => "tcp",
  PeerAddr => $server,
  PeerPort => $port,
  Reuse => 1
  );
 unless ($sock) { return("Unable to connect to Canada Post server"); }
 $sock->autoflush(1);

#---------send out the xml
 foreach $line (@inp) {
   print $sock "$line\n";
 }

#---------receive the response
my @out = '';
yup: while ( <$sock> ) {
     if (/ERROR_CODE=/i) {$op1 = 1;}
     last yup if (/END_OF_EPARCEL/);      #---check for end of line
     push (@out, $_);
     }
close $sock;

#------check for error, log @out if it failed
 if ($op1 == 1) {
   my $time = localtime;
   ::logDebug("\n$time\n@out");
   unshift (@out, "error");
 }
 return @out;
}
EOR

In /pages/ord/checkout.html you need to insert this perl block:

[perl subs=1 global=1]

my $num=0;
my @out;
my @cod;
my @nod;
my $lik;

#if nothing ordered then buzz off - nothing to ship!
my $tot = $Vend::Session->{latest_subtotal};
if ($tot == 0) {return('This cart is empty.');}

# if not logged in then suggest doing so
unless ($Values->{zip}) {return('Canada Post rates will be available
after you log in or enter your address.')}

push(@cod,$_->{code}) for (@{$Carts->{main}});
push(@nod,$_->{quantity}) for (@{$Carts->{main}});

#return('skipped'); # decide if total has changed - create scratch from
lastotal
$Scratch->{can1} = 0.00;  #this hides the routes on checkout
$Scratch->{can2} = 0.00;
$Scratch->{can3} = 0.00;
$Scratch->{can4} = 0.00;

#compose the xml as efficiently as possible
my @out = '<?xml version="1.0" ?>';
push (@out, '<eparcel>');
push (@out, '<language>en</language>');
push (@out, '<ratesAndServicesRequest>');
push (@out, '<merchantCPCID>CPC_HELOISE</merchantCPCID>');
push (@out, '<fromPostalCode>m6p2g7</fromPostalCode>');
push (@out, '<turnAroundTime>80</turnAroundTime>');
push (@out, "<itemsPrice>$tot</itemsPrice>");
push (@out, '<lineItems>');
foreach $lik (@cod) {
 push (@out, '<item>');
 push (@out, "<quantity>$nod[$num++]</quantity>");
 push (@out,
'<weight>',$Tag->data('products','weight',"$lik"),'</weight>');
 push (@out,
'<length>',$Tag->data('products','length',"$lik"),'</length>');
 push (@out,
'<width>',$Tag->data('products','width',"$lik"),'</width>');
 push (@out,
'<height>',$Tag->data('products','height',"$lik"),'</height>');
 push (@out,
'<description>',$Tag->data('products','description',"$lik"),'</description>');
 push (@out, '</item>');
}
push (@out, '</lineItems>');
push (@out, '<city>',$Values->{city},'</city>');
push (@out, '<provOrState>',$Values->{state},'</provOrState>');
push (@out, '<country>',$Values->{country},'</country>');
push (@out, '<postalCode>',$Values->{zip},'</postalCode>');
push (@out, '</ratesAndServicesRequest>');
push (@out, '</eparcel>');
push (@out, '');
#return ("@out");
#send the xml to the global usertag

#my @cod = canpost(@out);
@cod = $Tag->canpo('206.191.4.228', @out);
if ($cod[0] eq 'error') {return "@cod";}
my $lik='';

#get back package, extract numbers, set scratch variables

foreach $lik (@cod) {
if ($lik =~ /<input type="hidden" name="SHIPPING_RATE_1" value="(.*)">/)
 {$Scratch->{can1} = $1;}
if ($lik =~ /<input type="hidden" name="SHIPPING_RATE_2" value="(.*)">/)
 {$Scratch->{can2} = $1;}
if ($lik =~ /<input type="hidden" name="SHIPPING_RATE_3" value="(.*)">/)
 {$Scratch->{can3} = $1;}
if ($lik =~ /<input type="hidden" name="SHIPPING_RATE_4" value="(.*)">/)
 {$Scratch->{can4} = $1;}
}

#output and done
#return "@cod";
return('');

[/perl]
[if session ship_message][data session ship_message][/if]<BR>
<HR width="60%"><div align="left">
<table border="0" cellspacing="2" cellpadding="0" align="center">
<TR><td align=left>
[shipping label=1 format="<input type='radio' name='mv_shipmode'
onClick='this.form.submit()' value='%M'%C>
%D </TD><TD align=right> %F</TD></TR><TR><TD>"
mode=|[data table=country key='[default country CA]' col=shipmodes]|]
</TD></TR></table>
</div><HR width="75%">
<BR>

You have to choose to receive their response in HTML. They can also send
XML but I found the regex parsing of HTML to be pretty simple. The code
at the end makes the radio buttons.

Now you have to alter your products.txt or file database and add columns
named: length, width and height and make sure there is weight as well.
Enter data for these values for each of your products.

You have to add 4 shipping routes, or 7 if you get an expedited account
from CanPost. Here are the 4 routes:

1CAN1 Priority Courier		Formula		$Scratch->{can1}
1CAN2 Xpresspost		Formula		$Scratch->{can2}
1CAN3 CanadaPost Expedited	Formula		$Scratch->{can3}
1CAN4 Canada Post - Regular Delivery	Formula	$Scratch->{can4}

Sneaky huh? It just picks up the values parsed out of the response by
the perl code!

I guess that's about it. The checkout page scans through the contents of
the cart and composes the XML. This is sent along with the IP address of
the server, (because this will change from test to production) to the
usertag. The usertag opens the socket, sends the XML, receives the
reply, checks it for errors and logs them, and then sends back the HTML
response. The perl block receives the response, gives up if there was an
error, then parses out the numbers. These are slipped into scratch
variables, then the regular interchange routine builds the radio
buttons. The response will also include delivery dates if you want to
display them too. The customer picks their shipping method and
interchange updates the checkout page. Shipping for the countries you
are targeting are set to use the CAN routes. The eparcel service is both
domestic and international.

So there you go, I suspect this will help a great deal.