File Coverage

blib/lib/Vend/Payment/Nova.pm
Criterion Covered Total %
statement 18 73 24.6
branch 3 28 10.7
condition 2 29 6.9
subroutine 4 5 80.0
pod 0 1 0.0
total 27 136 19.8


line stmt bran cond sub pod time code
1              
2             package Vend::Payment::Nova;
3              
4             # This program is free software; you can redistribute it and/or modify
5             # it under the terms of the GNU General Public License as published by
6             # the Free Software Foundation; either version 2 of the License, or
7             # (at your option) any later version.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public
15             # License along with this program; if not, write to the Free
16             # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17             # MA 02111-1307 USA.
18              
19             =head1 NAME
20              
21             Vend::Payment::Nova - Interchange Nova Support
22              
23             =head1 SYNOPSIS
24              
25             &charge=nova
26              
27             or
28              
29             [charge mode=nova param1=value1 param2=value2]
30              
31             =head1 PREREQUISITES
32              
33             Net::SSLeay
34            
35             or
36            
37             LWP::UserAgent and Crypt::SSLeay
38              
39             Only one of these need be present and working.
40              
41             =head1 DESCRIPTION
42              
43             The Vend::Payment::Nova module implements the nova() routine for using
44             Nova IC payment services with Interchange. It is compatible on a call level
45             with the other Interchange payment modules -- in theory (and even usually in
46             practice) you could switch from CyberCash to Nova with a few configuration
47             file changes.
48              
49             To enable this module, place this directive in C:
50              
51             Require module Vend::Payment::Nova
52              
53             This I be in interchange.cfg or a file included from it.
54              
55             NOTE: Make sure CreditCardAuto is off (default in Interchange demos).
56              
57             The mode can be named anything, but the C parameter must be set
58             to C. To make it the default payment gateway for all credit
59             card transactions in a specific catalog, you can set in C:
60              
61             Variable MV_PAYMENT_MODE nova
62              
63             It uses several of the standard settings from Interchange payment. Any time
64             we speak of a setting, it is obtained either first from the tag/call options,
65             then from an Interchange order Route named for the mode, then finally a
66             default global payment variable, For example, the C parameter would
67             be specified by:
68              
69             [charge mode=nova merhcant_id=YourMerchantID]
70              
71             or
72              
73             Route nova merhcant_id YourMerchantID
74              
75             or with only Nova as a payment provider
76              
77             Variable MV_PAYMENT_ID YourMerchantID
78              
79             A fully valid catalog.cfg entry to work with the standard demo would be:
80              
81             Variable MV_PAYMENT_MODE nova
82             Variable MV_PAYMENT_MERCHANT_ID YourMerchantID
83             Variable MV_PAYMENT_USER_ID YourUserID
84             Variable MV_PAYMENT_PIN YourUserPIN
85              
86             =head1 VERSION HISTORY
87              
88             =over
89              
90             =item *
91              
92             06-10-2008 - Version 1.01
93              
94             =item
95              
96             01-13-2009 - Version 1.02
97             - Joseph Montanez reports that Nova might (rarely) return 'APPROVAL' instead of 'APPROVED'.
98              
99             =item
100              
101             03-17-2011 - Version 1.03
102             - added 'use strict;' -- an oversight, but didn't require any further changes. :-)
103             - some formatting changes, and made a DEBUG constant
104             - added VERSION to Makefile.PL
105              
106             =back
107              
108             =head1 BUGS
109              
110             The only currently supported transaction type is 'sale'.
111              
112             =head1 AUTHOR
113              
114             Murray Nesbitt (murray AT cpan.org)
115              
116             =cut
117              
118             BEGIN {
119              
120 1     1   20590 my $selected;
121 1         2 eval {
122             package Vend::Payment;
123 1         2364 require Net::SSLeay;
124 1         28461 import Net::SSLeay qw(post_https make_form make_headers);
125 1         4 $selected = "Net::SSLeay";
126             };
127              
128 1 50       6 $Vend::Payment::Have_Net_SSLeay = 1 unless $@;
129              
130 1 50       4 unless ($Vend::Payment::Have_Net_SSLeay) {
131              
132 0         0 eval {
133             package Vend::Payment;
134 0         0 require LWP::UserAgent;
135 0         0 require HTTP::Request::Common;
136 0         0 require Crypt::SSLeay;
137 0         0 import HTTP::Request::Common qw(POST);
138 0         0 $selected = "LWP and Crypt::SSLeay";
139             };
140              
141 0 0       0 $Vend::Payment::Have_LWP = 1 unless $@;
142              
143             }
144              
145 1 0 33     8 unless ($Vend::Payment::Have_Net_SSLeay or $Vend::Payment::Have_LWP) {
146 0         0 die __PACKAGE__ . " requires Net::SSLeay or Crypt::SSLeay";
147             }
148              
149 1 50 33     87 ::logGlobal("%s payment module initialized, using %s", __PACKAGE__, $selected)
150             unless $Vend::Quiet or ! $Global::VendRoot;
151              
152             }
153              
154 1     1   13 use vars qw/$Have_LWP $Have_Net_SSLeay/;
  1         2  
  1         73  
