--- /dev/null
+blib/
+*.sw?
+Makefile
+Makefile.old
+MYMETA.yml
+pm_to_blib
--- /dev/null
+Revision history for Perl module Business::OnlinePayment::vSecureProcessing
+
+0.01 Feb 06 2015
+ Initial release.
+
README
ignore.txt
lib/Business/OnlinePayment/vSecureProcessing.pm
-t/00-load.t
-t/boilerplate.t
-t/manifest.t
-t/pod-coverage.t
-t/pod.t
-t/transaction.t
-t/transaction_decline.t
+t/t_00-load.t
+t/t_boilerplate.t
+t/t_manifest.t
+t/t_pod-coverage.t
+t/t_pod.t
+t/t_transaction.t
+t/t_transaction_decline.t
--- /dev/null
+#!/usr/bin/perl -w
+
+#
+# Make sure to copy Business::OnlinePayment::vSecureProcessing into its
+# proper system directory (aka /usr/share/perl5/Business/Onlinepayment)
+#
+
+use strict;
+use Data::Dumper;
+use Business::OnlinePayment;
+
+my %opt = (
+ server =>'dvrotsos2.kattare.com',
+ platform => 'Buypass',
+ gid => '1432479912596791',
+ tid => '01',
+ userid=> 'tom@yiptv.com',
+ port => 443,
+ env => 'test'
+);
+
+my $action = shift || 'Normal Authorization';
+my $auth = shift || '';
+
+
+my %content = (
+ appid => 'yiptv',
+ action => $action,
+ auth => $auth,
+ description => 'Business::OnlinePayment visa test',
+# card_number => '4007000000027',
+ card_number => '4111111111111111',
+ cvv2 => '111',
+ expiration => expiration_date(),
+ amount => '42.24',
+ name => 'Murphy Law',
+ email => 'fake@acme.com',
+ address => '123 Anystreet',
+ zip => '84058',
+);
+
+main();
+
+sub main {
+ my $transaction = Business::OnlinePayment->new("vSecureProcessing", %opt);
+
+ print "MAKING PAYMENT\n";
+ ProcessTransaction($transaction);
+ $content{'action'} = 'void';
+ $content{'ref_num'} = $transaction->authorization();
+ $content{'txn_date'} = $transaction->txn_date();
+ $content{'amount'} = $transaction->txn_amount;
+ print "VOIDING PAYMENT\n";
+ ProcessTransaction($transaction);
+ $content{'action'} = 'Normal Authorization';
+ $content{'amount'} = '30.00';
+ print "MAKING PAYMENT\n";
+ ProcessTransaction($transaction);
+ $content{'action'} = 'credit';
+ $content{'ref_num'} = $transaction->authorization;
+ $content{'txn_date'} = $transaction->txn_date;
+ $content{'amount'} = $transaction->txn_amount;
+ print "REFUNDING PAYMENT\n";
+ ProcessTransaction($transaction);
+}
+
+sub ProcessTransaction {
+ my $transaction = shift;
+ print "Processing transaction with content:\n".Dumper(\%content)."\n";
+ $transaction->content(%content);
+
+ eval { $transaction->submit(); };
+
+ if ( $@ ) {
+
+ die "Error: $@\n";
+
+ } else {
+
+ if ( $transaction->is_success() ) {
+ print "Card processed successfully: ". $transaction->authorization()."\n";
+ } else {
+ print "Card was rejected: ". $transaction->error_message(). "\n";
+ }
+ }
+}
+
+
+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);
+}
--- /dev/null
+blib*
+Makefile
+Makefile.old
+Build
+Build.bat
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+cover_db
+pod2htm*.tmp
+Business-OnlinePayment-vSecureProcessing-*
\ No newline at end of file
--- /dev/null
+package Business::OnlinePayment::vSecureProcessing;
+
+use strict;
+use Carp;
+use Template;
+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
+
+# mapping out all possible endpoints
+# but this version will only be building out "charge", "void", & "credit"
+my %payment_actions = (
+ 'charge' => {
+ path => '/vsg2/processpayment',
+ },
+ 'void' => {
+ path => '/vsg2/processvoid',
+ },
+ 'refund' => {
+ path => '/vsg2/processrefund',
+ },
+ 'authorize' => {
+ path => '/vsg2/processauth',
+ },
+ 'authorize_cancel' => {
+ path => '/vsg2/processauthcancel',
+ },
+ 'capture' => {
+ path => '/vsg2/processcaptureonly',
+ },
+ 'create_token' => {
+ path => '/vsg2/createtoken',
+ },
+ 'delete_token' => {
+ path => '/vsg2/deletetoken',
+ },
+ 'query_token' => {
+ path => '/vsg2/querytoken',
+ },
+ 'update_exp_date' => {
+ path => '/vsg2/updateexpiration',
+ },
+ 'update_token' => {
+ path => '/vsg2/updatetoken',
+ },
+
+);
+
+my %action_mapping = (
+ 'normal authorization' => 'charge',
+ 'credit' => 'refund',
+ 'authorization only' => 'authorize',
+ 'post authorization' => 'capture',
+ 'reverse authorization' => 'authorize_cancel'
+ # void => void
+);
+
+sub set_defaults {
+ my $self = shift;
+ my %options = @_;
+
+ # inistialize standard B::OP attributes
+ $self->is_success(0);
+ $self->$_( '' ) for qw/authorization
+ result_code
+ error_message
+ server
+ port
+ path
+ server_response/;
+
+ # B::OP creates the following accessors:
+ # server, port, 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
+ /);
+
+ $DEBUG = exists($options{debug}) ? $options{debug} : $DEBUG;
+
+
+
+ $self->server($options{'server'});
+
+ $self->gid($options{'gid'});
+
+ $self->tid($options{'tid'});
+
+ $self->platform($options{'platform'});
+
+ $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();
+
+ {
+ no warnings 'uninitialized';
+
+ # strip non-digits from card number
+ my $card_number = '';
+ if ( $content{card_number} ) {
+ $content{card_number} =~ s/\D//g;
+ }
+
+ # separate month and year values for expiry_date
+ if ( $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);
+ }
+
+ 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/) {
+ $content{'street_number'} = $1;
+ }
+ }
+ warn "Content after cleaning:\n".Dumper(\%content)."\n" if ($DEBUG >2);
+ $self->content(%content);
+}
+
+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->path($payment_actions{ $self->action }{path})
+ unless length($self->path);
+ $self->appid($content{appid}) if (!$self->appid && $content{appid});
+}
+
+sub submit {
+ my $self = shift;
+
+ # inistialize standard B::OP attributes
+ $self->is_success(0);
+ $self->$_( '' ) for qw/authorization
+ result_code
+ error_message
+ server_response/;
+
+ # clean and process the $self->content info
+ $self->process_content();
+ my %content = $self->content;
+ my $action = $self->action();
+
+ my @acceptable_actions = ('charge', 'refund', 'void');
+
+ unless ( grep { $action eq $_ } @acceptable_actions ) {
+ croak "'$action' is not supported at this time.";
+ }
+
+ # fill out the template vars
+ my $template_vars = {
+
+ auth => {
+ platform => $self->platform,
+ userid => $self->userid,
+ gid => $self->gid,
+ tid => $self->tid
+ },
+
+ 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()
+ }
+ };
+
+
+ # create the list of required fields based on the action
+ my @required_fields = qw/ amount /;
+ if ($action eq 'charge') {
+ push(@required_fields, $_) foreach (qw/ account_number cvv exp_month exp_year /);
+ }elsif ($action eq 'void') {
+ push(@required_fields, $_) foreach (qw/ reference_number transaction_date /);
+ }elsif ($action eq 'refund') {
+ push(@required_fields, $_) foreach (qw/ amount account_number exp_month exp_year /);
+ }
+
+ # check the requirements are met.
+ my @missing_fields;
+ foreach my $field (@required_fields) {
+ push(@missing_fields, $field) if (!$template_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 $xml_data;
+ $tt->process( \$xml_template, $template_vars, \$xml_data ) || croak $tt->error();
+
+ warn "XML:\n$xml_data\n" if $DEBUG > 2;
+
+ my $boundary = sprintf('FormBoundary%06d', int(rand(1000000)));
+ # opts for B:OP:HTTPS::https_post
+ my $opts = { headers => {}};
+ $opts->{'Content-Type'} =
+ $opts->{headers}->{'Content-Type'} =
+ "multipart/form-data, boundary=$boundary";
+
+ my $content =
+ "--$boundary\n".
+ "Content-Disposition: form-data; name=\"param\"\n\n".
+ $xml_data."\n".
+ "--$boundary--\n";
+
+ # conform to RFC standards
+ $content =~ s/\n/\r\n/gs;
+
+ my ( $page, $server_response, %headers ) = $self->https_post( $opts, $content );
+
+ # store the server response.
+ $self->server_response($server_response);
+ # parse the result page.
+ $self->parse_response($page);
+
+ 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)
+ $self->error_message(
+ "(HTTPS response: ".$server_response.") ".
+ "(HTTPS headers: ".
+ join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
+ "(Raw HTTPS content: ".$page.")"
+ );
+ } else {
+ my $response_code = $self->response_code() || '';
+ if ($response_code) {
+ $self->error_message(qq|Error code ${response_code} was returned by vSecureProcessing. (enable debugging for raw HTTPS response)|);
+ }else {
+ $self->error_message('No error information was returned by vSecureProcessing (enable debugging for raw HTTPS response)');
+ }
+ }
+ }
+
+}
+
+# read $self->server_response and decipher any errors
+sub parse_response {
+ my $self = shift;
+ my $page = shift;
+
+ if ($self->server_response =~ /^200/) {
+ my $response = XMLin($page);
+ $self->result_code($response->{Status}); # 0 /1
+ $self->response_code($response->{ResponseCode}); # see documentation for translation
+ $self->avs_response($response->{AvsResponse}); # Y / N
+ $self->cvv_response($response->{CvvResponse}); # P / F
+ $self->txn_date($response->{TransactionDate}); # MMDDhhmmss
+ $self->txn_amount($response->{TransactionAmount} / 100); # 00000003500 / 100
+ $self->reference_number($response->{ReferenceNumber});
+
+ $self->is_success($self->result_code() eq '0' ? 1 : 0);
+ if ($self->is_success()) {
+ $self->authorization($response->{AuthIdentificationResponse});
+ }
+ # fill in error_message if there is is an error
+ if ( !$self->is_success && exists($response->{AdditionalResponseData})) {
+ $self->error_message('Error '.$response->{ResponseCode}.': '.$response->{AdditionalResponseData});
+ }elsif ( !$self->is_success && exists($response->{Receipt}) ) {
+ $self->error_message('Error '.$response->{ResponseCode}.': '.(exists($response->{Receipt})) ? $response->{Receipt} : '');
+ }
+
+ }else {
+ $self->is_success(0);
+ $self->error_message('Error communicating with vSecureProcessing server');
+ return;
+ }
+
+
+}
+
+sub _get_xml_template {
+ my $action = shift;
+
+ my $xml_template = q|<Request >
+ <MerchantData>
+ <Platform>[% auth.platform %]</Platform>
+ <UserId>[% auth.userid %]</UserId>
+ <GID>[% auth.gid %]</GID>
+ <Tid>[% auth.tid %]</Tid>
+ </MerchantData>
+ |;
+
+ 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 .= "</Request>";
+ $xml_template =~ s/[\n\t\s]*//g;
+
+ return $xml_template;
+}
+
+sub _get_xml_template_charge {
+ my $xml_template = q|<ProcessPayment>
+ <Amount>[% payment.amount %]</Amount>
+ <Trk1>[% payment.track1 %]</Trk1>
+ <Trk2>[% payment.track2 %]</Trk2>
+ <TypeOfSale>[% payment.type %]</TypeOfSale>
+ <Cf1>[% payment.cf1 %]</Cf1>
+ <Cf2>[% payment.cf2 %]</Cf2>
+ <Cf3>[% payment.cf3 %]</Cf3>
+ <AccountNumber>[% payment.account_number %]</AccountNumber>
+ <ExpirationMonth>[% payment.exp_month %]</ExpirationMonth>
+ <ExpirationYear>[% payment.exp_year %]</ExpirationYear>
+ <Cvv>[% payment.cvv %]</Cvv>
+ <CardHolderFirstName>[% payment.first_name %]</CardHolderFirstName>
+ <CardHolderLastName>[% payment.last_name %]</CardHolderLastName>
+ <AvsZip>[% payment.postal_code %]</AvsZip>
+ <AvsStreet>[% payment.street_address %]</AvsStreet>
+ <IndustryType>
+ <IndType >[% payment.industry_type %]</IndType >
+ <IndInvoice>[% payment.invoice_num %]</IndInvoice>
+ </IndustryType>
+ <ApplicationId>[% payment.appid %]</ApplicationId>
+ <Recurring>[% payment.recurring %]</Recurring>
+ </ProcessPayment>|;
+
+ # other options (that we are not using right now):
+# <Level2PurchaseInfo>
+# <Level2CardType>[% level2.card_type %]</Level2CardType >
+# <PurchaseCode>[% level2.purchase_code %]</PurchaseCode>
+# <ShipToCountryCode>[% level2.country_code %]</ShipToCountryCode>
+# <ShipToPostalCode>[% level2.ship_tp_postal_code %]</ShipToPostalCode>
+# <ShipFromPostalCode>[% level2.ship_from_postal_code %]</ShipFromPostalCode>
+# <SalesTax>[% level2.sales_tax %]</SalesTax>
+# <ProductDescription1>[% level2.product_description1 %]</ProductDescription1>
+# <ProductDescription2>[% level2.product_description2 %]</ProductDescription2>
+# <ProductDescription3>[% level2.product_description3 %]</ProductDescription3>
+# <ProductDescription4>[% level2.product_description4 %]</ProductDescription4>
+# </Level2PurchaseInfo>
+# <Level3PurchaseInfo>
+# <PurchaseOrderNumber>[% level3.purchase_order_num %]</PurchaseOrderNumber>
+# <OrderDate>[% level3.order_date %]</OrderDate>
+# <DutyAmount>[% level3.duty_amount %]</DutyAmount>
+# <AlternateTaxAmount>[% level3.alt_tax_amount %]</AlternateTaxAmount>
+# <DiscountAmount>[% level3.discount_amount %]</DiscountAmount>
+# <FreightAmount>[% level3.freight_amount %]</FreightAmount>
+# <TaxExemptFlag>[% level3.tax_exempt %]</TaxExemptFlag>
+# <LineItemCount>[% level3.line_item_count %]</LineItemCount>
+# <PurchaseItems>
+# [% level3.purchase_items %]
+# </PurchaseItems>
+# </Level3PurchaseInfo>
+
+ return $xml_template;
+}
+
+sub _parse_line_items {
+ my $self = shift;
+ my %content = $self->content();
+
+ return '' if (!$content{'items'});
+
+ my @line_items;
+ my $template = q| <LineItem>
+ <ItemSequenceNumber>[% seq_num %]</ItemSequenceNumber>
+ <ItemCode>[% code %]</ItemCode>
+ <ItemDescription>[% desc %]</ItemDescription>
+ <ItemQuantity>[% qty %]</ItemQuantity>
+ <ItemUnitOfMeasure>[% unit %]</ItemUnitOfMeasure>
+ <ItemUnitCost>[% unit_cost %]</ItemUnitCost>
+ <ItemAmount>[% amount %]</ItemAmount>
+ <ItemDiscountAmount>[% discount_amount %]</ItemDiscountAmount>
+ <ItemTaxAmount>[% tax_amount %]</ItemTaxAmount>
+ <ItemTaxRate>[% tax_rate %]</ItemTaxRate>
+ </LineItem>|;
+
+
+ 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|<ProcessVoid>
+ <Amount>[% payment.amount %]</Amount>
+ <AccountNumber>[% payment.account_number %]</AccountNumber>
+ <ExpirationMonth>[% payment.exp_month %]</ExpirationMonth>
+ <ExpirationYear>[% payment.exp_year %]</ExpirationYear>
+ <ReferenceNumber>[% payment.reference_number %]</ReferenceNumber>
+ <TransactionDate/>
+ <IndustryType1>[% payment.industry_type %]</IndustryType1>
+ <ApplicationId>[% payment.appid %]</ApplicationId>
+ </ProcessVoid>|;
+
+ return $xml_template;
+}
+
+sub _get_xml_template_refund {
+ my $xml_template = q|<ProcessRefund>
+ <Amount>[% payment.amount %]</Amount>
+ <AccountNumber>[% payment.account_number %]</AccountNumber>
+ <ExpirationMonth>[% payment.exp_month %]</ExpirationMonth>
+ <ExpirationYear>[% payment.exp_year %]</ExpirationYear>
+ <ApplicationId>[% payment.appid %]</ApplicationId>
+ </ProcessRefund>|;
+
+ 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__
+
+
+=head1 NAME
+
+Business::OnlinePayment::vSecureProcessing - vSecureProcessing backend for Business::OnlinePayment
+
+=head1 SYNOPSIS
+
+ use Business::OnlinePayment;
+ my %processor_info = (
+ platform => '####',
+ gid => 12345678901234567890,
+ tid => 01,
+ user_id => '####',
+ url => 'www.####.com'
+ );
+ my $tx =
+ new Business::OnlinePayment( "vSecureProcessing", %processor_info);
+ $tx->content(
+ appid => '######',
+ type => 'VISA',
+ 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 DESCRIPTION
+
+For detailed information see L<Business::OnlinePayment>.
+
+=head1 METHODS AND FUNCTIONS
+
+See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> 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
+ credit
+ void
+
+=head1 Setting vSecureProcessing parameters from content(%content)
+
+The following rules are applied to map data to vSecureProcessing parameters
+from content(%content):
+
+ # param => $content{<key>}
+ AccountNumber => 'card_number',
+ Cvv => 'cvv2',
+ ExpirationMonth => \( $month ), # MM from MM/YY of 'expiration'
+ ExpirationYear => \( $year ), # YY from MM/YY of 'expiration'
+ Trk1 => 'track1',
+ Trk2 => 'track2',
+ CardHolderFirstName => 'first_name',
+ CardHolderLastName => 'last_name',
+ Amount => 'amount'
+ AvsStreet => 'address',
+ AvsZip => 'zip',
+ Cf1 => 'UDField1',
+ Cf2 => 'UDField2',
+ IndustryType => 'IndustryInfo',
+
+=head1 NOTE
+
+=head1 COMPATIBILITY
+
+Business::OnlinePayment::vSecureProcessing uses vSecureProcessing XML Document Version: 140901 (September 1, 2014).
+
+See http://www.vsecureprocessing.com/ for more information.
+
+=head1 AUTHORS
+
+Original author: Alex Brelsfoard
+
+Current maintainer: Alex Brelsfoard
+
+=head1 COPYRIGHT
+
+Copyright (c) 2015 Freeside Internet Services, Inc.
+
+All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the same terms as Perl itself.
+
+=head1 ADVERTISEMENT
+
+Need a complete, open-source back-office and customer self-service solution?
+The Freeside software includes support for credit card and electronic check
+processing with vSecureProcessing and over 50 other gateways, invoicing, integrated
+trouble ticketing, and customer signup and self-service web interfaces.
+
+http://freeside.biz/freeside/
+
+=head1 SEE ALSO
+
+perl(1). L<Business::OnlinePayment>.
+
+=cut
+
+
--- /dev/null
+
+sub test_account {
+ # fill all these fields in to test out transactions
+ my %opts = (
+ server =>'', # be sure to leave out the 'https://'
+ platform => '',
+ gid => '',
+ tid => '',
+ userid=> 'name@server.com',
+ port => 443,
+ env => 'test',
+ appid => ''
+ );
+
+ return %opts;
+}
+
+sub expiration_date {
+ my($month, $year) = (localtime)[4,5];
+ $month += 1;
+ $year++; # So we expire next year.
+ $year %= 100;
+
+ return sprintf("%02d/%02d", $month, $year);
+}
+
+
+1;
+
--- /dev/null
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+ use_ok( 'Business::OnlinePayment::vSecureProcessing' ) || print "Bail out!
+";
+}
+
+diag( "Testing Business::OnlinePayment::vSecureProcessing $Business::OnlinePayment::vSecureProcessing::VERSION, Perl $], $^X" );
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+sub not_in_file_ok {
+ my ($filename, %regex) = @_;
+ open( my $fh, '<', $filename )
+ or die "couldn't open $filename for reading: $!";
+
+ my %violated;
+
+ while (my $line = <$fh>) {
+ while (my ($desc, $regex) = each %regex) {
+ if ($line =~ $regex) {
+ push @{$violated{$desc}||=[]}, $.;
+ }
+ }
+ }
+
+ if (%violated) {
+ fail("$filename contains boilerplate text");
+ diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
+ } else {
+ pass("$filename contains no boilerplate text");
+ }
+}
+
+sub module_boilerplate_ok {
+ my ($module) = @_;
+ not_in_file_ok($module =>
+ 'the great new $MODULENAME' => qr/ - The great new /,
+ 'boilerplate description' => qr/Quick summary of what the module/,
+ 'stub function definition' => qr/function[12]/,
+ );
+}
+
+ not_in_file_ok(README =>
+ "The README is used..." => qr/The README is used/,
+ "'version information here'" => qr/to provide version information/,
+ );
+
+ not_in_file_ok(Changes =>
+ "placeholder date/time" => qr(Date/time)
+ );
+
+ module_boilerplate_ok('lib/Business/OnlinePayment/vSecureProcessing.pm');
+
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+ plan( skip_all => "Author tests not required for installation" );
+}
+
+eval "use Test::CheckManifest 0.9";
+plan skip_all => "Test::CheckManifest 0.9 required" if $@;
+ok_manifest();
--- /dev/null
+use strict;
+use warnings;
+use Test::More skip_all => "don't care about POD coverage right now";
+
+# Ensure a recent version of Test::Pod::Coverage
+my $min_tpc = 1.08;
+eval "use Test::Pod::Coverage $min_tpc";
+plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
+ if $@;
+
+# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
+# but older versions don't recognize some common documentation styles
+my $min_pc = 0.18;
+eval "use Pod::Coverage $min_pc";
+plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
+ if $@;
+
+all_pod_coverage_ok();
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+# Ensure a recent version of Test::Pod
+my $min_tp = 1.22;
+eval "use Test::Pod $min_tp";
+plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
+
+all_pod_files_ok();
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use POSIX qw(strftime);
+use Test::More;
+use Business::OnlinePayment;
+require "t/lib/test_account.pl";
+
+my %opts = test_account_or_skip('card');
+
+if (!$opt{'gid'} || !$opt{'appid'}) {
+ plan skip_all => "no test credentials provided; fill out t/lib/test_account.pl to test communication with the gateway.",
+ 1;
+ exit(0);
+}
+
+plan tests => 2;
+
+###
+# Purchase
+###
+my %content = (
+ appid => $opts{'appid'},
+ action => 'Normal Authorization',
+ description => 'Business::OnlinePayment visa test',
+ card_number => '4111111111111111',
+ cvv2 => '111',
+ expiration => expiration_date(),
+ amount => '24.42',
+ name => 'Murphy Law',
+ email => 'fake@acme.com',
+ address => '123 Anystreet',
+ zip => '84058',
+);
+
+my $tx = new Business::OnlinePayment( 'vSecureProcessing', \%opts );
+
+$tx->content( %content,
+ action => 'Normal Authorization' );
+
+$tx->test_transaction(1);
+
+$tx->submit;
+
+is( $tx->is_success, 1, 'purchase' )
+ or diag('Gateway error: '. $tx->error_message);
+
+###
+# Refund
+###
+my $auth = $tx->authorization;
+$tx = new Business::OnlinePayment( 'vSecureProcessing' );
+$tx->content( %content,
+ action => 'Credit',
+ authorization => $auth );
+$tx->test_transaction(1);
+
+$tx->submit;
+
+is( $tx->is_success, 1, 'refund' )
+ or diag('Gateway error: '. $tx->error_message);
+
+1;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use POSIX qw(strftime);
+use Test::More;
+use Business::OnlinePayment;
+require "t/lib/test_account.pl";
+
+my %opts = test_account_or_skip('card');
+
+if (!$opt{'gid'} || !$opt{'appid'}) {
+ plan skip_all => "no test credentials provided; fill out t/lib/test_account.pl to test communication with the gateway.",
+ 1;
+ exit(0);
+}
+
+plan tests => 2;
+my %content = (
+ appid => $opts{'appid'},
+ action => 'Normal Authorization',
+ description => 'Business::OnlinePayment visa test',
+ card_number => '4111111111111112', # trigger failure
+ cvv2 => '111',
+ expiration => expiration_date(),
+ amount => '24.42',
+ name => 'Murphy Law',
+ email => 'fake@acme.com',
+ address => '123 Anystreet',
+ zip => '84058',
+);
+
+my $tx = new Business::OnlinePayment( 'vSecureProcessing', \%opts );
+
+$tx->content( %content );
+
+$tx->test_transaction(1);
+
+$tx->submit;
+
+is( $tx->is_success, 0, 'declined purchase')
+ or diag('Test transaction should have failed, but succeeded');
+is( $tx->failure_status, 'nsf', 'failure status' )
+ or diag('Failure status reported as '.$tx->failure_status);
+
+1;
+++ /dev/null
-#!/usr/bin/perl -w
-
-#
-# Make sure to copy Business::OnlinePayment::vSecureProcessing into its
-# proper system directory (aka /usr/share/perl5/Business/Onlinepayment)
-#
-
-use strict;
-use Business::OnlinePayment;
-
-my %opt = (
- server =>'dvrotsos2.kattare.com',
- platform => 'Buypass',
- gid => '1432479912596791',
- tid => '01',
- userid=> 'tom@yiptv.com',
- port => 443,
- env => 'test'
-);
-
-my $action = shift || 'Normal Authorization';
-my $auth = shift || '';
-
-
-my %content = (
- appid => 'yiptv',
- action => $action,
- auth => $auth,
- description => 'Business::OnlinePayment visa test',
-# card_number => '4007000000027',
- card_number => '4111111111111111',
- cvv2 => '111',
- expiration => expiration_date(),
- amount => '42.24',
- name => 'Murphy Law',
- email => 'fake@acme.com',
- address => '123 Anystreet',
- zip => '84058',
-);
-
-main();
-
-sub main {
- my $transaction = Business::OnlinePayment->new("vSecureProcessing", %opt);
-
- print "MAKING PAYMENT\n";
- MakPayment($transaction);
- $content{'action'} = 'void';
- $content{'reference_number'} = $transaction->authorization;
- $content{'transaction_date'} = $transaction->txn_date;
- $content{'amount'} = $transaction->txn_amount;
- print "VOIDING PAYMENT\n";
- VoidPayment($transaction);
- $content{'action'} = 'Normal Authorization';
- print "MAKING PAYMENT\n";
- MakePayment($transaction);
- print "REFUNDING PAYMENT\n";
- $content{'action'} = 'credit';
- $content{'reference_number'} = $transaction->authorization;
- $content{'transaction_date'} = $transaction->txn_date;
- $content{'amount'} = $transaction->txn_amount;
- refundPayment($transaction);
-}
-
-sub MakePayment {
- $transaction->content(%content);
-
- eval { $transaction->submit(); };
-
- if ( $@ ) {
-
- print "Error: $@\n";
-
- } else {
-
- if ( $transaction->is_success() ) {
- print "Card processed successfully: ". $transaction->authorization()."\n";
- } else {
- print "Card was rejected: ". $transaction->error_message(). "\n";
- }
- }
-}
-
-sub VoidPayment {
-
-}
-
-sub RefundPayment {
-
-}
-
-
-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);
-}
+++ /dev/null
-package Business::OnlinePayment::vSecureProcessing;
-
-use strict;
-use Carp;
-use Template;
-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
-
-# mapping out all possible endpoints
-# but this version will only be building out "charge", "void", & "credit"
-my %payment_actions = (
- 'charge' => {
- path => '/vsg2/processpayment',
- },
- 'void' => {
- path => '/vsg2/processvoid',
- },
- 'refund' => {
- path => '/vsg2/processrefund',
- },
- 'authorize' => {
- path => '/vsg2/processauth',
- },
- 'authorize_cancel' => {
- path => '/vsg2/processauthcancel',
- },
- 'capture' => {
- path => '/vsg2/processcaptureonly',
- },
- 'create_token' => {
- path => '/vsg2/createtoken',
- },
- 'delete_token' => {
- path => '/vsg2/deletetoken',
- },
- 'query_token' => {
- path => '/vsg2/querytoken',
- },
- 'update_exp_date' => {
- path => '/vsg2/updateexpiration',
- },
- 'update_token' => {
- path => '/vsg2/updatetoken',
- },
-
-);
-
-my %action_mapping = (
- 'normal authorization' => 'charge',
- 'credit' => 'refund',
- 'authorization only' => 'authorize',
- 'post authorization' => 'capture',
- 'reverse authorization' => 'authorize_cancel'
- # void => void
-);
-
-sub set_defaults {
- my $self = shift;
- my %options = @_;
-
- # inistialize standard B::OP attributes
- $self->is_success(0);
- $self->$_( '' ) for qw/authorization
- result_code
- error_message
- server
- port
- path
- server_response/;
-
- # B::OP creates the following accessors:
- # server, port, 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 cvv_response
- avs_response risk_score txn_amount txn_date
- /);
-
- $DEBUG = exists($options{debug}) ? $options{debug} : $DEBUG;
-
-
-
- $self->server($options{'server'});
-
- $self->gid($options{'gid'});
-
- $self->tid($options{'tid'});
-
- $self->platform($options{'platform'});
-
- $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();
-
- {
- no warnings 'uninitialized';
-
- # strip non-digits from card number
- my $card_number = '';
- if ( $content{card_number} ) {
- $content{card_number} =~ s/\D//g;
- }
-
- # separate month and year values for expiry_date
- if ( $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);
- }
-
- 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/) {
- $content{'street_number'} = $1;
- }
- }
- warn "Content after cleaning:\n".Dumper(\%content)."\n" if ($DEBUG >2);
- $self->content(%content);
-}
-
-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->path($payment_actions{ $self->action }{path})
- unless length($self->path);
- $self->appid($content{appid}) if (!$self->appid && $content{appid});
-}
-
-sub submit {
- my $self = shift;
-
- # inistialize standard B::OP attributes
- $self->is_success(0);
- $self->$_( '' ) for qw/authorization
- result_code
- error_message
- server_response/;
-
- # clean and process the $self->content info
- $self->process_content();
- my %content = $self->content;
- my $action = $self->action();
-
- my @acceptable_actions = ('charge', 'refund', 'void');
-
- unless ( grep { $action eq $_ } @acceptable_actions ) {
- croak "'$action' is not supported at this time.";
- }
-
- # fill out the template vars
- my $template_vars = {
-
- auth => {
- platform => $self->platform,
- userid => $self->userid,
- gid => $self->gid,
- tid => $self->tid
- },
-
- 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()
- }
- };
-
-
- # create the list of required fields based on the action
- my @required_fields = qw/ amount /;
- if ($action eq 'charge') {
- push(@required_fields, $_) foreach (qw/ account_number cvv exp_month exp_year /);
- }elsif ($action eq 'void') {
- push(@required_fields, $_) foreach (qw/ reference_number transaction_date /);
- }elsif ($action eq 'refund') {
- push(@required_fields, $_) foreach (qw/ amount account_number exp_month exp_year /);
- }
-
- # check the requirements are met.
- my @missing_fields;
- foreach my $field (@required_fields) {
- push(@missing_fields, $field) if (!$template_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 $xml_data;
- $tt->process( \$xml_template, $template_vars, \$xml_data ) || croak $tt->error();
-
- warn "XML:\n$xml_data\n" if $DEBUG > 2;
-
- my $boundary = sprintf('FormBoundary%06d', int(rand(1000000)));
- # opts for B:OP:HTTPS::https_post
- my $opts = { headers => {}};
- $opts->{'Content-Type'} =
- $opts->{headers}->{'Content-Type'} =
- "multipart/form-data, boundary=$boundary";
-
- my $content =
- "--$boundary\n".
- "Content-Disposition: form-data; name=\"param\"\n\n".
- $xml_data."\n".
- "--$boundary--\n";
-
- # conform to RFC standards
- $content =~ s/\n/\r\n/gs;
-
- my ( $page, $server_response, %headers ) = $self->https_post( $opts, $content );
-
- # store the server response.
- $self->server_response($server_response);
- # parse the result page.
- $self->parse_response($page);
-
- 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)
- $self->error_message(
- "(HTTPS response: ".$server_response.") ".
- "(HTTPS headers: ".
- join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
- "(Raw HTTPS content: ".$page.")"
- );
- } else {
- my $response_code = $self->response_code() || '';
- if ($response_code) {
- $self->error_message(qq|Error code ${response_code} was returned by vSecureProcessing. (enable debugging for raw HTTPS response)|);
- }else {
- $self->error_message('No error information was returned by vSecureProcessing (enable debugging for raw HTTPS response)');
- }
- }
- }
-
-}
-
-# read $self->server_response and decipher any errors
-sub parse_response {
- my $self = shift;
- my $page = shift;
-
- if ($self->server_response =~ /^200/) {
- my $response = XMLin($page);
- warn "RESPONSE: \n".Dumper($response)."\n";
- $self->result_code($response->{Status});
- $self->avs_response($response->{AvsResponse});
- $self->cvv_response($response->{CvvResponse});
- $self->txn_date($response->{TransactionDate});
- $self->txn_amount($response->{TransactionAmount} / 100);
- $self->cvv_response($response->{CvvResponse});
- $self->is_success($self->result_code() eq '0' ? 1 : 0);
- if ($self->is_success()) {
- $self->authorization($response->{AuthIdentificationResponse});
- }
- # fill in error_message if there is is an error
- if ( !$self->is_success && exists($response->{ResultCode})) {
- $self->error_message('Error '.$response->{ResponseCode}.': '.$response->{ResultCode});
- }elsif ( !$self->is_success && exists($response->{Receipt}) ) {
- $self->error_message('Error '.$response->{ResponseCode}.': '.(exists($response->{Receipt})) ? $response->{Receipt} : '');
- }
-
- }else {
- $self->is_success(0);
- $self->error_message('Error communicating with vSecureProcessing server');
- return;
- }
-
-
-}
-
-sub _get_xml_template {
- my $action = shift;
-
- my $xml_template = q|<Request >
- <MerchantData>
- <Platform>[% auth.platform %]</Platform>
- <UserId>[% auth.userid %]</UserId>
- <GID>[% auth.gid %]</GID>
- <Tid>[% auth.tid %]</Tid>
- </MerchantData>
- |;
-
- 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 .= "</Request>";
- $xml_template =~ s/[\n\t\s]*//g;
-
- return $xml_template;
-}
-
-sub _get_xml_template_charge {
- my $xml_template = q|<ProcessPayment>
- <Amount>[% payment.amount %]</Amount>
- <Trk1>[% payment.track1 %]</Trk1>
- <Trk2>[% payment.track2 %]</Trk2>
- <TypeOfSale>[% payment.type %]</TypeOfSale>
- <Cf1>[% payment.cf1 %]</Cf1>
- <Cf2>[% payment.cf2 %]</Cf2>
- <Cf3>[% payment.cf3 %]</Cf3>
- <AccountNumber>[% payment.account_number %]</AccountNumber>
- <ExpirationMonth>[% payment.exp_month %]</ExpirationMonth>
- <ExpirationYear>[% payment.exp_year %]</ExpirationYear>
- <Cvv>[% payment.cvv %]</Cvv>
- <CardHolderFirstName>[% payment.first_name %]</CardHolderFirstName>
- <CardHolderLastName>[% payment.last_name %]</CardHolderLastName>
- <AvsZip>[% payment.postal_code %]</AvsZip>
- <AvsStreet>[% payment.street_address %]</AvsStreet>
- <IndustryType>
- <IndType >[% payment.industry_type %]</IndType >
- <IndInvoice>[% payment.invoice_num %]</IndInvoice>
- </IndustryType>
- <ApplicationId>[% payment.appid %]</ApplicationId>
- <Recurring>[% payment.recurring %]</Recurring>
- </ProcessPayment>|;
-
- # other options (that we are not using right now):
-# <Level2PurchaseInfo>
-# <Level2CardType>[% level2.card_type %]</Level2CardType >
-# <PurchaseCode>[% level2.purchase_code %]</PurchaseCode>
-# <ShipToCountryCode>[% level2.country_code %]</ShipToCountryCode>
-# <ShipToPostalCode>[% level2.ship_tp_postal_code %]</ShipToPostalCode>
-# <ShipFromPostalCode>[% level2.ship_from_postal_code %]</ShipFromPostalCode>
-# <SalesTax>[% level2.sales_tax %]</SalesTax>
-# <ProductDescription1>[% level2.product_description1 %]</ProductDescription1>
-# <ProductDescription2>[% level2.product_description2 %]</ProductDescription2>
-# <ProductDescription3>[% level2.product_description3 %]</ProductDescription3>
-# <ProductDescription4>[% level2.product_description4 %]</ProductDescription4>
-# </Level2PurchaseInfo>
-# <Level3PurchaseInfo>
-# <PurchaseOrderNumber>[% level3.purchase_order_num %]</PurchaseOrderNumber>
-# <OrderDate>[% level3.order_date %]</OrderDate>
-# <DutyAmount>[% level3.duty_amount %]</DutyAmount>
-# <AlternateTaxAmount>[% level3.alt_tax_amount %]</AlternateTaxAmount>
-# <DiscountAmount>[% level3.discount_amount %]</DiscountAmount>
-# <FreightAmount>[% level3.freight_amount %]</FreightAmount>
-# <TaxExemptFlag>[% level3.tax_exempt %]</TaxExemptFlag>
-# <LineItemCount>[% level3.line_item_count %]</LineItemCount>
-# <PurchaseItems>
-# [% level3.purchase_items %]
-# </PurchaseItems>
-# </Level3PurchaseInfo>
-
- return $xml_template;
-}
-
-sub _parse_line_items {
- my $self = shift;
- my %content = $self->content();
-
- return '' if (!$content{'items'});
-
- my @line_items;
- my $template = q| <LineItem>
- <ItemSequenceNumber>[% seq_num %]</ItemSequenceNumber>
- <ItemCode>[% code %]</ItemCode>
- <ItemDescription>[% desc %]</ItemDescription>
- <ItemQuantity>[% qty %]</ItemQuantity>
- <ItemUnitOfMeasure>[% unit %]</ItemUnitOfMeasure>
- <ItemUnitCost>[% unit_cost %]</ItemUnitCost>
- <ItemAmount>[% amount %]</ItemAmount>
- <ItemDiscountAmount>[% discount_amount %]</ItemDiscountAmount>
- <ItemTaxAmount>[% tax_amount %]</ItemTaxAmount>
- <ItemTaxRate>[% tax_rate %]</ItemTaxRate>
- </LineItem>|;
-
-
- 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|<ProcessVoid>
- <Amount>[% payment.amount %]</Amount>
- <AccountNumber>[% payment.account_number %]</AccountNumber>
- <ExpirationMonth>[% payment.exp_month %]</ExpirationMonth>
- <ExpirationYear>[% payment.exp_year %]</ExpirationYear>
- <ReferenceNumber>[% payment.reference_number %]</ReferenceNumber>
- <TransactionDate/>
- <IndustryType1>[% payment.industry_type %]</IndustryType1>
- <ApplicationId>[% payment.appid %]</ApplicationId>
- </ProcessVoid>|;
-
- return $xml_template;
-}
-
-sub _get_xml_template_refund {
- my $xml_template = q|<ProcessRefund>
- <Amount>[% payment.amount %]</Amount>
- <AccountNumber>[% payment.account_number %]</AccountNumber>
- <ExpirationMonth>[% payment.exp_month %]</ExpirationMonth>
- <ExpirationYear>[% payment.exp_year %]</ExpirationYear>
- <ApplicationId>[% payment.appid %]</ApplicationId>
- </ProcessRefund>|;
-
- 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__
-
-
-=head1 NAME
-
-Business::OnlinePayment::vSecureProcessing - vSecureProcessing backend for Business::OnlinePayment
-
-=head1 SYNOPSIS
-
- use Business::OnlinePayment;
- my %processor_info = (
- platform => '####',
- gid => 12345678901234567890,
- tid => 01,
- user_id => '####',
- url => 'www.####.com'
- );
- my $tx =
- new Business::OnlinePayment( "vSecureProcessing", %processor_info);
- $tx->content(
- appid => '######',
- type => 'VISA',
- 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 DESCRIPTION
-
-For detailed information see L<Business::OnlinePayment>.
-
-=head1 METHODS AND FUNCTIONS
-
-See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> 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
- credit
- void
-
-=head1 Setting vSecureProcessing parameters from content(%content)
-
-The following rules are applied to map data to vSecureProcessing parameters
-from content(%content):
-
- # param => $content{<key>}
- AccountNumber => 'card_number',
- Cvv => 'cvv2',
- ExpirationMonth => \( $month ), # MM from MM/YY of 'expiration'
- ExpirationYear => \( $year ), # YY from MM/YY of 'expiration'
- Trk1 => 'track1',
- Trk2 => 'track2',
- CardHolderFirstName => 'first_name',
- CardHolderLastName => 'last_name',
- Amount => 'amount'
- AvsStreet => 'address',
- AvsZip => 'zip',
- Cf1 => 'UDField1',
- Cf2 => 'UDField2',
- IndustryType => 'IndustryInfo',
-
-=head1 NOTE
-
-=head1 COMPATIBILITY
-
-Business::OnlinePayment::vSecureProcessing uses vSecureProcessing XML Document Version: 140901 (September 1, 2014).
-
-See http://www.vsecureprocessing.com/ for more information.
-
-=head1 AUTHORS
-
-Original author: Alex Brelsfoard
-
-Current maintainer: Alex Brelsfoard
-
-=head1 COPYRIGHT
-
-Copyright (c) 2015 Freeside Internet Services, Inc.
-
-All rights reserved.
-
-This program is free software; you can redistribute it and/or modify it under
-the same terms as Perl itself.
-
-=head1 ADVERTISEMENT
-
-Need a complete, open-source back-office and customer self-service solution?
-The Freeside software includes support for credit card and electronic check
-processing with vSecureProcessing and over 50 other gateways, invoicing, integrated
-trouble ticketing, and customer signup and self-service web interfaces.
-
-http://freeside.biz/freeside/
-
-=head1 SEE ALSO
-
-perl(1). L<Business::OnlinePayment>.
-
-=cut
-
-