Initial release
authormark <mark>
Fri, 29 Jan 2010 21:15:24 +0000 (21:15 +0000)
committermark <mark>
Fri, 29 Jan 2010 21:15:24 +0000 (21:15 +0000)
Changes [new file with mode: 0644]
GlobalPayments.pm [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
t/00-load.t [new file with mode: 0644]
t/pod.t [new file with mode: 0644]

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..ed7b474
--- /dev/null
+++ b/Changes
@@ -0,0 +1,5 @@
+Revision history for Business-OnlinePayment-GlobalPayments
+
+0.01    Fri Jan 29 13:05:00 PST 2010
+        Initial release.
+
diff --git a/GlobalPayments.pm b/GlobalPayments.pm
new file mode 100644 (file)
index 0000000..a6ed89e
--- /dev/null
@@ -0,0 +1,256 @@
+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.01;
+$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 .= '<AuthCode>'.$c{'authorization'}.'</AuthCode>';
+  }
+  if($c{'force_duplicate'}) { # set to any true value
+    $ext_data .= '<Force>T</Force>';
+  }
+  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, 'order_number' should be set to 
+the order_number of the previous transaction.
+
+Alternately, Post Authorization can be sent with 'authorization' set to an 
+auth code obtained by telephone.  Similarly, Credit can be sent with credit 
+account information instead of an order_number.
+
+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 'force_duplicate' => 1.
+
+=head1 AUTHOR
+
+Mark Wells <mark@freeside.biz>
+
+=head1 SUPPORT
+
+Support for commercial users is available from Freeside Internet Services, 
+Inc. <http://www.freeside.biz>
+
+=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
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..fa3d4c8
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,7 @@
+Changes
+MANIFEST
+Makefile.PL
+README
+GlobalPayments.pm
+t/00-load.t
+t/pod.t
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..ff2517f
--- /dev/null
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME                => 'Business::OnlinePayment::GlobalPayments',
+    AUTHOR              => 'Mark Wells <mark@freeside.biz>',
+    VERSION_FROM        => 'GlobalPayments.pm',
+    ABSTRACT_FROM       => 'GlobalPayments.pm',
+    ($ExtUtils::MakeMaker::VERSION >= 6.3002
+      ? ('LICENSE'=> 'perl')
+      : ()),
+    PL_FILES            => {},
+    PREREQ_PM => {
+        'Test::More' => 0,
+    },
+    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+    clean               => { FILES => 'Business-OnlinePayment-GlobalPayments-*' },
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..589c628
--- /dev/null
+++ b/README
@@ -0,0 +1,41 @@
+Business-OnlinePayment-GlobalPayments
+A Business::OnlinePayment backend for Global Payments credit card processing.
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+       perl Makefile.PL
+       make
+       make test
+       make install
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the
+perldoc command.
+
+    perldoc Business::OnlinePayment::GlobalPayments
+
+You can also look for information at:
+
+    RT, CPAN's request tracker
+        http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-OnlinePayment-GlobalPayments
+
+    AnnoCPAN, Annotated CPAN documentation
+        http://annocpan.org/dist/Business-OnlinePayment-GlobalPayments
+
+    CPAN Ratings
+        http://cpanratings.perl.org/d/Business-OnlinePayment-GlobalPayments
+
+    Search CPAN
+        http://search.cpan.org/dist/Business-OnlinePayment-GlobalPayments/
+
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2009 Mark Wells
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644 (file)
index 0000000..eaff07f
--- /dev/null
@@ -0,0 +1,9 @@
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+       use_ok( 'Business::OnlinePayment::GlobalPayments' );
+}
+
+diag( "Testing Business::OnlinePayment::GlobalPayments $Business::OnlinePayment::GlobalPayments::VERSION, Perl $], $^X" );
diff --git a/t/pod.t b/t/pod.t
new file mode 100644 (file)
index 0000000..ee8b18a
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,12 @@
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+# Ensure a recent version of Test::Pod
+my $min_tp = 1.22;
+eval "use Test::Pod $min_tp";
+plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
+
+all_pod_files_ok();