#!/usr/local/bin/perl #?use CGI::Carp qw(fatalsToBrowser); # # Make sure "." is included in the @INC directory list so we can find our packages # my $bFound = 0; my $sDir; foreach $sDir (@INC) { if ($sDir eq ".") { $bFound = 1; last; } } if (!$bFound) { push (@INC, "."); } # # NT systems rarely execute the CGI scripts in the cgi-bin, so attempt to locate # the packages in that case. This may still fail if the cgi-bin folder is named # something else, but at least we will catch 80% of the cases. # push (@INC, "cgi-bin"); require al000000; require as000000; require ad000000; require ae000000; require ao000000; use Socket; use strict; ####################################################### # # # The above is the Path to Perl on the ISP's server # # # # Requires Perl version 5.002 or later # # # ####################################################### ####################################################### # # # CATALOG SHOPPING CART CGI/PERL SCRIPT # # # # Copyright (c) 1998 ACTINIC SOFTWARE LIMITED # # # # written by George Menyhert # # # ####################################################### # # Some global constants # $::prog_name = "ORDERSCR"; # Program Name $::prog_name = $::prog_name; $::prog_ver = '$Revision: 122 $ '; # program version $::prog_ver = substr($::prog_ver, 11); # strip the revision information $::prog_ver =~ s/ \$//; # and the trailers $::FORWARD = 0; # the direction of the order progress $::BACKWARD = 1; $::g_sSmtpServer = "smtp.netcom.net.uk"; # # Global variables # $::g_nCurrentSequenceNumber = -1; $::g_nNextSequenceNumber = -1; Init(); # initialize the script ProcessInput(); # execute the input commands exit; ####################################################### # # Init - initialize the script # ####################################################### sub Init { $::g_bFirstError = $::TRUE; # this flag indicates that the display page method has entered recursion # due to errors - it prevents infinite recursion my (@Response, $Status, $Message); @Response = ReadAndParseInput(); # read the input from the CGI call ($Status, $Message) = @Response; # parse the response if ($Status != $::SUCCESS) { ACTINIC::ReportError($Message, $::g_InputHash{'PATH'}); } @Response = ReadAndParseBlobs(); # read the catalog blobs ($Status, $Message) = @Response; # parse the response if ($Status != $::SUCCESS) { ACTINIC::ReportError($Message, $::g_InputHash{'PATH'}); } # # initialize some global hashes (must come after prompt file) # ACTINIC::InitMonthMap(); } ####################################################### # # ReadAndParseInput - read the input and parse it # # Expects: $ENV to be defined # # Affects: @::g_PageList - global list of pages visited # # Returns: ($ReturnCode, $Error) # if $ReturnCode = $FAILURE, the operation failed # for the reason specified in $Error # Otherwise everything is OK # ####################################################### sub ReadAndParseInput { my ($status, $message, $temp); ($status, $message, $::g_OriginalInputData, $temp, %::g_InputHash) = ACTINIC::ReadAndParseInput(); if ($status != $::SUCCESS) { return ($status, $message, 0, 0); } # # parse the ref page list # ($status, $message, @::g_PageList) = ACTINIC::ProcessReferencePageData(%::g_InputHash); if ($status != $::SUCCESS) { return ($status, $message, 0, 0); } ####### # retrieve the web site url ####### ($status, $message, $::g_sWebSiteUrl, $::g_sContentUrl) = ACTINIC::GetWebSiteURL(@::g_PageList); if ($status != $::SUCCESS) { return ($status, $message, 0, 0); } return ($::SUCCESS, "", 0, 0); } ####################################################### # # ReadAndParseBlobs - read the blobs and store them # in global data structures # # Expects: %::g_InputHash - the input hash table should # be defined # # Affects: $g_sCartId - the cart ID for this customer # %g_BillContact - the invoice contact information # %g_ShipContact - the delivery contact information # %g_ShipInfo - the shipping information # %g_TaxInfo - the tax information # %g_GeneralInfo - general information # %g_PaymentInfo - payment information # # Returns: ($ReturnCode, $Error) # if $ReturnCode = $FAILURE, the operation failed # for the reason specified in $Error # Otherwise everything is OK # ####################################################### sub ReadAndParseBlobs { my ($Status, $Message, @Response, $sPath); $sPath = $::g_InputHash{"PATH"}; # get the path to the web site @Response = ACTINIC::ReadCatalogFile($sPath); # read the catalog blob ($Status, $Message) = @Response; # parse the response if ($Status != $::SUCCESS) # on error, bail { return (@Response); } @Response = ACTINIC::ReadSetupFile($sPath); # read the setup ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { return (@Response); } # # read the phase blob # @Response = ACTINIC::ReadPhaseFile($sPath); if ($Response[0] != $::SUCCESS) { return (@Response); } # # read the prompt blob # @Response = ACTINIC::ReadPromptFile($sPath); if ($Response[0] != $::SUCCESS) { return (@Response); } # # read the cart ID # @Response = ActinicOrder::GetCartID($::g_InputHash{"PATH"}); # retrieve the cart ID ($Status, $Message, $::g_sCartId) = @Response; if ($Status != $::SUCCESS) # error out { return (@Response); } # # read the checkout status # my ($pBillContact, $pShipContact, $pShipInfo, $pTaxInfo, $pGeneralInfo, $pPaymentInfo); @Response = ActinicOrder::RetrieveCheckoutStatus($sPath, $::g_sCartId); if ($Response[0] != $::SUCCESS) { return (@Response); } ($Status, $Message, $pBillContact, $pShipContact, $pShipInfo, $pTaxInfo, $pGeneralInfo, $pPaymentInfo) = @Response; %::g_BillContact = %$pBillContact; # copy the hashes to global tables %::g_ShipContact = %$pShipContact; %::g_ShipInfo = %$pShipInfo; %::g_TaxInfo = %$pTaxInfo; %::g_GeneralInfo = %$pGeneralInfo; %::g_PaymentInfo = %$pPaymentInfo; return ($::SUCCESS, "", 0, 0); } ####################################################### # # ProcessInput - read the input parameters and # call the appropriate function in response # ####################################################### sub ProcessInput { my (@Response); # # Find out where we are # $::g_nCurrentSequenceNumber = $::g_InputHash{'SEQUENCE'}; # determine the sequence number of the calling page if (!defined $::g_nCurrentSequenceNumber) # if we are at the beginning { $::g_nCurrentSequenceNumber = $::STARTSEQUENCE; } # # Get the button names # my ($sStartButton, $sDoneButton, $sNextButton, $sFinishButton, $sBackButton, $sCancelButton); $sStartButton = ACTINIC::GetPhrase(-1, 113); $sDoneButton = ACTINIC::GetPhrase(-1, 114); $sNextButton = ACTINIC::GetPhrase(-1, 502); $sBackButton = ACTINIC::GetPhrase(-1, 503); $sFinishButton = ACTINIC::GetPhrase(-1, 504); $sCancelButton = ACTINIC::GetPhrase(-1, 505); # # If the progress is forward # my ($sHTML, $sAction, $eDirection); $sAction = $::g_InputHash{'ACTION'}; if ($sAction =~ m/$sStartButton/i || $sAction =~ m/$sNextButton/i || $sAction =~ m/$sFinishButton/i || $sAction =~ m/AUTHORIZE/i || $sAction =~ m/RECORDORDER/i) { $eDirection = $::FORWARD; } elsif ($sAction =~ m/$sBackButton/i) # move backwards { $eDirection = $::BACKWARD; } elsif ($sAction =~ m/$sDoneButton/i) # return to the catalog header page { my ($sRefPage) = $::g_sWebSiteUrl . $::g_InputHash{'HOME'}; # find the catalog page @Response = ACTINIC::BounceToPagePlain(0, undef, undef, \@::g_PageList, $::g_sWebSiteUrl, $::g_sContentUrl, $::g_pSetupBlob, $sRefPage, \%::g_InputHash); if ($Response[0] != $::SUCCESS) { ACTINIC::ReportError($Response[1], $::g_InputHash{'PATH'}); return; } $sHTML = $Response[2]; goto THEEND; } else # cancel { $sHTML = GetCancelPage(); goto THEEND; } # # Validate and store the current page information # @Response = ValidateInput($eDirection); if ($Response[0] == $::BADDATA) { $sHTML = $Response[1]; goto THEEND; } elsif ($Response[0] != $::SUCCESS) { ACTINIC::ReportError($Response[1], $::g_InputHash{'PATH'}); return; } # # Determine the next step # if ($eDirection == $::FORWARD) { $::g_nNextSequenceNumber = $::g_nCurrentSequenceNumber + 1; } else { $::g_nNextSequenceNumber = $::g_nCurrentSequenceNumber - 1; } # # Display the next page # @Response = DisplayPage("", $::g_nNextSequenceNumber, $eDirection); if ($Response[0] != $::SUCCESS) { ACTINIC::ReportError($Response[1], $::g_InputHash{'PATH'}); return; } $sHTML = $Response[2]; THEEND: ACTINIC::UpdateDisplay($sHTML, $::g_OriginalInputData, \@::g_PageList); } ####################################################### # # ValidateInput - validate and save any input from the # current page. If the input parameter is false, # the validation is skipped. This occurs when we are # moving backwards. # # Params: 0 - the direction # # Returns: 0 - status ($::BADDATA if the validation # fails) # 1 - error message (HTML of error page # if status is $::BADDATA) # ####################################################### sub ValidateInput { my ($eDirection); if ($#_ != 0) { $eDirection = $::FORWARD; } ($eDirection) = @_; my ($bActuallyValidate) = ($eDirection == $::FORWARD); # only validate text when moving forward my (@Response); # # special case - startup # if ($::g_nCurrentSequenceNumber == $::STARTSEQUENCE) # if this is startup { @Response = ValidateStart($bActuallyValidate); # validate the input/cart settings return (@Response); } else { # # get the phase list for this page # my ($sPhaseList) = $$::g_pPhaseList{$::g_nCurrentSequenceNumber}; # # validate each phase in the current block # my (@Phases) = split (//, $sPhaseList); my ($nPhase, $sError); foreach $nPhase (@Phases) { # # dispatch the page-specific data # if ($nPhase == $::BILLCONTACTPHASE) { $sError .= ValidateBill($bActuallyValidate); } elsif ($nPhase == $::SHIPCONTACTPHASE) { $sError .= ValidateShipContact($bActuallyValidate); } elsif ($nPhase == $::SHIPCHARGEPHASE) { $sError .= ValidateShipCharge($bActuallyValidate); } elsif ($nPhase == $::TAXCHARGEPHASE) { $sError .= ActinicOrder::ValidateTax($bActuallyValidate); } elsif ($nPhase == $::GENERALPHASE) { $sError .= ValidateGeneral($bActuallyValidate); } elsif ($nPhase == $::PAYMENTPHASE) { $sError .= ValidatePayment($bActuallyValidate); } elsif ($nPhase == $::COMPLETEPHASE) # applet page { # no-op # here when returning from a remote OCC site (SEQUENCE number is 3) } elsif ($nPhase == $::RECEIPTPHASE) # receipt page { # no-op } elsif ($nPhase == $::PRELIMINARYINFOPHASE) { $sError .= ActinicOrder::ValidatePreliminaryInfo($bActuallyValidate); } } if ($sError ne '') # if an error occured { @Response = DisplayPage($sError, $::g_nCurrentSequenceNumber, $eDirection); # redisplay this page with the error messages if ($Response[0] != $::SUCCESS) { return (@Response); } $Response[0] = $::BADDATA; $Response[1] = $Response[2]; return (@Response); } } return (UpdateCheckoutRecord()); } ####################################################### # # ValidateStart - validate the beginning of the order # process # # Params: 0 - $::TRUE if the data should be validated # # Returns: 0 - status ($::BADDATA if the validation # fails) # 1 - error message (HTML of error page # if status is $::BADDATA) # ####################################################### sub ValidateStart { if ($#_ != 0) { return ($::FAILURE, ACTINIC::GetPhrase(-1, 12, 'ValidateStart'), 0, 0); } my ($bActuallyValidate) = @_; # # validate the input (if necessary) # if (!$bActuallyValidate) { return ($::SUCCESS, "", 0, 0); } my ($nLineCount, @Response, $Status, $Message); @Response = ActinicOrder::CountCartItems($::g_sCartId, $::g_InputHash{'PATH'}); # count the items in the cart ($Status, $Message, $nLineCount) = @Response; if ($Status == $::FAILURE) # error out { return (@Response); } if ($nLineCount <= 0) # if the cart is empty { my ($sLocalPage, $sHTML); $sLocalPage = pop @::g_PageList; # get the last item in the list push (@::g_PageList, $sLocalPage); # put the item back push (@::g_PageList, $sLocalPage); # dup the item (this allows us to bounce properly using ReturnToLastPage) @Response = ACTINIC::ReturnToLastPage(5, "" . ACTINIC::GetPhrase(-1, 44, $::g_sCart, $::g_sCart) . "", $$::g_pSetupBlob{CHECKOUT_DESCRIPTION}, \@::g_PageList, $::g_sWebSiteUrl, $::g_sContentUrl, $::g_pSetupBlob, %::g_InputHash); # bounce back in the broswer ($Status, $Message, $sHTML) = @Response; # parse the response if ($Status != $::SUCCESS) # error out { return (@Response); } return ($::BADDATA, $sHTML, 0, 0); # return the goods } return ($::SUCCESS, "", 0, 0); } ####################################################### # # ValidateBill - validate the billing contact data # # Params: 0 - $::TRUE if the data should be validated # # Returns: 0 - error message # ####################################################### sub ValidateBill { if ($#_ != 0) { ACTINIC::ReportError(ACTINIC::GetPhrase(-1, 12, 'ValidateBill')); } my ($bActuallyValidate) = @_; # # gather the data # $::g_BillContact{'SALUTATION'} = $::g_InputHash{'INVOICESALUTATION'}; $::g_BillContact{'NAME'} = $::g_InputHash{'INVOICENAME'}; $::g_BillContact{'JOBTITLE'} = $::g_InputHash{'INVOICEJOBTITLE'}; $::g_BillContact{'COMPANY'} = $::g_InputHash{'INVOICECOMPANY'}; $::g_BillContact{'ADDRESS1'} = $::g_InputHash{'INVOICEADDRESS1'}; $::g_BillContact{'ADDRESS2'} = $::g_InputHash{'INVOICEADDRESS2'}; $::g_BillContact{'ADDRESS3'} = $::g_InputHash{'INVOICEADDRESS3'}; $::g_BillContact{'ADDRESS4'} = $::g_InputHash{'INVOICEADDRESS4'}; $::g_BillContact{'POSTALCODE'} = $::g_InputHash{'INVOICEPOSTALCODE'}; $::g_BillContact{'COUNTRY'} = $::g_InputHash{'INVOICECOUNTRY'}; $::g_BillContact{'PHONE'} = $::g_InputHash{'INVOICEPHONE'}; $::g_BillContact{'FAX'} = $::g_InputHash{'INVOICEFAX'}; $::g_BillContact{'EMAIL'} = $::g_InputHash{'INVOICEEMAIL'}; $::g_BillContact{'USERDEFINED'} = $::g_InputHash{'INVOICEUSERDEFINED'}; $::g_BillContact{'MOVING'} = ($::g_InputHash{'INVOICEMOVING'} ne "") ? $::TRUE : $::FALSE; $::g_BillContact{'PRIVACY'} = ($::g_InputHash{'INVOICEPRIVACY'} ne "") ? $::TRUE : $::FALSE; $::g_BillContact{'SEPARATE'} = ($::g_InputHash{'SEPARATESHIP'} ne "") ? $::TRUE : $::FALSE; # # clean up the input # ACTINIC::TrimHashEntries(\%::g_BillContact); # # validate field input # my ($sError); if (!$bActuallyValidate) { return ($sError); } # # validate field requirement status # my (@Response); if (ACTINIC::IsPromptRequired(0, 0) && $::g_BillContact{'SALUTATION'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 0); } if (ACTINIC::IsPromptRequired(0, 1) && $::g_BillContact{'NAME'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 1); } if (ACTINIC::IsPromptRequired(0, 2) && $::g_BillContact{'JOBTITLE'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 2); } if (ACTINIC::IsPromptRequired(0, 3) && $::g_BillContact{'COMPANY'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 3); } if (ACTINIC::IsPromptRequired(0, 4) && $::g_BillContact{'ADDRESS1'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 4); } if (ACTINIC::IsPromptRequired(0, 5) && $::g_BillContact{'ADDRESS2'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 5); } if (ACTINIC::IsPromptRequired(0, 6) && $::g_BillContact{'ADDRESS3'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 6); } if (ACTINIC::IsPromptRequired(0, 7) && $::g_BillContact{'ADDRESS4'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 7); } if (ACTINIC::IsPromptRequired(0, 8) && $::g_BillContact{'POSTALCODE'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 8); } if (ACTINIC::IsPromptRequired(0, 9) && $::g_BillContact{'COUNTRY'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 9); } if (ACTINIC::IsPromptRequired(0, 10) && $::g_BillContact{'PHONE'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 10); } if (ACTINIC::IsPromptRequired(0, 11) && $::g_BillContact{'FAX'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 11); } if (ACTINIC::IsPromptRequired(0, 12) && $::g_BillContact{'EMAIL'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 12); } if (ACTINIC::IsPromptRequired(0, 14) && $::g_BillContact{'USERDEFINED'} eq "") { $sError .= ACTINIC::GetRequiredMessage(0, 14); } if ($sError ne "") # if there are any errors { # indicate the problem phase $sError = "" . ACTINIC::GetPhrase(-1, 147) . "
" . $sError . "

