1 package Business::OnlinePayment::IPPay;
8 use Business::OnlinePayment;
9 use Business::OnlinePayment::HTTPS;
10 use vars qw($VERSION $DEBUG @ISA $me);
12 @ISA = qw(Business::OnlinePayment::HTTPS);
15 $me = 'Business::OnlinePayment::IPPay';
21 # standard B::OP methods/data
22 $self->server('gateway17.jetpay.com') unless $self->server;
23 $self->port('443') unless $self->port;
24 $self->path('/jetpay') unless $self->path;
26 $self->build_subs(qw( order_number avs_code cvv2_response
27 response_page response_code response_headers
30 # module specific data
32 $self->debug( $opts{debug} );
37 foreach my $key (keys %opts) {
38 $key =~ /^default_(\w*)$/ or next;
39 $_defaults{$1} = $opts{$key};
42 $self->{_defaults} = \%_defaults;
48 my %content = $self->content();
51 my %types = ( 'visa' => 'CC',
53 'american express' => 'CC',
57 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
58 $self->transaction_type($content{'type'});
61 my $action = lc($content{'action'});
63 ( 'normal authorization' => 'SALE',
64 'authorization only' => 'AUTHONLY',
65 'post authorization' => 'CAPT',
70 ( 'normal authorization' => 'CHECK',
72 'credit' => 'REVERSAL',
74 if ($self->transaction_type eq 'CC') {
75 $content{'TransactionType'} = $actions{$action} || $action;
76 }elsif ($self->transaction_type eq 'ECHECK') {
77 $content{'TransactionType'} = $check_actions{$action} || $action;
82 my %account_types = ('personal checking' => 'Checking',
83 'personal savings' => 'Savings',
84 'business checking' => 'BusinessCk',
86 $content{'account_type'} = $account_types{lc($content{'account_type'})}
87 || $content{'account_type'};
89 $content{Origin} = 'RECURRING'
90 if ($content{recurring_billing} &&$content{recurring_billing} eq 'YES' );
92 # stuff it back into %content
93 $self->content(%content);
98 my ($self, $exp) = (shift, shift);
100 if ( defined($exp) and $exp =~ /^(\d+)\D+\d*\d{2}$/ ) {
101 $month = sprintf( "%02d", $1 );
102 }elsif ( defined($exp) and $exp =~ /^(\d{2})\d{2}$/ ) {
103 $month = sprintf( "%02d", $1 );
109 my ($self, $exp) = (shift, shift);
111 if ( defined($exp) and $exp =~ /^\d+\D+\d*(\d{2})$/ ) {
112 $year = sprintf( "%02d", $1 );
113 }elsif ( defined($exp) and $exp =~ /^\d{2}(\d{2})$/ ) {
114 $year = sprintf( "%02d", $1 );
121 tie my(%map), 'Tie::IxHash', @_;
122 my %content = $self->content();
125 if ( ref( $map{$_} ) eq 'HASH' ) {
126 $value = $map{$_} if ( keys %{ $map{$_} } );
127 }elsif( ref( $map{$_} ) ) {
128 $value = ${ $map{$_} };
129 }elsif( exists( $content{ $map{$_} } ) ) {
130 $value = $content{ $map{$_} };
133 if (defined($value)) {
144 $self->is_success(0);
147 my @required_fields = qw(action login type);
149 my $action = lc($self->{_content}->{action});
150 my $type = $self->transaction_type();
151 if ( $action eq 'normal authorization'
152 || $action eq 'credit'
153 || $action eq 'authorization only' && $type eq 'CC')
155 push @required_fields, qw( amount );
157 push @required_fields, qw( card_number expiration )
160 push @required_fields,
161 qw( routing_code account_number name ) # account_type
162 if ($type eq "ECHECK");
164 }elsif ( $action eq 'post authorization' && $type eq 'CC') {
165 push @required_fields, qw( order_number );
166 }elsif ( $action eq 'void') {
167 push @required_fields, qw( order_number amount );
169 push @required_fields, qw( authorization card_number )
172 push @required_fields,
173 qw( routing_code account_number name ) # account_type
174 if ($type eq "ECHECK");
177 croak "$me can't handle transaction type: ".
178 $self->{_content}->{action}. " for ".
179 $self->transaction_type();
182 my %content = $self->content();
183 foreach ( keys ( %{($self->{_defaults})} ) ) {
184 $content{$_} = $self->{_defaults}->{$_} unless exists($content{$_});
186 $self->content(%content);
188 $self->required_fields(@required_fields);
190 if ($self->test_transaction()) {
191 $self->server('test1.jetpay.com');
193 $self->path('/jetpay');
196 my $transaction_id = $content{'order_number'};
197 unless ($transaction_id) {
198 my ($page, $server_response, %headers) = $self->https_get('dummy' => 1);
199 warn "fetched transaction id: (HTTPS response: $server_response) ".
201 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
202 "(Raw HTTPS content: $page)"
204 return unless $server_response=~ /^200/;
205 $transaction_id = $page;
208 my $cardexpmonth = $self->expdate_month($content{expiration});
209 my $cardexpyear = $self->expdate_year($content{expiration});
210 my $cardstartmonth = $self->expdate_month($content{card_start});
211 my $cardstartyear = $self->expdate_year($content{card_start});
214 if (defined($content{amount})) {
215 $amount = sprintf("%.2f", $content{amount});
219 my $check_number = $content{check_number} || "100" # make one up
220 if($content{account_number});
222 my $terminalid = $content{login} if $type eq 'CC';
223 my $merchantid = $content{login} if $type eq 'ECHECK';
225 tie my %ach, 'Tie::IxHash',
226 $self->revmap_fields(
227 #AccountType => 'account_type',
228 AccountNumber => 'account_number',
229 ABA => 'routing_code',
230 CheckNumber => \$check_number,
233 tie my %industryinfo, 'Tie::IxHash',
234 $self->revmap_fields(
235 Type => 'IndustryInfo',
238 tie my %shippingaddr, 'Tie::IxHash',
239 $self->revmap_fields(
240 Address => 'ship_address',
242 StateProv => 'ship_state',
243 Country => 'ship_country',
244 Phone => 'ship_phone',
247 unless ( $type ne 'CC' || keys %shippingaddr ) {
248 tie %shippingaddr, 'Tie::IxHash',
249 $self->revmap_fields(
250 Address => 'address',
252 StateProv => 'state',
253 Country => 'country',
258 tie my %shippinginfo, 'Tie::IxHash',
259 $self->revmap_fields(
260 CustomerPO => 'CustomerPO',
261 ShippingMethod => 'ShippingMethod',
262 ShippingName => 'ship_name',
263 ShippingAddr => \%shippingaddr,
266 tie my %req, 'Tie::IxHash',
267 $self->revmap_fields(
268 TransactionType => 'TransactionType',
269 TerminalID => 'login',
270 # TerminalID => \$terminalid,
271 # MerchantID => \$merchantid,
272 TransactionID => \$transaction_id,
273 RoutingCode => 'RoutingCode',
274 Approval => 'authorization',
275 BatchID => 'BatchID',
277 Password => 'password',
278 OrderNumber => 'invoice_number',
279 CardNum => 'card_number',
281 Issue => 'issue_number',
282 CardExpMonth => \$cardexpmonth,
283 CardExpYear => \$cardexpyear,
284 CardStartMonth => \$cardstartmonth,
285 CardStartYear => \$cardstartyear,
290 DispositionType => 'DispositionType',
291 TotalAmount => \$amount,
292 FeeAmount => 'FeeAmount',
293 TaxAmount => 'TaxAmount',
294 BillingAddress => 'address',
295 BillingCity => 'city',
296 BillingStateProv => 'state',
297 BillingPostalCode => 'zip',
298 BillingCountry => 'country',
299 BillingPhone => 'phone',
301 UserIPAddr => 'customer_ip',
302 UserHost => 'UserHost',
303 UDField1 => 'UDField1',
304 UDField2 => 'UDField2',
305 UDField3 => 'UDField3',
306 ActionCode => 'ActionCode',
307 IndustryInfo => \%industryinfo,
308 ShippingInfo => \%shippinginfo,
312 my $writer = new XML::Writer( OUTPUT => \$post_data,
315 ENCODING => 'us-ascii',
318 $writer->startTag('JetPay');
319 foreach ( keys ( %req ) ) {
320 $self->_xmlwrite($writer, $_, $req{$_});
322 $writer->endTag('JetPay');
325 warn "$post_data\n" if $DEBUG;
327 my ($page,$server_response,%headers) = $self->https_post($post_data);
329 warn "$page\n" if $DEBUG;
332 if ($server_response =~ /^200/){
333 $response = XMLin($page);
334 if ( exists($response->{ActionCode}) && !exists($response->{ErrMsg})) {
335 $self->error_message($response->{ResponseText});
337 $self->error_message($response->{Errmsg});
340 # $self->error_message("Server Failed");
343 $self->result_code($response->{ActionCode} || '');
344 $self->order_number($response->{TransactionID} || '');
345 $self->authorization($response->{Approval} || '');
346 $self->cvv2_response($response->{CVV2} || '');
347 $self->avs_code($response->{AVS} || '');
349 $self->is_success($self->result_code() eq '000' ? 1 : 0);
351 unless ($self->is_success()) {
352 unless ( $self->error_message() ) { #additional logging information
353 $self->error_message(
354 "(HTTPS response: $server_response) ".
356 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
357 "(Raw HTTPS content: $page)"
365 my ($self, $writer, $item, $value) = @_;
366 $writer->startTag($item);
367 if ( ref( $value ) eq 'HASH' ) {
368 foreach ( keys ( %$value ) ) {
369 $self->_xmlwrite($writer, $_, $value->{$_});
372 $writer->characters($value);
374 $writer->endTag($item);
382 Business::OnlinePayment::IPPay - IPPay backend for Business::OnlinePayment
386 use Business::OnlinePayment;
389 new Business::OnlinePayment( "IPPay",
390 'default_Origin' => 'PHONE ORDER',
394 login => 'testdrive',
395 password => '', #password
396 action => 'Normal Authorization',
397 description => 'Business::OnlinePayment test',
399 customer_id => 'tfb',
400 name => 'Tofu Beast',
401 address => '123 Anystreet',
405 card_number => '4007000000027',
406 expiration => '09/02',
407 cvv2 => '1234', #optional
411 if($tx->is_success()) {
412 print "Card processed successfully: ".$tx->authorization."\n";
414 print "Card was rejected: ".$tx->error_message."\n";
417 =head1 SUPPORTED TRANSACTION TYPES
419 =head2 CC, Visa, MasterCard, American Express, Discover
421 Content required: type, login, action, amount, card_number, expiration.
425 Content required: type, login, action, amount, name, account_number, routing_code.
429 For detailed information see L<Business::OnlinePayment>.
431 =head1 METHODS AND FUNCTIONS
433 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.
437 Returns the response error code.
441 Returns the response error description text.
443 =head2 server_response
445 Returns the complete response from the server.
447 =head1 Handling of content(%content) data:
451 The following actions are valid
459 =head1 Setting IPPay parameters from content(%content)
461 The following rules are applied to map data to IPPay parameters
462 from content(%content):
464 # param => $content{<key>}
465 TransactionType => 'TransactionType',
466 TerminalID => 'login',
467 TransactionID => 'order_number',
468 RoutingCode => 'RoutingCode',
469 Approval => 'authorization',
470 BatchID => 'BatchID',
472 Password => 'password',
473 OrderNumber => 'invoice_number',
474 CardNum => 'card_number',
476 Issue => 'issue_number',
477 CardExpMonth => \( $month ), # MM from MM(-)YY(YY) of 'expiration'
478 CardExpYear => \( $year ), # YY from MM(-)YY(YY) of 'expiration'
479 CardStartMonth => \( $month ), # MM from MM(-)YY(YY) of 'card_start'
480 CardStartYear => \( $year ), # YY from MM(-)YY(YY) of 'card_start'
484 AccountNumber => 'account_number',
485 ABA => 'routing_code',
486 CheckNumber => 'check_number',
488 DispositionType => 'DispositionType',
489 TotalAmount => 'amount' reformatted into cents
490 FeeAmount => 'FeeAmount',
491 TaxAmount => 'TaxAmount',
492 BillingAddress => 'address',
493 BillingCity => 'city',
494 BillingStateProv => 'state',
495 BillingPostalCode => 'zip',
496 BillingCountry => 'country',
497 BillingPhone => 'phone',
499 UserIPAddr => 'customer_ip',
500 UserHost => 'UserHost',
501 UDField1 => 'UDField1',
502 UDField2 => 'UDField2',
503 UDField3 => 'UDField3',
504 ActionCode => 'ActionCode',
506 Type => 'IndustryInfo',
508 CustomerPO => 'CustomerPO',
509 ShippingMethod => 'ShippingMethod',
510 ShippingName => 'ship_name',
512 Address => 'ship_address',
514 StateProv => 'ship_state',
515 Country => 'ship_country',
516 Phone => 'ship_phone',
522 Business::OnlinePayment::IPPay uses IPPay XML Product Specifications version
525 See http://www.ippay.com/ for more information.
529 Jeff Finucane, ippay@weasellips.com
533 perl(1). L<Business::OnlinePayment>.