X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=InternetSecure.pm;h=2b63019c69c92177fb09bf2f69d9a0dd6a6a24e4;hb=ba77e0adce510afadfa3d926637efe487e889fd1;hp=58b6efd5549d6cca3cf57440d8d7c5d71a5c7e02;hpb=f84475d7ed493be9c8f036c77afa93306440f8af;p=Business-OnlinePayment-InternetSecure.git diff --git a/InternetSecure.pm b/InternetSecure.pm index 58b6efd..2b63019 100755 --- a/InternetSecure.pm +++ b/InternetSecure.pm @@ -18,8 +18,9 @@ our $VERSION = '0.01'; use constant CARD_TYPES => { VI => 'Visa', MC => 'MasterCard', - AX => 'American Express', + AX => 'American Express', # FIXME: AM? NN => 'Discover', + # JB? }; @@ -36,23 +37,24 @@ sub set_defaults { $self->path('/process.cgi'); $self->build_subs(qw( - receipt_number sales_order_number - cardholder card_type + receipt_number sales_number + date + card_type cardholder total_amount avs_response cvv2_response )); } -# OnlinePayment's remap_fields is buggy, so we simply rewrite it +# OnlinePayment's get_fields now filters out undefs in 3.x. :( # -sub remap_fields { - my ($self, %map) = @_; +sub get_fields { + my ($self, @fields) = @_; - my %content = $self->content(); - foreach (keys %map) { - $content{$map{$_}} = delete $content{$_}; - } - $self->content(%content); + my %content = $self->content; + + my %new = map +($_ => $content{$_}), @fields; + + return %new; } # Combine get_fields and remap_fields for convenience @@ -63,10 +65,6 @@ sub get_remap_fields { $self->remap_fields(reverse %map); my %data = $self->get_fields(keys %map); - foreach (values %data) { - $_ = '' unless defined; - } - return %data; } @@ -99,14 +97,13 @@ sub parse_expdate { # Convert a single product into a product string # sub prod_string { - my ($self, $currency, $taxes, %data) = @_; + my ($self, $currency, %data) = @_; croak "Missing amount in product" unless defined $data{amount}; my @flags = ($currency); - $taxes = uc $data{taxes} if defined $data{taxes}; - foreach (split ' ' => $taxes) { + foreach (split ' ' => uc($data{taxes} || '')) { croak "Unknown tax code $_" unless /^(GST|PST|HST)$/; push @flags, $_; } @@ -140,16 +137,14 @@ sub to_xml { croak 'Unsupported action' unless $content{action} =~ /^Normal Authori[zs]ation$/i; - $content{currency} ||= 'CAD'; - $content{currency} = uc $content{currency}; + $content{currency} = uc($content{currency} || 'CAD'); croak "Unknown currency code ", $content{currency} unless $content{currency} =~ /^(CAD|USD)$/; - $content{taxes} ||= ''; - $content{taxes} = uc $content{taxes}; + $content{taxes} = uc($content{taxes} || ''); my %data = $self->get_remap_fields(qw( - xxxCardNumber card_number + xxxCard_Number card_number xxxName name xxxCompany company @@ -174,7 +169,8 @@ sub to_xml { $data{MerchantNumber} = $self->merchant_id; - $data{xxxCardNumber} =~ tr/ //d; + $data{xxxCard_Number} =~ tr/ //d; + $data{xxxCard_Number} =~ s/^[^3-6]/4/ if $self->test_transaction; my ($y, $m) = $self->parse_expdate($content{exp_date}); $data{xxxCCYear} = sprintf '%.4u' => $y; @@ -190,24 +186,26 @@ sub to_xml { if (ref $content{description}) { $data{Products} = join '|' => map $self->prod_string( - $content{currency}, - $content{taxes}, - %$_), - @{ $content{description} }; + $content{currency}, + taxes => $content{taxes}, + %$_), + @{ $content{description} }; } else { $self->required_fields(qw(amount)); $data{Products} = $self->prod_string( $content{currency}, - $content{taxes}, - amount => $content{amount}, + taxes => $content{taxes}, + amount => $content{amount}, description => $content{description}, ); } xml_out(\%data, - NoAttr => 1, - RootName => 'TranxRequest', - XMLDecl => '', + NoAttr => 1, + NumericEscape => 2, + RootName => 'TranxRequest', + SuppressEmpty => undef, + XMLDecl => '', ); } @@ -219,7 +217,7 @@ sub infuse { while (my ($k, $v) = each %map) { no strict 'refs'; - $self->$v($data->{$k}); + $self->$k($data->{$v}); } } @@ -229,6 +227,8 @@ sub parse_response { my ($self, $response) = @_; $self->server_response($response); + + local $/ = "\n"; # Make sure to avoid bug #17687 $response = xml_in($response, ForceArray => [qw(product flag)], @@ -240,18 +240,22 @@ sub parse_response { my $code = $self->result_code($response->{Page}); $self->is_success($code eq '2000' || $code eq '90000' || $code eq '900P1'); - $self->infuse($response, qw( - ReceiptNumber receipt_number - SalesOrderNumber sales_order_number - xxxName cardholder - CardType card_type - Page result_code - ApprovalCode authorization - Verbiage error_message - TotalAmount total_amount - AVSResponseCode avs_response - CVV2ResponseCode cvv2_response - )); + $self->infuse($response, + receipt_number => 'ReceiptNumber', + sales_number => 'SalesOrderNumber', + date => 'Date', + cardholder => 'xxxName', + card_type => 'CardType', + result_code => 'Page', + authorization => 'ApprovalCode', + error_message => 'Verbiage', + total_amount => 'TotalAmount', + avs_response => 'AVSResponseCode', + cvv2_response => 'CVV2ResponseCode', + ); + + # Completely undocumented field that sometimes override + $self->error_message($response->{Error}) if $response->{Error}; $self->card_type(CARD_TYPES->{$self->card_type}); @@ -274,15 +278,16 @@ sub submit { undef, make_form( xxxRequestMode => 'X', - xxxRequestData => Encode::encode_utf8( - $self->to_xml - ), + xxxRequestData => $self->to_xml, ) ); croak 'Error connecting to server' unless $page; croak 'Server responded, but not in XML' unless $page =~ /^<\?xml/; + # The response is marked UTF-8, but it's really Latin-1. Sigh. + $page =~ s/^(<\?xml.*?) encoding="utf-8"/$1 encoding="iso-8859-1"/si; + $self->parse_response($page); } @@ -339,7 +344,7 @@ Business::OnlinePayment::InternetSecure - InternetSecure backend for Business::O Business::OnlinePayment::InternetSecure is an implementation of L that allows for processing online credit card -payments through Internet Secure. +payments through InternetSecure. See L for more information about the generic Business::OnlinePayment interface. @@ -348,11 +353,12 @@ Business::OnlinePayment interface. Object creation is done via L; see its manpage for details. The I processor option is required, and corresponds -to the merchant ID assigned to you by Internet Secure. +to the merchant ID assigned to you by InternetSecure. =head1 METHODS -(See L for more methods.) +(Other methods are also available -- see L for more +details.) =head2 Before order submission @@ -415,12 +421,14 @@ Code (CVC2) or Card Identification number (CID), depending on the card issuer. A short description of the purchase. See L<"Products list syntax"> for an alternate syntax that allows a list of products to be specified. -=item amount +=item amount (usually required) + +Total amount to be billed, excluding taxes if they are to be added separately +by InternetSecure. -Total amount to be billed, excluding taxes if they are to be added separately. -This field is required if B is a string, and should be left -undefined if B contains a list of products, as outlined in -L<"Products list syntax">. +This field is required if B is a string, but should be left +undefined if B contains a list of products instead, as outlined +in L<"Products list syntax">. =item currency @@ -429,17 +437,15 @@ C (default) or C. =item taxes -Taxes to be added automatically. These should not be included in B; -they will be automatically added by Internet Secure later on. +Taxes to be added automatically to B by InternetSecure. -Available taxes are C, C and C. Taxes can be combined by -separating them with spaces, such as C. +Available taxes are C, C and C. Multiple taxes can specified +by concatenating them with spaces, such as C. =item name / company / address / city / state / zip / country / phone / email -Facultative customer information. B should be either a postal -abbreviation or a two-letter code taken from ISO 3166-2, and B should -be a two-letter code taken from ISO 3166-1. +Customer information. B should be a two-letter code taken from ISO +3166-1. =back @@ -449,9 +455,19 @@ be a two-letter code taken from ISO 3166-1. =over 4 -=item receipt_number() / sales_order_number() +=item receipt_number() + +Receipt number of this transaction; this is actually a string, unique to all +InternetSecure transactions. + +=item sales_number() + +Sales order number of this transaction. This is a number, unique to each +merchant, which is incremented by 1 each time. + +=item date() -Receipt number and sales order number of submitted order. +Date and time of the transaction. Format is C. =item total_amount() @@ -481,7 +497,7 @@ following: =item avs_response() / cvv2_response() -Results of the AVS and CVV2 checks. See the Internet Secure documentation for +Results of the AVS and CVV2 checks. See the InternetSecure documentation for the list of possible values. =item products_raw() @@ -509,7 +525,7 @@ Unit price of this product. =item quantity -Ordered quantity of this product. This can be a decimal value. +Ordered quantity of this product. =item sku @@ -532,9 +548,9 @@ be left undefined. =head2 Character encoding -Since communication to/from Internet Secure is encoded with UTF-8, all Unicode +Since communication to/from InternetSecure is encoded with UTF-8, all Unicode characters are theoretically available when submitting information via -B. (Further restrictions may be imposed by Internet Secure itself.) +B. (Further restrictions may be imposed by InternetSecure itself.) When using non-ASCII characters, all data provided to B should either be in the current native encoding (typically latin-1, unless it was modified @@ -554,11 +570,11 @@ L =head1 AUTHOR -Frederic Briere, Efbriere@fbriere.netE +Frédéric Brière, Efbriere@fbriere.netE =head1 COPYRIGHT AND LICENSE -Copyright (C) 2006 by Frederic Briere +Copyright (C) 2006 by Frédéric Brière This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.4 or,