summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormark <mark>2010-01-29 21:15:24 +0000
committermark <mark>2010-01-29 21:15:24 +0000
commitce9fa73d247b7398f94af6262ae97151c18aac89 (patch)
tree1ecc01ec9aabc047e8e901a4909e8586a3fba4b3
Initial release
-rw-r--r--Changes5
-rw-r--r--GlobalPayments.pm256
-rw-r--r--MANIFEST7
-rw-r--r--Makefile.PL19
-rw-r--r--README41
-rw-r--r--t/00-load.t9
-rw-r--r--t/pod.t12
7 files changed, 349 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
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
index 0000000..a6ed89e
--- /dev/null
+++ b/GlobalPayments.pm
@@ -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
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
index 0000000..ff2517f
--- /dev/null
+++ b/Makefile.PL
@@ -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
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
index 0000000..eaff07f
--- /dev/null
+++ b/t/00-load.t
@@ -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
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();