quieter tests on older perl
[Business-OnlinePayment-PaymentsGateway.git] / PaymentsGateway.pm
1 package Business::OnlinePayment::PaymentsGateway;
2
3 use strict;
4 use Carp;
5 use Business::OnlinePayment;
6 use Net::SSLeay qw(sslcat);
7 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $DEBUG);
8
9 require Exporter;
10
11 @ISA = qw( Exporter AutoLoader Business::OnlinePayment);
12 @EXPORT = qw();
13 @EXPORT_OK = qw();
14 $VERSION = '0.01';
15
16 $DEBUG = 0;
17
18 my %pg_response_code = (
19   'A01' => 'Transaction approved/completed',
20   'U01' => 'Merchant not allowed to access customer account',
21   'U02' => 'Customer account is in the ACH Direct "known bad" list',
22   'U03' => 'Merchant daily limit exceeded',
23   'U04' => 'Merchant monthly limit exceeded',
24   'U05' => 'AVS state/zipcode check failed',
25   'U06' => 'AVS state/area code check failed',
26   'U07' => 'AVS anonymous email check failed',
27   'U08' => 'Account has more transactions than the merchant\'s daily velocity'.
28            ' limit allows for',
29   'U09' => 'Account has more transactions than the merchant\'s velocity'.
30            ' window allows for',
31   'U10' => 'Transaction has the same attributes as another transaction'.
32            ' within the time set by the merchant',
33   'U11' => '(RECUR TRANS NOT FOUND) Transaction types 40-42 only',
34   'U12' => 'Original transaction not voidable or capture-able',
35   'U13' => 'Transaction to be voided or captured was not found',
36   'U14' => 'void/capture and original transaction types do not agree (CC/EFT)',
37   'U18' => 'Void or Capture failed',
38   #'U19' => 'Account ABA number if invalid',
39   'U19' => 'Account ABA number is invalid',
40   'U20' => 'Credit card number is invalid',
41   'U21' => 'Date is malformed',
42   'U22' => 'Swipe data is malformed',
43   'U23' => 'Malformed expiration date',
44   'U51' => 'Merchant is not "live"',
45   'U52' => 'Merchant not approved for transaction type (CC or EFT)',
46   'U53' => 'Transaction amount exceeds merchant\'s per transaction limit',
47   'U54' => 'Merchant\'s configuration requires updating - call customer'.
48            ' support',
49   'U80' => 'Transaction was declined due to preauthorization (ATM Verify)'.
50            ' result',
51   'U84' => 'Preauthorizer not responding',
52   'U85' => 'Preauthorizer error',
53   'U83' => 'Transaction was declined due to authorizer declination',
54   'U84' => 'Authorizer not responding',
55   'U85' => 'Authorizer error',
56   'U86' => 'Authorizer AVS check failed',
57   'F01' => 'Required field is missing',
58   'F03' => 'Name is not recognized',
59   'F04' => 'Value is not allowed',
60   'F05' => 'Field is repeated in message',
61   'F07' => 'Fields cannot both be present',
62   #'E10' => 'Merchant id or password in incorrect',
63   'E10' => 'Merchant id or password is incorrect',
64   'E20' => 'Transaction message not received (I/O flush required?)',
65   'E90' => 'Originating IP not on merchant\'s approved IP list',
66   'E99' => 'An unspecified error has occurred',
67 );
68
69 sub set_defaults {
70   my $self = shift;
71   $self->server('paymentsgateway.net');
72   $self->port( 5050 );
73   $self->build_subs(qw(test_transaction));
74 }
75
76 sub map_fields {
77   my $self = shift;
78   my %content = $self->content();
79
80   #ACTION MAP
81   my %actions = (
82     'normal authorization' => 0,
83     'authorization only'   => 1,
84     'post authorization'   => 2,
85     'credit'               => 3,
86   );
87
88   my %types = (
89     'visa'             => 10,
90     'mastercard'       => 10,
91     'american express' => 10,
92     'discover'         => 10,
93     'cc'               => 10,
94     'check'            => 20,
95   );
96
97   #pg_type/action = action + type  
98
99   $self->transaction_type( $actions{ lc($content{'action'}) } 
100                            + $types{ lc($content{'type'  }) }    );
101
102   #$self->content(%content);
103 }
104
105 sub revmap_fields {
106     my($self, %map) = @_;
107     my %content = $self->content();
108     foreach(keys %map) {
109         $content{$_} = ref($map{$_})
110                          ? ${ $map{$_} }
111                          : $content{$map{$_}};
112     }
113     $self->content(%content);
114 }
115
116 sub submit {
117   my $self = shift;
118   $self->map_fields();
119
120   #my %content = $self->content();
121
122   $self->revmap_fields( 
123     'PG_MERCHANT_ID'                   => 'login',
124     'pg_password'                      => 'password',
125     'pg_transaction_type'              => \($self->transaction_type()),
126     #'pg_merchant_data_1'
127     #...
128     #'pg_merchant_data_9'
129     'pg_total_amount'                  => 'amount',
130     #'pg_sales_tax_amount'
131     'pg_consumer_id'                   => 'customer_id',
132     'ecom_consumerorderid'             => 'invoice_number', #???
133     #'ecom_walletid'                    =>
134     'pg_billto_postal_name_company'    => 'company', #????
135     'ecom_billto_postal_name_first'    => 'first_name', #????
136     'ecom_billto_postal_name_last'     => 'last_name', # ????
137     'ecom_billto_postal_street_line1'  => 'address',
138     #'ecom_billto_postal_street_line2'
139     'ecom_billto_postal_city'          => 'city',
140     'ecom_billto_postal_stateprov'     => 'state',
141     'ecom_billto_postal_postalcode'    => 'zip',
142     'ecom_billto_postal_countrycode'   => 'country',
143     'ecom_billto_telecom_phone_number' => 'phone',
144     'ecom_billto_online_email'         => 'email',
145     #'pg_billto_ssn'
146     #'pg_billto_dl_number'
147     #'pg_billto_dl_state'
148     'ecom_payment_check_trn'           => 'routing_code',
149     'ecom_payment_check_account'       => 'account_number',
150     'ecom_payment_check_account_type'  => \'C', #checking
151     #'ecom_payment_check_checkno'       =>
152   );
153   my %content = $self->content();
154
155   # name (first_name & last_name ) ?
156   # fax
157
158   # card_number exp_date
159
160   #account_number routing_code bank_name
161
162   my @fields = (
163     qw( PG_MERCHANT_ID pg_password pg_transaction_type ),
164     ( map { "pg_merchant_$_" } ( 1 .. 9 ) ),
165     qw( pg_total_amount pg_sales_tax_amount pg_consumer_id
166         ecom_consumerorderid ecom_walletid
167         pg_billto_postal_name_company
168         ecom_billto_postal_name_first ecom_billto_postal_name_last
169         ecom_billto_postal_street_line1
170         ecom_billto_postal_street_line2
171         ecom_billto_postal_city ecom_billto_postal_stateprov
172         ecom_billto_postal_postalcode ecom_billto_postal_countrycode
173         ecom_billto_telecom_phone_number ecom_billto_online_email
174         pg_billto_ssn pg_billto_dl_number pg_billto_dl_state
175     )
176   );
177
178   if ( $content{'type'} =~ /^check$/i ) {
179     push @fields, qw( ecom_payment_check_trn
180                       ecom_payment_check_account
181                       ecom_payment_check_account_type );
182   } else {
183     croak $content{'type'}. ' not (yet) supported';
184   }
185
186   my $request = join("\n", map { "$_=". $content{$_} }
187                            grep { defined($content{$_}) && $content{$_} ne '' }
188                            @fields                     ).
189                 "\nendofdata\n";
190
191   warn $request if $DEBUG;
192
193   warn "TEST: ". $self->test_transaction(). "\n" if $DEBUG;
194
195   $self->port( $self->port() + 1000 ) if $self->test_transaction();
196
197   warn "SERVER ". $self->server(). "\n" if $DEBUG;
198   warn "PORT ". $self->port(). "\n" if $DEBUG;
199
200   my $reply = sslcat( $self->server(), $self->port(), $request );
201   die "no reply from server" unless $reply;
202
203   warn "reply from server: $reply\n" if $DEBUG;
204
205   my %response = map { /^(\w+)=(.*)$/ or /^(endofdata)()$/
206                          or warn "can't parse response line: $_";
207                        ($1, $2);
208                      } split(/\n/, $reply);
209
210   if ( $response{'pg_response_type'} eq 'A' ) {
211     $self->is_success(1);
212     $self->result_code($response{'pg_response_code'});
213     $self->authorization($response{'pg_authorization_code'});
214   } else {
215     $self->is_success(0);
216     $self->result_code($response{'pg_response_code'});
217     $self->error_message( $pg_response_code{$response{'pg_response_code'}}.
218                           ': '. $response{'pg_response_description'} );
219   }
220 }
221
222 1;
223
224 __END__
225
226 =head1 NAME
227
228 Business::OnlinePayment::PaymentsGateway - PaymentsGateway.Net backend for Business::OnlinePayment
229
230 =head1 SYNOPSIS
231
232   use Business::OnlinePayment;
233
234   my $tx = new Business::OnlinePayment("PaymentsGateway");
235   $tx->content(
236       type           => 'CHECK',
237       login          => 'test',
238       password       => 'test',
239       action         => 'Normal Authorization',
240       description    => 'Business::OnlinePayment test',
241       amount         => '49.95',
242       invoice_number => '100100',
243       name           => 'Tofu Beast',
244       account_number => '12345',
245       routing_code   => '123456789',
246       bank_name      => 'First National Test Bank',
247   );
248   $tx->submit();
249
250   if($tx->is_success()) {
251       print "Card processed successfully: ".$tx->authorization."\n";
252   } else {
253       print "Card was rejected: ".$tx->error_message."\n";
254   }
255
256 =head1 DESCRIPTION
257
258 For detailed information see L<Business::OnlinePayment>.
259
260 =head1 NOTE
261
262 This module only implements 'CHECK' (ACH) transactions at this time.  Credit
263 card transactions are not (yet) supported.
264
265 =head1 COMPATIBILITY
266
267 This module implements the interface documented in the
268 "PaymentsGateway.net Integration Guide, Version 2.1, September 2002"
269
270 =head1 AUTHOR
271
272 Ivan Kohler <ivan-paymentsgateway@420.am>
273
274 =head1 SEE ALSO
275
276 perl(1). L<Business::OnlinePayment>
277
278 =cut
279