155 1     1   5 use strict;
  1         2  
  1         260  
156              
157 1     1   14 use constant DEBUG => 0;
  1         2  
  1         1152  
158              
159             my %AVS_CODES = (
160             'A' => 'Address matches - Zip Code does not match.',
161             'B' => 'Street address match, Postal code in wrong format. (International issuer)',
162             'C' => 'Street address and postal code in wrong formats',
163             'D' => 'Street address and postal code match (international issuer)',
164             'E' => 'AVS Error',
165             'G' => 'Service not supported by non-US issuer',
166             'I' => 'Address information not verified by international issuer.',
167             'M' => 'Street Address and Postal code match (international issuer)',
168             'N' => 'No Match on Address (Street) or Zip',
169             'O' => 'No Response sent',
170             'P' => 'Postal codes match, Street address not verified due to incompatible formats.',
171             'R' => 'Retry, System unavailable or Timed out',
172             'S' => 'Service not supported by issuer',
173             'U' => 'Address information is unavailable',
174             'W' => '9 digit Zip matches, Address (Street) does not match.',
175             'X' => 'Exact AVS Match',
176             'Y' => 'Address (Street) and 5-digit Zip match.',
177             'Z' => '5 digit Zip matches, Address (Street) does not match.',
178             );
179              
180             my %CVV_CODES = (
181             'M' => 'CVV2 Match',
182             'N' => 'CVV2 No match',
183             'P' => 'Not Processed',
184             'S' => 'Issuer indicates that CVV2data should be present on the card, but the merchant has indicated that the CVV2 data is not resent on the card',
185             'U' => 'Issuer has not certified for CVV2 or Issuer has not provided Visa with the CVV2 encryption Keys.',
186             );
187              
188             sub nova {
189 0     0 0   my ($user, $amount) = @_;
190              
191 0           my ($opt, $merchant_id, $user_id, $pin);
192 0 0         if(ref $user) {
193 0           $opt = $user;
194 0   0       $merchant_id = $opt->{merchant_id} || charge_param('merchant_id');
195 0   0       $user_id = $opt->{user_id} || charge_param('user_id');
196 0   0       $pin = $opt->{pin} || charge_param('pin');
197             }
198              
199 0           my %actual;
200 0 0         if($opt->{actual}) {
201 0           %actual = %{$opt->{actual}};
  0            
202             }
203             else {
204 0           %actual = map_actual();
205             }
206              
207 0           ::logDebug("Mapping: " . ::uneval(%actual)) if DEBUG;
208              
209 0           my $exp = sprintf('%02d%02d', $actual{mv_credit_card_exp_month}, $actual{mv_credit_card_exp_year});
210              
211 0           $actual{mv_credit_card_number} =~ s/\D//g;
212 0           $actual{phone_day} =~ s/\D//g;
213              
214 0   0       my $precision = $opt->{precision} || charge_param('precision') || 2;
215              
216 0   0       $amount = $opt->{total_cost} || undef;
217              
218 0   0       $opt->{transaction} ||= 'sale';
219              
220 0 0         unless($amount) {
221 0           $amount = Vend::Interpolate::total_cost();
222 0           $amount = Vend::Util::round_to_frac_digits($amount,$precision);
223             }
224              
225 0           my %values = (
226             ssl_merchant_id => $merchant_id,
227             ssl_user_id => $user_id,
228             ssl_pin => $pin,
229             ssl_show_form => 'false',
230             ssl_transaction_type => 'CCSALE',
231             ssl_result_format => 'ASCII',
232             ssl_first_name => $actual{b_fname},
233             ssl_last_name => $actual{b_lname},
234             ssl_email => $actual{email},
235             ssl_city => $actual{b_city},
236             ssl_state => $actual{b_state},
237             ssl_avs_zip => $actual{b_zip},
238             ssl_invoice_number => $opt->{order_id},
239             ssl_card_number => $actual{mv_credit_card_number},
240             ssl_exp_date => $exp,
241             ssl_amount => $amount,
242             ssl_description => "0001~Generic Order String~$amount~1~N~||",
243             ssl_ship_to_phone => $actual{phone_day},
244              
245             # CVV
246             ssl_cvv2 => 'present',
247             ssl_cvv2cvc2_indicator => 1,
248             ssl_cvv2cvc2 => $actual{mv_credit_card_cvv2},
249              
250             # AVS
251             ssl_avs_address => substr($actual{b_address}, 0, 20),
252             ssl_avs_zip => $actual{b_zip},
253             );
254              
255             #$values{'ssl_test_mode'} = 'TRUE';
256              
257 0           ::logDebug("Values to be sent: " . ::uneval(%values)) if DEBUG;
258              
259 0   0       $opt->{submit_url} = $opt->{submit_url}
260             || 'https://www.myvirtualmerchant.com/VirtualMerchant/process.do';
261              
262 0           my $merchant_return = post_data($opt, \%values);
263              
264 0           ::logDebug("request returned: $merchant_return->{result_page}") if DEBUG;
265              
266 0           my %result;
267              
268 0           my @lines = split /\n/, $merchant_return->{result_page};
269 0           foreach (@lines) {
270 0           chomp;
271 0           ::logDebug("found response line=$_") if DEBUG;
272 0           my ($name, $val) = split(/=/,$_);
273 0           ::logDebug("name=$name value=$val") if DEBUG;
274 0           $result{$name} = $val;
275             }
276              
277 0 0 0       if ($result{ssl_result} == 0 && ($result{ssl_result_message} eq 'APPROVED' || $result{ssl_result_message} eq 'APPROVAL')) {
      0        
278 0           $result{MStatus} = $result{'pop.status'} = 'success';
279 0           $result{'order-id'} = $opt->{order_id};
280             }
281             else {
282 0           $result{MStatus} = 'failed';
283 0           $result{MErrMsg} = $result{ssl_result_message};
284             }
285              
286 0 0         $result{'pop.auth-code'} = $result{'ssl_approval_code'} if defined $result{'ssl_approval_code'};
287 0 0         $result{'pop.avs_code'} = $AVS_CODES{$result{'ssl_avs_response'}} if defined $result{'ssl_avs_response'};
288 0 0         $result{'pop.txn-id'} = $result{'ssl_txn_id'} if defined $result{'ssl_txn_id'};
289 0 0         $result{'pop.error-message'} = $result{'ssl_result_message'} if defined $result{'ssl_result_message'};
290 0 0         $result{'pop.cvv2_code'} = $CVV_CODES{$result{'ssl_cvv2_response'}} if defined $result{'ssl_cvv2_response'};
291              
292 0           ::logDebug("Nova request result: " . ::uneval(\%result) ) if DEBUG;
293              
294 0           return %result;
295             }
296              
297             1;