From ce9fa73d247b7398f94af6262ae97151c18aac89 Mon Sep 17 00:00:00 2001 From: mark Date: Fri, 29 Jan 2010 21:15:24 +0000 Subject: [PATCH] Initial release --- Changes | 5 ++ GlobalPayments.pm | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ MANIFEST | 7 ++ Makefile.PL | 19 ++++ README | 41 +++++++++ t/00-load.t | 9 ++ t/pod.t | 12 +++ 7 files changed, 349 insertions(+) create mode 100644 Changes create mode 100644 GlobalPayments.pm create mode 100644 MANIFEST create mode 100644 Makefile.PL create mode 100644 README create mode 100644 t/00-load.t create mode 100644 t/pod.t 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 .= ''.$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, '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 + +=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 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 ', + 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(); -- 2.11.0