File Coverage

blib/lib/Business/OnlinePayment/GlobalPayments.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::GlobalPayments;
2              
3 1     1   24099 use warnings;
  1         3  
  1         37  
4 1     1   6 use strict;
  1         2  
  1         35  
5 1     1   5 use Carp qw(croak);
  1         6  
  1         80  
6 1     1   4 use vars qw($VERSION $DEBUG @ISA $me);
  1         2  
  1         113  
7 1     1   6 use base 'Business::OnlinePayment::HTTPS';
  1         2  
  1         1065  
8 1     1   58583 use XML::Simple 'XMLin'; # for parsing reply
  0            
  0            
9              
10             $VERSION = 0.02;
11             $DEBUG = 0;
12             $me = __PACKAGE__;
13              
14             my %trans_type = (
15             'normal authorization' => 'Sale',
16             'authorization only' => 'Auth',
17             'post authorization' => 'Force',
18             'void' => 'Void',
19             'credit' => 'Return',
20             );
21              
22             my %cc_fields = (
23             'GlobalUserName' => 'login',
24             'GlobalPassword' => 'password',
25             'TransType' => sub { my %c = @_; $trans_type{ lc($c{action}) } },
26             'CardNum' => 'card_number',
27             'ExpDate' => sub { my %c = @_; join('', split /\D/,$c{'expiration'}) },
28             'MagData' => 'track2',
29             'NameOnCard' => sub { my %c = @_; $c{'first_name'} . ' ' . $c{'last_name'} },
30             'Amount' => 'amount',
31             'InvNum' => 'invoice_number',
32             'Zip' => 'zip',
33             'Street' => 'address',
34             'CVNum' => 'cvv2',
35             'PNRef' => 'order_number',
36             'ExtData' => \&ext_data,
37             );
38              
39             sub ext_data {
40             my %c = @_; # = $self->{_content}
41             my $ext_data = '';
42             if($c{'authorization'}) {
43             $ext_data .= ''.$c{'authorization'}.'';
44             }
45             if($c{'force_duplicate'}) { # set to any true value
46             $ext_data .= 'T';
47             }
48             return $ext_data;
49             }
50              
51             my %required_fields = (
52             'All' => [ qw(GlobalUserName GlobalPassword TransType) ],
53             'Sale' => [ qw(CardNum ExpDate Amount) ],
54             'Auth' => [ qw(CardNum ExpDate Amount) ],
55             'Force' => [ ],
56             'Void' => [ 'PNRef' ],
57             'Return' => [ ],
58             'Return.blind' => [ qw(CardNum ExpDate Amount) ],
59             );
60              
61             sub set_defaults {
62             my $self = shift;
63             $self->port(443);
64             $self->path('/GlobalPay/transact.asmx/ProcessCreditCard');
65             $self->build_subs('domain', 'avs_code', 'cvv2_response' );
66              
67             return;
68             }
69              
70             sub remap_fields {
71             my ($self, %map) = @_;
72             my %content = $self->content();
73              
74             foreach (keys(%map)) {
75             if(ref($map{$_}) eq 'CODE') {
76             $content{$_} = $map{$_}->(%content);
77             }
78             else {
79             $content{$_} = $content{$map{$_}} if defined( $content{$map{$_}} );
80             }
81             }
82              
83             if(lc($content{'action'}) eq 'post authorization') {
84             # GlobalPayments uses this transaction type to complete an authorized
85             # transaction, given either its PNRef (if it was authorized by an Auth
86             # transaction to the gateway) or its AuthCode (if it was authorized by
87             # telephone).
88             if(!exists($content{'PNRef'}) and !exists($content{'authorization'})) {
89             croak("missing required field(s): PNRef or AuthCode\n");
90             }
91             }
92              
93             $self->content(%content);
94             return;
95             }
96              
97             sub submit {
98             my $self = shift;
99             my $content = $self->{_content};
100             $DB::single = 1 if $DEBUG;
101            
102             $self->setup_test if $self->test_transaction();
103              
104             die "missing required option: domain\n" if !$self->domain();
105             $self->server($self->domain() . '.globalpay.com');
106              
107             $self->remap_fields(%cc_fields);
108              
109             my $action = $content->{'TransType'} or
110             croak "unknown action: '".$content->{'action'}."'\n";
111             $self->required_fields(@{ $required_fields{'All'} });
112             $self->required_fields(@{ $required_fields{$action} });
113              
114             if($action eq 'Return' and !exists($content->{'PNRef'})) {
115             # This handles the case where a credit is ordered "blind", without
116             # an order_number. Card information must be supplied. Allowing
117             # these is somewhat risky, and can be disabled at the account level
118             # by the "Require Original PNRef" flag.
119             $self->required_fields(@{ $required_fields{'Return.blind'} });
120             }
121            
122             tie my %request, 'Tie::IxHash',
123             map { $_ => $self->{_content}->{$_} } keys(%cc_fields);
124              
125             $Business::OnlinePayment::HTTPS::DEBUG = $DEBUG;
126             $DB::single = 1 if $DEBUG;
127             my ($page, $response, %headers) = $self->https_post(\%request);
128              
129             $self->server_response($page);
130             $self->is_success(0);
131             if(not $response =~ /^200/) {
132             $self->error_message("Connection failed: '$response'\n");
133             return;
134             }
135             my $data = XMLin($page);
136             if(!$data or !exists($data->{'Result'})) {
137             $self->error_message("Malformed server response: '$page'\n");
138             return;
139             }
140             $self->result_code($data->{'Result'});
141             $self->avs_code($data->{'GetAVSResult'});
142             $self->cvv2_response($data->{'GetCVResult'});
143             if($data->{'Result'} != 0) {
144             $self->error_message($data->{'Message'});
145             return;
146             }
147             else {
148             $self->is_success(1);
149             $self->authorization($data->{'AuthCode'});
150             $self->order_number($data->{'PNRef'});
151             return;
152             }
153             }
154              
155             sub setup_test {
156             my $self = shift;
157             $self->domain('certapia');
158             # For test card information, see Global Transport API documentation.
159             }
160              
161              
162             =head1 NAME
163              
164             Business::OnlinePayment::GlobalPayments - Global Transport backend for Business::OnlinePayment
165              
166             =head1 SYNOPSIS
167              
168             =head2 Initialization
169              
170             my $trans = new Business::OnlinePayment('GlobalPayments',
171             domain => 'mymerchant' # Your account rep will supply this
172             );
173              
174             =head2 Sale transaction
175              
176             $trans->content(
177             login => 'login',
178             password => 'password',
179             type => 'CC',
180             card_number => '5500000000000004',
181             expiration => '0211',
182             cvv2 => '255',
183             invoice_number => '123321',
184             first_name => 'Joe',
185             last_name => 'Schmoe',
186             address => '123 Anystreet',
187             city => 'Sacramento',
188             state => 'CA',
189             zip => '95824',
190             action => 'normal authorization',
191             amount => '24.99'
192             );
193              
194             =head2 Processing
195              
196             $trans->submit;
197             if($trans->is_approved) {
198             print "Approved\n",
199             "Authorization: ", $trans->authorization, "\n",
200             "Order ID: ", $trans->order_number, "\n"
201             }
202             else {
203             print "Failed: ".$trans->error_message;
204             }
205              
206             =head2 Void transaction
207             (or Return (credit) for full amount of original sale)
208              
209             $trans->content(
210             login => 'login',
211             password => 'password',
212             action => 'void', # or 'credit' for a Return
213             order_number => '1001245',
214             );
215             $trans->submit;
216              
217             =head1 NOTES
218              
219             The following transaction types are supported:
220             Normal Authorization
221             Authorization Only
222             Post Authorization
223             Credit
224             Void
225              
226             For Post Authorization, Credit, and Void, I should be set to
227             the order_number of the previous transaction.
228              
229             Alternately, Post Authorization can be sent with I set to an
230             auth code obtained by telephone. Similarly, Credit can be sent with credit
231             account information instead of an I.
232              
233             By default, Global Transport will reject duplicate transactions (identical
234             card number, expiration date, and amount) sent on the same day. This can be
235             overridden by setting I => 1.
236              
237             =head1 AUTHOR
238              
239             Mark Wells
240              
241             =head1 SUPPORT
242              
243             Support for commercial users is available from Freeside Internet Services,
244             Inc.
245              
246             =head1 COPYRIGHT & LICENSE
247              
248             Copyright 2009 Mark Wells, all rights reserved.
249              
250             This program is free software; you can redistribute it and/or modify it
251             under the same terms as Perl itself.
252              
253              
254             =cut
255              
256             1; # End of Business::OnlinePayment::GlobalPayments