"; } return ($sError); } ####################################################### # # ValidateShipContact - validate the shipping contact data # # Params: 0 - $::TRUE if the data should be validated # # Returns: 0 - error message # ####################################################### sub ValidateShipContact { if ($#_ != 0) { ACTINIC::ReportError(ACTINIC::GetPhrase(-1, 12, 'ValidateShipContact')); } my ($bActuallyValidate) = @_; # # If the ship separate flag has been disabled, then assume that the shipping address is required # my ($bDeliverAddressRequired) = ACTINIC::IsPromptHidden(0, 16); # # gather the data # # # if they indicated that they are shipping to the same address, set the shipping address to the billing address # and mark it as finished. Otherwise mark it as unfinished # # Presnet: handle reversal of check box action - start of un-comment # my $bCheckReversed = (defined $$::g_pSetupBlob{'REVERSE_ADDRESS_CHECK'} && $$::g_pSetupBlob{'REVERSE_ADDRESS_CHECK'}); if (((!$bCheckReversed && !$::g_BillContact{'SEPARATE'}) || ($bCheckReversed && $::g_BillContact{'SEPARATE'})) && # ship address = bill address !$bDeliverAddressRequired) # # Presnet: end of un-comment / start of comment-out # # if (!$::g_BillContact{'SEPARATE'}) || # !$bDeliverAddressRequired) # # Presnet: end of comment-out # { $::g_ShipContact{'SALUTATION'} = $::g_BillContact{'SALUTATION'}; $::g_ShipContact{'NAME'} = $::g_BillContact{'NAME'}; $::g_ShipContact{'JOBTITLE'} = $::g_BillContact{'JOBTITLE'}; $::g_ShipContact{'COMPANY'} = $::g_BillContact{'COMPANY'}; $::g_ShipContact{'ADDRESS1'} = $::g_BillContact{'ADDRESS1'}; $::g_ShipContact{'ADDRESS2'} = $::g_BillContact{'ADDRESS2'}; $::g_ShipContact{'ADDRESS3'} = $::g_BillContact{'ADDRESS3'}; $::g_ShipContact{'ADDRESS4'} = $::g_BillContact{'ADDRESS4'}; $::g_ShipContact{'POSTALCODE'} = $::g_BillContact{'POSTALCODE'}; $::g_ShipContact{'COUNTRY'} = $::g_BillContact{'COUNTRY'}; $::g_ShipContact{'PHONE'} = $::g_BillContact{'PHONE'}; $::g_ShipContact{'FAX'} = $::g_BillContact{'FAX'}; $::g_ShipContact{'EMAIL'} = $::g_BillContact{'EMAIL'}; $::g_ShipContact{'USERDEFINED'} = $::g_BillContact{'USERDEFINED'}; } else { $::g_ShipContact{'SALUTATION'} = $::g_InputHash{'DELIVERSALUTATION'}; $::g_ShipContact{'NAME'} = $::g_InputHash{'DELIVERNAME'}; $::g_ShipContact{'JOBTITLE'} = $::g_InputHash{'DELIVERJOBTITLE'}; $::g_ShipContact{'COMPANY'} = $::g_InputHash{'DELIVERCOMPANY'}; $::g_ShipContact{'ADDRESS1'} = $::g_InputHash{'DELIVERADDRESS1'}; $::g_ShipContact{'ADDRESS2'} = $::g_InputHash{'DELIVERADDRESS2'}; $::g_ShipContact{'ADDRESS3'} = $::g_InputHash{'DELIVERADDRESS3'}; $::g_ShipContact{'ADDRESS4'} = $::g_InputHash{'DELIVERADDRESS4'}; $::g_ShipContact{'POSTALCODE'} = $::g_InputHash{'DELIVERPOSTALCODE'}; $::g_ShipContact{'COUNTRY'} = $::g_InputHash{'DELIVERCOUNTRY'}; $::g_ShipContact{'PHONE'} = $::g_InputHash{'DELIVERPHONE'}; $::g_ShipContact{'FAX'} = $::g_InputHash{'DELIVERFAX'}; $::g_ShipContact{'EMAIL'} = $::g_InputHash{'DELIVEREMAIL'}; $::g_ShipContact{'USERDEFINED'} = $::g_InputHash{'DELIVERUSERDEFINED'}; } $::g_ShipContact{'PRIVACY'} = $::g_BillContact{'PRIVACY'}; # the privacy setting is always the same for delivery and invoice contacts # # clean up the input # ACTINIC::TrimHashEntries(\%::g_ShipContact); # # validate field input # my ($sError); if (!$bActuallyValidate || # if we are not validating, or !$::g_BillContact{'SEPARATE'}) # if the delivery address is ignored { return ($sError); # don't do the validation } # # validate field requirement status # my (@Response); if (ACTINIC::IsPromptRequired(1, 0) && $::g_ShipContact{'SALUTATION'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 0); } if (ACTINIC::IsPromptRequired(1, 1) && $::g_ShipContact{'NAME'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 1); } if (ACTINIC::IsPromptRequired(1, 2) && $::g_ShipContact{'JOBTITLE'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 2); } if (ACTINIC::IsPromptRequired(1, 3) && $::g_ShipContact{'COMPANY'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 3); } if (ACTINIC::IsPromptRequired(1, 4) && $::g_ShipContact{'ADDRESS1'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 4); } if (ACTINIC::IsPromptRequired(1, 5) && $::g_ShipContact{'ADDRESS2'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 5); } if (ACTINIC::IsPromptRequired(1, 6) && $::g_ShipContact{'ADDRESS3'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 6); } if (ACTINIC::IsPromptRequired(1, 7) && $::g_ShipContact{'ADDRESS4'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 7); } if (ACTINIC::IsPromptRequired(1, 8) && $::g_ShipContact{'POSTALCODE'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 8); } if (ACTINIC::IsPromptRequired(1, 9) && $::g_ShipContact{'COUNTRY'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 9); } if (ACTINIC::IsPromptRequired(1, 10) && $::g_ShipContact{'PHONE'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 10); } if (ACTINIC::IsPromptRequired(1, 11) && $::g_ShipContact{'FAX'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 11); } if (ACTINIC::IsPromptRequired(1, 12) && $::g_ShipContact{'EMAIL'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 12); } if (ACTINIC::IsPromptRequired(1, 13) && $::g_ShipContact{'USERDEFINED'} eq "") { $sError .= ACTINIC::GetRequiredMessage(1, 13); } if ($sError ne "") # if there are any errors { # indicate the problem phase $sError = "" . ACTINIC::GetPhrase(-1, 148) . "

" . $sError . "

"; } return ($sError); } ####################################################### # # ValidateShipCharge - validate the shipping charge data # # Params: 0 - $::TRUE if the data should be validated # # Returns: 0 - error message # # Expects: $::g_ShipInfo to be defined # ####################################################### sub ValidateShipCharge { if ($#_ != 0) { ACTINIC::ReportError(ACTINIC::GetPhrase(-1, 12, 'ValidateShipCharge')); } my ($bActuallyValidate) = @_; # # retrieve and validate the shipping values # my ($sError); if ($$::g_pSetupBlob{MAKE_SHIPPING_CHARGE} && # shipping is enabled and !ActinicOrder::IsPhaseHidden($::SHIPCHARGEPHASE)) # it is not hidden { # # do advanced shipping validation - only report validation problems if we are actually validating # my @Response = ActinicOrder::CallShippingPlugIn(); if ($bActuallyValidate) # if we are actually validating the input and { if ($Response[0] != $::SUCCESS) # the script failed { $sError .= "" . ACTINIC::GetPhrase(-1, 102) . " - ". $Response[1] . "
\n"; } elsif (${$Response[2]}{ValidateFinalInput} != $::SUCCESS) # the validation failed { # return the error $sError .= "" . ACTINIC::GetPhrase(-1, 102) . " - ". ${$Response[3]}{ValidateFinalInput} . "
\n"; } } } # # retrieve and validate the user defined shipping # $::g_ShipInfo{'USERDEFINED'} = $::g_InputHash{'SHIPUSERDEFINED'}; # # clean up the input # ACTINIC::TrimHashEntries(\%::g_ShipInfo); if ($bActuallyValidate && ACTINIC::IsPromptRequired(2, 1) && $::g_ShipInfo{'USERDEFINED'} eq "") { $sError .= ACTINIC::GetRequiredMessage(2, 1); } if ($sError ne "") # if there are any errors { # indicate the problem phase $sError = "" . ACTINIC::GetPhrase(-1, 149) . "

" . $sError . "

"; } return ($sError); } ####################################################### # # ValidateGeneral - validate the general info data # # Params: 0 - $::TRUE if the data should be validated # # Returns: 0 - error message # ####################################################### sub ValidateGeneral { if ($#_ != 0) { ACTINIC::ReportError(ACTINIC::GetPhrase(-1, 12, 'ValidateGeneral')); } my ($bActuallyValidate) = @_; # # gather the data # $::g_GeneralInfo{'HOWFOUND'} = $::g_InputHash{'GENERALHOWFOUND'}; $::g_GeneralInfo{'WHYBUY'} = $::g_InputHash{'GENERALWHYBUY'}; $::g_GeneralInfo{'USERDEFINED'} = $::g_InputHash{'GENERALUSERDEFINED'}; # # clean up the input # ACTINIC::TrimHashEntries(\%::g_GeneralInfo); # # validate field input # my ($sError); if (!$bActuallyValidate) { return ($sError); } # # validate field requirement status # my (@Response); if (ACTINIC::IsPromptRequired(4, 0) && $::g_GeneralInfo{'HOWFOUND'} eq "") { $sError .= ACTINIC::GetRequiredMessage(4, 0); } if (ACTINIC::IsPromptRequired(4, 1) && $::g_GeneralInfo{'WHYBUY'} eq "") { $sError .= ACTINIC::GetRequiredMessage(4, 1); } if (ACTINIC::IsPromptRequired(4, 2) && $::g_GeneralInfo{'USERDEFINED'} eq "") { $sError .= ACTINIC::GetRequiredMessage(4, 2); } if ($sError ne "") # if there are any errors { # indicate the problem phase $sError = "" . ACTINIC::GetPhrase(-1, 151) . "

" . $sError . "

