X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=lib%2FBusiness%2FOnlinePayment%2FvSecureProcessing.pm;h=75675f3c4a46696e8de7fa98691e9a98d7860e9b;hb=51c7503002afd10e1070a60494bb1daca9daba16;hp=e7514a370f86b746c6269916bdb6345069378d05;hpb=1e90419f5b23ab4b4fe17b5861958d75ac285c4d;p=Business-OnlinePayment-vSecureProcessing.git
diff --git a/lib/Business/OnlinePayment/vSecureProcessing.pm b/lib/Business/OnlinePayment/vSecureProcessing.pm
index e7514a3..75675f3 100644
--- a/lib/Business/OnlinePayment/vSecureProcessing.pm
+++ b/lib/Business/OnlinePayment/vSecureProcessing.pm
@@ -1,35 +1,45 @@
package Business::OnlinePayment::vSecureProcessing;
use strict;
+use vars qw($VERSION $DEBUG @ISA);
use Carp;
-use Template;
+use XML::Writer;
use XML::Simple;
use Data::Dumper;
-
use Business::OnlinePayment;
use Business::OnlinePayment::HTTPS;
-#use Net::SSLeay qw(post_http post_https make_headers make_form);
-use vars qw($VERSION $DEBUG @ISA $me);
@ISA = qw(Business::OnlinePayment::HTTPS);
$DEBUG = 0;
-$VERSION = '0.01';
-$me = 'Business::OnlinePayment::vSecureProcessing';
-
-
-# $server: http://dvrotsos2.kattare.com
+$VERSION = '0.03';
# mapping out all possible endpoints
# but this version will only be building out "charge", "void", & "credit"
my %payment_actions = (
'charge' => {
path => '/vsg2/processpayment',
+ process => 'ProcessPayment',
+ fields => [qw/
+ Amount Trk1 Trk2 TypeOfSale Cf1 Cf2 Cf AccountNumber
+ ExpirationMonth ExpirationYear Cvv
+ CardHolderFirstName CardHolderLastName AvsZip AvsStreet
+ IndustryType ApplicationId Recurring
+ /],
},
'void' => {
path => '/vsg2/processvoid',
+ process => 'ProcessVoid',
+ fields => [qw(
+ Amount AccountNumber ExpirationMonth ExpirationYear ReferenceNumber
+ TransactionDate IndustryType ApplicationId
+ )],
},
'refund' => {
path => '/vsg2/processrefund',
+ process => 'ProcessRefund',
+ fields => [qw(
+ Amount AccountNumber ExpirationMonth ExpirationYear ApplicationId
+ )],
},
'authorize' => {
path => '/vsg2/processauth',
@@ -77,27 +87,23 @@ sub set_defaults {
result_code
error_message
server
- port
path
server_response/;
# B::OP creates the following accessors:
- # server, port, path, test_transaction, transaction_type,
+ # server, path, test_transaction, transaction_type,
# server_response, is_success, authorization,
# result_code, error_message,
$self->build_subs(qw/
- env platform userid gid tid appid action reference_number cvv_response
- avs_response risk_score txn_amount txn_date response_code
+ platform tid appid
+ action reference_number cvv_response avs_response response_code
+ risk_score txn_amount txn_date
/);
$DEBUG = exists($options{debug}) ? $options{debug} : $DEBUG;
-
-
- $self->server($options{'server'});
-
- $self->gid($options{'gid'});
+ $self->server('svr1.vsecureprocessing.com');
$self->tid($options{'tid'});
@@ -105,13 +111,10 @@ sub set_defaults {
$self->appid($options{'appid'});
- $self->env((defined($options{'env'})) ? $options{'env'} : 'live'); # 'live'/'test'
-
$self->port(443);
+
}
-
-
sub clean_content {
my ($self,$content) = @_;
my %content = $self->content();
@@ -125,15 +128,25 @@ sub clean_content {
$content{card_number} =~ s/\D//g;
}
+ if ($content{'description'} && length($content{'description'}) >20) {
+ $content{'description'} = substr($content{'description'},0,20);
+ }
+
# separate month and year values for expiry_date
if ( $content{expiration} ) {
- ($content{exp_month}, $content{exp_year}) = split /\//, $content{expiration};
+ ($content{exp_month}, $content{exp_year}) =
+ split /\//, $content{expiration};
$content{exp_month} = sprintf "%02d", $content{exp_month};
- $content{exp_year} = substr($content{exp_year},0,2) if ($content{exp_year} > 99);
+ $content{exp_year} = substr($content{exp_year},0,2)
+ if ($content{exp_year} > 99);
}
- if (!$content{'first_name'} || !$content{'last_name'} && $content{'name'}) {
- ($content{'first_name'}, $content{'last_name'}) = split(' ', $content{'name'}, 2);
+ if ( !$content{'first_name'}
+ || !$content{'last_name'} && $content{'name'}
+ )
+ {
+ ($content{'first_name'}, $content{'last_name'}) =
+ split(' ', $content{'name'}, 2);
}
if ($content{'address'} =~ m/[\D ]*(\d+)\D/) {
@@ -148,7 +161,10 @@ sub process_content {
my $self = shift;
$self->clean_content();
my %content = $self->content();
- $self->action(($action_mapping{lc $content{'action'}}) ? $action_mapping{lc $content{'action'}} : lc $content{'action'});
+ $self->action( ($action_mapping{lc $content{'action'}})
+ ? $action_mapping{lc $content{'action'}}
+ : lc $content{'action'}
+ );
$self->path($payment_actions{ $self->action }{path})
unless length($self->path);
$self->appid($content{appid}) if (!$self->appid && $content{appid});
@@ -168,6 +184,10 @@ sub submit {
$self->process_content();
my %content = $self->content;
my $action = $self->action();
+
+ if ( $self->test_transaction ) {
+ $self->server('dvrotsos2.kattare.com');
+ }
my @acceptable_actions = ('charge', 'refund', 'void');
@@ -175,98 +195,99 @@ sub submit {
croak "'$action' is not supported at this time.";
}
- # fill out the template vars
- my $template_vars = {
-
+ # fill in the xml vars
+ my $xml_vars = {
auth => {
- platform => $self->platform,
- userid => $self->userid,
- gid => $self->gid,
- tid => $self->tid
+ Platform => $self->platform,
+ UserId => $content{'login'},
+ GID => $content{'password'},
+ Tid => $self->tid || '01',
},
payment => {
- amount => $content{'amount'},
- track1 => ($content{'track1'}) ? $content{'track1'} : '',
- track2 => ($content{'track2'}) ? $content{'track2'} : '',
- type => ($content{'description'}) ? $content{'description'} : '',
- cf1 => ($content{'UDField1'}) ? $content{'UDField1'} : '',
- cf2 => ($content{'UDField2'}) ? $content{'UDField2'} : '',
- cf3 => '',
- account_number => ($content{'card_number'}) ? $content{'card_number'} : '',
- exp_month => $content{'exp_month'},
- exp_year => $content{'exp_year'},
- cvv => ($content{'cvv'}) ? $content{'cvv'} : ($content{'cvv2'}) ? $content{'cvv2'} : '',
- first_name => ($content{'first_name'}) ? $content{'first_name'} : '',
- last_name => ($content{'last_name'}) ? $content{'last_name'} : '',
- postal_code => ($content{'zip'}) ? $content{'zip'} : '',
- street_address => ($content{'street_number'}) ? $content{'street_number'} : '',
- industry_type => ($content{'IndustryInfo'} && lc($content{'IndustryInfo'}) eq 'ecommerce') ? 'ecom_3' : '',
- invoice_num => ($content{'invoice_number'}) ? $content{'invoice_number'} : '',
- appid => $self->appid(),
- recurring => ($content{'recurring_billing'} && $content{'recurring_billing'} eq 'YES' ) ? 1 : 0,
- response_code => ($content{'response_code'}) ? $content{'response_code'} : '',
- reference_number=> ($content{'ref_num'}) ? $content{'ref_num'} : '',
- token => ($content{'token'}) ? $content{'token'} : '',
- receipt => ($content{'receipt'}) ? $content{'receipt'} : '',
- transaction_date=> ($content{'txn_date'}) ? $content{'txn_date'} : '',
- merchant_data => ($content{'merchant_data'}) ? $content{'merchant_data'} : '',
- },
-
- # we won't be using level2 nor level3. So I'm leaving them blank for now.
- level2 => {
- card_type => '',
- purchase_code => '',
- country_code => '',
- ship_tp_postal_code => '',
- ship_from_postal_code => '',
- sales_tax => '',
- product_description1 => '',
- product_description2 => '',
- product_description3 => '',
- product_description4 => ''
- },
-
- level3 => {
- purchase_order_num => '',
- order_date => '',
- duty_amount => '',
- alt_tax_amount => '',
- discount_amount => '',
- freight_amount => '',
- tax_exempt => '',
- line_item_count => '',
- purchase_items => $self->_parse_line_items()
+ Amount => $content{'amount'},
+ Trk1 => ($content{'track1'}) ? $content{'track1'} : '',
+ Trk2 => ($content{'track2'}) ? $content{'track2'} : '',
+ TypeOfSale => ($content{'description'}) ? $content{'description'} : '',
+ Cf1 => ($content{'UDField1'}) ? $content{'UDField1'} : '',
+ Cf2 => ($content{'UDField2'}) ? $content{'UDField2'} : '',
+ Cf3 => '',
+ AccountNumber => ($content{'card_number'}) ? $content{'card_number'} : '',
+ ExpirationMonth => $content{'exp_month'},
+ ExpirationYear => $content{'exp_year'},
+ Cvv => ($content{'cvv'}) ? $content{'cvv'} : ($content{'cvv2'}) ? $content{'cvv2'} : '',
+ CardHolderFirstName => ($content{'first_name'}) ? $content{'first_name'} : '',
+ CardHolderLastName => ($content{'last_name'}) ? $content{'last_name'} : '',
+ AvsZip => ($content{'zip'}) ? $content{'zip'} : '',
+ AvsStreet => ($content{'street_number'}) ? $content{'street_number'} : '',
+# IndustryType => {
+# IndType => ($content{'IndustryInfo'} && lc($content{'IndustryInfo'}) eq 'ecommerce') ? 'ecom_3' : '',
+# IndInvoice => ($content{'invoice_number'}) ? $content{'invoice_number'} : ''
+# },
+ ApplicationId => $self->appid(),
+ Recurring => ($content{'recurring_billing'} && $content{'recurring_billing'} eq 'YES' ) ? 1 : 0,
+ ReferenceNumber => ($content{'ref_num'}) ? $content{'ref_num'} : '',
+ Token => ($content{'token'}) ? $content{'token'} : '',
+ Receipt => ($content{'receipt'}) ? $content{'receipt'} : '',
+ TransactionDate => ($content{'txn_date'}) ? $content{'txn_date'} : ''
}
+ # we won't be using level2 nor level3. So I'm leaving them out for now.
};
-
# create the list of required fields based on the action
- my @required_fields = qw/ amount /;
+ my @required_fields = qw/ Amount /;
if ($action eq 'charge') {
- push(@required_fields, $_) foreach (qw/ account_number cvv exp_month exp_year /);
+ push @required_fields, $_
+ foreach (qw/ AccountNumber ExpirationMonth ExpirationYear /);
}elsif ($action eq 'void') {
- push(@required_fields, $_) foreach (qw/ reference_number transaction_date /);
+ push @required_fields, $_
+ foreach (qw/ ReferenceNumber /);
}elsif ($action eq 'refund') {
- push(@required_fields, $_) foreach (qw/ amount account_number exp_month exp_year /);
+ push @required_fields, $_
+ foreach (qw/ Amount AccountNumber ExpirationMonth ExpirationYear /);
}
# check the requirements are met.
my @missing_fields;
foreach my $field (@required_fields) {
- push(@missing_fields, $field) if (!$template_vars->{payment}{$field});
+ push(@missing_fields, $field) if (!$xml_vars->{payment}{$field});
}
if (scalar(@missing_fields)) {
croak "Missing required fields: ".join(', ', @missing_fields);
}
- # read in the appropriate xml template
- my $xml_template = _get_xml_template( $action );
- # create a template object.
- my $tt = Template->new();
- # populate the XML template.
+ my $process_action = $action;
+ $process_action =~ s/\b([a-z])/\u$1/g;
+ $process_action = 'Process'.$process_action;
my $xml_data;
- $tt->process( \$xml_template, $template_vars, \$xml_data ) || croak $tt->error();
+ my $writer = new XML::Writer( OUTPUT => \$xml_data,
+ DATA_MODE => 0,
+ DATA_INDENT => 0,
+ ENCODING => 'utf-8',
+ );
+ $writer->xmlDecl();
+ $writer->startTag('Request');
+ $writer->startTag('MerchantData');
+ foreach my $key ( keys ( %{$xml_vars->{auth}} ) ) {
+ $writer->dataElement( $key, $xml_vars->{auth}{$key} );
+ }
+ $writer->endTag('MerchantData');
+ $writer->startTag($payment_actions{ $self->action }{process});
+ foreach my $key ( @{$payment_actions{ $self->action }{fields}} ) {
+ next if (!$xml_vars->{payment}{$key});
+ if (ref $xml_vars->{payment}{$key} eq '') {
+ $writer->dataElement( $key, $xml_vars->{payment}{$key});
+ }else {
+ $writer->startTag($key);
+ foreach my $key2 (keys %{$xml_vars->{payment}{$key}}) {
+ $writer->dataElement( $key2, $xml_vars->{payment}{$key}{$key2} );
+ }
+ $writer->endTag($key);
+ }
+ }
+ $writer->endTag($payment_actions{ $self->action }{process});
+ $writer->endTag('Request');
+ $writer->end();
warn "XML:\n$xml_data\n" if $DEBUG > 2;
@@ -286,7 +307,8 @@ sub submit {
# conform to RFC standards
$content =~ s/\n/\r\n/gs;
- my ( $page, $server_response, %headers ) = $self->https_post( $opts, $content );
+ my ( $page, $server_response, %headers ) =
+ $self->https_post( $opts, $content );
# store the server response.
$self->server_response($server_response);
@@ -295,9 +317,7 @@ sub submit {
if (!$self->is_success() && !$self->error_message() ) {
if ( $DEBUG ) {
- #additional logging information, possibly too sensitive for an error msg
- # (vSecureProcessing seems to have a failure mode where they return the full
- # original request including card number)
+ #additional logging information, possibly too sensitive for an error
$self->error_message(
"(HTTPS response: ".$server_response.") ".
"(HTTPS headers: ".
@@ -323,6 +343,7 @@ sub parse_response {
if ($self->server_response =~ /^200/) {
my $response = XMLin($page);
+ warn "Response:\n".Dumper($response)."\n" if $DEBUG > 2;
$self->result_code($response->{Status}); # 0 /1
$self->response_code($response->{ResponseCode}); # see documentation for translation
$self->avs_response($response->{AvsResponse}); # Y / N
@@ -333,7 +354,7 @@ sub parse_response {
$self->is_success($self->result_code() eq '0' ? 1 : 0);
if ($self->is_success()) {
- $self->authorization($response->{AuthIdentificationResponse});
+ $self->authorization($response->{ReferenceNumber});
}
# fill in error_message if there is is an error
if ( !$self->is_success && exists($response->{AdditionalResponseData})) {
@@ -342,218 +363,13 @@ sub parse_response {
$self->error_message('Error '.$response->{ResponseCode}.': '.(exists($response->{Receipt})) ? $response->{Receipt} : '');
}
- }else {
- $self->is_success(0);
- $self->error_message('Error communicating with vSecureProcessing server');
+ } else {
+ die 'Error communicating with vSecureProcessing server';
return;
}
-
-}
-
-sub _get_xml_template {
- my $action = shift;
-
- my $xml_template = q|
-
- [% auth.platform %]
- [% auth.userid %]
- [% auth.gid %]
- [% auth.tid %]
-
- |;
-
- if ($action eq 'charge') {
- $xml_template .= _get_xml_template_charge();
- }elsif($action eq 'void') {
- $xml_template .= _get_xml_template_void();
- }elsif($action eq 'authorize') {
- $xml_template .= _get_xml_template_auth();
- }elsif($action eq 'authorize_cancel') {
- $xml_template .= _get_xml_template_auth_cancel();
- }elsif($action eq 'refund') {
- $xml_template .= _get_xml_template_refund();
- }elsif($action eq 'capture') {
- $xml_template .= _get_xml_template_capture();
- }elsif($action eq 'create_token') {
- $xml_template .= _get_xml_template_create_token();
- }elsif($action eq 'delete_token') {
- $xml_template .= _get_xml_template_delete_token();
- }elsif($action eq 'query_token') {
- $xml_template .= _get_xml_template_query_token();
- }elsif($action eq 'update_exp_date') {
- $xml_template .= _get_xml_template_update_exp_date();
- }elsif($action eq 'update_token') {
- $xml_template .= _get_xml_template_update_token();
- }
-
- $xml_template .= "";
- $xml_template =~ s/[\n\t\s]*//g;
-
- return $xml_template;
-}
-
-sub _get_xml_template_charge {
- my $xml_template = q|
- [% payment.amount %]
- [% payment.track1 %]
- [% payment.track2 %]
- [% payment.type %]
- [% payment.cf1 %]
- [% payment.cf2 %]
- [% payment.cf3 %]
- [% payment.account_number %]
- [% payment.exp_month %]
- [% payment.exp_year %]
- [% payment.cvv %]
- [% payment.first_name %]
- [% payment.last_name %]
- [% payment.postal_code %]
- [% payment.street_address %]
-
- [% payment.industry_type %]
- [% payment.invoice_num %]
-
- [% payment.appid %]
- [% payment.recurring %]
- |;
-
- # other options (that we are not using right now):
-#
-# [% level2.card_type %]
-# [% level2.purchase_code %]
-# [% level2.country_code %]
-# [% level2.ship_tp_postal_code %]
-# [% level2.ship_from_postal_code %]
-# [% level2.sales_tax %]
-# [% level2.product_description1 %]
-# [% level2.product_description2 %]
-# [% level2.product_description3 %]
-# [% level2.product_description4 %]
-#
-#
-# [% level3.purchase_order_num %]
-# [% level3.order_date %]
-# [% level3.duty_amount %]
-# [% level3.alt_tax_amount %]
-# [% level3.discount_amount %]
-# [% level3.freight_amount %]
-# [% level3.tax_exempt %]
-# [% level3.line_item_count %]
-#
-# [% level3.purchase_items %]
-#
-#
-
- return $xml_template;
-}
-
-sub _parse_line_items {
- my $self = shift;
- my %content = $self->content();
-
- return '' if (!$content{'items'});
-
- my @line_items;
- my $template = q|
- [% seq_num %]
- [% code %]
- [% desc %]
- [% qty %]
- [% unit %]
- [% unit_cost %]
- [% amount %]
- [% discount_amount %]
- [% tax_amount %]
- [% tax_rate %]
- |;
-
-
- my @items = $content{'items'};
- foreach my $item (@items) {
- # fille in the slots from $template with details in $item
- # push to @line_items
- }
-
- return join("\n", @line_items);
-}
-
-sub _get_xml_template_void {
- my $xml_template = q|
- [% payment.amount %]
- [% payment.account_number %]
- [% payment.exp_month %]
- [% payment.exp_year %]
- [% payment.reference_number %]
-
- [% payment.industry_type %]
- [% payment.appid %]
- |;
-
- return $xml_template;
-}
-
-sub _get_xml_template_refund {
- my $xml_template = q|
- [% payment.amount %]
- [% payment.account_number %]
- [% payment.exp_month %]
- [% payment.exp_year %]
- [% payment.appid %]
- |;
-
- return $xml_template;
-}
-
-sub _get_xml_template_auth {
- my $xml_template = '';
-
- return $xml_template;
}
-sub _get_xml_template_auth_cancel {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-sub _get_xml_template_capture {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-sub _get_xml_template_create_token {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-sub _get_xml_template_delete_token {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-sub _get_xml_template_query_token {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-sub _get_xml_template_update_exp_date {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-sub _get_xml_template_update_token {
- my $xml_template = '';
-
- return $xml_template;
-}
-
-
1;
__END__
@@ -566,17 +382,17 @@ Business::OnlinePayment::vSecureProcessing - vSecureProcessing backend for Busin
use Business::OnlinePayment;
my %processor_info = (
- platform => '####',
- gid => 12345678901234567890,
- tid => 01,
- user_id => '####',
- url => 'www.####.com'
+ platform => 'vsecure_platform',
+ appid => 'vsecure_appid',
+ tid => '54', #optional, defaults to 01
);
my $tx =
new Business::OnlinePayment( "vSecureProcessing", %processor_info);
$tx->content(
- appid => '######',
- type => 'VISA',
+ login => 'vsecure@user.id',
+ password => '12345678901234567890', #vsecure gid
+
+ type => 'CC',
action => 'Normal Authorization',
description => 'Business::OnlinePayment test',
amount => '49.95',
@@ -653,7 +469,8 @@ from content(%content):
=head1 COMPATIBILITY
-Business::OnlinePayment::vSecureProcessing uses vSecureProcessing XML Document Version: 140901 (September 1, 2014).
+Business::OnlinePayment::vSecureProcessing uses vSecureProcessing XML Document
+Version: 140901 (September 1, 2014).
See http://www.vsecureprocessing.com/ for more information.
@@ -661,7 +478,7 @@ See http://www.vsecureprocessing.com/ for more information.
Original author: Alex Brelsfoard
-Current maintainer: Alex Brelsfoard
+Current maintainer: Ivan Kohler
=head1 COPYRIGHT
@@ -687,4 +504,3 @@ perl(1). L.
=cut
-