package Business::OnlinePayment::GlobalPayments;
use warnings;
use strict;
use Carp qw(croak);
use vars qw($VERSION $DEBUG @ISA $me);
use base 'Business::OnlinePayment::HTTPS';
use XML::Simple 'XMLin'; # for parsing reply
$VERSION = 0.02;
$DEBUG = 0;
$me = __PACKAGE__;
my %trans_type = (
'normal authorization' => 'Sale',
'authorization only' => 'Auth',
'post authorization' => 'Force',
'void' => 'Void',
'credit' => 'Return',
);
my %cc_fields = (
'GlobalUserName' => 'login',
'GlobalPassword' => 'password',
'TransType' => sub { my %c = @_; $trans_type{ lc($c{action}) } },
'CardNum' => 'card_number',
'ExpDate' => sub { my %c = @_; join('', split /\D/,$c{'expiration'}) },
'MagData' => 'track2',
'NameOnCard' => sub { my %c = @_; $c{'first_name'} . ' ' . $c{'last_name'} },
'Amount' => 'amount',
'InvNum' => 'invoice_number',
'Zip' => 'zip',
'Street' => 'address',
'CVNum' => 'cvv2',
'PNRef' => 'order_number',
'ExtData' => \&ext_data,
);
sub ext_data {
my %c = @_; # = $self->{_content}
my $ext_data = '';
if($c{'authorization'}) {
$ext_data .= ''.$c{'authorization'}.'';
}
if($c{'force_duplicate'}) { # set to any true value
$ext_data .= 'T';
}
return $ext_data;
}
my %required_fields = (
'All' => [ qw(GlobalUserName GlobalPassword TransType) ],
'Sale' => [ qw(CardNum ExpDate Amount) ],
'Auth' => [ qw(CardNum ExpDate Amount) ],
'Force' => [ ],
'Void' => [ 'PNRef' ],
'Return' => [ ],
'Return.blind' => [ qw(CardNum ExpDate Amount) ],
);
sub set_defaults {
my $self = shift;
$self->port(443);
$self->path('/GlobalPay/transact.asmx/ProcessCreditCard');
$self->build_subs('domain', 'avs_code', 'cvv2_response' );
return;
}
sub remap_fields {
my ($self, %map) = @_;
my %content = $self->content();
foreach (keys(%map)) {
if(ref($map{$_}) eq 'CODE') {
$content{$_} = $map{$_}->(%content);
}
else {
$content{$_} = $content{$map{$_}} if defined( $content{$map{$_}} );
}
}
if(lc($content{'action'}) eq 'post authorization') {
# GlobalPayments uses this transaction type to complete an authorized
# transaction, given either its PNRef (if it was authorized by an Auth
# transaction to the gateway) or its AuthCode (if it was authorized by
# telephone).
if(!exists($content{'PNRef'}) and !exists($content{'authorization'})) {
croak("missing required field(s): PNRef or AuthCode\n");
}
}
$self->content(%content);
return;
}
sub submit {
my $self = shift;
my $content = $self->{_content};
$DB::single = 1 if $DEBUG;
$self->setup_test if $self->test_transaction();
die "missing required option: domain\n" if !$self->domain();
$self->server($self->domain() . '.globalpay.com');
$self->remap_fields(%cc_fields);
my $action = $content->{'TransType'} or
croak "unknown action: '".$content->{'action'}."'\n";
$self->required_fields(@{ $required_fields{'All'} });
$self->required_fields(@{ $required_fields{$action} });
if($action eq 'Return' and !exists($content->{'PNRef'})) {
# This handles the case where a credit is ordered "blind", without
# an order_number. Card information must be supplied. Allowing
# these is somewhat risky, and can be disabled at the account level
# by the "Require Original PNRef" flag.
$self->required_fields(@{ $required_fields{'Return.blind'} });
}
tie my %request, 'Tie::IxHash',
map { $_ => $self->{_content}->{$_} } keys(%cc_fields);
$Business::OnlinePayment::HTTPS::DEBUG = $DEBUG;
$DB::single = 1 if $DEBUG;
my ($page, $response, %headers) = $self->https_post(\%request);
$self->server_response($page);
$self->is_success(0);
if(not $response =~ /^200/) {
$self->error_message("Connection failed: '$response'\n");
return;
}
my $data = XMLin($page);
if(!$data or !exists($data->{'Result'})) {
$self->error_message("Malformed server response: '$page'\n");
return;
}
$self->result_code($data->{'Result'});
$self->avs_code($data->{'GetAVSResult'});
$self->cvv2_response($data->{'GetCVResult'});
if($data->{'Result'} != 0) {
$self->error_message($data->{'Message'});
return;
}
else {
$self->is_success(1);
$self->authorization($data->{'AuthCode'});
$self->order_number($data->{'PNRef'});
return;
}
}
sub setup_test {
my $self = shift;
$self->domain('certapia');
# For test card information, see Global Transport API documentation.
}
=head1 NAME
Business::OnlinePayment::GlobalPayments - Global Transport backend for Business::OnlinePayment
=head1 SYNOPSIS
=head2 Initialization
my $trans = new Business::OnlinePayment('GlobalPayments',
domain => 'mymerchant' # Your account rep will supply this
);
=head2 Sale transaction
$trans->content(
login => 'login',
password => 'password',
type => 'CC',
card_number => '5500000000000004',
expiration => '0211',
cvv2 => '255',
invoice_number => '123321',
first_name => 'Joe',
last_name => 'Schmoe',
address => '123 Anystreet',
city => 'Sacramento',
state => 'CA',
zip => '95824',
action => 'normal authorization',
amount => '24.99'
);
=head2 Processing
$trans->submit;
if($trans->is_approved) {
print "Approved\n",
"Authorization: ", $trans->authorization, "\n",
"Order ID: ", $trans->order_number, "\n"
}
else {
print "Failed: ".$trans->error_message;
}
=head2 Void transaction
(or Return (credit) for full amount of original sale)
$trans->content(
login => 'login',
password => 'password',
action => 'void', # or 'credit' for a Return
order_number => '1001245',
);
$trans->submit;
=head1 NOTES
The following transaction types are supported:
Normal Authorization
Authorization Only
Post Authorization
Credit
Void
For Post Authorization, Credit, and Void, I should be set to
the order_number of the previous transaction.
Alternately, Post Authorization can be sent with I set to an
auth code obtained by telephone. Similarly, Credit can be sent with credit
account information instead of an I.
By default, Global Transport will reject duplicate transactions (identical
card number, expiration date, and amount) sent on the same day. This can be
overridden by setting I => 1.
=head1 AUTHOR
Mark Wells
=head1 SUPPORT
Support for commercial users is available from Freeside Internet Services,
Inc.
=head1 COPYRIGHT & LICENSE
Copyright 2009 Mark Wells, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1; # End of Business::OnlinePayment::GlobalPayments