"; } return ($sError); } ####################################################### # # ValidatePayment - validate the payment info data # # Params: 0 - $::TRUE if the data should be validated # # Returns: 0 - error message # ####################################################### sub ValidatePayment { if ($#_ != 0) { ACTINIC::ReportError(ACTINIC::GetPhrase(-1, 12, 'ValidatePayment')); } my ($bActuallyValidate) = @_; # # gather the data # $::g_PaymentInfo{'METHOD'} = $::g_InputHash{'PAYMENTMETHOD'}; $::g_PaymentInfo{'USERDEFINED'} = $::g_InputHash{'PAYMENTUSERDEFINED'}; $::g_PaymentInfo{'PONO'} = $::g_InputHash{'PAYMENTPONO'}; $::g_PaymentInfo{'CARDTYPE'} = $::g_InputHash{'PAYMENTCARDTYPE'}; $::g_PaymentInfo{'CARDNUMBER'} = $::g_InputHash{'PAYMENTCARDNUMBER'}; $::g_PaymentInfo{'CARDISSUE'} = $::g_InputHash{'PAYMENTCARDISSUE'}; $::g_PaymentInfo{'EXPMONTH'} = $::g_InputHash{'PAYMENTEXPMONTH'}; $::g_PaymentInfo{'EXPYEAR'} = $::g_InputHash{'PAYMENTEXPYEAR'}; $::g_PaymentInfo{'STARTMONTH'} = $::g_InputHash{'PAYMENTSTARTMONTH'}; $::g_PaymentInfo{'STARTYEAR'} = $::g_InputHash{'PAYMENTSTARTYEAR'}; # # clean up the input # ACTINIC::TrimHashEntries(\%::g_PaymentInfo); # # if there is only one payment option, take it. # if there are no payment options, assume pre-pay # EnsurePaymentSelection(); # # validate field input # my ($sError); if (!$bActuallyValidate) { return ($sError); } # # validate field requirement status # my (@Response); if (ACTINIC::IsPromptRequired(5, 6) && $::g_PaymentInfo{'PONO'} eq "") { $sError .= ACTINIC::GetRequiredMessage(5, 6); } if (ACTINIC::IsPromptRequired(5, 7) && $::g_PaymentInfo{'USERDEFINED'} eq "") { $sError .= ACTINIC::GetRequiredMessage(5, 7); } # # validate credit card fields if they exist # my ($ePaymentMethod) = ActinicOrder::PaymentStringToEnum($::g_PaymentInfo{'METHOD'}); # the payment method is stored as "ENUMERATEDID:DESCRIPTION" if ($ePaymentMethod == $::PAYMENT_CREDIT_CARD && # only validate the cc info if they paid with a CC and !$$::g_pSetupBlob{USE_DH} && # the Java encryption is off and $$::g_pSetupBlob{OCC} == -1) # we are not in on-line CC mode { # # validation rules # # CARDTYPE - required # CARDNUMBER - required and checksum # CARDISSUE - required? (depends are card type) and 1-255 # EXPMONTH - required and combined with EXPYEAR must be > this month # EXPYEAR - required # STARTMONTH - required? (depends are card type) and combined with STARTYEAR must be <= this month # STARTYEAR - required? (depends are card type) # if ($::g_PaymentInfo{'CARDTYPE'} eq "") # the card type is required { $sError .= ACTINIC::GetRequiredMessage(5, 1); } # # locate the information for the card in question # my ($nIndex, $sCCID, $bFound); $bFound = $::FALSE; for ($nIndex = 0; $nIndex < 12; $nIndex++) # search for the selected card in the stack { $sCCID = sprintf('CC%d', $nIndex); # format the card key name if ($$::g_pSetupBlob{$sCCID} eq $::g_PaymentInfo{'CARDTYPE'}) # if this was the card of interest, use it { $bFound = $::TRUE; # note that the card was found last; # break out } } if (!$bFound) # if the matching card was found, report the error { $sError .= "" . ACTINIC::GetPhrase(5, 1) . "" . ACTINIC::GetPhrase(-1, 107, $::g_PaymentInfo{'CARDTYPE'}) . "
\n" } # # check the credit card number # my ($nNumber) = $::g_PaymentInfo{'CARDNUMBER'}; $nNumber =~ s/\s//g; # remove any white space $nNumber =~ s/-//g; # remove any dashes if ($nNumber eq "") # the card number is required { $sError .= ACTINIC::GetRequiredMessage(5, 2); } if ($nNumber =~ /[^0-9]/) # the card number should only contain numbers { $sError .= "" . ACTINIC::GetPhrase(5, 2) . "" . ACTINIC::GetPhrase(-1, 108) . "
\n" } my ($nCheckSum, $nDigitCount) = (0, 0); my ($nDigit, $nCheck); for($nIndex = (length $nNumber) - 1; $nIndex >= 0; $nIndex--) { $nDigit = substr($nNumber, $nIndex, 1); # get this digit $nCheck = (1 + $nDigitCount++ % 2) * # calculate the checksum $nDigit; if ( $nCheck >= 10) { $nCheck++; } $nCheckSum += $nCheck; } if (($nCheckSum % 10) != 0) # if the checksum failed { $sError .= "" . ACTINIC::GetPhrase(5, 2) . "" . ACTINIC::GetPhrase(-1, 109) . "
\n" } # # validate the issue number # if ($$::g_pSetupBlob{$sCCID . '_ISSUENUMBERFLAG'}) # this credit card requires an issue number { if ($::g_PaymentInfo{'CARDISSUE'} eq "" || # the issue number must exist and be between 1-255 $::g_PaymentInfo{'CARDISSUE'} < 1 || $::g_PaymentInfo{'CARDISSUE'} > 255) { $sError .= ACTINIC::GetPhrase(-1, 110, "" . ACTINIC::GetPhrase(5, 5) . "", $::g_PaymentInfo{'CARDTYPE'}) . "
\n" } } else # the issue number is not required, make sure it is blank { $::g_PaymentInfo{'CARDISSUE'} = ""; } # # validate the start date # my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $sDate); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); # platform independent time $mon++; # make month 1 based $year += 1900; # make year AD based if ($$::g_pSetupBlob{$sCCID . '_STARTDATEFLAG'}) # this credit card requires a start date { if ($::g_PaymentInfo{'STARTMONTH'} eq "" || # if the start month or year are blank $::g_PaymentInfo{'STARTYEAR'} eq "") { $sError .= ACTINIC::GetRequiredMessage(5, 3); #point out that they are required } if ($::g_PaymentInfo{'STARTYEAR'} == $year && # if the card has not started yet $::g_PaymentInfo{'STARTMONTH'} > $mon) { $sError .= "" . ACTINIC::GetPhrase(5, 3) . "" . ACTINIC::GetPhrase(-1, 111) . "
\n" } } else # the start date is not required, make sure it is blank { $::g_PaymentInfo{'STARTMONTH'} = ""; $::g_PaymentInfo{'STARTYEAR'} = ""; } # # validate the expiration date # if ($::g_PaymentInfo{'EXPMONTH'} eq "" || # if the expiration month or year are blank $::g_PaymentInfo{'EXPYEAR'} eq "") { $sError .= ACTINIC::GetRequiredMessage(5, 4); # point out that they are required } if ($::g_PaymentInfo{'EXPYEAR'} == $year && # if the card has expired $::g_PaymentInfo{'EXPMONTH'} < $mon) { $sError .= "" . ACTINIC::GetPhrase(5, 4) . "" . ACTINIC::GetPhrase(-1, 112) . "
\n" } } else { $::g_PaymentInfo{'CARDTYPE'} = ""; $::g_PaymentInfo{'CARDNUMBER'} = ""; $::g_PaymentInfo{'CARDISSUE'} = ""; $::g_PaymentInfo{'EXPMONTH'} = ""; $::g_PaymentInfo{'EXPYEAR'} = ""; $::g_PaymentInfo{'STARTMONTH'} = ""; $::g_PaymentInfo{'STARTYEAR'} = ""; } if ($sError ne "") # if there are any errors { # indicate the problem phase $sError = "" . ACTINIC::GetPhrase(-1, 152) . "

" . $sError . "

"; } return ($sError); } ####################################################### # # DisplayPage - display the specified page with the # optional error message # # Params: 0 - error message if any # 1 - page number to display # 2 - advance direction ($::FORWARD, $::BACKWARD) # # Returns: 0 - status # 1 - error message # 2 - HTML # ####################################################### sub DisplayPage { if ($#_ != 2) { return ($::FAILURE, ACTINIC::GetPhrase(-1, 12, 'DisplayPage'), 0, 0); } my (%VariableTable); my ($sError, $nPageNumber, $eDirection) = @_; my (@Response, $sPath); $sPath = $::g_InputHash{"PATH"}; # get the path to the web site # # Read the shopping cart - this has to be done before the call to ProcessPage since # process page kills the cart when receipt is called. # my ($pCartList); @Response = ActinicOrder::ReadCart($::g_sCartId, $sPath); # read the shopping cart if ($Response[0] != $::SUCCESS) # general error { return (@Response); } $pCartList = $Response[2]; # # make sure there are no "deleted" items in the cart # my $nLineCount = CountValidCartItems($pCartList); my $sMessage; if ($nLineCount != scalar @$pCartList && $::g_bFirstError) { $::g_bFirstError = $::FALSE; $sMessage = "

" . ACTINIC::GetPhrase(-1, 175) . " \n"; return(DisplayPage($sMessage, $::g_nCurrentSequenceNumber, $eDirection)); # redisplay the incoming page with the error messages } # # Process the phases in this page # my (@DeleteDelimiters, @KeepDelimiters, $nInc, $status); my ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters, @keys); $nInc = ($eDirection == $::FORWARD) ? 1 : -1; while ($#keys == -1 && # as long as the page is not used $nPageNumber >= 0) # and the page is a valid number { ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = ProcessPage($nPageNumber); # process the current page if ($status != $::SUCCESS) # if an error occured creating the pages, display the previous page { # with the error message if ($::g_bFirstError) # make sure we don't run into recursion problems { $::g_bFirstError = $::FALSE; $sMessage = "

