don't need to build_subs test_transaction
[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.02';
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 }
74
75 sub map_fields {
76   my $self = shift;
77   my %content = $self->content();
78
79   #ACTION MAP
80   my %actions = (
81     'normal authorization' => 0,
82     'authorization only'   => 1,
83     'post authorization'   => 2,
84     'credit'               => 3,
85   );
86
87   my %types = (
88     'visa'             => 10,
89     'mastercard'       => 10,
90     'american express' => 10,
91     'discover'         => 10,
92     'cc'               => 10,
93     'check'            => 20,
94   );
95
96   #pg_type/action = action + type  
97
98   $self->transaction_type( $actions{ lc($content{'action'}) } 
99                            + $types{ lc($content{'type'  }) }    );
100
101   #$self->content(%content);
102 }
103
104 sub revmap_fields {
105     my($self, %map) = @_;
106     my %content = $self->content();
107     foreach(keys %map) {
108         $content{$_} = ref($map{$_})
109                          ? ${ $map{$_} }
110                          : $content{$map{$_}};
111     }
112     $self->content(%content);
113 }
114
115 sub submit {
116   my $self = shift;
117   $self->map_fields();
118
119   #my %content = $self->content();
120
121   $self->revmap_fields( 
122     'PG_MERCHANT_ID'                   => 'login',
123     'pg_password'                      => 'password',
124     'pg_transaction_type'              => \($self->transaction_type()),
125     #'pg_merchant_data_1'
126     #...
127     #'pg_merchant_data_9'
128     'pg_total_amount'                  => 'amount',
129     #'pg_sales_tax_amount'
130     'pg_consumer_id'                   => 'customer_id',
131     'ecom_consumerorderid'             => 'invoice_number', #???
132     #'ecom_walletid'                    =>
133     'pg_billto_postal_name_company'    => 'company', #????
134     'ecom_billto_postal_name_first'    => 'first_name', #????
135     'ecom_billto_postal_name_last'     => 'last_name', # ????
136     'ecom_billto_postal_street_line1'  => 'address',
137     #'ecom_billto_postal_street_line2'
138     'ecom_billto_postal_city'          => 'city',
139     'ecom_billto_postal_stateprov'     => 'state',
140     'ecom_billto_postal_postalcode'    => 'zip',
141     'ecom_billto_postal_countrycode'   => 'country',
142     'ecom_billto_telecom_phone_number' => 'phone',
143     'ecom_billto_online_email'         => 'email',
144     #'pg_billto_ssn'
145     #'pg_billto_dl_number'
146     #'pg_billto_dl_state'
147     'ecom_payment_check_trn'           => 'routing_code',
148     'ecom_payment_check_account'       => 'account_number',
149     'ecom_payment_check_account_type'  => \'C', #checking
150     #'ecom_payment_check_checkno'       =>
151   );
152   my %content = $self->content();
153
154   # name (first_name & last_name ) ?
155   # fax
156
157   # card_number exp_date
158
159   #account_number routing_code bank_name
160
161   my @fields = (
162     qw( PG_MERCHANT_ID pg_password pg_transaction_type ),
163     ( map { "pg_merchant_$_" } ( 1 .. 9 ) ),
164     qw( pg_total_amount pg_sales_tax_amount pg_consumer_id
165         ecom_consumerorderid ecom_walletid
166         pg_billto_postal_name_company
167         ecom_billto_postal_name_first ecom_billto_postal_name_last
168         ecom_billto_postal_street_line1
169         ecom_billto_postal_street_line2
170         ecom_billto_postal_city ecom_billto_postal_stateprov
171         ecom_billto_postal_postalcode ecom_billto_postal_countrycode
172         ecom_billto_telecom_phone_number ecom_billto_online_email
173         pg_billto_ssn pg_billto_dl_number pg_billto_dl_state
174     )
175   );
176
177   if ( $content{'type'} =~ /^check$/i ) {
178     push @fields, qw( ecom_payment_check_trn
179                       ecom_payment_check_account
180                       ecom_payment_check_account_type );
181   } else {
182     croak $content{'type'}. ' not (yet) supported';
183   }
184
185   my $request = join("\n", map { "$_=". $content{$_} }
186                            grep { defined($content{$_}) && $content{$_} ne '' }
187                            @fields                     ).
188                 "\nendofdata\n";
189
190   warn $request if $DEBUG;
191
192   warn "TEST: ". $self->test_transaction(). "\n" if $DEBUG;
193
194   $self->port( $self->port() + 1000 ) if $self->test_transaction();
195
196   warn "SERVER ". $self->server(). "\n" if $DEBUG;
197   warn "PORT ". $self->port(). "\n" if $DEBUG;
198
199   my $reply = sslcat( $self->server(), $self->port(), $request );
200   die "no reply from server" unless $reply;
201
202   warn "reply from server: $reply\n" if $DEBUG;
203
204   my %response = map { /^(\w+)=(.*)$/ or /^(endofdata)()$/
205                          or warn "can't parse response line: $_";
206                        ($1, $2);
207                      } split(/\n/, $reply);
208
209   if ( $response{'pg_response_type'} eq 'A' ) {
210     $self->is_success(1);
211     $self->result_code($response{'pg_response_code'});
212     $self->authorization($response{'pg_authorization_code'});
213   } else {
214     $self->is_success(0);
215     $self->result_code($response{'pg_response_code'});
216     $self->error_message( $pg_response_code{$response{'pg_response_code'}}.
217                           ': '. $response{'pg_response_description'} );
218   }
219 }
220
221 1;
222
223 __END__
224
225 =head1 NAME
226
227 Business::OnlinePayment::PaymentsGateway - PaymentsGateway.Net backend for Business::OnlinePayment
228
229 =head1 SYNOPSIS
230
231   use Business::OnlinePayment;
232
233   my $tx = new Business::OnlinePayment("PaymentsGateway");
234   $tx->content(
235       type           => 'CHECK',
236       login          => 'test',
237       password       => 'test',
238       action         => 'Normal Authorization',
239       description    => 'Business::OnlinePayment test',
240       amount         => '49.95',
241       invoice_number => '100100',
242       name           => 'Tofu Beast',
243       account_number => '12345',
244       routing_code   => '123456789',
245       bank_name      => 'First National Test Bank',
246   );
247   $tx->submit();
248
249   if($tx->is_success()) {
250       print "Card processed successfully: ".$tx->authorization."\n";
251   } else {
252       print "Card was rejected: ".$tx->error_message."\n";
253   }
254
255 =head1 DESCRIPTION
256
257 For detailed information see L<Business::OnlinePayment>.
258
259 =head1 NOTE
260
261 This module only implements 'CHECK' (ACH) transactions at this time.  Credit
262 card transactions are not (yet) supported.
263
264 =head1 COMPATIBILITY
265
266 This module implements the interface documented in the
267 "PaymentsGateway.net Integration Guide, Version 2.1, September 2002"
268
269 =head1 AUTHOR
270
271 Ivan Kohler <ivan-paymentsgateway@420.am>
272
273 =head1 SEE ALSO
274
275 perl(1). L<Business::OnlinePayment>
276
277 =cut
278