1 package Business::OnlinePayment::IPPay;
9 use Business::OnlinePayment;
10 use Business::OnlinePayment::HTTPS;
11 use vars qw($VERSION $DEBUG @ISA $me);
13 @ISA = qw(Business::OnlinePayment::HTTPS);
16 $me = 'Business::OnlinePayment::IPPay';
22 # standard B::OP methods/data
23 $self->server('gateway17.jetpay.com') unless $self->server;
24 $self->port('443') unless $self->port;
25 $self->path('/jetpay') unless $self->path;
27 $self->build_subs(qw( order_number avs_code cvv2_response
28 response_page response_code response_headers
31 # module specific data
33 $self->debug( $opts{debug} );
38 foreach my $key (keys %opts) {
39 $key =~ /^default_(\w*)$/ or next;
40 $_defaults{$1} = $opts{$key};
43 $self->{_defaults} = \%_defaults;
49 my %content = $self->content();
52 my %types = ( 'visa' => 'CC',
54 'american express' => 'CC',
58 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
59 $self->transaction_type($content{'type'});
62 my $action = lc($content{'action'});
64 ( 'normal authorization' => 'SALE',
65 'authorization only' => 'AUTHONLY',
66 'post authorization' => 'CAPT',
71 ( 'normal authorization' => 'CHECK',
73 'credit' => 'REVERSAL',
75 if ($self->transaction_type eq 'CC') {
76 $content{'TransactionType'} = $actions{$action} || $action;
77 }elsif ($self->transaction_type eq 'ECHECK') {
78 $content{'TransactionType'} = $check_actions{$action} || $action;
83 my %account_types = ('personal checking' => 'Checking',
84 'personal savings' => 'Savings',
85 'business checking' => 'BusinessCk',
87 $content{'account_type'} = $account_types{lc($content{'account_type'})}
88 || $content{'account_type'};
90 $content{Origin} = 'RECURRING'
91 if ($content{recurring_billing} &&$content{recurring_billing} eq 'YES' );
93 # stuff it back into %content
94 $self->content(%content);
99 my ($self, $exp) = (shift, shift);
101 if ( defined($exp) and $exp =~ /^(\d+)\D+\d*\d{2}$/ ) {
102 $month = sprintf( "%02d", $1 );
103 }elsif ( defined($exp) and $exp =~ /^(\d{2})\d{2}$/ ) {
104 $month = sprintf( "%02d", $1 );
110 my ($self, $exp) = (shift, shift);
112 if ( defined($exp) and $exp =~ /^\d+\D+\d*(\d{2})$/ ) {
113 $year = sprintf( "%02d", $1 );
114 }elsif ( defined($exp) and $exp =~ /^\d{2}(\d{2})$/ ) {
115 $year = sprintf( "%02d", $1 );
122 tie my(%map), 'Tie::IxHash', @_;
123 my %content = $self->content();
126 if ( ref( $map{$_} ) eq 'HASH' ) {
127 $value = $map{$_} if ( keys %{ $map{$_} } );
128 }elsif( ref( $map{$_} ) ) {
129 $value = ${ $map{$_} };
130 }elsif( exists( $content{ $map{$_} } ) ) {
131 $value = $content{ $map{$_} };
134 if (defined($value)) {
145 $self->is_success(0);
148 my @required_fields = qw(action login type);
150 my $action = lc($self->{_content}->{action});
151 my $type = $self->transaction_type();
152 if ( $action eq 'normal authorization'
153 || $action eq 'credit'
154 || $action eq 'authorization only' && $type eq 'CC')
156 push @required_fields, qw( amount );
158 push @required_fields, qw( card_number expiration )
161 push @required_fields,
162 qw( routing_code account_number name ) # account_type
163 if ($type eq "ECHECK");
165 }elsif ( $action eq 'post authorization' && $type eq 'CC') {
166 push @required_fields, qw( order_number );
167 }elsif ( $action eq 'void') {
168 push @required_fields, qw( order_number amount );
170 push @required_fields, qw( authorization card_number )
173 push @required_fields,
174 qw( routing_code account_number name ) # account_type
175 if ($type eq "ECHECK");
178 croak "$me can't handle transaction type: ".
179 $self->{_content}->{action}. " for ".
180 $self->transaction_type();
183 my %content = $self->content();
184 foreach ( keys ( %{($self->{_defaults})} ) ) {
185 $content{$_} = $self->{_defaults}->{$_} unless exists($content{$_});
187 $self->content(%content);
189 $self->required_fields(@required_fields);
191 if ($self->test_transaction()) {
192 $self->server('test1.jetpay.com');
194 $self->path('/jetpay');
197 my $transaction_id = $content{'order_number'};
198 unless ($transaction_id) {
199 my ($page, $server_response, %headers) = $self->https_get('dummy' => 1);
200 warn "fetched transaction id: (HTTPS response: $server_response) ".
202 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
203 "(Raw HTTPS content: $page)"
205 return unless $server_response=~ /^200/;
206 $transaction_id = $page;
209 my $cardexpmonth = $self->expdate_month($content{expiration});
210 my $cardexpyear = $self->expdate_year($content{expiration});
211 my $cardstartmonth = $self->expdate_month($content{card_start});
212 my $cardstartyear = $self->expdate_year($content{card_start});
215 if (defined($content{amount})) {
216 $amount = sprintf("%.2f", $content{amount});
220 my $check_number = $content{check_number} || "100" # make one up
221 if($content{account_number});
223 my $terminalid = $content{login} if $type eq 'CC';
224 my $merchantid = $content{login} if $type eq 'ECHECK';
226 my $country = country2code( $content{country}, LOCALE_CODE_ALPHA_3 );
227 $country = country_code2code( $content{country},
232 $country = $content{country}
234 $country = uc($country) if $country;
237 country2code( $content{ship_country}, LOCALE_CODE_ALPHA_3 );
238 $ship_country = country_code2code( $content{ship_country},
242 unless $ship_country;
243 $ship_country = $content{ship_country}
244 unless $ship_country;
245 $ship_country = uc($ship_country) if $ship_country;
247 tie my %ach, 'Tie::IxHash',
248 $self->revmap_fields(
249 #AccountType => 'account_type',
250 AccountNumber => 'account_number',
251 ABA => 'routing_code',
252 CheckNumber => \$check_number,
255 tie my %industryinfo, 'Tie::IxHash',
256 $self->revmap_fields(
257 Type => 'IndustryInfo',
260 tie my %shippingaddr, 'Tie::IxHash',
261 $self->revmap_fields(
262 Address => 'ship_address',
264 StateProv => 'ship_state',
265 Country => \$ship_country,
266 Phone => 'ship_phone',
269 unless ( $type ne 'CC' || keys %shippingaddr ) {
270 tie %shippingaddr, 'Tie::IxHash',
271 $self->revmap_fields(
272 Address => 'address',
274 StateProv => 'state',
275 Country => \$country,
280 tie my %shippinginfo, 'Tie::IxHash',
281 $self->revmap_fields(
282 CustomerPO => 'CustomerPO',
283 ShippingMethod => 'ShippingMethod',
284 ShippingName => 'ship_name',
285 ShippingAddr => \%shippingaddr,
288 tie my %req, 'Tie::IxHash',
289 $self->revmap_fields(
290 TransactionType => 'TransactionType',
291 TerminalID => 'login',
292 # TerminalID => \$terminalid,
293 # MerchantID => \$merchantid,
294 TransactionID => \$transaction_id,
295 RoutingCode => 'RoutingCode',
296 Approval => 'authorization',
297 BatchID => 'BatchID',
299 Password => 'password',
300 OrderNumber => 'invoice_number',
301 CardNum => 'card_number',
303 Issue => 'issue_number',
304 CardExpMonth => \$cardexpmonth,
305 CardExpYear => \$cardexpyear,
306 CardStartMonth => \$cardstartmonth,
307 CardStartYear => \$cardstartyear,
312 DispositionType => 'DispositionType',
313 TotalAmount => \$amount,
314 FeeAmount => 'FeeAmount',
315 TaxAmount => 'TaxAmount',
316 BillingAddress => 'address',
317 BillingCity => 'city',
318 BillingStateProv => 'state',
319 BillingPostalCode => 'zip',
320 BillingCountry => \$country,
321 BillingPhone => 'phone',
323 UserIPAddr => 'customer_ip',
324 UserHost => 'UserHost',
325 UDField1 => 'UDField1',
326 UDField2 => 'UDField2',
327 UDField3 => 'UDField3',
328 ActionCode => 'ActionCode',
329 IndustryInfo => \%industryinfo,
330 ShippingInfo => \%shippinginfo,
334 my $writer = new XML::Writer( OUTPUT => \$post_data,
337 ENCODING => 'us-ascii',
340 $writer->startTag('JetPay');
341 foreach ( keys ( %req ) ) {
342 $self->_xmlwrite($writer, $_, $req{$_});
344 $writer->endTag('JetPay');
347 warn "$post_data\n" if $DEBUG;
349 my ($page,$server_response,%headers) = $self->https_post($post_data);
351 warn "$page\n" if $DEBUG;
354 if ($server_response =~ /^200/){
355 $response = XMLin($page);
356 if ( exists($response->{ActionCode}) && !exists($response->{ErrMsg})) {
357 $self->error_message($response->{ResponseText});
359 $self->error_message($response->{Errmsg});
362 # $self->error_message("Server Failed");
365 $self->result_code($response->{ActionCode} || '');
366 $self->order_number($response->{TransactionID} || '');
367 $self->authorization($response->{Approval} || '');
368 $self->cvv2_response($response->{CVV2} || '');
369 $self->avs_code($response->{AVS} || '');
371 $self->is_success($self->result_code() eq '000' ? 1 : 0);
373 unless ($self->is_success()) {
374 unless ( $self->error_message() ) { #additional logging information
375 $self->error_message(
376 "(HTTPS response: $server_response) ".
378 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
379 "(Raw HTTPS content: $page)"
387 my ($self, $writer, $item, $value) = @_;
388 $writer->startTag($item);
389 if ( ref( $value ) eq 'HASH' ) {
390 foreach ( keys ( %$value ) ) {
391 $self->_xmlwrite($writer, $_, $value->{$_});
394 $writer->characters($value);
396 $writer->endTag($item);
404 Business::OnlinePayment::IPPay - IPPay backend for Business::OnlinePayment
408 use Business::OnlinePayment;
411 new Business::OnlinePayment( "IPPay",
412 'default_Origin' => 'PHONE ORDER',
416 login => 'testdrive',
417 password => '', #password
418 action => 'Normal Authorization',
419 description => 'Business::OnlinePayment test',
421 customer_id => 'tfb',
422 name => 'Tofu Beast',
423 address => '123 Anystreet',
427 card_number => '4007000000027',
428 expiration => '09/02',
429 cvv2 => '1234', #optional
433 if($tx->is_success()) {
434 print "Card processed successfully: ".$tx->authorization."\n";
436 print "Card was rejected: ".$tx->error_message."\n";
439 =head1 SUPPORTED TRANSACTION TYPES
441 =head2 CC, Visa, MasterCard, American Express, Discover
443 Content required: type, login, action, amount, card_number, expiration.
447 Content required: type, login, action, amount, name, account_number, routing_code.
451 For detailed information see L<Business::OnlinePayment>.
453 =head1 METHODS AND FUNCTIONS
455 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.
459 Returns the response error code.
463 Returns the response error description text.
465 =head2 server_response
467 Returns the complete response from the server.
469 =head1 Handling of content(%content) data:
473 The following actions are valid
481 =head1 Setting IPPay parameters from content(%content)
483 The following rules are applied to map data to IPPay parameters
484 from content(%content):
486 # param => $content{<key>}
487 TransactionType => 'TransactionType',
488 TerminalID => 'login',
489 TransactionID => 'order_number',
490 RoutingCode => 'RoutingCode',
491 Approval => 'authorization',
492 BatchID => 'BatchID',
494 Password => 'password',
495 OrderNumber => 'invoice_number',
496 CardNum => 'card_number',
498 Issue => 'issue_number',
499 CardExpMonth => \( $month ), # MM from MM(-)YY(YY) of 'expiration'
500 CardExpYear => \( $year ), # YY from MM(-)YY(YY) of 'expiration'
501 CardStartMonth => \( $month ), # MM from MM(-)YY(YY) of 'card_start'
502 CardStartYear => \( $year ), # YY from MM(-)YY(YY) of 'card_start'
506 AccountNumber => 'account_number',
507 ABA => 'routing_code',
508 CheckNumber => 'check_number',
510 DispositionType => 'DispositionType',
511 TotalAmount => 'amount' reformatted into cents
512 FeeAmount => 'FeeAmount',
513 TaxAmount => 'TaxAmount',
514 BillingAddress => 'address',
515 BillingCity => 'city',
516 BillingStateProv => 'state',
517 BillingPostalCode => 'zip',
518 BillingCountry => 'country', # forced to ISO-3166-alpha-3
519 BillingPhone => 'phone',
521 UserIPAddr => 'customer_ip',
522 UserHost => 'UserHost',
523 UDField1 => 'UDField1',
524 UDField2 => 'UDField2',
525 UDField3 => 'UDField3',
526 ActionCode => 'ActionCode',
528 Type => 'IndustryInfo',
530 CustomerPO => 'CustomerPO',
531 ShippingMethod => 'ShippingMethod',
532 ShippingName => 'ship_name',
534 Address => 'ship_address',
536 StateProv => 'ship_state',
537 Country => 'ship_country', # forced to ISO-3166-alpha-3
538 Phone => 'ship_phone',
544 Business::OnlinePayment::IPPay uses IPPay XML Product Specifications version
547 See http://www.ippay.com/ for more information.
551 Jeff Finucane, ippay@weasellips.com
555 perl(1). L<Business::OnlinePayment>.