$sMessage \n"; return(DisplayPage($sMessage, $::g_nCurrentSequenceNumber, $eDirection)); # redisplay the incoming page with the error messages } else # unfortunate error that can be recovered { return($status, $sMessage, 0, 0); } } @keys = keys %$pVarTable; $nPageNumber += $nInc; # try the next/previous page } $nPageNumber -= $nInc; # roll back the page number since it was unnecessarily incremented # # Handle the special case when the back button takes us back to the catalog rather than a previous order page # if ($#keys == -1) # we wound back too far (never found a valid page) { if (length $sError > 0) # if an error message exists, we still need to display it { my ($sRefPage) = $::g_PageList[0]; # find the original referencing page my @Response = ACTINIC::BounceToPageEnhanced(-1, $sError, ACTINIC::GetPhrase(-1, 25), \@::g_PageList, $::g_sWebSiteUrl, $::g_sContentUrl, $::g_pSetupBlob, $sRefPage, \%::g_InputHash); if ($Response[0] != $::SUCCESS) { ACTINIC::ReportError($sError, $::g_InputHash{'PATH'}); } return ($::SUCCESS, '', $Response[2]); } else { return ($::SUCCESS, "", GetCancelPage()); # go back to the catalog } } # # Now display a summary of the shopping cart - this must be post page-specific formatting # since the hidden status of the shipping phases may update the data # @Response = ActinicOrder::GenerateShoppingCartLines($pCartList); if ($Response[0] != $::SUCCESS) { return (@Response); } $VariableTable{$::VARPREFIX.'THEORDERDETAILS'} = $Response[2]; # add the order lines to the reciept # # combine the variable tables - build the total page # my (@a1, @a2); @a1 = %VariableTable; @a2 = %$pVarTable; push (@a1, @a2); %VariableTable = @a1; # get easier-to-handle copies @DeleteDelimiters = @$pDeleteDelimiters; @KeepDelimiters = @$pKeepDelimiters; # # By here, we have all of the phase specific values. Now generate the generic values. # $sError = GroomError($sError); # make the error look nice for the HTML $VariableTable{$::VARPREFIX.'SEQUENCE'} = $nPageNumber; # add the sequence number to the var list $VariableTable{$::VARPREFIX.'ERROR'} = $sError; # add the error message to the var list # # build the file # my ($sFileName); $sFileName = sprintf('order%2.2d.html', $nPageNumber); @Response = ACTINIC::TemplateFile($sPath.$sFileName, \%VariableTable); # make the substitutions if ($Response[0] != $::SUCCESS) { return (@Response); } # # clean up the links # @Response = ACTINIC::MakeLinksAbsolute($Response[2], $::g_sWebSiteUrl, $::g_sContentUrl); if ($Response[0] != $::SUCCESS) { return (@Response); } my ($sHTML) = $Response[2]; # # remove unused form blocks # my ($sDelimiter); foreach $sDelimiter (@DeleteDelimiters) # for each delimited section that is to be deleted { $sHTML =~ s/$::DELPREFIX$sDelimiter(.*?)$::DELPREFIX$sDelimiter//gs; # delete it (/s removes the \n limitation of .) } # # remove unused delimiters # foreach $sDelimiter (@KeepDelimiters) # for each delimiter that is not used { $sHTML =~ s/$::DELPREFIX$sDelimiter//gs; # delete it } return ($::SUCCESS, "", $sHTML, 0); } ####################################################### # # ProcessPage - process the page specific variable # lists # # Params: 0 - page number # # Returns: 0 - status # 1 - error message if any # 2 - a pointer to the substitution variable table # 3 - a pointer to the list of delimiters of areas to delete # 4 - a pointer to the list of delimiters of areas to keep # # Affects: %s_LargeVariableTable, @s_LargeDeleteDelimiters, # @s_LargeKeepDelimiters # ####################################################### sub ProcessPage { if ($#_ != 0) { return($::SUCCESS, ACTINIC::GetPhrase(-1, 12, 'ProcessPage'), undef, undef, undef); } my ($nPageNumber) = $_[0]; my @scratch = keys %$::g_pPhaseList; my $nPhaseCount = $#scratch - 1; if ($nPageNumber > $nPhaseCount) { return($::SUCCESS, ACTINIC::GetPhrase(-1, 146, $nPageNumber, $nPhaseCount), undef, undef, undef); } # # get the phase list for this page # my ($sPhaseList) = $$::g_pPhaseList{$nPageNumber}; # # process each phase in the current block # my ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters); my (@Phases) = split (//, $sPhaseList); my ($nPhase, $status, $sMessage); foreach $nPhase (@Phases) { if ($nPhase == $::BILLCONTACTPHASE) { ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayBillContactPhase(); } elsif ($nPhase == $::SHIPCONTACTPHASE) { ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayShipContactPhase(); } elsif ($nPhase == $::SHIPCHARGEPHASE) { ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayShipChargePhase(); if ($status != $::SUCCESS) # on error - bail { # # since displaying the shipping charge phase failed, unselect the default # country since it may change. It may have been erroneously entered. # my @Response = ActinicOrder::CallShippingPlugIn(); # get the country if ($Response[0] == $::SUCCESS && # no errors - use the country ${$Response[2]}{GetCountryDescription} == $::SUCCESS) { my $sCountry = $Response[7]; if ($::g_BillContact{COUNTRY} eq $sCountry) # if the bill contact country had been defaulted to the one selected in the preliminary phase { undef $::g_BillContact{COUNTRY}; # unselect it } if ($::g_ShipContact{COUNTRY} eq $sCountry) # same for the destination country { undef $::g_BillContact{COUNTRY}; } } return ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters); } UpdateCheckoutRecord(); # update the checkout record since the plugin is free to change it } elsif ($nPhase == $::TAXCHARGEPHASE) { ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = ActinicOrder::DisplayTaxPhase(); if ($status != $::SUCCESS) # on error - bail { return ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters); } } elsif ($nPhase == $::GENERALPHASE) { ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayGeneralPhase(); } elsif ($nPhase == $::PAYMENTPHASE) { ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayPaymentPhase(); if ($status != $::SUCCESS) # on error - bail { return ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters); } } elsif ($nPhase == $::COMPLETEPHASE) { if (length $::g_PaymentInfo{'METHOD'} == 0) # if the payment method is undefined at this point { # it is because the payment information was hidden EnsurePaymentSelection(); } my ($ePaymentMethod) = ActinicOrder::PaymentStringToEnum($::g_PaymentInfo{'METHOD'}); # the payment method is stored as "ENUMERATEDID:DESCRIPTION" # # complete the order based on the payment method # if ($ePaymentMethod == $::PAYMENT_CREDIT_CARD && # if they are paying with a credit card and $$::g_pSetupBlob{USE_DH} && # Java encryption is enabled and $$::g_pSetupBlob{OCC} == -1) # we are not in OCC mode { # use java encryption ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayApplet(); if ($status != $::SUCCESS) # on error - bail { return ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters); } } # # this is an on-line credit card transaction # elsif ($ePaymentMethod == $::PAYMENT_CREDIT_CARD && # if this is a credit card transaction and $$::g_pSetupBlob{OCC} != -1) # we are in on-line mode { # # call the plug in # my (@Response) = CallOCCPlugIn(); # # of the CC was accepted, there is no UI to display, so continue to the receipt # if ($Response[0] == $::ACCEPTED) # card was accepted { @Response = CompleteOrder(); # record the order if ($Response[0] != $::SUCCESS) { return(@Response); } # # this call does not return any variables. this causes the next page to be loaded (the receipt) # undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } # # the credit card acceptance is pending, record the order and display the plug-in UI so # it can complete the transaction # elsif ($Response[0] == $::PENDING) { my ($sHTML) = $Response[2]; @Response = CompleteOrder(); # record the order if ($Response[0] != $::SUCCESS) { return (@Response); } # # display the plug-in UI to complete the transaction # ACTINIC::PrintPage($sHTML, undef, $::FALSE); exit; } # # credit card rejected # elsif ($Response[0] == $::REJECTED) { # # display the plug-in error page # ACTINIC::PrintPage($Response[2], undef, $::FALSE); exit; } else { return (@Response); } } else # use script encryption { my (@Response) = CompleteOrder(); if ($Response[0] != $::SUCCESS) { return (@Response); } # # this call does not return any variables. this causes the next page to be loaded (the receipt) # undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; ($pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } } elsif ($nPhase == $::RECEIPTPHASE) # here when order is done { my ($ePaymentMethod); if (length $::g_PaymentInfo{METHOD} == 0) # if we used Java encryption, this field is undefined - but should be CC's { $ePaymentMethod = $::PAYMENT_CREDIT_CARD; # the : is to help parsing } else { ($ePaymentMethod) = ActinicOrder::PaymentStringToEnum($::g_PaymentInfo{METHOD}); # the payment method is stored as "ENUMERATEDID:DESCRIPTION" } # # If this is a OCC response, record the authorization - we no longer display the reciept at this point # if ($ePaymentMethod == $::PAYMENT_CREDIT_CARD && $$::g_pSetupBlob{OCC} != -1 && $::g_InputHash{'ACTION'} =~ m/AUTHORIZE/i) { my $sText; my $sError = RecordAuthorization(); if (length $sError != 0) # if there were any errors, { ACTINIC::RecordErrors($sError, $::g_InputHash{PATH}); # record the error to error.err $sText = "0" . $sError; } else { $sText = "1"; } ACTINIC::PrintText($sText); # # processing is complete at this point # exit; } # # if this is a request from the applet to record the order, then do it. # elsif ($ePaymentMethod == $::PAYMENT_CREDIT_CARD && $$::g_pSetupBlob{OCC} == -1 && $::g_InputHash{'ACTION'} =~ m/RECORDORDER/i) { my $sText; my $sError = RecordOrder($::g_InputHash{ORDERNUMBER}, \$::g_InputHash{BLOB}); if (length $sError != 0) # if there were any errors, { ACTINIC::RecordErrors($sError, $::g_InputHash{PATH}); # record the error to error.err $sText = "0" . $sError; } else { $sText = "1"; } ACTINIC::PrintText($sText); # # processing is complete at this point # exit; } else { # # Display the receipt # ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = DisplayReceiptPhase($::g_InputHash{ORDERNUMBER}, $ePaymentMethod); if ($status != $::SUCCESS) # on error - bail { return ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters); } # # clean up the cart and checkout file # my (@ClearResponse); @ClearResponse = ActinicOrder::ClearFiles($::g_InputHash{PATH}, $::g_sCartId); if ($ClearResponse[0] != $::SUCCESS) # on error { ACTINIC::RecordErrors($ClearResponse[1]); # just record the problem - there is no good way to report it to the customer } } } elsif ($nPhase == $::PRELIMINARYINFOPHASE) { ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters) = ActinicOrder::DisplayPreliminaryInfoPhase(); if ($status != $::SUCCESS) # on error - bail { return ($status, $sMessage, $pVarTable, $pDeleteDelimiters, $pKeepDelimiters); } UpdateCheckoutRecord(); # update the checkout record since the plugin is free to change it } my (@Array1, @Array2); @Array1 = %$pVarTable; @Array2 = %::s_LargeVariableTable; push (@Array1, @Array2); %::s_LargeVariableTable = @Array1; push (@::s_LargeDeleteDelimiters, @$pDeleteDelimiters); push (@::s_LargeKeepDelimiters, @$pKeepDelimiters); # # now find the bulk of the delimit stats # ($pDeleteDelimiters, $pKeepDelimiters) = ActinicOrder::ParseDelimiterStatus($nPhase); push (@::s_LargeDeleteDelimiters, @$pDeleteDelimiters); push (@::s_LargeKeepDelimiters, @$pKeepDelimiters); } return ($::SUCCESS, '', \%::s_LargeVariableTable, \@::s_LargeDeleteDelimiters, \@::s_LargeKeepDelimiters); } ####################################################### # # DisplayBillContactPhase - display the bill contact # page # # Returns: 0 - pointer to variable table # 1 - pointer to list of delimited regions # to remove # 2 - pointer to list of unused delimiters # # Affects: %::s_VariableTable, @::s_DeleteDelimiters, # @::s_KeepDelimiters # ####################################################### sub DisplayBillContactPhase { undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; # # if the phase is done, don't display it # if (ActinicOrder::IsPhaseComplete($::BILLCONTACTPHASE) || ActinicOrder::IsPhaseHidden($::BILLCONTACTPHASE)) { push (@::s_DeleteDelimiters, 'INVOICEPHASE'); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } else { push (@::s_KeepDelimiters, 'INVOICEPHASE'); } # # if the country is not defined, but the shipping country has been selected, default # to the shipping country. # if (0 == length $::g_BillContact{'COUNTRY'}) { my @Response = ActinicOrder::CallShippingPlugIn(); if ($Response[0] == $::SUCCESS && # no errors - use the country ${$Response[2]}{GetCountryDescription} == $::SUCCESS) { $::g_BillContact{'COUNTRY'} = $Response[7]; } } # # restore the default values from the table # $::s_VariableTable{$::VARPREFIX.'INVOICESALUTATION'} = ACTINIC::EncodeText2($::g_BillContact{'SALUTATION'}); $::s_VariableTable{$::VARPREFIX.'INVOICENAME'} = ACTINIC::EncodeText2($::g_BillContact{'NAME'}); $::s_VariableTable{$::VARPREFIX.'INVOICEJOBTITLE'} = ACTINIC::EncodeText2($::g_BillContact{'JOBTITLE'}); $::s_VariableTable{$::VARPREFIX.'INVOICECOMPANY'} = ACTINIC::EncodeText2($::g_BillContact{'COMPANY'}); $::s_VariableTable{$::VARPREFIX.'INVOICEADDRESS1'} = ACTINIC::EncodeText2($::g_BillContact{'ADDRESS1'}); $::s_VariableTable{$::VARPREFIX.'INVOICEADDRESS2'} = ACTINIC::EncodeText2($::g_BillContact{'ADDRESS2'}); $::s_VariableTable{$::VARPREFIX.'INVOICEADDRESS3'} = ACTINIC::EncodeText2($::g_BillContact{'ADDRESS3'}); $::s_VariableTable{$::VARPREFIX.'INVOICEADDRESS4'} = ACTINIC::EncodeText2($::g_BillContact{'ADDRESS4'}); $::s_VariableTable{$::VARPREFIX.'INVOICEPOSTALCODE'} = ACTINIC::EncodeText2($::g_BillContact{'POSTALCODE'}); $::s_VariableTable{$::VARPREFIX.'INVOICECOUNTRY'} = ACTINIC::EncodeText2($::g_BillContact{'COUNTRY'}); $::s_VariableTable{$::VARPREFIX.'INVOICEPHONE'} = ACTINIC::EncodeText2($::g_BillContact{'PHONE'}); $::s_VariableTable{$::VARPREFIX.'INVOICEFAX'} = ACTINIC::EncodeText2($::g_BillContact{'FAX'}); $::s_VariableTable{$::VARPREFIX.'INVOICEEMAIL'} = ACTINIC::EncodeText2($::g_BillContact{'EMAIL'}); $::s_VariableTable{$::VARPREFIX.'INVOICEUSERDEFINED'} = ACTINIC::EncodeText2($::g_BillContact{'USERDEFINED'}); $::s_VariableTable{$::VARPREFIX.'INVOICETITLE'} = ACTINIC::GetPhrase(-1, 147); # # handle check boxes # if ($::g_BillContact{'MOVING'}) # if the moving check is on, message the data a bit { $::s_VariableTable{$::VARPREFIX.'INVOICEMOVINGCHECKSTATUS'} = 'CHECKED'; } else { $::s_VariableTable{$::VARPREFIX.'INVOICEMOVINGCHECKSTATUS'} = ''; } if ($::g_BillContact{'PRIVACY'}) # if the privacy check is on, message the data a bit { $::s_VariableTable{$::VARPREFIX.'INVOICEPRIVACYCHECKSTATUS'} = 'CHECKED'; } else { $::s_VariableTable{$::VARPREFIX.'INVOICEPRIVACYCHECKSTATUS'} = ''; } if ($::g_BillContact{'SEPARATE'}) # if the ship separate flag is on, turn on the check box { $::s_VariableTable{$::VARPREFIX.'INVOICESEPARATECHECKSTATUS'} = 'CHECKED'; } else { $::s_VariableTable{$::VARPREFIX.'INVOICESEPARATECHECKSTATUS'} = ''; } return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } ####################################################### # # DisplayShipContactPhase - display the ship contact # page # # Returns: 0 - pointer to variable table # 1 - pointer to list of delimited regions # to remove # 2 - pointer to list of unused delimiters # # Affects: %::s_VariableTable, @::s_DeleteDelimiters, # @::s_KeepDelimiters # ####################################################### sub DisplayShipContactPhase { undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; # # if the phase is done, don't display it # if (ActinicOrder::IsPhaseComplete($::SHIPCONTACTPHASE) || ActinicOrder::IsPhaseHidden($::SHIPCONTACTPHASE)) { push (@::s_DeleteDelimiters, 'DELIVERPHASE'); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } # # Presnet: start of un-comment # elsif (defined $$::g_pSetupBlob{'REVERSE_ADDRESS_CHECK'} && $$::g_pSetupBlob{'REVERSE_ADDRESS_CHECK'}) { # # Presnet: handle reversing the meaning of the check box # if ($::g_BillContact{'SEPARATE'}) { push (@::s_DeleteDelimiters, 'DELIVERPHASE'); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } else { push (@::s_KeepDelimiters, 'DELIVERPHASE'); } } # # Presnet: end of un-comment # else { if (!$::g_BillContact{'SEPARATE'}) { push (@::s_DeleteDelimiters, 'DELIVERPHASE'); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } else { push (@::s_KeepDelimiters, 'DELIVERPHASE'); } } # # if the country is not defined, but the shipping country has been selected, default # to the shipping country. # if (0 == length $::g_ShipContact{'COUNTRY'}) { my @Response = ActinicOrder::CallShippingPlugIn(); if ($Response[0] == $::SUCCESS && # no errors - use the country ${$Response[2]}{GetCountryDescription} == $::SUCCESS) { $::g_ShipContact{'COUNTRY'} = $Response[7]; } } # # restore the default values from the table # $::s_VariableTable{$::VARPREFIX.'DELIVERSALUTATION'} = ACTINIC::EncodeText2($::g_ShipContact{'SALUTATION'}); $::s_VariableTable{$::VARPREFIX.'DELIVERNAME'} = ACTINIC::EncodeText2($::g_ShipContact{'NAME'}); $::s_VariableTable{$::VARPREFIX.'DELIVERJOBTITLE'} = ACTINIC::EncodeText2($::g_ShipContact{'JOBTITLE'}); $::s_VariableTable{$::VARPREFIX.'DELIVERCOMPANY'} = ACTINIC::EncodeText2($::g_ShipContact{'COMPANY'}); $::s_VariableTable{$::VARPREFIX.'DELIVERADDRESS1'} = ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS1'}); $::s_VariableTable{$::VARPREFIX.'DELIVERADDRESS2'} = ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS2'}); $::s_VariableTable{$::VARPREFIX.'DELIVERADDRESS3'} = ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS3'}); $::s_VariableTable{$::VARPREFIX.'DELIVERADDRESS4'} = ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS4'}); $::s_VariableTable{$::VARPREFIX.'DELIVERPOSTALCODE'} = ACTINIC::EncodeText2($::g_ShipContact{'POSTALCODE'}); $::s_VariableTable{$::VARPREFIX.'DELIVERCOUNTRY'} = ACTINIC::EncodeText2($::g_ShipContact{'COUNTRY'}); $::s_VariableTable{$::VARPREFIX.'DELIVERPHONE'} = ACTINIC::EncodeText2($::g_ShipContact{'PHONE'}); $::s_VariableTable{$::VARPREFIX.'DELIVERFAX'} = ACTINIC::EncodeText2($::g_ShipContact{'FAX'}); $::s_VariableTable{$::VARPREFIX.'DELIVEREMAIL'} = ACTINIC::EncodeText2($::g_ShipContact{'EMAIL'}); $::s_VariableTable{$::VARPREFIX.'DELIVERUSERDEFINED'} = ACTINIC::EncodeText2($::g_ShipContact{'USERDEFINED'}); $::s_VariableTable{$::VARPREFIX.'DELIVERTITLE'} = ACTINIC::GetPhrase(-1, 148); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } ####################################################### # # DisplayShipChargePhase - display the ship charge # page # # Returns: 0 - status # 1 - error if any # 2 - pointer to variable table # 3 - pointer to list of delimited regions # to remove # 4 - pointer to list of unused delimiters # # Affects: %s_::s_VariableTable, @::s_DeleteDelimiters, # @::s_KeepDelimiters # ####################################################### sub DisplayShipChargePhase { undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; # # if the phase is done, don't display it # if (ActinicOrder::IsPhaseComplete($::SHIPCHARGEPHASE) || # the shipping is already completed or ActinicOrder::IsPhaseHidden($::SHIPCHARGEPHASE)) # is hidden { push (@::s_DeleteDelimiters, 'SHIPANDHANDLEPHASE'); # hide the shipping stuff return ($::SUCCESS, '', \%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } else { push (@::s_KeepDelimiters, 'SHIPANDHANDLEPHASE'); } # # restore the advanced shipping settings # if ($$::g_pSetupBlob{MAKE_SHIPPING_CHARGE}) { my @Response = ActinicOrder::CallShippingPlugIn(); if ($Response[0] != $::SUCCESS) { return (@Response); } elsif (${$Response[2]}{RestoreFinalUI} != $::SUCCESS) { return (${$Response[2]}{RestoreFinalUI}, ${$Response[3]}{RestoreFinalUI}); } %::s_VariableTable = %{$Response[10]};# save the var table } $::s_VariableTable{$::VARPREFIX.'SHIPUSERDEFINED'} = ACTINIC::EncodeText2($::g_ShipInfo{'USERDEFINED'}); return ($::SUCCESS, '', \%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } ####################################################### # # DisplayGeneralPhase - display the ship charge # page # # Returns: 0 - pointer to variable table # 1 - pointer to list of delimited regions # to remove # 2 - pointer to list of unused delimiters # # Affects: %s_::s_VariableTable, @::s_DeleteDelimiters, # @::s_KeepDelimiters # ####################################################### sub DisplayGeneralPhase { undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; # # if the phase is done, don't display it # if (ActinicOrder::IsPhaseComplete($::GENERALPHASE) || ActinicOrder::IsPhaseHidden($::GENERALPHASE)) { push (@::s_DeleteDelimiters, 'GENERALPHASE'); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } else { push (@::s_KeepDelimiters, 'GENERALPHASE'); } # # restore the default values from the table # $::s_VariableTable{$::VARPREFIX.'GENERALHOWFOUND'} = ACTINIC::EncodeText2($::g_GeneralInfo{'HOWFOUND'}); $::s_VariableTable{$::VARPREFIX.'GENERALWHYBUY'} = ACTINIC::EncodeText2($::g_GeneralInfo{'WHYBUY'}); $::s_VariableTable{$::VARPREFIX.'GENERALUSERDEFINED'} = ACTINIC::EncodeText2($::g_GeneralInfo{'USERDEFINED'}); $::s_VariableTable{$::VARPREFIX.'GENERALTITLE'} = ACTINIC::GetPhrase(-1, 151); return (\%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } ####################################################### # # DisplayPaymentPhase - display the ship charge # page # # Returns: 0 - status # 1 - error if any # 2 - pointer to variable table # 3 - pointer to list of delimited regions # to remove # 4 - pointer to list of unused delimiters # # Affects: %s_::s_VariableTable, @::s_DeleteDelimiters, # @::s_KeepDelimiters # ####################################################### sub DisplayPaymentPhase { undef %::s_VariableTable; undef @::s_DeleteDelimiters; undef @::s_KeepDelimiters; # # if the phase is done, don't display it # if (ActinicOrder::IsPhaseComplete($::PAYMENTPHASE) || ActinicOrder::IsPhaseHidden($::PAYMENTPHASE)) { push (@::s_DeleteDelimiters, 'PAYMENTPHASE'); return ($::SUCCESS, '', \%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } my ($Status, $Message, $pCartList, @Response); @Response = ActinicOrder::ReadCart($::g_sCartId, $::g_InputHash{'PATH'}); # read the shopping cart ($Status, $Message, $pCartList) = @Response; if ($Status != $::SUCCESS) # general error { return (@Response); } my (@SummaryResponse, $nTotal); @SummaryResponse = ActinicOrder::SummarizeOrder($pCartList); # get the real order total if ($SummaryResponse[0] != $::SUCCESS) # if we were successful { return (@SummaryResponse); } $nTotal = $SummaryResponse[6]; # # 1) If (total == 0 || prices are off) # If (user defined field is hidden) # # mark payment complete # mark method pre-pay # skip processing # # else If (user defined is not hidden) # # mark method pre-pay # hide payment stuff # my ($bPaymentHidden) = ($nTotal == 0 || !$$::g_pSetupBlob{'PRICES_DISPLAYED'}); if ($bPaymentHidden) { EnsurePaymentSelection(); } if ( $bPaymentHidden && # there are no prompts in this phase, ACTINIC::IsPromptHidden(5, 7)) # skip it { push (@::s_DeleteDelimiters, 'PAYMENTPHASE'); return ($::SUCCESS, '', \%::s_VariableTable, \@::s_DeleteDelimiters, \@::s_KeepDelimiters); } else # if we've made it this far, the phase is here to stay { push (@::s_KeepDelimiters, 'PAYMENTPHASE'); } # # restore the default values from the table # $::s_VariableTable{$::VARPREFIX.'PAYMENTUSERDEFINED'} = ACTINIC::EncodeText2($::g_PaymentInfo{'USERDEFINED'}); $::s_VariableTable{$::VARPREFIX.'PAYMENTPONO'} = ACTINIC::EncodeText2($::g_PaymentInfo{'PONO'}); # # add the CC info if it exists # $::s_VariableTable{$::VARPREFIX.'PAYMENTCARDNUMBER'} = ACTINIC::EncodeText2($::g_PaymentInfo{'CARDNUMBER'}); $::s_VariableTable{$::VARPREFIX.'PAYMENTCARDISSUE'} = ACTINIC::EncodeText2($::g_PaymentInfo{'CARDISSUE'}); # # add the CC dates # my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $sDate); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); # platform independent time $mon++; # make month 1 based $year += 1900; # make year AD based my ($nYear, $nMonth); for ($nMonth = 1; $nMonth < 13; $nMonth++) # first process the months { # # populate the start date month field # if ($::g_PaymentInfo{'STARTMONTH'} eq '' && # if there is no pre-defined date $nMonth == $mon) # use the current month as the default value { $::s_VariableTable{$::VARPREFIX.'PAYMENTSTARTMONTHS'} .= '


