Business-OnlinePayment-IPPay-0.08/0000755000175000017500000000000012062712114015444 5ustar ivanivanBusiness-OnlinePayment-IPPay-0.08/META.yml0000644000175000017500000000134612062712114016721 0ustar ivanivan--- #YAML:1.0 name: Business-OnlinePayment-IPPay version: 0.08 abstract: ~ author: - Ivan Kohler license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Business::OnlinePayment: 3 Business::OnlinePayment::HTTPS: 0.09 Locale::Country: 2 Test::More: 0.42 Tie::IxHash: 0 XML::Simple: 0 XML::Writer: 0 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.57_05 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 Business-OnlinePayment-IPPay-0.08/MANIFEST0000644000175000017500000000031211751051615016577 0ustar ivanivanIPPay.pm Changes MANIFEST Makefile.PL README t/00load.t t/card.t t/check.t t/bop.t t/introspection.t t/lib/test_account.pl META.yml Module meta-data (added by MakeMaker) Business-OnlinePayment-IPPay-0.08/Makefile.PL0000644000175000017500000000115612043417547017434 0ustar ivanivanuse ExtUtils::MakeMaker; WriteMakefile( 'NAME' => 'Business::OnlinePayment::IPPay', 'VERSION_FROM' => 'IPPay.pm', # finds $VERSION 'AUTHOR' => 'Ivan Kohler ', 'PREREQ_PM' => { 'Business::OnlinePayment' => 3, 'Business::OnlinePayment::HTTPS' => 0.09, 'Locale::Country' => 2, 'Test::More' => 0.42, 'Tie::IxHash' => 0, 'XML::Simple' => 0, 'XML::Writer' => 0, }, ); Business-OnlinePayment-IPPay-0.08/t/0000755000175000017500000000000012062712114015707 5ustar ivanivanBusiness-OnlinePayment-IPPay-0.08/t/check.t0000644000175000017500000000505412062711675017170 0ustar ivanivan#!/usr/bin/perl -w use Test::More; require "t/lib/test_account.pl"; my($login, $password, %opt) = test_account_or_skip('check'); plan tests => 16; use_ok 'Business::OnlinePayment'; my %content = ( type => 'CHECK', login => $login, password => $password, action => 'Normal Authorization', amount => '49.95', customer_id => 'jsk', name => 'Tofu Beast', account_number => '12345', routing_code => '111000025', # BoA in Texas taken from Wikipedia bank_name => 'First National Test Bank', account_type => 'Business Checking', ); my $voidable; #check test { my $ctx = Business::OnlinePayment->new("IPPay", %opt); $ctx->content(%content); tx_check( $ctx, desc => 'normal ACH transaction', is_success => 1, result_code => '000', error_message => 'CHECK ACCEPTED', authorization => qr/^000000$/, name => 'Tofu Beast', ); $voidable = $ctx->order_number if $ctx->is_success; } #check void test { my $ctx = Business::OnlinePayment->new("IPPay", %opt); $ctx->content(%content, action => 'void', order_number => $voidable); tx_check( $ctx, desc => 'ACH void transaction', is_success => 1, result_code => '000', error_message => 'CHECK ACCEPTED', authorization => qr/^000000$/, ); } #check credit test { my $ctx = Business::OnlinePayment->new("IPPay", %opt); $ctx->content(%content, action => 'credit'); tx_check( $ctx, desc => 'ACH credit transaction', is_success => 1, result_code => '000', error_message => 'CHECK ACCEPTED', authorization => qr/^000000$/, ); } sub tx_check { my $tx = shift; my %o = @_; $tx->test_transaction(1); $tx->submit; is( $tx->is_success, $o{is_success}, "$o{desc}: " . tx_info($tx) ); is( $tx->result_code, $o{result_code}, "result_code(): RESULT" ); is( $tx->error_message, $o{error_message}, "error_message() / RESPMSG" ); like( $tx->authorization, $o{authorization}, "authorization() / AUTHCODE" ); like( $tx->order_number, qr/^\w{18}/, "order_number() / PNREF" ); } sub tx_info { my $tx = shift; no warnings 'uninitialized'; return ( join( "", "is_success(", $tx->is_success, ")", " order_number(", $tx->order_number, ")", " error_message(", $tx->error_message, ")", " result_code(", $tx->result_code, ")", " auth_info(", $tx->authorization, ")", ) ); } Business-OnlinePayment-IPPay-0.08/t/card.t0000644000175000017500000001373612043420227017017 0ustar ivanivan#!/usr/bin/perl -w use Test::More; require "t/lib/test_account.pl"; my($login, $password, %opt) = test_account_or_skip('card'); plan tests => 50; use_ok 'Business::OnlinePayment'; my %content = ( type => 'VISA', login => $login, password => $password, action => 'Normal Authorization', description => 'Business::OnlinePayment visa test', # card_number => '4007000000027', card_number => '4111111111111111', cvv2 => '123', expiration => expiration_date(), amount => '49.95', name => 'Tofu Beast', email => 'ippay@weasellips.com', address => '123 Anystreet', city => 'Anywhere', state => 'UT', zip => '84058', country => 'US', # will be forced to USA customer_id => 'tfb', ); my $voidable; my $voidable_auth; my $voidable_amount = 0; # valid card number test { my $tx = Business::OnlinePayment->new("IPPay", %opt); $tx->content(%content); tx_check( $tx, desc => "valid card_number", is_success => 1, result_code => '000', error_message => 'APPROVED', authorization => qr/TEST\d{2}/, # avs_code => 'U', # so rather pointless :\ avs_code => 'Y', # so very pointless :\ cvv2_response => 'P', # ... ); $voidable = $tx->order_number if $tx->is_success; $voidable_auth = $tx->authorization if $tx->is_success; $voidable_amount = $content{amount} if $tx->is_success; } # invalid card number test { my $tx = Business::OnlinePayment->new("IPPay", %opt); $tx->content(%content, card_number => "4111111111111112" ); tx_check( $tx, desc => "invalid card_number", is_success => 0, result_code => '900', #'912' with old jetpay gw error_message => 'Invalid card number. ', #'INVALID CARD NUMBER' w/old gw authorization => qr/^$/, avs_code => '', # so rather pointless :\ cvv2_response => '', # ... ); } # authorization only test { my $tx = Business::OnlinePayment->new("IPPay", %opt); $tx->content(%content, action => 'authorization only', amount => '3.00' ); tx_check( $tx, desc => "authorization only", is_success => 1, result_code => '000', error_message => 'APPROVED', authorization => qr/TEST\d{2}/, # avs_code => 'U', # so rather pointless :\ avs_code => 'Y', # so very pointless :\ cvv2_response => 'P', # ... ); $postable = $tx->order_number if $tx->is_success; $postable_auth = $tx->authorization if $tx->is_success; $postable_amount = $content{amount} if $tx->is_success; } # authorization void test { my $tx = Business::OnlinePayment->new("IPPay", %opt); $tx->content(%content, action => 'authorization only', amount => '3.00' ); $tx->test_transaction(1); $tx->submit; if ($tx->is_success) { my $void_tx = Business::OnlinePayment->new("IPPay", %opt ); $tx->content(%content, action => 'reverse authorization', order_number => $tx->order_number ); tx_check( $tx, desc => "reverse authorization", is_success => 1, result_code => '000', error_message => 'APPROVED', authorization => qr/TEST\d{2}/, avs_code => '', # so rather pointless :\ cvv2_response => '', # ... ); } else { } } # post authorization test SKIP: { my $tx = new Business::OnlinePayment( "IPPay", %opt ); $tx->content( %content, 'action' => "post authorization", 'amount' => $postable_amount, # not required 'order_number' => $postable, ); tx_check( $tx, desc => "post authorization", is_success => 1, result_code => '000', error_message => 'APPROVED', authorization => qr/^$postable_auth$/, avs_code => '', cvv2_response => '', ); } # void test SKIP: { my $tx = new Business::OnlinePayment( "IPPay", %opt ); $tx->content( %content, 'action' => "Void", 'order_number' => $voidable, 'authorization' => $voidable_auth, ); tx_check( $tx, desc => "void", is_success => 1, result_code => '000', error_message => 'VOID PROCESSED', authorization => qr/^$voidable_auth$/, avs_code => '', cvv2_response => '', ); } # credit test SKIP: { my $tx = new Business::OnlinePayment( "IPPay", %opt ); $tx->content( %content, 'action' => "credit"); tx_check( $tx, desc => "credit", is_success => 1, result_code => '000', error_message => 'RETURN ACCEPTED', authorization => qr/\d{6}/, avs_code => '', cvv2_response => '', ); } sub tx_check { my $tx = shift; my %o = @_; $tx->test_transaction(1); $tx->submit; is( $tx->is_success, $o{is_success}, "$o{desc}: " . tx_info($tx) ); is( $tx->result_code, $o{result_code}, "result_code(): RESULT" ); is( $tx->error_message, $o{error_message}, "error_message() / RESPMSG" ); like( $tx->authorization, $o{authorization}, "authorization() / AUTHCODE" ); is( $tx->avs_code, $o{avs_code}, "avs_code() / AVSADDR and AVSZIP" ); is( $tx->cvv2_response, $o{cvv2_response}, "cvv2_response() / CVV2MATCH" ); like( $tx->order_number, qr/^\w{18}/, "order_number() / PNREF" ); } sub tx_info { my $tx = shift; no warnings 'uninitialized'; return ( join( "", "is_success(", $tx->is_success, ")", " order_number(", $tx->order_number, ")", " error_message(", $tx->error_message, ")", " result_code(", $tx->result_code, ")", " auth_info(", $tx->authorization, ")", " avs_code(", $tx->avs_code, ")", " cvv2_response(", $tx->cvv2_response, ")", ) ); } Business-OnlinePayment-IPPay-0.08/t/lib/0000755000175000017500000000000012062712114016455 5ustar ivanivanBusiness-OnlinePayment-IPPay-0.08/t/lib/test_account.pl0000644000175000017500000000160512043417426021516 0ustar ivanivansub test_account_or_skip { my $suffix = shift; my($login, $password, %opt) = test_account($suffix); unless( defined $login ) { plan skip_all => "No test account"; } return($login, $password, %opt); } sub test_account { my $suffix = shift || 'card'; my($login, $password) = ('TESTTERMINAL', ''); my %opt; if ( $suffix eq 'check ' ) { %opt = ('Origin' => 'RECURRING'); } else { %opt = ('default_Origin' => 'RECURRING'); } return($login, $password, %opt); } sub expiration_date { my($month, $year) = (localtime)[4,5]; $month += 1; $year++; # So we expire next year. $year %= 100; # y2k? What's that? return sprintf("%02d/%02d", $month, $year); } #sub tomorrow { # my($day, $month, $year) = (localtime(time+86400))[3..5]; # return sprintf("%04d-%02d-%02d", $year+1900, ++$month, $day); #} 1; Business-OnlinePayment-IPPay-0.08/t/00load.t0000644000175000017500000000013111751051615017154 0ustar ivanivan#!/usr/bin/perl -w use Test::More tests => 1; use_ok 'Business::OnlinePayment::IPPay'; Business-OnlinePayment-IPPay-0.08/t/bop.t0000644000175000017500000000012211751051615016655 0ustar ivanivan#!/usr/bin/perl -w use Test::More tests => 1; use_ok 'Business::OnlinePayment'; Business-OnlinePayment-IPPay-0.08/t/introspection.t0000644000175000017500000000070211751051615021001 0ustar ivanivan#!/usr/bin/perl -w use Test::More; eval 'use Business::OnlinePayment 3.01;'; if ( $@ ) { plan skip_all => 'Business::OnlinePayment 3.01+ not available'; } else { plan tests => 1; } my($login, $password, @opts) = ('TESTMERCHANT', '', 'default_Origin' => 'RECURRING' ); my $tx = Business::OnlinePayment->new("IPPay", @opts); ok( $tx->info('CC_void_requires_card') == 1, 'CC_void_requires_card introspection' ); Business-OnlinePayment-IPPay-0.08/README0000644000175000017500000000157111751051615016336 0ustar ivanivanCopyright (c) 1999 Jason Kohles. Copyright (c) 2002-2003 Ivan Kohler Copyright (c) 2008 Jeff Finucane All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. This is Business::OnlinePayment::IPPay, a Business::OnlinePayment backend module for IPPay. It is only useful if you have a merchant account with IPPay Merchant Services: http://www.ippay.com This module implements the IPPay XML Product Specification Revision 1.1.2 Jeff Finucane is the original author. Please send patches as unified diffs (diff -u). Mr. Kohles and Mr. Kohler provided material for cribbing. Business::OnlinePayment is a generic interface for processing payments through online credit card processors, online check acceptance houses, etc. (If you like buzzwords, call it an "multiplatform ecommerce-enabling middleware solution"). Business-OnlinePayment-IPPay-0.08/IPPay.pm0000644000175000017500000005112612062710653016777 0ustar ivanivanpackage Business::OnlinePayment::IPPay; use strict; use Carp; use Tie::IxHash; use XML::Simple; use XML::Writer; use Locale::Country; use Business::OnlinePayment; use Business::OnlinePayment::HTTPS; use vars qw($VERSION $DEBUG @ISA $me); @ISA = qw(Business::OnlinePayment::HTTPS); $VERSION = '0.08'; $VERSION = eval $VERSION; # modperlstyle: convert the string into a number $DEBUG = 0; $me = 'Business::OnlinePayment::IPPay'; sub _info { { 'info_version' => '0.01', 'module_version' => $VERSION, 'supported_types' => [ qw( CC ECHECK ) ], 'supported_actions' => { 'CC' => [ 'Normal Authorization', 'Authorization Only', 'Post Authorization', 'Void', 'Credit', 'Reverse Authorization', ], 'ECHECK' => [ 'Normal Authorization', 'Void', 'Credit', ], }, 'CC_void_requires_card' => 1, 'ECHECK_void_requires_account' => 1, }; } sub set_defaults { my $self = shift; my %opts = @_; # standard B::OP methods/data $self->server('gtwy.ippay.com') unless $self->server; $self->port('443') unless $self->port; $self->path('/ippay') unless $self->path; $self->build_subs(qw( order_number avs_code cvv2_response response_page response_code response_headers )); # module specific data if ( $opts{debug} ) { $self->debug( $opts{debug} ); delete $opts{debug}; } my %_defaults = (); foreach my $key (keys %opts) { $key =~ /^default_(\w*)$/ or next; $_defaults{$1} = $opts{$key}; delete $opts{$key}; } $self->{_defaults} = \%_defaults; } sub map_fields { my($self) = @_; my %content = $self->content(); # TYPE MAP my %types = ( 'visa' => 'CC', 'mastercard' => 'CC', 'american express' => 'CC', 'discover' => 'CC', 'check' => 'ECHECK', ); $content{'type'} = $types{lc($content{'type'})} || $content{'type'}; $self->transaction_type($content{'type'}); # ACTION MAP my $action = lc($content{'action'}); my %actions = ( 'normal authorization' => 'SALE', 'authorization only' => 'AUTHONLY', 'post authorization' => 'CAPT', 'reverse authorization' => 'REVERSEAUTH', 'void' => 'VOID', 'credit' => 'CREDIT', ); my %check_actions = ( 'normal authorization' => 'CHECK', 'void' => 'VOIDACH', 'credit' => 'REVERSAL', ); if ($self->transaction_type eq 'CC') { $content{'TransactionType'} = $actions{$action} || $action; } elsif ($self->transaction_type eq 'ECHECK') { $content{'TransactionType'} = $check_actions{$action} || $action; # ACCOUNT TYPE MAP my %account_types = ('personal checking' => 'CHECKING', 'personal savings' => 'SAVINGS', 'business checking' => 'CHECKING', 'business savings' => 'SAVINGS', #not technically B:OP valid i guess? 'checking' => 'CHECKING', 'savings' => 'SAVINGS', ); $content{'account_type'} = $account_types{lc($content{'account_type'})} || $content{'account_type'}; } $content{Origin} = 'RECURRING' if ($content{recurring_billing} &&$content{recurring_billing} eq 'YES' ); # stuff it back into %content $self->content(%content); } sub expdate_month { my ($self, $exp) = (shift, shift); my $month; if ( defined($exp) and $exp =~ /^(\d+)\D+\d*\d{2}$/ ) { $month = sprintf( "%02d", $1 ); }elsif ( defined($exp) and $exp =~ /^(\d{2})\d{2}$/ ) { $month = sprintf( "%02d", $1 ); } return $month; } sub expdate_year { my ($self, $exp) = (shift, shift); my $year; if ( defined($exp) and $exp =~ /^\d+\D+\d*(\d{2})$/ ) { $year = sprintf( "%02d", $1 ); }elsif ( defined($exp) and $exp =~ /^\d{2}(\d{2})$/ ) { $year = sprintf( "%02d", $1 ); } return $year; } sub revmap_fields { my $self = shift; tie my(%map), 'Tie::IxHash', @_; my %content = $self->content(); map { my $value; if ( ref( $map{$_} ) eq 'HASH' ) { $value = $map{$_} if ( keys %{ $map{$_} } ); }elsif( ref( $map{$_} ) ) { $value = ${ $map{$_} }; }elsif( exists( $content{ $map{$_} } ) ) { $value = $content{ $map{$_} }; } if (defined($value)) { ($_ => $value); }else{ (); } } (keys %map); } sub submit { my($self) = @_; $self->is_success(0); $self->map_fields(); my @required_fields = qw(action login type); my $action = lc($self->{_content}->{action}); my $type = $self->transaction_type(); if ( $action eq 'normal authorization' || $action eq 'credit' || $action eq 'authorization only' && $type eq 'CC') { push @required_fields, qw( amount ); push @required_fields, qw( card_number expiration ) if ($type eq "CC"); push @required_fields, qw( routing_code account_number name ) # account_type if ($type eq "ECHECK"); }elsif ( $action eq 'post authorization' && $type eq 'CC') { push @required_fields, qw( order_number ); }elsif ( $action eq 'reverse authorization' && $type eq 'CC') { push @required_fields, qw( order_number card_number expiration amount ); }elsif ( $action eq 'void') { push @required_fields, qw( order_number amount ); push @required_fields, qw( authorization card_number ) if ($type eq "CC"); push @required_fields, qw( routing_code account_number name ) # account_type if ($type eq "ECHECK"); }else{ croak "$me can't handle transaction type: ". $self->{_content}->{action}. " for ". $self->transaction_type(); } my %content = $self->content(); foreach ( keys ( %{($self->{_defaults})} ) ) { $content{$_} = $self->{_defaults}->{$_} unless exists($content{$_}); } if ($self->test_transaction()) { $content{'login'} = 'TESTTERMINAL'; } $self->content(%content); $self->required_fields(@required_fields); #quick validation because ippay dumps an error indecipherable to the end user if (grep { /^routing_code$/ } @required_fields) { unless( $content{routing_code} =~ /^\d{9}$/ ) { $self->_error_response('Invalid routing code'); return; } } my $transaction_id = $content{'order_number'}; unless ($transaction_id) { my ($page, $server_response, %headers) = $self->https_get('dummy' => 1); warn "fetched transaction id: (HTTPS response: $server_response) ". "(HTTPS headers: ". join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ". "(Raw HTTPS content: $page)" if $DEBUG; return unless $server_response=~ /^200/; $transaction_id = $page; } my $cardexpmonth = $self->expdate_month($content{expiration}); my $cardexpyear = $self->expdate_year($content{expiration}); my $cardstartmonth = $self->expdate_month($content{card_start}); my $cardstartyear = $self->expdate_year($content{card_start}); my $amount; if (defined($content{amount})) { $amount = sprintf("%.2f", $content{amount}); $amount =~ s/\.//; } my $check_number = $content{check_number} || "100" # make one up if($content{account_number}); my $terminalid = $content{login} if $type eq 'CC'; my $merchantid = $content{login} if $type eq 'ECHECK'; my $country = country2code( $content{country}, LOCALE_CODE_ALPHA_3 ); $country = country_code2code( $content{country}, LOCALE_CODE_ALPHA_2, LOCALE_CODE_ALPHA_3 ) unless $country; $country = $content{country} unless $country; $country = uc($country) if $country; my $ship_country = country2code( $content{ship_country}, LOCALE_CODE_ALPHA_3 ); $ship_country = country_code2code( $content{ship_country}, LOCALE_CODE_ALPHA_2, LOCALE_CODE_ALPHA_3 ) unless $ship_country; $ship_country = $content{ship_country} unless $ship_country; $ship_country = uc($ship_country) if $ship_country; tie my %ach, 'Tie::IxHash', $self->revmap_fields( #wtf, this is a "Type"" attribute of the ACH element, # not a child element like the others #AccountType => 'account_type', AccountNumber => 'account_number', ABA => 'routing_code', CheckNumber => \$check_number, ); tie my %industryinfo, 'Tie::IxHash', $self->revmap_fields( Type => 'IndustryInfo', ); tie my %shippingaddr, 'Tie::IxHash', $self->revmap_fields( Address => 'ship_address', City => 'ship_city', StateProv => 'ship_state', Country => \$ship_country, Phone => 'ship_phone', ); unless ( $type ne 'CC' || keys %shippingaddr ) { tie %shippingaddr, 'Tie::IxHash', $self->revmap_fields( Address => 'address', City => 'city', StateProv => 'state', Country => \$country, Phone => 'phone', ); } delete $shippingaddr{Country} unless $shippingaddr{Country}; tie my %shippinginfo, 'Tie::IxHash', $self->revmap_fields( CustomerPO => 'CustomerPO', ShippingMethod => 'ShippingMethod', ShippingName => 'ship_name', ShippingAddr => \%shippingaddr, ); tie my %req, 'Tie::IxHash', $self->revmap_fields( TransactionType => 'TransactionType', TerminalID => 'login', # TerminalID => \$terminalid, # MerchantID => \$merchantid, TransactionID => \$transaction_id, RoutingCode => 'RoutingCode', Approval => 'authorization', BatchID => 'BatchID', Origin => 'Origin', Password => 'password', OrderNumber => 'invoice_number', CardNum => 'card_number', CVV2 => 'cvv2', Issue => 'issue_number', CardExpMonth => \$cardexpmonth, CardExpYear => \$cardexpyear, CardStartMonth => \$cardstartmonth, CardStartYear => \$cardstartyear, Track1 => 'track1', Track2 => 'track2', ACH => \%ach, CardName => 'name', DispositionType => 'DispositionType', TotalAmount => \$amount, FeeAmount => 'FeeAmount', TaxAmount => 'TaxAmount', BillingAddress => 'address', BillingCity => 'city', BillingStateProv => 'state', BillingPostalCode => 'zip', BillingCountry => \$country, BillingPhone => 'phone', Email => 'email', UserIPAddr => 'customer_ip', UserHost => 'UserHost', UDField1 => 'UDField1', UDField2 => 'UDField2', UDField3 => 'UDField3', ActionCode => 'ActionCode', IndustryInfo => \%industryinfo, ShippingInfo => \%shippinginfo, ); delete $req{BillingCountry} unless $req{BillingCountry}; my $post_data; my $writer = new XML::Writer( OUTPUT => \$post_data, DATA_MODE => 1, DATA_INDENT => 1, ENCODING => 'us-ascii', ); $writer->xmlDecl(); $writer->startTag('JetPay'); foreach ( keys ( %req ) ) { $self->_xmlwrite($writer, $_, $req{$_}); } $writer->endTag('JetPay'); $writer->end(); warn "$post_data\n" if $DEBUG; my ($page,$server_response,%headers) = $self->https_post($post_data); warn "$page\n" if $DEBUG; my $response = {}; if ($server_response =~ /^200/){ $response = XMLin($page); if ( exists($response->{ActionCode}) && !exists($response->{ErrMsg})) { $self->error_message($response->{ResponseText}); }else{ $self->error_message($response->{ErrMsg}); } # }else{ # $self->error_message("Server Failed"); } $self->result_code($response->{ActionCode} || ''); $self->order_number($response->{TransactionID} || ''); $self->authorization($response->{Approval} || ''); $self->cvv2_response($response->{CVV2} || ''); $self->avs_code($response->{AVS} || ''); $self->is_success($self->result_code() eq '000' ? 1 : 0); unless ($self->is_success()) { unless ( $self->error_message() ) { #additional logging information $self->error_message( "(HTTPS response: $server_response) ". "(HTTPS headers: ". join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ". "(Raw HTTPS content: $page)" ); } } } sub _error_response { my ($self, $error_message) = (shift, shift); $self->result_code(''); $self->order_number(''); $self->authorization(''); $self->cvv2_response(''); $self->avs_code(''); $self->is_success( 0); $self->error_message($error_message); } sub _xmlwrite { my ($self, $writer, $item, $value) = @_; my %att = (); if ( $item eq 'ACH' ) { $att{'Type'} = $self->{_content}->{'account_type'} if $self->{_content}->{'account_type'}; #necessary so we don't pass empty? $att{'SEC'} = 'PPD'; } $writer->startTag($item, %att); if ( ref( $value ) eq 'HASH' ) { foreach ( keys ( %$value ) ) { $self->_xmlwrite($writer, $_, $value->{$_}); } }else{ $writer->characters($value); } $writer->endTag($item); } 1; __END__ =head1 NAME Business::OnlinePayment::IPPay - IPPay backend for Business::OnlinePayment =head1 SYNOPSIS use Business::OnlinePayment; my $tx = new Business::OnlinePayment( "IPPay", 'default_Origin' => 'PHONE ORDER', ); $tx->content( type => 'VISA', login => 'testdrive', password => '', #password action => 'Normal Authorization', description => 'Business::OnlinePayment test', amount => '49.95', customer_id => 'tfb', name => 'Tofu Beast', address => '123 Anystreet', city => 'Anywhere', state => 'UT', zip => '84058', card_number => '4007000000027', expiration => '09/02', cvv2 => '1234', #optional ); $tx->submit(); if($tx->is_success()) { print "Card processed successfully: ".$tx->authorization."\n"; } else { print "Card was rejected: ".$tx->error_message."\n"; } =head1 SUPPORTED TRANSACTION TYPES =head2 CC, Visa, MasterCard, American Express, Discover Content required: type, login, action, amount, card_number, expiration. =head2 Check Content required: type, login, action, amount, name, account_number, routing_code. =head1 DESCRIPTION For detailed information see L. =head1 METHODS AND FUNCTIONS See L for the complete list. The following methods either override the methods in L or provide additional functions. =head2 result_code Returns the response error code. =head2 error_message Returns the response error description text. =head2 server_response Returns the complete response from the server. =head1 Handling of content(%content) data: =head2 action The following actions are valid normal authorization authorization only reverse authorization post authorization credit void =head1 Setting IPPay parameters from content(%content) The following rules are applied to map data to IPPay parameters from content(%content): # param => $content{} TransactionType => 'TransactionType', TerminalID => 'login', TransactionID => 'order_number', RoutingCode => 'RoutingCode', Approval => 'authorization', BatchID => 'BatchID', Origin => 'Origin', Password => 'password', OrderNumber => 'invoice_number', CardNum => 'card_number', CVV2 => 'cvv2', Issue => 'issue_number', CardExpMonth => \( $month ), # MM from MM(-)YY(YY) of 'expiration' CardExpYear => \( $year ), # YY from MM(-)YY(YY) of 'expiration' CardStartMonth => \( $month ), # MM from MM(-)YY(YY) of 'card_start' CardStartYear => \( $year ), # YY from MM(-)YY(YY) of 'card_start' Track1 => 'track1', Track2 => 'track2', ACH AccountNumber => 'account_number', ABA => 'routing_code', CheckNumber => 'check_number', CardName => 'name', DispositionType => 'DispositionType', TotalAmount => 'amount' reformatted into cents FeeAmount => 'FeeAmount', TaxAmount => 'TaxAmount', BillingAddress => 'address', BillingCity => 'city', BillingStateProv => 'state', BillingPostalCode => 'zip', BillingCountry => 'country', # forced to ISO-3166-alpha-3 BillingPhone => 'phone', Email => 'email', UserIPAddr => 'customer_ip', UserHost => 'UserHost', UDField1 => 'UDField1', UDField2 => 'UDField2', UDField3 => 'UDField3', ActionCode => 'ActionCode', IndustryInfo Type => 'IndustryInfo', ShippingInfo CustomerPO => 'CustomerPO', ShippingMethod => 'ShippingMethod', ShippingName => 'ship_name', ShippingAddr Address => 'ship_address', City => 'ship_city', StateProv => 'ship_state', Country => 'ship_country', # forced to ISO-3166-alpha-3 Phone => 'ship_phone', =head1 NOTE =head1 COMPATIBILITY Version 0.07 changes the server name and path for IPPay's late 2012 update. Business::OnlinePayment::IPPay uses IPPay XML Product Specifications version 1.1.2. See http://www.ippay.com/ for more information. =head1 AUTHORS Original author: Jeff Finucane Current maintainer: Ivan Kohler Reverse Authorization patch from dougforpres =head1 SEE ALSO perl(1). L. =cut Business-OnlinePayment-IPPay-0.08/Changes0000644000175000017500000000241312062711001016731 0ustar ivanivanRevision history for Perl extension Business::OnlinePayment::IPPay. 0.08 Fri Dec 14 12:54:14 PST 2012 - Pass ACH account type 0.07 Sun Oct 28 23:24:21 PDT 2012 - add Reverse Authorization support, patch from dougforpres - gateway URL change -- note this version will be the minimum required once IPPay turns off the old URL 0.06 Wed Jan 26 13:15:21 PST 2011 - fix january bug in tests 0.05 Mon Jul 19 10:19:34 EDT 2010 - add introspection info used in Business::OnlinePayment 3.01+ - (0.05_02) add ECHECK_void_requires_account to introspection info - add a quick routing_code validation for a better error message - never send an empty country for shipping or billing address 0.04 Tue Jul 22 12:19:54 2008 EDT - force country and ship country to ISA-3166-alpha-3 0.03 Sun Jul 10 21:00:58 2008 EDT - require Business::OnlinePayment::HTTPS 0.09 - remove unnecessary requirements - ensure transaction id and transaction are from the same server - more debugging info 0.02 Sun Jul 3 17:44:08 2008 PDT - add live default server; - send billing address as shipping address for card transactions where no shipping address is provided 0.01 Sun Jun 22 17:56:23 2008 EDT - original version;