use vars qw($VERSION $DEBUG @ISA);
@ISA = qw(Business::OnlinePayment::HTTPS);
-$VERSION = '0.03';
+$VERSION = '0.08_02';
$DEBUG = 0;
sub set_defaults {
my $self = shift;
+ my %opts = @_;
#USD
#$self->server('esplusqa.moneris.com'); # development
$self->port('443');
- $self->build_subs(qw( order_number avs_code ));
+ $self->build_subs(qw( order_number avs_code skip_avs skip_cvv ));
# avs_code order_type md5 cvv2_response cavv_response
+
+ $self->skip_avs( $opts{skip_avs} );
+ $self->skip_cvv( $opts{skip_cvv} );
}
sub submit {
my($self) = @_;
- if ( $self->{_content}{'currency'} eq 'CAD' ) {
+ if ( defined( $self->{_content}{'currency'} )
+ && $self->{_content}{'currency'} eq 'CAD' ) {
$self->server('www3.moneris.com');
$self->path('/gateway2/servlet/MpgRequest');
} else { #sorry, default to USD
}
if ($self->test_transaction) {
- if ( $self->{_content}{'currency'} eq 'CAD' ) {
+ if ( defined( $self->{_content}{'currency'} )
+ && $self->{_content}{'currency'} eq 'CAD' ) {
$self->server('esqa.moneris.com');
$self->{_content}{'login'} = 'store2'; # store[123]
$self->{_content}{'password'} = 'yesguy';
}
}
+ my %cust_id = ( 'invoice_number' => 'cust_id' );
+
+ my $invoice_number = $self->{_content}{invoice_number};
+
# BOP field => eSelectPlus field
#$self->map_fields();
$self->remap_fields(
# => 'transaction_type',
#login => 'store_id',
#password => 'api_token',
+
#authorization =>
- #customer_ip =>
#name =>
#first_name =>
#last_name =>
#company =>
- #address =>
+ #address => 'avs_street_number'/'avs_street_name' handled below
#city =>
#state =>
- #zip =>
+ zip => 'avs_zipcode',
#country =>
- phone =>
+ phone => 'avs_custphone',
#fax =>
- email =>
+ email => 'avs_email',
+ customer_ip => 'avs_custip',
+
card_number => 'pan',
- #expiration =>
- # => 'expdate',
+ #expiration => 'expdate', #handled below
+ cvv2 => 'cvd_value',
'amount' => 'amount',
- invoice_number => 'cust_id',
- #customer_id => 'cust_id',
+ customer_id => 'cust_id',
order_number => 'order_id', # must be unique number
- authorization => 'txn_number' # reference to previous trans
-
- #cvv2 =>
+ authorization => 'txn_number', # reference to previous trans
);
my $action = $self->{_content}{'action'};
} elsif ( $self->{_content}{'action'} =~ /^\s*post\s*authorization\s*$/i ) {
$action = 'completion';
} elsif ( $self->{_content}{'action'} =~ /^\s*void\s*$/i ) {
- $action = 'void';
+ $action = 'purchasecorrection';
} elsif ( $self->{_content}{'action'} =~ /^\s*credit\s*$/i ) {
if ( $self->{_content}{'authorization'} ) {
$action = 'refund';
if ( $action =~ /^(purchase|preauth|ind_refund)$/ ) {
- $self->required_fields(
- qw( login password amount card_number expiration )
- );
+ $self->required_fields(qw(
+ login password amount card_number expiration
+ ));
#cardexpiremonth & cardexpireyear
$self->{_content}{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
$month = '0'. $month if $month =~ /^\d$/;
$self->{_content}{expdate} = $year.$month;
+ #CVD Indicator
+ #0 = CVD value is deliberately bypassed or is not provided by the merchant
+ #1 = CVD value is present.
+ #2 = CVD value is on the card, but is illegible.
+ #9 = Cardholder states that the card has no CVD imprint.
+ $self->{_content}{cvd_indicator} = $self->{_content}{cvd_value} ? 1 : 0;
+
$self->generate_order_id;
+ $self->{_content}{order_id} .= '-'. ($invoice_number || 0);
+
$self->{_content}{amount} = sprintf('%.2f', $self->{_content}{amount} );
- } elsif ( $action eq 'completion' || $action eq 'void' ) {
+ } elsif ( $action =~ /^(completion|purchasecorrection|refund)$/ ) {
- $self->required_fields( qw( login password order_number authorization ) );
+ $self->required_fields(qw(
+ login password order_number authorization
+ ));
- } elsif ( $action eq 'refund' ) {
+ if ( $action eq 'completion' ) {
+ $self->{_content}{comp_amount} = delete $self->{_content}{amount};
+ } elsif ( $action eq 'purchasecorrection' ) {
+ delete $self->{_content}{amount};
+ #} elsif ( $action eq 'refund' ) {
+ }
- $self->required_fields(
- qw( login password order_number authorization )
- );
+ }
+ if ( $self->{_content}{address} ) {
+ my $number = '';
+ my $name = $self->{_content}{address};
+ if ( $name =~ s/^\s*(\d+)\w\s+// ) {
+ $number = $1;
+ }
+ $name = substr( $name, 0, 19 - length($number) );
+ $self->{_content}{avs_street_number} = $number;
+ $self->{_content}{avs_street_name} = $name;
}
+ $self->{_content}{avs_zipcode} =~ s/\W//g
+ if defined $self->{_content}{avs_zipcode};
+
# E-Commerce Indicator (see eSelectPlus docs)
$self->{_content}{'crypt_type'} ||= 7;
- $action = "us_$action" unless $self->{_content}{'currency'} eq 'CAD';
+ $action = "us_$action"
+ unless defined( $self->{_content}{'currency'} )
+ && $self->{_content}{'currency'} eq 'CAD';
#no, values aren't escaped for XML. their "mpgClasses.pl" example doesn't
#appear to do so, i dunno
'<store_id>'. $self->{_content}{'login'}. '</store_id>'.
'<api_token>'. $self->{_content}{'password'}. '</api_token>'.
"<$action>".
- join('', map "<$_>$fields{$_}</$_>", keys %fields ).
+ join('', map "<$_>$fields{$_}</$_>", keys %fields );
+
+ if ( $action =~ /^(purchase|preauth|ind_refund)$/ ) {
+ tie my %avs_fields, 'Tie::IxHash', $self->get_fields( $self->avs_fields );
+ $post_data .=
+ '<avs_info>'.
+ join('', map "<$_>$avs_fields{$_}</$_>", keys %avs_fields ).
+ '</avs_info>'
+ if ! $self->skip_avs && grep $_, values %avs_fields;
+
+ tie my %cvd_fields, 'Tie::IxHash', $self->get_fields( $self->cvd_fields );
+ $post_data .=
+ '<cvd_info>'.
+ join('', map "<$_>$cvd_fields{$_}</$_>", keys %cvd_fields ).
+ '</cvd_info>'
+ if ! $self->skip_cvv && grep $_, values %cvd_fields;
+ }
+
+ $post_data .=
"</$action>".
'</request>';
my( $page, $response, @reply_headers) = $self->https_post( $post_data );
- #my %reply_headers = @reply_headers;
- #warn join('', map { " $_ => $reply_headers{$_}\n" } keys %reply_headers )
- # if $DEBUG;
+ if ($DEBUG > 1) {
+ my %reply_headers = @reply_headers;
+ warn join('', map { " $_ => $reply_headers{$_}\n" } keys %reply_headers)
+ }
if ($response !~ /^200/) {
# Connection error
$self->is_success(0);
my $diag_message = $response || "connection error";
die $diag_message;
-
}
# avs_code - eSELECTplus_Perl_IG.pdf Appendix F
die "gateway error: ". $self->GetXMLProp( $page, 'Message' )
if $result =~ /^null$/i;
- # New unique reference created by the gateway
- $self->order_number($self->GetXMLProp($page, 'ReferenceNum'));
# Original order_id supplied to the gateway
- #$self->order_number($self->GetXMLProp($page, 'ReceiptId'));
+ $self->order_number($self->GetXMLProp($page, 'ReceiptId'));
# We (Whizman & DonorWare) do not have enough info about "ISO"
# response codes to make use of them.
if ( $result =~ /^\d+$/ && $result < 50 ) {
$self->is_success(1);
- $self->authorization($self->GetXMLProp($page, 'AuthCode'));
+ $self->authorization($self->GetXMLProp($page, 'TransID'));
} elsif ( $result =~ /^\d+$/ ) {
$self->is_success(0);
my $tmp_msg = $self->GetXMLProp( $page, 'Message' );
);
}
+sub avs_fields {
+ my $self = shift;
+
+ #order is important to this processor
+ qw(
+ avs_street_number
+ avs_street_name
+ avs_zipcode
+ avs_email
+ avs_hostname
+ avs_browser
+ avs_shiptocountry
+ avs_shipmethod
+ avs_merchprodsku
+ avs_custip
+ avs_custphone
+ );
+}
+
+sub cvd_fields {
+ my $self = shift;
+
+ #order is important to this processor
+ qw(
+ cvd_indicator
+ cvd_value
+ );
+}
+
sub GetXMLProp {
my( $self, $raw, $prop ) = @_;
local $^W=0;
For detailed information see L<Business::OnlinePayment>.
-=head1 Note for Canadian merchants upgrading to 0.03
+=head1 NOTES
+
+=head2 Note for Canadian merchants upgrading to 0.03
As of version 0.03, this module now defaults to the US Moneris. Make sure to
pass currency=>'CAD' for Canadian transactions.
+=head2 Note for upgrading to 0.05
+
+As of version 0.05, the bank authorization code is discarded (AuthCode),
+so that authorization() and order_number() can return the 2 fields needed
+for capture. See also
+cpansearch.perl.org/src/IVAN/Business-OnlinePayment-3.02/notes_for_module_writers_v3
+
+=head2 Note for upgrading to 0.08 without AVS/CVV enabled with Moneris
+
+This version now passes AVS and CVV info (previous versions did not). If your
+Moneris account is not enabled for these services, you can omit them by passing
+the "skip_avs" and/or "skip_cvv" options set to a true value:
+
+ my $tx = new Business::OnlinePayment('eSelectPlus',
+ 'skip_avs' => 1,
+ 'skip_cvv' => 1,
+ );
+
=head1 AUTHOR
Ivan Kohler <ivan-eselectplus@420.am>
-Randall Whitman <www.whizman.com>
+Randall Whitman L<whizman.com|http://whizman.com>
=head1 SEE ALSO