"; return ($sError); } ####################################################### # # CompleteOrder - complete the order process. This # includes saving the order, cleaning up any temporary # files and then displaying the reciept. If a back # plug in exists, call it. # # Returns: 0 - status # 1 - error message # ####################################################### sub CompleteOrder { # # Dump the order to the nq script # my $sPath = $::g_InputHash{'PATH'}; # # Generate the order number and save it for the command header # my ($Status, $Message, $sOrderNumber); ($Status, $Message, $sOrderNumber) = GetOrderNumber(); if ($Status != $::SUCCESS) { return ($Status, $Message); } $::g_InputHash{'ORDERNUMBER'} = $sOrderNumber; my (@FieldList, @FieldType); push (@FieldList, hex("10")); # the magic number push (@FieldType, $::RBWORD); push (@FieldList, 9); # the version push (@FieldType, $::RBBYTE); push (@FieldList, $sOrderNumber); # the order number push (@FieldType, $::RBSTRING); # # The Invoice Contact # push (@FieldList, $::g_BillContact{'NAME'}); # the contact name push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'SALUTATION'}); # the salutation push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'JOBTITLE'}); # the contact job title push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'COMPANY'}); # the contact company push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'ADDRESS1'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'ADDRESS2'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'ADDRESS3'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'ADDRESS4'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'COUNTRY'}); # the contact country push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'POSTALCODE'}); # the contact post code push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'PHONE'}); # the contact phone push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'FAX'}); # the contact fax push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'EMAIL'}); # the contact email push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'USERDEFINED'}); # the contact user defined push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'PRIVACY'}); # the privacy flag push (@FieldType, $::RBBYTE); # # The Delivery Contact # push (@FieldList, $::g_ShipContact{'NAME'}); # the contact name push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'SALUTATION'}); # the salutation push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'JOBTITLE'}); # the contact job title push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'COMPANY'}); # the contact company push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'ADDRESS1'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'ADDRESS2'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'ADDRESS3'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'ADDRESS4'}); # the contact address push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'COUNTRY'}); # the contact country push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'POSTALCODE'}); # the contact post code push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'PHONE'}); # the contact phone push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'FAX'}); # the contact fax push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'EMAIL'}); # the contact email push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'USERDEFINED'}); # the contact user defined push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipContact{'PRIVACY'}); # the privacy flag push (@FieldType, $::RBBYTE); # # The Payment Information # my ($ePaymentMethod); push (@FieldList, $$::g_pCatalogBlob{'SINTLSYMBOLS'}); # the 3 digit international currency symbol push (@FieldType, $::RBSTRING); ($ePaymentMethod) = ActinicOrder::PaymentStringToEnum($::g_PaymentInfo{'METHOD'}); # the payment method is stored as "ENUMERATEDID:DESCRIPTION" push (@FieldList, $ePaymentMethod); # the payment method enumerated id push (@FieldType, $::RBWORD); push (@FieldList, $::g_PaymentInfo{'USERDEFINED'}); # the generic payment user defined field push (@FieldType, $::RBSTRING); push (@FieldList, $::g_BillContact{'MOVING'}); # the moving in next month flag push (@FieldType, $::RBBYTE); # # the general marketing questions # push (@FieldList, $::g_GeneralInfo{'WHYBUY'}); # the Why Did You Purchase field push (@FieldType, $::RBSTRING); push (@FieldList, $::g_GeneralInfo{'HOWFOUND'}); # the How Did You Find field push (@FieldType, $::RBSTRING); my @Response = GetGeneralUD3(); # retrieve the appropriate user defined 3 value if ($Response[0] != $::SUCCESS) { return ($Response[0], $Response[1]); } push (@FieldList, $Response[2]); # the generic user defined field push (@FieldType, $::RBSTRING); # # get the shopping cart information # my $pCartList; @Response = ActinicOrder::ReadCart($::g_sCartId, $sPath); # read the shopping cart ($Status, $Message, $pCartList) = @Response; if ($Status != $::SUCCESS) # general error { return (@Response); } # # the numbers # @Response = ActinicOrder::SummarizeOrder($pCartList); # total the order if ($Response[0] != $::SUCCESS) { return (@Response); } # # 2 - sub total # 3 - shipping # 4 - tax 1 # 5 - tax 2 # 6 - total # 7 - tax 1 on shipping (fraction of 4 that is # due to shipping) # 8 - tax 2 on shipping (fraction of 5 that is # due to shipping) # my ($Ignore, $Ignore2, $nSubTotal, $nShipping, $nTax1, $nTax2, $nTotal, $nShippingTax1, $nShippingTax2) = @Response; push (@FieldList, $nSubTotal); # product total push (@FieldType, $::RBQWORD); push (@FieldList, 0); # discount percent push (@FieldType, $::RBDWORD); push (@FieldList, 0); # discount total push (@FieldType, $::RBQWORD); push (@FieldList, $nSubTotal); # sub-total (product total - discount) push (@FieldType, $::RBQWORD); # # The shipping information # push (@FieldList, $nShipping); # shipping push (@FieldType, $::RBQWORD); push (@FieldList, AdjustTaxTreatment($$::g_pSetupBlob{SHIPPING_TAX_TREATMENT})); # shipping tax treatment push (@FieldType, $::RBWORD); push (@FieldList, $nShippingTax1); push (@FieldType, $::RBQWORD); push (@FieldList, $nShippingTax2); push (@FieldType, $::RBQWORD); push (@FieldList, $::g_ShipInfo{'USERDEFINED'}); # the generic user defined field push (@FieldType, $::RBSTRING); # # tax information # push (@FieldList, $$::g_pSetupBlob{'TAX_1_RATE'}); # tax percent 1 push (@FieldType, $::RBDWORD); push (@FieldList, $nTax1); # tax 1 push (@FieldType, $::RBQWORD); push (@FieldList, $$::g_pSetupBlob{'TAX_2_RATE'}); # tax percent 2 push (@FieldType, $::RBDWORD); push (@FieldList, $nTax2); # tax 2 push (@FieldType, $::RBQWORD); push (@FieldList, $::g_TaxInfo{'USERDEFINED'}); # the generic user defined field push (@FieldType, $::RBSTRING); # # complete the order information # push (@FieldList, $nTotal); # the order total push (@FieldType, $::RBQWORD); # # the order detail summary - make sure the line count is accurate # my ($nLineCount) = CountValidCartItems($pCartList); push (@FieldList, $nLineCount); # the total lines push (@FieldType, $::RBDWORD); push (@FieldList, 0); # the lines shipped push (@FieldType, $::RBDWORD); push (@FieldList, 0); # the lines cancelled push (@FieldType, $::RBDWORD); # # the credit card information. note that it is currently hard-coded to 0 for now. # later this will be replaced by the applet encryption. # if ($ePaymentMethod == $::PAYMENT_CREDIT_CARD) # if paying with CC, send the details { push (@FieldList, $::g_PaymentInfo{'CARDISSUE'});# the cc issue number push (@FieldType, $::RBBYTE); push (@FieldList, $::g_PaymentInfo{'STARTYEAR'} . # the cc start date ($::g_PaymentInfo{'STARTYEAR'} eq "" ? '' : '/') . # if the data is NULL, don't enter the / $::g_PaymentInfo{'STARTMONTH'}); push (@FieldType, $::RBSTRING); } else # no paying with CC's, enter blank stuff { push (@FieldList, 0); # the cc issue number push (@FieldType, $::RBBYTE); # push (@FieldList, ""); # the cc start date push (@FieldType, $::RBSTRING); # } # # Get the current date/time on the server # my ($sDate) = ACTINIC::GetActinicDate(); # # Misc info # push (@FieldList, $sDate); # the order date push (@FieldType, $::RBSTRING); push (@FieldList, $::g_PaymentInfo{'PONO'}); # the purchase order number push (@FieldType, $::RBSTRING); push (@FieldList, ""); # the order reference number push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipInfo{'ADVANCED'}); # the advanced shipping data push (@FieldType, $::RBSTRING); push (@FieldList, $$::g_pSetupBlob{'AUTH_KEY'}); # the catalog authorization key push (@FieldType, $::RBSTRING); push (@FieldList, $::g_ShipInfo{'LOCATION'}); # the plug-in location data push (@FieldType, $::RBSTRING); if ($$::g_pSetupBlob{MAKE_SHIPPING_CHARGE}) # shipping is enabled { @Response = ActinicOrder::CallShippingPlugIn(); # get the shipping description if ($Response[0] != $::SUCCESS) { return (@Response); } elsif (${$Response[2]}{GetShippingDescription} != $::SUCCESS) { return(${$Response[2]}{GetShippingDescription}, ${$Response[3]}{GetShippingDescription}); } push (@FieldList, $Response[6]); # the plug-in description push (@FieldType, $::RBSTRING); } else # shipping is disabled { push (@FieldList, ''); # empty plug-in description push (@FieldType, $::RBSTRING); } # # Now process the order detail lines # my ($pOrderDetail); my (%CurrentItem, $pProduct); no strict 'refs'; foreach $pOrderDetail (@$pCartList) # for each item in the cart { %CurrentItem = %$pOrderDetail; # get the next item # # locate this product's object # @Response = ACTINIC::GetProduct($CurrentItem{"PRODUCT_REFERENCE"}, $CurrentItem{SECTION_BLOB}, $sPath); # get this product object ($Status, $Message, $pProduct) = @Response; if ($Status == $::NOTFOUND) # the item has been removed from the catalog { next; } if ($Status != $::SUCCESS) { return (@Response); } push (@FieldList, hex("11")); # the order detail magic number push (@FieldType, $::RBWORD); push (@FieldList, 4); # the version number push (@FieldType, $::RBBYTE); push (@FieldList, $CurrentItem{"PRODUCT_REFERENCE"}); # the product reference push (@FieldType, $::RBSTRING); push (@FieldList, $$pProduct{"NAME"}); # the product description push (@FieldType, $::RBSTRING); push (@FieldList, $CurrentItem{"QUANTITY"}); # the quantity ordered push (@FieldType, $::RBDWORD); push (@FieldList, $$pProduct{"PRICE"}); # the item price push (@FieldType, $::RBQWORD); my ($nTotal); $nTotal = $$pProduct{"PRICE"} * $CurrentItem{"QUANTITY"}; push (@FieldList, $nTotal); # the line price push (@FieldType, $::RBQWORD); if (defined $CurrentItem{"DATE"}) { push (@FieldList, $CurrentItem{"DATE"}); # the date field } else { push (@FieldList, ""); # null field } push (@FieldType, $::RBSTRING); if (defined $CurrentItem{"INFOINPUT"}) { push (@FieldList, $CurrentItem{"INFOINPUT"}); # the info field } else { push (@FieldList, ""); # null field } push (@FieldType, $::RBSTRING); push (@FieldList, 0); # the quantity already shipped push (@FieldType, $::RBDWORD); push (@FieldList, 0); # the quantity already cancelled push (@FieldType, $::RBDWORD); push (@FieldList, AdjustTaxTreatment($$pProduct{TAX_TREATMENT})); # the tax treatment push (@FieldType, $::RBWORD); my ($nTax); @Response = ActinicOrder::CalculateTax(1, $nTotal, $$pProduct{TAX_TREATMENT}, $$::g_pSetupBlob{TAX_1_RATE}, $$::g_pSetupBlob{TAX_1_ROUNDING} ); # calculate tax 1 on this product if ($Response[0] != $::SUCCESS) { return (@Response); } $nTax = $Response[2]; push (@FieldList, $nTax); # tax 1 push (@FieldType, $::RBQWORD); @Response = ActinicOrder::CalculateTax(2, $nTotal, $$pProduct{TAX_TREATMENT}, $$::g_pSetupBlob{TAX_2_RATE}, $$::g_pSetupBlob{TAX_2_ROUNDING} ); # calculate tax 1 on this product if ($Response[0] != $::SUCCESS) { return (@Response); } $nTax = $Response[2]; push (@FieldList, $nTax); # tax 2 push (@FieldType, $::RBQWORD); push (@FieldList, $$pProduct{'OPAQUE_SHIPPING_DATA'}); # advanced shipping data push (@FieldType, $::RBSTRING); push (@FieldList, 0); # discount total push (@FieldType, $::RBQWORD); push (@FieldList, 0); # discount percent push (@FieldType, $::RBDWORD); } # # pack the Safer blob # @Response = ACTINIC::OpenWriteBlob("memory"); # open the output blob ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { return (@Response); } @Response = ACTINIC::WriteBlob(\@FieldList, \@FieldType); # write the blob ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { return (@Response); } @Response = ACTINIC::CloseWriteBlob(); # close up if ($Response[0] != $::SUCCESS) { return (@Response); } my ($SaferBlob) = $Response[2]; # # now process the DH encrypted data. If there is no data to encrypt, leave the blob undefed. # my $DHBlob; if (length $::g_PaymentInfo{'CARDNUMBER'} > 0 || length $::g_PaymentInfo{'CARDTYPE'} > 0 || length $::g_PaymentInfo{'EXPYEAR'} > 0 || length $::g_PaymentInfo{'EXPMONTH'} > 0) { @FieldList = (); @FieldType = (); push (@FieldList, $::g_PaymentInfo{'CARDNUMBER'});# the cc number push (@FieldType, $::RBSTRING); push (@FieldList, $::g_PaymentInfo{'CARDTYPE'});# the cc card name push (@FieldType, $::RBSTRING); push (@FieldList, $::g_PaymentInfo{'EXPYEAR'} . '/' . $::g_PaymentInfo{'EXPMONTH'}); # the cc expiration date push (@FieldType, $::RBSTRING); # # pack the D-H blob # @Response = ACTINIC::OpenWriteBlob("memory"); # open the output blob ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { return (@Response); } @Response = ACTINIC::WriteBlob(\@FieldList, \@FieldType); # write the blob ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { return (@Response); } @Response = ACTINIC::CloseWriteBlob(); # close up if ($Response[0] != $::SUCCESS) { return (@Response); } $DHBlob = $Response[2]; } # # encrypt the portion of the blob that requires encryption # # # encryption information # ActinicEncrypt::InitEncrypt(@{$$::g_pSetupBlob{PUBLIC_KEY_128BIT}}); # initialize the data tables my $EncryptedBlob = ActinicEncrypt::Encrypt($DHBlob, $SaferBlob); # encrypt the data # # save the order # my $sError = RecordOrder($sOrderNumber, \$EncryptedBlob); if ($sError) # if an error occured { return ($::FAILURE, $sError, 0, 0); } return ($::SUCCESS, "", 0, 0); } ####################################################### # # UpdateCheckoutRecord - Update the checkout record # # Returns: 0 - status # 1 - message # ####################################################### sub UpdateCheckoutRecord { # # save the modified data. The payment info only saves the method, purchase order number, and the user # defined field. This prevents security leaks of CC information. # my (%EmptyPaymentInfo); $EmptyPaymentInfo{'METHOD'} = $::g_PaymentInfo{'METHOD'}; $EmptyPaymentInfo{'USERDEFINED'} = $::g_PaymentInfo{'USERDEFINED'}; $EmptyPaymentInfo{'PONO'} = $::g_PaymentInfo{'PONO'}; return (ActinicOrder::SaveCheckoutStatus($::g_InputHash{'PATH'}, $::g_sCartId, \%::g_BillContact, \%::g_ShipContact, \%::g_ShipInfo, \%::g_TaxInfo, \%::g_GeneralInfo, \%EmptyPaymentInfo)); } ####################################################### # # GetCancelPage - retrieve the cancel page text # # Returns: 0 - page HTML # ####################################################### sub GetCancelPage { my ($sRefPage) = $::g_PageList[0]; # find the original referencing page my @Response = ACTINIC::BounceToPagePlain(0, undef, undef, \@::g_PageList, $::g_sWebSiteUrl, $::g_sContentUrl, $::g_pSetupBlob, $sRefPage, \%::g_InputHash); if ($Response[0] != $::SUCCESS) { ACTINIC::ReportError($Response[1], $::g_InputHash{'PATH'}); return; } return ($Response[2]); } ####################################################### # # RecordAuthorization - record the authorization blob # from the OCC server # # Returns: 0 - Error message (if any) # ####################################################### sub RecordAuthorization { # # make sure a reasonable order number exists # if (length $::g_InputHash{ON} < 5) { return(ACTINIC::GetPhrase(-1, 185, (length $::g_InputHash{ON}), $::g_InputHash{ON})); } # # record the authorization blob # my (@FieldList, @FieldType); push (@FieldList, hex("22")); # the magic number push (@FieldType, $::RBWORD); push (@FieldList, 2); # the version push (@FieldType, $::RBBYTE); push (@FieldList, $$::g_pSetupBlob{OCC}); # the OCC provider ID push (@FieldType, $::RBDWORD); push (@FieldList, ($ENV{TM} ? 1 : 0)); # the test mode push (@FieldType, $::RBBYTE); push (@FieldList, $::g_OriginalInputData);# the data push (@FieldType, $::RBSTRING); # # pack the unencrypted portion of the blob # my $sPath = $::g_InputHash{PATH}; my @Response = ACTINIC::OpenWriteBlob('memory'); # open the output blob my ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { my $sError = (0 == length $Response[1]) ? "Error opening the write blob" : $Response[1]; return($sError); } @Response = ACTINIC::WriteBlob(\@FieldList, \@FieldType); # write the blob ($Status, $Message) = @Response; if ($Status != $::SUCCESS) { my $sError = (0 == length $Response[1]) ? "Error writing blob" : $Response[1]; return($sError); } @Response = ACTINIC::CloseWriteBlob(); # close up if ($Response[0] != $::SUCCESS) { my $sError = (0 == length $Response[1]) ? "Error closing the write blob" : $Response[1]; return($sError); } my ($ClearBlob) = $Response[2]; # grab the unencrypted portion of the blob # # encrypt the portion of the blob that requires encryption # # # encryption information # my ($EncryptedBlob); ActinicEncrypt::InitEncrypt(@{$$::g_pSetupBlob{PUBLIC_KEY_128BIT}}); # initialize the data tables $EncryptedBlob = ActinicEncrypt::Encrypt(undef, $ClearBlob); # encrypt the data # # dump the blob to a temporary file. if the filename changes from OrderNumber.occ, # the C++ function CFileTransfer::CleanUpCorruptAuthorizationBlobs must be updated since # it relies on deriving the order number from the blob name. # my ($sTempFilename) = $sPath . $::g_InputHash{ON} . '.occ'; unless ( open (COMPLETEFILE, ">" . $sTempFilename)) # open the file { return(ACTINIC::GetPhrase(-1, 21, $sTempFilename, $!)); } binmode COMPLETEFILE; unless (print COMPLETEFILE $EncryptedBlob) # write the file { my ($sError) = $!; close COMPLETEFILE; unlink $sTempFilename; return(ACTINIC::GetPhrase(-1, 28, $sTempFilename, $sError)); } close COMPLETEFILE; #? ACTINIC::ASSERT($sTempFilename =~ /$::g_InputHash{ON}/, "The authorization blob filename must be derived from the order number.", __LINE__, __FILE__); return (undef); } ####################################################### # # CallOCCPlugIn - call the online credit card plug-in # # Returns: 0 - status # 1 - error message if any # 2 - HTML to display (if any) # ####################################################### sub CallOCCPlugIn { # # The online credit card plug-in expects the following values: # # Expects: $::sOrderNumber - the alphanumeric order number for this order # $::nOrderTotal - the total for this order (stored in based currency format e.g. 1000 = $10.00) # %::PriceFormatBlob - the price format data # %::InvoiceContact - the customer invoice contact information # $::sCallBackURLAuth - the URL of the authorization callback script # $::sCallBackURLBack - the URL of the backup script # $::sCallBackURLUser - the URL of the receipt script # $::sPath - the path to the Catalog directory # $::sWebSiteUrl - the referrer URL # $::sContentUrl - the content URL # local ($::sOrderNumber, $::nOrderTotal, %::PriceFormatBlob, %::InvoiceContact, $::sCallBackURLUser); local ($::sCallBackURLAuth, $s::CallBackURLBack); # # get the order summary for validation # my ($Status, $Message, $pCartList); my @Response = ActinicOrder::ReadCart($::g_sCartId, $::g_InputHash{'PATH'}); # read the shopping cart ($Status, $Message, $pCartList) = @Response; if ($Status != $::SUCCESS && # general error $Status != $::EOF) { return($::FAILURE, $Message, ''); } @Response = ActinicOrder::SummarizeOrder($pCartList, $::TRUE);# calculate the order total if ($Response[0] != $::SUCCESS) { return (@Response); } $::nOrderTotal = $Response[6]; # the order total %::PriceFormatBlob = %{$::g_pCatalogBlob}; # the catalog blob can be used for prices since it contains the price fields %::InvoiceContact = %::g_BillContact; # the invoice address information ($Status, $Message, $::sOrderNumber) = GetOrderNumber(); if ($Status != $::SUCCESS) { return ($Status, $Message, undef); } # # build the base URL for all other actions # @Response = ACTINIC::EncodeText($::g_InputHash{PATH}, $::FALSE); # the path my ($sBaseUrl) = sprintf("%sos%6.6d%s?PATH=%s&", $$::g_pSetupBlob{CGI_URL}, $$::g_pSetupBlob{CGI_ID}, $$::g_pSetupBlob{CGI_EXT}, $Response[1]); # # build the record authorization URL # $::sCallBackURLAuth = $sBaseUrl . "SEQUENCE=3&ACTION=AUTHORIZE&"; # # build the reciept URL # @Response = ACTINIC::EncodeText(ACTINIC::GetPhrase(-1, 504), $::FALSE); my ($sFinish) = $Response[1]; @Response = ACTINIC::EncodeText($::g_sWebSiteUrl, $::FALSE); # the reference page my $sRefPage = $Response[1]; # # Passing the true sequence number can cause problems in some cases (no payment page), so hard code 3 here # $sParam = sprintf($sParamFormat, 'SEQUENCE', $::g_nNextSequenceNumber); # $::sCallBackURLUser = $sBaseUrl . "SEQUENCE=3&ACTION=$sFinish" . "&ORDERNUMBER=$::sOrderNumber&REFPAGE=" . $sRefPage . "&"; # # build the back url # @Response = ACTINIC::EncodeText(ACTINIC::GetPhrase(-1, 503), $::FALSE); my $sBack = $Response[1]; @Response = ACTINIC::EncodeText($::g_InputHash{PATH}, $::FALSE); $::sCallBackURLBack = ACTINIC::GetReferrer() . "?PATH=" . $Response[1] . "&SEQUENCE=" . $::g_nNextSequenceNumber . "&ACTION=" . $sBack . "&REFPAGE=" . $sRefPage . "&"; # # load the plug-in # @Response = GetOCCScript($::g_InputHash{PATH}); if ($Response[0] != $::SUCCESS) # couldn't load the script { return (@Response); # bail out } my ($sScript) = $Response[2]; # # some utilitarian values # local $::sPath = $::g_InputHash{PATH}; local $::sWebSiteUrl = $::g_sWebSiteUrl; local $::sContentUrl = $::g_sContentUrl; # # now execute the plug-in # if (eval($sScript) != $::SUCCESS) # execute the script { return ($::FAILURE, ACTINIC::GetPhrase(-1, 170, $@)); } return ($::eStatus, $::sErrorMessage, $::sHTML); } ####################################################### # # GetOCCScript - read and return the OCC # script # # Params: 0 - the path # # Returns: 0 - status # 1 - error message (if any) # 2 - script # # Affects: $::s_sOCCScript - the script # ####################################################### sub GetOCCScript { if (defined $::s_sOCCScript)# if it is already in memory, { return ($::SUCCESS, "", $::s_sOCCScript); # we are done } if ($#_ < 0) # validate params { return ($::FAILURE, ACTINIC::GetPhrase(-1, 12, 'GetOCCScript'), 0, 0); } my ($sPath) = $_[0]; # grab the path my ($sFilename) = $sPath . "OCCProcessor.fil"; my @Response = ACTINIC::ReadAndVerifyFile($sFilename); if ($Response[0] == $::SUCCESS) # if successful { $::s_sOCCScript = $Response[2]; # record the script } return (@Response); } ####################################################### # # GetOrderNumber - retrieve the order number for this # order. The order number is generated as follows: # # Order Number = FLHHHHHHHXXXXX # F - first character of first name # L - first character of last name # HHHHHHH - encoded IP address of processing host # XXXXX - 5 digit incremented ID # # The encoded IP address of the processing host and # the 5 digit incremented ID are 0 prepadded if # necessary. If one of the # # Returns: 0 - status # 1 - message # 2 - order number in a string # 3 - undef # # Affects: $::s_sOrderNumber # ####################################################### sub GetOrderNumber { if (length $::s_sOrderNumber > 0) { return ($::SUCCESS, undef, $::s_sOrderNumber, undef); } my (@CharacterSet) = split(//, "3456789ABCDEFGHJKLMNPQRSTUVWXY"); # # first attempt to get the current hostname # my $sLocalhost = $ENV{SERVER_NAME}; # try the environment $sLocalhost =~ s/[^-a-zA-Z0-9.]//g; # strip any bad characters if (!$sLocalhost) # if still no hostname is found { $sLocalhost = $ENV{HOST}; # try a different environment variable $sLocalhost =~ s/[^-a-zA-Z0-9.]//g; # strip any bad characters } if (!$sLocalhost) # if still no hostname is found { $sLocalhost = $ENV{HTTP_HOST}; # try a different environment variable $sLocalhost =~ s/[^-a-zA-Z0-9.]//g; # strip any bad characters } if (!$sLocalhost) # if still no hostname is found { $sLocalhost = $ENV{LOCALDOMAIN}; # try a different environment variable $sLocalhost =~ s/[^-a-zA-Z0-9.]//g; # strip any bad characters } if (!$sLocalhost) # if still no hostname is found { $sLocalhost = `hostname`; # try the command line $sLocalhost =~ s/[^-a-zA-Z0-9.]//g; # strip any bad characters } if (!$sLocalhost && # if still no hostname and $^O eq 'MSWin32') # NT { my $sHost = `ipconfig`; # run ipconfig and gather the collection of addresses $sHost =~ /IP Address\D*([0-9.]*)/; # get the first address in the list $sLocalhost = $1; $sLocalhost =~ s/[^-a-zA-Z0-9.]//g; # strip any bad characters } if (!$sLocalhost) # if no hostname is found - fatal { return ($::FAILURE, ACTINIC::GetPhrase(-1, 176), undef, undef); } my $ServerIP = inet_aton($sLocalhost); # do a dns lookup and get the ip address of this server if (!defined $ServerIP) # dns lookup failed { my $sError = $!; # # this is fairly common on NT, so try to get the IP address from the ipconfig exe # if ($^O eq 'MSWin32') # NT { my $sLocalhost = `ipconfig`; # run ipconfig and gather the collection of addresses $sLocalhost =~ /IP Address\D*([0-9.]*)/; # get the first address in the list my $address = $1; my @bytes = split(/\./, $address); # get the individual components if ($#bytes == 3) # if it looks like we have a valid address { $ServerIP = pack('C4', @bytes); # pack the array into a 32 bit int } } if (!defined $ServerIP) # still not resolved { # # here on UNIX systems with DNS failure and on NT systems with DNS failure *and* ipconfig failure # return ($::FAILURE, ACTINIC::GetPhrase(-1, 13, $sLocalhost . ", " . $sError), undef, undef); # record error } } my $nUniqueServerID = unpack('N', $ServerIP); # convert the server address to an unsigned int #? ACTINIC::ASSERT($nUniqueServerID != 0, "Server ID null.", __LINE__, __FILE__); # # Now convert the server ID to a base 30 string to compact it as much as possible # my $nDigit; my $sServerString; my $nBase = (scalar @CharacterSet); while ($nUniqueServerID > 0) { $nDigit = ACTINIC::Modulus($nUniqueServerID, $nBase); $nUniqueServerID = int ($nUniqueServerID / $nBase); # trim off the used digit $sServerString .= $CharacterSet[$nDigit]; # convert the digit to a base 30 character. NOTE that the string is stored in reveresed order, but we don't care. } # # attempt to extract the first characters of the name. If this fails - use the least significant digits of the # process ID. # my $sInitials; my $sName = $::g_BillContact{'NAME'}; $sName =~ s/[^a-zA-Z0-9 ]//g; # drop any non-alphanums or non-spaces $sName =~ s/^\s*//; # clear leading and traling spaces $sName =~ s/\s*$//; if (!$sName) # if the name DNE, take the last two digits from the process ID { $sInitials = substr("00" . ACTINIC::Modulus($$, 100), -2); } elsif (2 >= length $sName) # the name field only contains 1 or 2 characters - grab them { $sInitials = substr($sName . ACTINIC::Modulus($$, 10), 0, 2); } elsif ($sName =~ /([^ \t\r\n]+)\s*([^ \t\r\n]+)/) # two names - get the true initials { $sInitials = substr($1, 0, 1) . substr($2, 0, 1); } else # just get the first two characters of the name { $sInitials = substr($sName, 0, 2); } $sInitials = uc($sInitials); # always use upper case # # now comes the important part - get a unique order number for this order/server/merchant combo # # # locate a local temporary directory for the lock file # my $sLocalTempDirectory; if ($^O eq 'MSWin32') # the temp directory is OS specific { $sLocalTempDirectory = $ENV{TEMP}; # the windows temp directory can be found in %TEMP% if (!$sLocalTempDirectory) # if the TEMP environment variable is undefined { $sLocalTempDirectory = "c:/temp/"; # attempt to use c:/temp } $sLocalTempDirectory =~ s#\\#/#g; # convert the backslashes $sLocalTempDirectory =~ s#([^/])$#$1/#; # make sure there is a trailing slash } else # UNIX servers have a /tmp/ directory { $sLocalTempDirectory = '/tmp/'; } if (!$sLocalTempDirectory) # no directory was found { return ($::FAILURE, ACTINIC::GetPhrase(-1, 177), undef, undef); } if (!-e $sLocalTempDirectory) # directory does not exist { return ($::FAILURE, ACTINIC::GetPhrase(-1, 178, $sLocalTempDirectory), undef, undef); } if (!-d $sLocalTempDirectory) # is not a directory { return ($::FAILURE, ACTINIC::GetPhrase(-1, 179, $sLocalTempDirectory), undef, undef); } if (!-r $sLocalTempDirectory) # is not readable { return ($::FAILURE, ACTINIC::GetPhrase(-1, 180, $sLocalTempDirectory), undef, undef); } if (!-w $sLocalTempDirectory) # is not writable { return ($::FAILURE, ACTINIC::GetPhrase(-1, 181, $sLocalTempDirectory), undef, undef); } # # Now open and lock the file # my $sLockFilename = $sLocalTempDirectory . 'ActinicCatalog' . $$::g_pSetupBlob{AUTH_KEY} . '.lock'; unless (open (LOCK, ">$sLockFilename")) { return ($::FAILURE, ACTINIC::GetPhrase(-1, 182, $sLockFilename, $!), undef, undef); } if (-1 == flock(LOCK, $::LOCK_EX)) # attempt to lock the file { my $sError = $!; close (LOCK); return ($::FAILURE, ACTINIC::GetPhrase(-1, 183, $sLockFilename, $sError), undef, undef); } # # we have control of the counter file now # # # open the counter file to read the contents # my $sCounterFilename = $::g_InputHash{PATH} . $sServerString . '.num'; my $nIncrementalNumber; my $nIntSize = 4; my $buf; if (open (COUNTER, "<$sCounterFilename")) # if the file exists and can be read { binmode COUNTER; # set to binary mode # # read the current incremental number # my $ReadResponse = read(COUNTER, $buf, $nIntSize); close (COUNTER); # we are done with the read if ($ReadResponse == $nIntSize) # previous value read { $nIncrementalNumber = unpack('N', $buf); # convert the binary data to an Perl number } else { my $sError = $!; flock (LOCK, $::LOCK_UN); close (LOCK); return ($::FAILURE, ACTINIC::GetPhrase(-1, 105, $sCounterFilename, $sError . " " . $ReadResponse), undef, undef); } } else { my $sError = $!; if (-e $sCounterFilename) # the file exists, but can not be read { flock (LOCK, $::LOCK_UN); close (LOCK); return ($::FAILURE, ACTINIC::GetPhrase(-1, 21, $sCounterFilename, $sError), undef, undef); } $nIncrementalNumber = 0; # its OK, the file is new } my $nIncrementalOrderNumber = $nIncrementalNumber; # store the counter number for this order $nIncrementalNumber++; # increment the order number for the next order if ($nIncrementalNumber > 99999) # manually wrap around at 5 digits since that is all of the space { # we have available in our numbering scheme $nIncrementalNumber = 0; } # # now write the modified number back to the file # if (open (COUNTER, ">$sCounterFilename")) # if the file exists and can be written to or dne and can be created { $buf = pack('N', $nIncrementalNumber); # pack the number back into a binary unless (print COUNTER $buf) # write to the file { my $sError = $!; close (COUNTER); flock (LOCK, $::LOCK_UN); close (LOCK); return ($::FAILURE, ACTINIC::GetPhrase(-1, 28, $sCounterFilename, $sError), undef, undef); } close (COUNTER); } else { my $sError = $!; flock (LOCK, $::LOCK_UN); close (LOCK); return ($::FAILURE, ACTINIC::GetPhrase(-1, 21, $sCounterFilename, $sError), undef, undef); } flock (LOCK, $::LOCK_UN); close (LOCK); # # now we are ready to construct the order number. the substr operations make sure there are 7 zero-buffered # digits for the server string and 5 for the incremental number # $::s_sOrderNumber = $sInitials . substr("0000000" . $sServerString, -7) . substr("00000" . $nIncrementalOrderNumber, -5); #? ACTINIC::ASSERT(14 == length $::s_sOrderNumber, "Order number is not 14 characters long (" . (length $::s_sOrderNumber) . ", " . $::s_sOrderNumber . ").", __LINE__, __FILE__); return ($::SUCCESS, undef, $::s_sOrderNumber, undef); } ####################################################### # # GetGeneralUD3 - get the General phase user defined # 3 prompt value. The value is either the value # entered by the customer, or the value retrieved # from the cookie ACTINIC_SOURCE which is a string # indicating the referring marketing entity. If # the UD3 prompt is visible and ACTINIC_SOURCE is # defined, return an error. # # Returns: 0 - status # 1 - error message if any # 2 - value # ####################################################### sub GetGeneralUD3 { my $sCookieName = 'ACTINIC_SOURCE'; my ($sCookie, $sCookies); $sCookies = $ENV{'HTTP_COOKIE'}; # try to retrieve the cookie # # if the cookie is defined and the prompt field is visible, error out. # if ($sCookies =~ /$sCookieName/ && !ACTINIC::IsPromptHidden(4, 2)) { return ($::FAILURE, ACTINIC::GetPhrase(-1, 172)); } # # if we are here and the cookie is not defined, just return the user defined value # if ($sCookies !~ /$sCookieName/) { return ($::SUCCESS, undef, $::g_GeneralInfo{'USERDEFINED'}); } # # return the cookie value # my (@CookieList) = split(/;/, $sCookies); # separate the various cookie variables in the list my ($sLabel, $sValue); foreach $sCookie (@CookieList) { if ($sCookie =~ /$sCookieName/) { ($sLabel, $sValue) = split (/=/, $sCookie); # retrieve the value return ($::SUCCESS, undef, $sValue); } } return ($::FAILURE, undef, undef); # never here } ####################################################### # # CountValidCartItems - count the cart items # eliminating any items that no longer exist in the # catalog # # Params: 0 - pointer to the cart list # # Returns: 0 - item count # ####################################################### sub CountValidCartItems { if ($#_ != 0) # validate params { return ($::FAILURE, ACTINIC::GetPhrase(-1, 12, 'CountValidCartItems'), 0, 0); } my $pCartList = $_[0]; my ($pOrderDetail, @Response); my (%CurrentItem, $pProduct); my $nLineCount = 0; foreach $pOrderDetail (@$pCartList) # for each item in the cart { %CurrentItem = %$pOrderDetail; # get the next item # # locate this product's object # @Response = ACTINIC::GetProduct($CurrentItem{"PRODUCT_REFERENCE"}, $CurrentItem{SECTION_BLOB}, $::g_InputHash{'PATH'}); # get this product object if ($Response[0] != $::NOTFOUND) # the item has been removed from the catalog { $nLineCount++; # increment the line count } } return ($nLineCount); } ####################################################### # # EnsurePaymentSelection - ensure that a valid payment # method is selected if there are no options or just # one option. # # Expects: g_pSetupBlob - the setup blob to be defined # # Affects: g_PaymentInfo - the payment method # ####################################################### sub EnsurePaymentSelection { # # if there is only one payment option, take it. # if there are no payment options, assume pre-pay # my $nPaymentOptions = ActinicOrder::CountPaymentOptions(); if ($nPaymentOptions == 0) # if no payment options were offered, assume pre-pay { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 137); # default to prepay } elsif ($nPaymentOptions == 1) # if only one option is offered, the UI is hidden, so hard code the correct selection here { if ($$::g_pSetupBlob{PAYMENT_CREDIT_CARD}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 133); } elsif ($$::g_pSetupBlob{PAYMENT_CASH_ON_DELIVERY}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 134); } elsif ($$::g_pSetupBlob{PAYMENT_CHECK_ON_DELIVERY}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 135); } elsif ($$::g_pSetupBlob{PAYMENT_INVOICE}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 136); } elsif ($$::g_pSetupBlob{PAYMENT_INVOICE_AND_PREPAY}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 137); # default to prepay } elsif ($$::g_pSetupBlob{PAYMENT_SEND_DETAILS}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 138); } } elsif (length $::g_PaymentInfo{'METHOD'} == 0) # if the payment method is still undefined - take the first method crossed { #if ($$::g_pSetupBlob{PAYMENT_CREDIT_CARD}) Disable auto-selection of CC because this causes problems with hidden prices, free orders, etc. # { # $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 133); # } if ($$::g_pSetupBlob{PAYMENT_CASH_ON_DELIVERY}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 134); } elsif ($$::g_pSetupBlob{PAYMENT_CHECK_ON_DELIVERY}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 135); } elsif ($$::g_pSetupBlob{PAYMENT_INVOICE}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 136); } elsif ($$::g_pSetupBlob{PAYMENT_INVOICE_AND_PREPAY}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 137); # default to prepay } elsif ($$::g_pSetupBlob{PAYMENT_SEND_DETAILS}) { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 138); } } # # Failsafe - if the payment method is still undefined, use pre-pay # if (length $::g_PaymentInfo{'METHOD'} == 0) # if the payment method is still undefined { $::g_PaymentInfo{'METHOD'} = ACTINIC::GetPhrase(-1, 137); # default to prepay } } ####################################################### # # RecordOrder - record the order blob - send mail if # the blob # # Params: 0 - the order number # 1 - a reference to the order blob # # Returns: 0 - Error message (if any) # ####################################################### sub RecordOrder { if ($#_ != 1) # validate params { return ($::FAILURE, ACTINIC::GetPhrase(-1, 12, 'RecordOrder'), 0, 0); } my ($sOrderNumber, $pBlob) = @_; # # see if any orders already exist # my ($Status, $Message, @FileList) = ACTINIC::ReadTheDir($::g_InputHash{PATH});# read the contents of the directory if ($Status != $::SUCCESS) { @FileList = (); } my $sFileList = join(' ', @FileList); my $bOrderExists = ($sFileList =~ /\.ord( |$)/); # # dump the blob to a file # my ($sTempFilename) = $::g_InputHash{PATH} . $sOrderNumber . '.ord'; if (-e $sTempFilename) # if the file exists overwrite it { ACTINIC::ChangeAccess('rw', $sTempFilename); } unless ( open (COMPLETEFILE, ">" . $sTempFilename)) # open the file { return(ACTINIC::GetPhrase(-1, 21, $sTempFilename, $!)); } binmode COMPLETEFILE; unless (print COMPLETEFILE $$pBlob) # write the file { my ($sError) = $!; close COMPLETEFILE; unlink $sTempFilename; return(ACTINIC::GetPhrase(-1, 28, $sTempFilename, $sError)); } close COMPLETEFILE; ACTINIC::ChangeAccess('', $sTempFilename); # # if this is the first order, and the vendor requested email, and a valid email address exists, # and a valid SMTP server exists, then send email # if (!$bOrderExists && $$::g_pSetupBlob{EMAIL_REQUESTED} && $$::g_pSetupBlob{EMAIL} ne "" && $::g_sSmtpServer ne "") { ($Status, $Message) = ACTINIC::SendMail($::g_sSmtpServer, $$::g_pSetupBlob{EMAIL}, "New Catalog order received at your web site since you last downloaded", "New Catalog order received at your web site since you last downloaded"); # # ignore return code (just record the error) # if ($Status != $::SUCCESS) { ACTINIC::RecordErrors($Message, $::g_InputHash{PATH}); } } return (undef); } ####################################################### # # GeneratePresnetMail - generate the Presnet email # # Expects: %::g_InputHash, and %g_SetupBlob # should be defined # # Returns: ($ReturnCode, $Error) # if $ReturnCode = $::FAILURE, the operation failed # for the reason specified in $Error # Otherwise everything is OK # ####################################################### sub GeneratePresnetMail { my ($sTextMailBody, $sCartID, @Response, $Status, $Message); @Response = ActinicOrder::GetCartID($::g_InputHash{"PATH"}); # retrieve the cart ID ($Status, $Message, $sCartID) = @Response; if ($Status != $::SUCCESS) # error out { return (@Response); } my ($pCartList); @Response = ActinicOrder::ReadCart($sCartID, $::g_InputHash{'PATH'}); # read the shopping cart ($Status, $Message, $pCartList) = @Response; if ($Status != $::SUCCESS && $Status != $::EOF) # error out { return (@Response); } # # add order number # $sTextMailBody = "Order#: $::g_InputHash{ORDERNUMBER}\r\n"; # # add company name # $sTextMailBody .= "Shop Name: $$::g_pSetupBlob{COMPANY_NAME}\r\n"; # # add company email address # $sTextMailBody .= "Shop's Email: $$::g_pSetupBlob{EMAIL}\r\n"; # # add sender's email address # $sTextMailBody .= "Sender's Email: $::g_BillContact{EMAIL}\r\n"; # # add sender's town/city # $sTextMailBody .= "Sender's Town/City: $::g_BillContact{ADDRESS4}\r\n"; # # add sender's country # $sTextMailBody .= "Sender's Country: $::g_BillContact{COUNTRY}\r\n"; # # add recipient's town/city # $sTextMailBody .= "Recipient's Town/City: $::g_ShipContact{ADDRESS4}\r\n"; # # add recipient's country # $sTextMailBody .= "Recipient's Country: $::g_ShipContact{COUNTRY}\r\n"; # # add the referring source # @Response = GetGeneralUD3(); if ($Response[0] == $::SUCCESS) # if the value was retrieved, post it { $sTextMailBody .= "Referrer: " . $Response[2] . "\r\n"; } elsif ($Response[1]) # if an error occurred { $sTextMailBody .= "Referrer: " . $Response[1] . "\r\n"; } # # add currency # @Response = ACTINIC::EncodeText($$::g_pCatalogBlob{'SINTLSYMBOLS'}); # print the currency $sTextMailBody .= "Currency: $Response[1]\r\n"; # # add order value # @Response = ActinicOrder::SummarizeOrder($pCartList); # calculate the order total if ($Response[0] != $::SUCCESS) { return (@Response); } my ($Ignore0, $Ignore1, $nSubTotal, $nShipping, $nTax1, $nTax2, $nTotal, $nShippingTax1, $nShippingTax2) = @Response; # # convert currency into highest unit # my ($nIntegral, $nFractional, $nFactor); $nFactor = 10 ** $$::g_pCatalogBlob{'ICURRDIGITS'}; if ($nFactor == 1) # only one currency denomination { $sTextMailBody .= "Order Value: $nTotal\r\n"; } else # format as 9.99 or whatever { my ($sFormat, $sFormattedTotal); $sFormat = sprintf("%%d.%%0%dd", $$::g_pCatalogBlob{'ICURRDIGITS'}); $sFormattedTotal = sprintf($sFormat, $nTotal / $nFactor, ACTINIC::Modulus($nTotal, $nFactor) ); $sTextMailBody .= "Order Value: $sFormattedTotal\r\n"; } # # add the order date and time # my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst, $sDate); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime(time); # platform independent time $mon++; # make month 1 based $year += 1900; # make year AD based $sDate = sprintf("%02d/%02d/%4d %2.2d:%2.2d", $mday, $mon, $year, $hour, $min); $sTextMailBody .= "Order Date & time: $sDate\r\n"; # # add the latest delivery date # $sTextMailBody .= "Latest delivery date: $::g_ShipContact{USERDEFINED}\r\n"; # # now process the list # my ($pOrderDetail, %CurrentItem, $pProduct, $sLine); foreach $pOrderDetail (@$pCartList) { %CurrentItem = %$pOrderDetail; # get the next item # # locate this product's object. # @Response = ACTINIC::GetProduct($CurrentItem{"PRODUCT_REFERENCE"}, $CurrentItem{SECTION_BLOB}, $::g_InputHash{'PATH'}); # get this product object ($Status, $Message, $pProduct) = @Response; if ($Status == $::NOTFOUND) # the item has been removed from the catalog { #no-op - deleted product is OK here } if ($Status == $::FAILURE) { return (@Response); } $sLine = sprintf("Item: %-21s", $$pProduct{'REFERENCE'}); $sLine .= $$pProduct{'NAME'}; $sTextMailBody .= "$sLine\r\n"; } my ($sSubject, $sEmailRecpt); # # build the subject line # $sSubject = $$::g_pSetupBlob{COMPANY_NAME}; # # set the mail recipient # $sEmailRecpt .= 'orderorder@pres.net'; # set the recipient ($Status, $Message) = ACTINIC::SendMail($::g_sSmtpServer, $sEmailRecpt, $sSubject, $sTextMailBody); if($Status != $::SUCCESS) { return ($::FAILURE, $Message); } return ($::SUCCESS, ""); } ####################################################### # # AdjustTaxTreatment - adjust the tax treatment # according to the current exemption settings. # # Params: 0 - tax treatment # # Returns: 0 - modified tax treatment # ####################################################### sub AdjustTaxTreatment { my ($eTreatment) = @_; # # if the customer is exempt from tax 1, remove tax one from the tax treatment # if ($::g_TaxInfo{EXEMPT1}) { if ($ActinicOrder::TAX1 == $eTreatment) { $eTreatment = $ActinicOrder::EXEMPT; } elsif ($ActinicOrder::BOTH == $eTreatment) { $eTreatment = $ActinicOrder::TAX2; } } # # if the customer is exempt from tax 2, remove tax 2 from the tax treatment # if ($::g_TaxInfo{EXEMPT2}) { if ($ActinicOrder::TAX2 == $eTreatment) { $eTreatment = $ActinicOrder::EXEMPT; } elsif ($ActinicOrder::BOTH == $eTreatment) { $eTreatment = $ActinicOrder::TAX1; } } return ($eTreatment); }