1 ## Business::OnlinePayment::Skipjack
3 ## Original Skipjack.pm developed by New York Connect Net (http://nyct.net)
4 ## Michael Bacarella <mbac@nyct.net>
6 ## Modified for GetCareer.com by Slipstream.com
7 ## Troy Davis <troy@slipstream.com>
9 ## 'Adapted' (completely rewritten) for Business::OnlinePayment
10 ## by Fire2Wire Internet Services (http://www.fire2wire.com)
11 ## Mark Wells <mark@pc-intouch.com>
12 ## Kristian Hoffmann <khoff@pc-intouch.com>
13 ## James Switzer <jamess@fire2wire.com>
18 ## Business::OnlinePayment
21 package Business::OnlinePayment::Skipjack;
25 use Business::OnlinePayment ;#3;
26 #use Business::OnlinePayment::HTTPS;
28 use vars qw( @ISA $VERSION $DEBUG );
33 #@ISA = qw( Business::OnlinePayment::HTTPS );
34 @ISA = qw( Business::OnlinePayment );
37 '-1' => 'Invalid length (-1)',
38 '-35' => 'Invalid credit card number (-35)',
39 '-37' => 'Failed communication (-37)',
40 '-39' => 'Serial number is too short (-39)',
41 '-51' => 'The zip code is invalid',
42 '-52' => 'The shipto zip code is invalid',
43 '-53' => 'Length of expiration date (-53)',
44 '-54' => 'Length of account number date (-54)',
45 '-55' => 'Length of street address (-55)',
46 '-56' => 'Length of shipto street address (-56)',
47 '-57' => 'Length of transaction amount (-57)',
48 '-58' => 'Length of name (-58)',
49 '-59' => 'Length of location (-59)',
50 '-60' => 'Length of state (-60)',
51 '-61' => 'Length of shipto state (-61)',
52 '-62' => 'Length of order string (-62)',
53 '-64' => 'Invalid phone number (-64)',
54 '-65' => 'Empty name (-65)',
55 '-66' => 'Empty email (-66)',
56 '-67' => 'Empty street address (-66)',
57 '-68' => 'Empty city (-68)',
58 '-69' => 'Empty state (-69)',
59 '-70' => 'Empty zip code (-70)',
60 '-71' => 'Empty order number (-71)',
61 '-72' => 'Empty account number (-72)',
62 '-73' => 'Empty expiration month (-73)',
63 '-74' => 'Empty expiration year (-74)',
64 '-75' => 'Empty serial number (-75)',
65 '-76' => 'Empty transaction amount (-76)',
66 '-79' => 'Length of customer name (-79)',
67 '-80' => 'Length of shipto customer name (-80)',
68 '-81' => 'Length of customer location (-81)',
69 '-82' => 'Length of customer state (-82)',
70 '-83' => 'Length of shipto phone (-83)',
71 '-84' => 'Pos Error duplicate ordernumber (-84)',
72 '-91' => 'Pos Error CVV2 (-91)',
73 '-92' => 'Pos Error Approval Code (-92)',
74 '-93' => 'Pos Error Blind Credits Not Allowed (-93)',
75 '-94' => 'Pos Error Blind Credits Failed (-94)',
76 '-95' => 'Pos Error Voice Authorizations Not Allowed (-95)',
80 'X' => 'Exact match, 9 digit zip',
81 'Y' => 'Exact match, 5 digit zip',
82 'A' => 'Address match only',
83 'W' => '9 digit match only',
84 'Z' => '5 digit match only',
85 'N' => 'No address or zip match',
86 'U' => 'Address unavailable',
87 'R' => 'Issuer system unavailable',
88 'E' => 'Not a mail/phone order',
89 'S' => 'Service not supported'
95 address => 'Streetaddress',
99 order_number => 'Ordernumber',
100 card_number => 'Accountnumber',
101 exp_month => 'Month',
103 amount => 'Transactionamount',
104 orderstring => 'Orderstring',
105 phone => 'Shiptophone',
106 login => 'Serialnumber',
109 my %CHANGE_STATUS_FIELDS = (
110 login => 'szSerialNumber',
111 password => 'szDeveloperSerialNumber',
112 order_number => 'szOrderNumber',
113 # => 'szTransactionId',
114 amount => 'szAmount',
117 my @CHANGE_STATUS_RESPONSE = (
131 my @CHANGE_STATUS_RESPONSE_RECORD = (
132 'Serial Number (Record)',
136 'Status Response Message',
141 my %CHANGE_STATUS_ERROR_CODES = (
143 '-1' => 'Invalid Command',
144 '-2' => 'Parameter Missing',
145 '-3' => 'Failed retrieving response',
146 '-4' => 'Invalid Status',
147 '-5' => 'Failed reading security flags',
148 '-6' => 'Developer serial number not found',
149 '-7' => 'Invalid Serial Number',
150 '-8' => 'Expiration year not four characters',
151 '-9' => 'Credit card expired',
152 '-10' => 'Invalid starting date (recurring payment)',
153 '-11' => 'Failed adding recurring payment',
154 '-12' => 'Invalid frequency (recurring payment)',
157 my %GET_STATUS_FIELDS = (
158 login => 'szSerialNumber',
159 password => 'szDeveloperSerialNumber',
160 order_number => 'szOrderNumber',
161 #date => 'szDate', # would probably need some massaging
162 # and parse_SJAPI_TransactionStatusRequest would
163 # need to handle multiple records...
166 my @GET_STATUS_RESPONSE = (
180 my @GET_STATUS_RESPONSE_RECORD = (
181 'Serial Number (Record)',
183 'Transaction Status Code',
184 'Transaction Status Message',
192 my %GET_STATUS_ERROR_CODES = (
194 '-1' => 'Invalid Command',
195 '-2' => 'Parameter Missing',
196 '-3' => 'Failed retrieving response',
197 '-4' => 'Invalid Status',
198 '-5' => 'Failed reading security flags',
199 '-6' => 'Developer serial number not found',
200 '-7' => 'Invalid Serial Number',
201 '-8' => 'Expiration year not four characters',
202 '-9' => 'Credit card expired',
205 my %CUR_STATUS_CODES = (
216 my %PEND_STATUS_CODES = (
218 '1' => 'Pending Credit',
219 '2' => 'Pending Settlement ',
220 '3' => 'Pending Delete',
221 '4' => 'Pending Authorization',
222 '5' => 'Pending Settle Force (for Manual Accts)',
223 '6' => 'Pending Recurring',
226 sub _gen_ordernum { return int(rand(4000000000)); }
233 $self->server('www.skipjackic.com');
244 my %c = $self->content;
245 my (%input, %output);
247 unless ( $c{type} =~ /(cc|visa|mastercard|american express|discover)/i ) {
248 croak 'Business::OnlinePayment::Skipjack does not support "' .
249 $c{type}. '" transactions';
252 # skipjack kicks out "Length of transaction amount (-57)" or "Invalid amount"
253 # if the amount is missing .XX
254 $c{amount} = sprintf('%.2f', $c{amount})
255 if defined($c{amount}) && length($c{amount});
257 if ( lc($c{action}) eq 'normal authorization' ) {
258 $self->{_action} = 'normal authorization';
259 $self->path('/scripts/evolvcc.dll?AuthorizeAPI');
261 $c{expiration} =~ /(\d\d?)\D*(\d\d?)/; # Slightly less crude way to extract the exp date.
262 $c{exp_month} = sprintf('%02d',$1);
263 $c{exp_year} = sprintf('%02d',$2);
265 $c{order_number} = _gen_ordernum unless $c{order_number};
267 $c{orderstring} = '0~'.$c{description}.'~'.$c{amount}.'~1~N~||'
268 unless $c{orderstring};
270 %input = map { ($FIELDS{$_} || $_), $c{$_} } keys(%c);
272 } elsif ( $c{action} =~ /^(credit|void|post authorization)$/i ) {
274 $self->path('/scripts/evolvcc.dll?SJAPI_TransactionChangeStatusRequest');
276 %input = map { ($CHANGE_STATUS_FIELDS{$_} || $_), $c{$_} } keys %c;
278 if ( lc($c{action} ) eq 'credit' ) {
279 $self->{_action} = 'credit';
280 $input{szDesiredStatus} = 'CREDIT';
281 } elsif ( lc($c{action} ) eq 'void' ) {
282 $self->{_action} = 'void';
283 $input{szDesiredStatus} = 'DELETE';
284 } elsif ( lc($c{action} ) eq 'post authorization' ) {
285 $self->{_action} = 'postauth';
286 $input{szDesiredStatus} = 'SETTLE';
288 die "fatal: $c{action} is not credit or void!";
291 } elsif ( lc($c{action}) eq 'status' ) {
293 $self->{_action} = 'status';
294 $self->path('/scripts/evolvcc.dll?SJAPI_TransactionStatusRequest');
295 %input = map { ($GET_STATUS_FIELDS{$_} || $_), $c{$_} } keys(%c);
299 croak 'Business::OnlinePayment::Skipjack does not support "'.
300 $c{action}. '" actions';
304 $self->server('developer.skipjackic.com') # test mode
305 if $self->test_transaction();
307 my( $page, $response ) = $self->https_post( %input );
308 warn "\n$page\n" if $DEBUG;
310 if ( $self->{_action} eq 'normal authorization' ) {
311 %output = parse_Authorize_API($page);
312 } elsif ( $self->{_action} =~ /^(credit|void|postauth)$/ ) {
313 %output = parse_SJAPI_TransactionChangeStatusRequest($page);
314 } elsif ( $self->{_action} eq 'status' ) {
315 %output = parse_SJAPI_TransactionStatusRequest($page);
317 die "fatal: unknown action: ". $self->{_action};
320 $self->{_result} = \%output;
321 $self->authorization($output{'AUTHCODE'});
329 if ( $self->{_action} eq 'normal authorization' ) {
331 return( $self->{_result}->{'szIsApproved'} == 1 );
333 } elsif ( $self->{_action} =~ /^(credit|void|postauth)$/ ) {
335 return( $self->{_result}{'Error Code'} eq '0' # == 0 matches ''
336 && uc($self->{_result}{'Status Response'}) eq 'SUCCESSFUL'
339 } elsif ( $self->{_action} eq 'status' ) {
341 return( $self->{_result}{'Error Code'} eq '0' ); # == 0 matches ''
344 die "fatal: unknown action: ". $self->{_action};
354 if($self->is_success) { return ''; }
356 if ( $self->{_action} eq 'normal authorization' ) {
358 if(($r = $self->{_result}->{'szReturnCode'}) < 0) { return $CC_ERRORS{$r}; }
359 if($r = $self->{_result}->{'szAVSResponseMessage'}) { return $r; }
360 if($r = $self->{_result}->{'szAuthorizationDeclinedMessage'}) { return $r; }
362 } elsif ( $self->{_action} =~ /^(credit|void|postauth)$/ ) {
364 if ( ( $r = $self->{_result}{'Error Code'} ) < 0 ) {
365 return $CHANGE_STATUS_ERROR_CODES{$r};
367 return $self->{_result}{'Status Response Message'};
370 } elsif ( $self->{_action} eq 'status' ) {
372 if ( ( $r = $self->{_result}{'Error Code'} ) < 0 ) {
373 return $CHANGE_STATUS_ERROR_CODES{$r};
375 return $self->{_result}{'Status Response Message'};
379 die "fatal: unknown action: ". $self->{_action};
385 #sub result_code { shift->{_result}->{'ezIsApproved'}; }
386 sub authorization { shift->{_result}{'szAuthorizationResponseCode'}; }
387 sub avs_code { shift->{_result}{'szAVSResponseCode'}; }
388 sub order_number { shift->{_result}{'szOrderNumber'}; }
389 sub cvv2_response { shift->{_result}{'szCVV2ResponseCode'}; }
390 sub cavv_response { shift->{_result}{'szCAVVResponseCode'}; }
395 substr( $self->{_result}{'Transaction Status Code'}, 0, 1 )
402 substr( $self->{_result}{'Transaction Status Code'}, 1, 2 )
406 sub parse_Authorize_API
411 my $csv_keys = new Text::CSV_XS;
412 my $csv_values = new Text::CSV_XS;
414 my ($keystring, $valuestring) = split(/\r\n/, $page);
415 $csv_keys->parse($keystring);
416 $csv_values->parse($valuestring);
417 @output{$csv_keys->fields()} = $csv_values->fields();
423 sub parse_SJAPI_TransactionChangeStatusRequest
427 my $csv = new Text::CSV_XS;
431 my @records = split(/\r\n/, $page);
433 $csv->parse(shift @records)
434 or die "CSV parse failed on " . $csv->error_input;
435 @output{@CHANGE_STATUS_RESPONSE} = $csv->fields();
437 # we only handle a single record reponse, as that's all this module will
438 # currently submit...
439 $csv->parse(shift @records)
440 or die "CSV parse failed on " . $csv->error_input;
441 @output{@CHANGE_STATUS_RESPONSE_RECORD} = $csv->fields();
447 sub parse_SJAPI_TransactionStatusRequest
451 my $csv = new Text::CSV_XS;
455 my @records = split(/\r\n/, $page);
457 #$csv->parse(shift @records)
458 $csv->parse(shift @records)
459 or die "CSV parse failed on " . $csv->error_input;
460 @output{@GET_STATUS_RESPONSE} = $csv->fields();
462 # we only handle a single record reponse, as that's all this module will
463 # currently submit...
464 $csv->parse(shift @records)
465 or die "CSV parse failed on " . $csv->error_input;
466 @output{@GET_STATUS_RESPONSE_RECORD} = $csv->fields();
478 Business::OnlinePayment::Skipjack - Skipjack backend module for Business::OnlinePayment
482 use Business::OnlinePayment;
485 # One step transaction, the simple case.
488 my $tx = new Business::OnlinePayment("Skipjack");
491 login => '000178101827', # "HTML serial number"
492 action => 'Normal Authorization',
493 description => 'Business::OnlinePayment test',
495 invoice_number => '100100',
496 customer_id => 'jsk',
497 first_name => 'Jason',
498 last_name => 'Kohles',
499 address => '123 Anystreet',
503 card_number => '4007000000027',
504 expiration => '09/02',
505 cvv2 => '1234', #optional
506 #referer => 'http://valid.referer.url/',
510 if($tx->is_success()) {
511 print "Card processed successfully: ".$tx->authorization."\n";
513 print "Card was rejected: ".$tx->error_message."\n";
517 # Process a credit...
520 my $tx = new Business::OnlinePayment( "Skipjack" );
524 login => '000178101827', # "HTML serial number"
525 password => '100594217288', # "developer serial number"
526 action => 'Normal Authorization',
527 description => 'Business::OnlinePayment test',
529 invoice_number => '100100',
530 customer_id => 'jsk',
531 first_name => 'Jason',
532 last_name => 'Kohles',
533 address => '123 Anystreet',
537 card_number => '4007000000027',
538 expiration => '09/02',
539 cvv2 => '1234', #optional
540 #referer => 'http://valid.referer.url/',
544 if($tx->is_success()) {
545 print "Card credited successfully: ".$tx->authorization."\n";
547 print "Credit was rejected: ".$tx->error_message."\n";
551 =head1 SUPPORTED TRANSACTION TYPES
553 =head2 CC, Visa, MasterCard, American Express, Discover
555 Content required for Normal Authorization : login, action, amount, card_number,
556 expiration, name, address, city, state, zip, phone, email
558 Content required for Void or Credit: login, password, action, order_number
562 For detailed information see L<Business::OnlinePayment>
566 Net::SSLeay _or_ ( Crypt::SSLeay and LWP )
568 =head1 NOTE ON CREDITS
570 If you want to process credits, you must have your developer serial number
571 applied to your production account. See
572 http://www.skipjack.com/resources/Education/serialnumbers.htm
576 This modules supports a non-standard "status" action that corresponds to
577 Skipjack's TransactionStatusRequest. It should be documented.
581 Inspiried by (but no longer contains) code from:
583 Original Skipjack.pm developed by New York Connect Net (http://nyct.net)
584 Michael Bacarella <mbac@nyct.net>
586 Modified for GetCareer.com by Slipstream.com
587 Troy Davis <troy@slipstream.com>
589 'Adapted' (completely rewritten) for Business::OnlinePayment
590 by Fire2Wire Internet Services (http://www.fire2wire.com)
591 Mark Wells <mark@pc-intouch.com>
592 Kristian Hoffmann <khoff@pc-intouch.com>
593 James Switzer <jamess@fire2wire.com>
595 Boring 0.2 update by Ivan Kohler <ivan-skipjack@420.am>
599 Copyright (c) 2006 Fire2Wire Internet Services (http://www.fire2wire.com)
600 All rights reserved. This program is free software; you can redistribute it
601 and/or modify it under the same terms as Perl itself.
603 Inspiried by (but no longer contains) code from:
605 Original Skipjack.pm developed by New York Connect Net (http://nyct.net)
606 Michael Bacarella <mbac@nyct.net>
608 Modified for GetCareer.com by Slipstream.com
609 Troy Davis <troy@slipstream.com>
613 L<Business::OnlinePayment>