initial import
[Business-OnlinePayment-PPIPayMover.git] / lib / Business / OnlinePayment / PPIPayMover.pm
1 package Business::OnlinePayment::PPIPayMover;
2
3 use strict;
4 use vars qw($VERSION @ISA $DEBUG);
5 use Carp;
6 use Business::OnlinePayment::PPIPayMover::constants;
7 use Business::OnlinePayment::PPIPayMover::TransactionClient;
8 use Business::OnlinePayment::PPIPayMover::CreditCardRequest;
9 use Business::OnlinePayment::PPIPayMover::CountryCodes;
10 use Business::OnlinePayment::PPIPayMover::CreditCardResponse;
11
12 $VERSION = '0.01';
13 @ISA = qw(Business::OnlinePayment);
14 $DEBUG = 0;
15
16 my $tranclient = new Business::OnlinePayment::PPIPayMover::TransactionClient;
17 #my $ccreq = new Business::OnlinePayment::PPIPayMover::CreditCardRequest;
18
19 sub set_defaults {
20   my $self = shift;
21
22     #$self->server('secure.linkpt.net');
23     #$self->port('1129');
24
25     $self->build_subs(qw(order_number avs_code));
26
27 }
28
29 sub map_fields {
30   my $self = shift;
31
32   my %content = $self->content();
33
34   # ACTION MAP
35   #    target types: SALE, ADJUSTMENT, AUTH, CAPTURE, CREDIT, FORCE_AUTH,
36   #                  FORCE_SALE, QUERY_CREDIT, QUERY_PAYMENT or VOID
37   my %actions = (
38     'normal authorization' => 'SALE',
39     'authorization only'   => 'AUTH',
40     'credit'               => 'CREDIT',
41     'post authorization'   => 'CAPTURE',
42     'void'                 => 'VOID',
43   );
44   $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
45
46   # TYPE MAP
47   my %types = (
48     'visa'              => 'CC',
49     'mastercard'        => 'CC',
50     'american express'  => 'CC',
51     'discover'          => 'CC',
52     'cc'                => 'CC',
53     #'check'
54   );
55   $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
56   $self->transaction_type($content{'type'});
57
58   # stuff it back into %content
59   $self->content(%content);
60 }
61
62 sub submit {
63   my $self = shift;
64
65     #type          =>
66     #login         =>
67     #password      =>
68     #authorization =>
69
70                     #name
71
72     #order_number
73
74                     #currency          =>
75
76                     #check_type        =>
77                     #account_name      =>
78                     #account_number    => 
79                     #account_type      =>
80                    #bank_name         => 
81                    #routing_code      =>
82                     #customer_org      =>
83                   #customer_ssn      =>
84                   #license_num       =>
85                     #license_state     =>
86                   #license_dob       =>
87                    #get from new() args instead# payee             =>
88                  #check_number      =>
89
90                     #recurring_billing => 'cnp_recurring',
91
92   $self->map_fields();
93
94   my %content = $self->content;
95
96   my($month, $year);
97   unless ( $content{action} eq 'CAPTURE'
98            || ( $content{'action'} =~ /^(CREDIT|VOID)$/
99                 && exists $content{'order_number'} )
100          ) {
101
102     if (  $self->transaction_type() =~
103             /^(cc|visa|mastercard|american express|discover)$/i
104        ) {
105     } else {
106         Carp::croak("PPIPayMover can't handle transaction type: ".
107                     $self->transaction_type());
108     }
109
110     $content{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
111       or croak "unparsable expiration $content{expiration}";
112
113     ( $month, $year ) = ( $1, "20$2" );
114     $month = '0'. $month if $month =~ /^\d$/;
115   }
116
117   my $ccreq = new Business::OnlinePayment::PPIPayMover::CreditCardRequest;
118
119   $self->revmap_fields( $ccreq,
120
121     'ChargeTotal'                  => 'amount',
122     'ChargeType'                   => 'action',
123     'CreditCardNumber'             => 'card_number',
124     'CreditCardVerificationNumber' => 'cvv2',
125     'ExpireMonth'                  => \$month,
126     'ExpireYear'                   => \$year,
127
128     'BillAddressOne'               => 'address',
129     #'BillAddressTwo'               => '',
130     'BillCity'                     => 'city',
131     'BillCompany'                  => 'company',
132     'BillCountryCode'              => 'country',
133     #'BillCustomerTitle'            => '',
134     'BillEmail',                   => 'email',
135     'BillFax'                      => 'fax',
136     'BillFirstName'                => 'first_name',
137     'BillLastName'                 => 'last_name',
138     #'BillMiddleName'               => '',
139     'BillNote'                      => '',
140     'BillPhone'                    => 'phone',
141     'BillPostalCode'               => 'zip',
142     'BillStateOrProvince'          => 'state',
143
144     'ShipAddressOne'               => 'ship_address',
145     #'ShipAddressTwo'               => '',
146     'ShipCity'                     => 'ship_city',
147     'ShipCompany'                  => 'ship_company',
148     'ShipCountryCode'              => 'ship_country',
149     #'ShipCustomerTitle'            => '',
150     'ShipEmail',                   => 'ship_email',
151     'ShipFax'                      => 'ship_fax',
152     'ShipFirstName'                => 'ship_first_name',
153     'ShipLastName'                 => 'ship_last_name',
154     #'ShipMiddleName'               => '',
155     'ShipNote'                      => '',
156     'ShipPhone'                    => 'ship_phone',
157     'ShipPostalCode'               => 'ship_zip',
158     'ShipStateOrProvince'          => 'ship_state',
159
160     #'OrderId'                      => 'order_number',
161     'OrderId'                      => (int (rand 999999998) + 1 ), # XXX This can result in duplicate order ids.  You should use your own sequence instead.
162     'BuyerCode'                    => '83487235',
163     'CustomerIPAddress'            => 'customer_ip',
164     'OrderCustomerId'              => 'customer_id',
165     'OrderDescription'             => 'description',
166     #'OrderUserId'                  => '',
167     #'PurchaseOrderNumber'          => '',
168     'TransactionConditionCode'     => \( TCC_CARDHOLDER_NOT_PRESENT_SECURE_ECOMMERCE ),
169     #'ShippingCharge'               => '',
170     #'StateTax'                     => '',
171     #'TaxAmount'                    => '',
172     #'TaxExempt'                    => '',
173
174     'InvoiceNumber'                => 'invoice_number',
175     'Industry'                     => \( RETAIL ),
176     #'FolioNumber'                  => '',
177
178     #'ChargeTotalIncludesRestaurant'
179     #'ChargeTotalIncludesGiftshop'
180     #'ChargeTotalIncludesMinibar'
181     #'ChargeTotalIncludesPhone'
182     #'ChargeTotalIncludesLaundry'
183     #'ChargeTotalIncludesOther'
184
185     #'ServiceRate'
186
187     #'ServiceStartDay'
188     #'ServiceStartMonth'
189     #'ServiceStartYear'
190     #'ServiceEndMonth'
191     #'ServiceEndYear'
192     #'ServiceEndDay'
193
194     #'ServiceNoShow'
195
196     #'ReferenceId'    => '', # XXX Use reference ID for follow-on transactions (CAPTURE, VOID)
197     #'CAVV'
198     #'XID'
199     #'Track1'
200     #'Track2'
201
202
203   );
204
205   # Send the transaction! (test token)
206
207   my $token = $content{'login'};
208   $token = "TEST$token" if $self->test_transaction();
209   
210   my $ccresponse = $tranclient->doTransaction( 
211     "",     # transaction key (?)
212     $ccreq, #cc request
213     $token, #token
214   );
215
216   die $tranclient->GetErrorString unless defined $ccresponse;
217
218   $self->result_code($ccresponse->GetResponseCode);
219   $self->avs_code($ccresponse->GetAVSCode);
220   $self->order_number($ccresponse->GetOrderId);
221
222   if ( $self->result_code == 1 ) { # eq '1' ?
223     $self->is_success(1);
224     #$self->authorization($ccresponse->GetBankApprovalCode);
225     $self->authorization($ccresponse->GetReferenceId); #"Identifier for follow-on transactions"
226   } else {
227     $self->is_success(0);
228     $self->error_message($ccresponse->GetResponseCodeText);
229   }
230
231 }
232
233 ##  print "ResponseCode            : ", $ccresponse->GetResponseCode, "\n";
234 ##  print "ResponseCodeText        : ", $ccresponse->GetResponseCodeText, "\n";
235 #  print "Timestamp               : ", $datetime, "\n";
236 #  print "IsoCode                 : ", $ccresponse->GetIsoCode, "\n";
237 ##  print "OrderId                 : ", $ccresponse->GetOrderId, "\n";
238 ##  print "BankApprovalCode        : ", $ccresponse->GetBankApprovalCode, "\n";
239 #  print "State                   : ", $ccresponse->GetState, "\n";
240 #  print "AuthorizedAmount        : ", $ccresponse->GetAuthorizedAmount, "\n";
241 #  print "OriginalAuthorizedAmount: ", $ccresponse->GetOriginalAuthorizedAmount,
242 #"\n";
243 #  print "CapturedAmount          : ", $ccresponse->GetCapturedAmount, "\n";
244 #  print "CreditedAmount          : ", $ccresponse->GetCreditedAmount, "\n";
245 #  print "TimeStampCreated        : ", $ccresponse->GetTimeStampCreated, "\n";
246 ##  print "ReferenceId             : ", $ccresponse->GetReferenceId, "\n";
247 #  print "BankTransactionId       : ", $ccresponse->GetBankTransactionId, "\n";
248 #  print "BatchId                 : ", $ccresponse->GetBatchId, "\n";
249 #  #print "AVS Code                : ", $ccresponse->GetAVSCode, "\n";
250
251 #this is different from a "normal" B:OP revmap, it sets things in $ccreq
252 sub revmap_fields {
253     my($self, $ccreq, %map) = @_;
254     my %content = $self->content();
255     foreach(keys %map) {
256       my $method = "Set$_";
257       my $content = ref($map{$_}) ? ${ $map{$_} } : $content{$map{$_}};
258       $ccreq->$method($content);
259     }
260 }
261
262
263 1;
264 __END__
265
266 =head1 NAME
267
268 Business::OnlinePayment::PPIPayMover - PPI PayMover backend for Business::OnlinePayment
269
270 =head1 SYNOPSIS
271
272   use Business::OnlinePayment;
273
274   my $tx = new Business::OnlinePayment( 'PPIPayMover' );
275
276   $tx->content(
277       login          => '195325FCC230184964CAB3A8D93EEB31888C42C714E39CBBB2E541884485D04B', #token
278       type           => 'VISA',
279       action         => 'Normal Authorization',
280       description    => 'Business::OnlinePayment test',
281       amount         => '49.95',
282       invoice_number => '100100',
283       customer_id    => 'jsk',
284       name           => 'Grub Tetris',
285       address        => '123 Anystreet',
286       city           => 'Anywhere',
287       state          => 'UT',
288       zip            => '84058',
289       email          => 'ivan-ppipaymover@420.am',
290       card_number    => '4007000000027',
291       expiration     => '09/12',
292   );
293   $tx->submit();
294
295   if($tx->is_success()) {
296       print "Card processed successfully: ".$tx->authorization."\n";
297   } else {
298       print "Card was rejected: ".$tx->error_message."\n";
299   }
300
301 =head1 SUPPORTED TRANSACTION TYPES
302
303 =head2 Visa, MasterCard, American Express, JCB, Discover/Novus, Carte blanche/Di
304 ners Club
305
306 =head1 DESCRIPTION
307
308 For detailed information see L<Business::OnlinePayment>.
309
310 =head1 BUGS
311
312 =head1 AUTHOR
313
314 Ivan Kohler <ivan-ppipaymover@420.am>
315
316 =head1 COPYRIGHT AND LICENSE
317
318 Based on API components from PPI PayMover provided without clear licensing, so,
319 probably not freely licensable at the moment... assuming that can be resolved:
320
321 Business::OnlinePayment conversion copyright (c) 2006 Ivan Kohler
322 All rights reserved. This program is free software; you can redistribute it
323 and/or modify it under the same terms as Perl itself.
324
325 =head1 SEE ALSO
326
327 perl(1), L<Business::OnlinePayment>.
328
329 =cut