paymentMethod
[ class tree: paymentMethod ] [ index: paymentMethod ] [ all elements ]

Source for file paypaldp.php

Documentation is available at paypaldp.php

  1. <?php
  2. /**
  3.  * paypaldp.php payment module class for Paypal Website Payments Pro
  4.  *
  5.  * @package paymentMethod
  6.  * @copyright Copyright 2003-2011 Zen Cart Development Team
  7.  * @copyright Portions Copyright 2005 CardinalCommerce
  8.  * @copyright Portions Copyright 2003 osCommerce
  9.  * @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
  10.  * @version $Id: paypaldp.php 19782 2011-10-11 17:54:15Z drbyte $
  11.  */
  12. /**
  13.  * The transaction URL for the Cardinal Centinel 3D-Secure service.
  14.  */
  15. define('MODULE_PAYMENT_PAYPALDP_CARDINAL_TXN_URL''https://paypal.cardinalcommerce.com/maps/processormodule.asp');
  16. //define('MODULE_PAYMENT_PAYPALDP_CARDINAL_TXN_URL', 'https://centineltest.cardinalcommerce.com/maps/processormodule.asp');
  17. /**
  18.  * debug flag for developer use only:
  19.  */
  20. if (!defined('MODULE_PAYMENT_CARDINAL_CENTINEL_DEBUGGING')) define('MODULE_PAYMENT_CARDINAL_CENTINEL_DEBUGGING'FALSE);
  21. /**
  22.  * load the communications layer code
  23.  */
  24. require_once(DIR_FS_CATALOG DIR_WS_MODULES 'payment/paypal/paypal_curl.php');
  25. /**
  26.  * the PayPal payment module for Website Payments Pro (Direct Payment API)
  27.  */
  28. class paypaldp extends base {
  29.   /**
  30.    * name of this module
  31.    *
  32.    * @var string 
  33.    */
  34.   var $code;
  35.   /**
  36.    * displayed module title
  37.    *
  38.    * @var string 
  39.    */
  40.   var $title;
  41.   /**
  42.    * displayed module description
  43.    *
  44.    * @var string 
  45.    */
  46.   var $description;
  47.   /**
  48.    * module status - set based on various config and zone criteria
  49.    *
  50.    * @var string 
  51.    */
  52.   var $enabled;
  53.   /**
  54.    * the zone to which this module is restricted for use
  55.    *
  56.    * @var string 
  57.    */
  58.   var $zone;
  59.   /**
  60.    * array holding accepted DP/gateway card types
  61.    *
  62.    * @var array 
  63.    */
  64.   var $cards = array();
  65.   /**
  66.    * JS code used for gateway/DP mode
  67.    *
  68.    * @var string 
  69.    */
  70.   var $cc_type_javascript = '';
  71.   /**
  72.    * JS code used for gateway/DP mode
  73.    *
  74.    * @var string 
  75.    */
  76.   var $cc_type_check = '';
  77.   /**
  78.    * debugging flag
  79.    *
  80.    * @var boolean 
  81.    */
  82.   var $enableDebugging = false;
  83.   /**
  84.    * is DP enabled ?
  85.    *
  86.    * @var boolean 
  87.    */
  88.   var $enableDirectPayment = true;
  89.   /**
  90.    * sort order of display
  91.    *
  92.    * @var int 
  93.    */
  94.   var $sort_order = 0;
  95.   /**
  96.    * Button Source / BN code -- enables the module to work for Zen Cart sites
  97.    *
  98.    * @var string 
  99.    */
  100.   var $buttonSource = 'ZenCart-DP_us';
  101.   /**
  102.    * order status setting for pending orders
  103.    *
  104.    * @var int 
  105.    */
  106.   var $order_pending_status = 1;
  107.   /**
  108.    * order status setting for completed orders
  109.    *
  110.    * @var int 
  111.    */
  112.   var $order_status = DEFAULT_ORDERS_STATUS_ID;
  113.   /**
  114.    * Debug tools
  115.    */
  116.   var $_logDir = 'includes/modules/payment/paypal/logs/';
  117.   var $_logLevel = 0;
  118.   /**
  119.    * FMF
  120.    */
  121.   var $fmfResponse = '';
  122.   var $fmfErrors = array();
  123.   /**
  124.    * class constructor
  125.    */
  126.   function paypaldp({
  127.     include_once(zen_get_file_directory(DIR_FS_CATALOG DIR_WS_LANGUAGES $_SESSION['language''/modules/payment/''paypaldp.php''false'));
  128.     global $order;
  129.     $this->code = 'paypaldp';
  130.     $this->codeTitle MODULE_PAYMENT_PAYPALDP_TEXT_ADMIN_TITLE_WPP;
  131.     $this->codeVersion '1.5.0';
  132.     $this->enableDirectPayment = true;
  133.     $this->enabled = (MODULE_PAYMENT_PAYPALDP_STATUS == 'True');
  134.     // Set the title & description text based on the mode we're in
  135.     if (IS_ADMIN_FLAG === true{
  136.       $this->description = sprintf(MODULE_PAYMENT_PAYPALDP_TEXT_ADMIN_DESCRIPTION' (rev' $this->codeVersion ')');
  137.       $this->title = MODULE_PAYMENT_PAYPALDP_TEXT_ADMIN_TITLE_WPP (defined('MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY'' (' MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY ')' '');
  138.       if ($this->enabled{
  139.         if ( ((MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'US' || MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'Canada'&& (MODULE_PAYMENT_PAYPALWPP_APISIGNATURE == '' || MODULE_PAYMENT_PAYPALWPP_APIUSERNAME == '' || MODULE_PAYMENT_PAYPALWPP_APIPASSWORD == ''))
  140.               || (!defined('MODULE_PAYMENT_PAYPALWPP_STATUS'|| MODULE_PAYMENT_PAYPALWPP_STATUS != 'True')
  141.           $this->title .= '<span class="alert"><strong> NOT CONFIGURED YET</strong></span>';
  142.         if (MODULE_PAYMENT_PAYPALDP_SERVER =='sandbox'$this->title .= '<strong><span class="alert"> (sandbox active)</span></strong>';
  143.         if (MODULE_PAYMENT_PAYPALDP_DEBUGGING =='Log File' || MODULE_PAYMENT_PAYPALDP_DEBUGGING =='Log and Email'$this->title .= '<strong> (Debug)</strong>';
  144.         if (!function_exists('curl_init')) $this->title .= '<strong><span class="alert"> CURL NOT FOUND. Cannot Use.</span></strong>';
  145.       }
  146.     else {
  147.       $this->description = MODULE_PAYMENT_PAYPALDP_TEXT_DESCRIPTION;
  148.       $this->title = MODULE_PAYMENT_PAYPALDP_TEXT_TITLE//cc
  149.     }
  150.  
  151.     if ((!defined('PAYPAL_OVERRIDE_CURL_WARNING'|| (defined('PAYPAL_OVERRIDE_CURL_WARNING'&& PAYPAL_OVERRIDE_CURL_WARNING != 'True')) && !function_exists('curl_init')) $this->enabled = false;
  152.  
  153.     $this->enableDebugging = (MODULE_PAYMENT_PAYPALDP_DEBUGGING == 'Log File' || MODULE_PAYMENT_PAYPALDP_DEBUGGING =='Log and Email');
  154.     $this->emailAlerts (MODULE_PAYMENT_PAYPALDP_DEBUGGING == 'Log File' || MODULE_PAYMENT_PAYPALDP_DEBUGGING =='Log and Email' || MODULE_PAYMENT_PAYPALDP_DEBUGGING == 'Alerts Only');
  155.     $this->sort_order = MODULE_PAYMENT_PAYPALDP_SORT_ORDER;
  156.  
  157.     $this->buttonSource = (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK''ZenCart-DP_uk' 'ZenCart-DP_us';
  158.  
  159.     $this->order_pending_status = MODULE_PAYMENT_PAYPALDP_ORDER_PENDING_STATUS_ID;
  160.     if ((int)MODULE_PAYMENT_PAYPALDP_ORDER_STATUS_ID 0{
  161.       $this->order_status = MODULE_PAYMENT_PAYPALDP_ORDER_STATUS_ID;
  162.     }
  163. //    $this->new_acct_notify = MODULE_PAYMENT_PAYPALDP_NEW_ACCT_NOTIFY;
  164.     $this->zone = (int)MODULE_PAYMENT_PAYPALDP_ZONE;
  165.     if (is_object($order)) $this->update_status();
  166.  
  167.     if (PROJECT_VERSION_MAJOR != '1' && substr(PROJECT_VERSION_MINOR03!= '5.0'$this->enabled = false;
  168.  
  169.     // offer credit card choices for pull-down menu -- only needed for UK version
  170.     $this->cards = array();
  171.     if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK'{
  172.       if (CC_ENABLED_VISA=='1')    $this->cards[array('id' => 'Visa''text' => 'Visa');
  173.       if (CC_ENABLED_MC=='1')      $this->cards[array('id' => 'MasterCard''text' => 'MasterCard');
  174.       if (CC_ENABLED_MAESTRO=='1'$this->cards[array('id' => 'Maestro''text' => 'Maestro');
  175.       if (CC_ENABLED_SWITCH=='1')  $this->cards[array('id' => 'Switch''text' => 'Switch');
  176.       if (CC_ENABLED_SOLO=='1')    $this->cards[array('id' => 'Solo''text' => 'Solo');
  177.     }
  178.  
  179.     // debug setup
  180.     if (!@is_writable($this->_logDir)) $this->_logDir = DIR_FS_CATALOG $this->_logDir;
  181.     if (!@is_writable($this->_logDir)) $this->_logDir = DIR_FS_SQL_CACHE;
  182.     // Regular mode:
  183.     if ($this->enableDebugging$this->_logLevel = 2;
  184.     // DEV MODE:
  185.     if (defined('PAYPAL_DEV_MODE'&& PAYPAL_DEV_MODE == 'true'$this->_logLevel = 3;
  186.  
  187.     if (IS_ADMIN_FLAG === true$this->tableCheckup();
  188.  
  189.   }
  190.   /**
  191.    *  Sets payment module status based on zone restrictions etc
  192.    */
  193.   function update_status({
  194.     global $order$db;
  195. //    $this->zcLog('update_status', 'Checking whether module should be enabled or not.');
  196.     // if store is not running in SSL, cannot offer credit card module, for PCI reasons
  197.     if (!defined('ENABLE_SSL'|| ENABLE_SSL != 'true'{
  198.       $this->enabled = FALSE;
  199.       $this->zcLog('update_status''Module disabled because SSL is not enabled on this site.');
  200.     }
  201.     // check other reasons for the module to be deactivated:
  202.     if ($this->enabled && (int)$this->zone > 0{
  203.       $check_flag false;
  204.       $sql "SELECT zone_id
  205.               FROM " TABLE_ZONES_TO_GEO_ZONES "
  206.               WHERE geo_zone_id = :zoneId
  207.               AND zone_country_id = :countryId
  208.               ORDER BY zone_id";
  209.       $sql $db->bindVars($sql':zoneId'$this->zone'integer');
  210.       $sql $db->bindVars($sql':countryId'$order->billing['country']['id']'integer');
  211.       $check $db->Execute($sql);
  212.       while (!$check->EOF{
  213.         if ($check->fields['zone_id'1{
  214.           $check_flag true;
  215.           break;
  216.         elseif ($check->fields['zone_id'== $order->billing['zone_id']{
  217.           $check_flag true;
  218.           break;
  219.         }
  220.         $check->MoveNext();
  221.       }
  222.  
  223.       if (!$check_flag{
  224.         $this->enabled = false;
  225.         $this->zcLog('update_status''Module disabled due to zone restriction. Billing address is not within the Payment Zone selected in the module settings.');
  226.       }
  227.  
  228.       // module cannot be used for purchase > $10,000 USD
  229.       $order_amount $this->calc_order_amount($order->info['total']'USD');
  230.       if ($order_amount 10000{
  231.         $this->enabled = false;
  232.         $this->zcLog('update_status''Module disabled because purchase price (' $order_amount ') exceeds PayPal-imposed maximum limit of 10,000 USD.');
  233.       }
  234.       if ($order->info['total'== 0{
  235.         $this->enabled = false;
  236.         $this->zcLog('update_status''Module disabled because purchase amount is set to 0.00.' "\n" print_r($ordertrue));
  237.       }
  238.     }
  239.   }
  240.   /**
  241.    *  Validate the credit card information via javascript (Number, Owner, and CVV Lengths)
  242.    */
  243.   function javascript_validation({
  244.     return '  if (payment_value == "' $this->code . '") {' "\n" .
  245.            '    var cc_firstname = document.checkout_payment.paypalwpp_cc_firstname.value;' "\n" .
  246.            '    var cc_lastname = document.checkout_payment.paypalwpp_cc_lastname.value;' "\n" .
  247.            '    var cc_number = document.checkout_payment.paypalwpp_cc_number.value;' "\n" .
  248.            '    var cc_checkcode = document.checkout_payment.paypalwpp_cc_checkcode.value;' "\n" .
  249.            '    if (cc_firstname == "" || cc_lastname == "" || eval(cc_firstname.length) + eval(cc_lastname.length) < ' CC_OWNER_MIN_LENGTH ') {' "\n" .
  250.            '      error_message = error_message + "' MODULE_PAYMENT_PAYPALDP_TEXT_JS_CC_OWNER '";' "\n" .
  251.            '      error = 1;' "\n" .
  252.            '    }' "\n" .
  253.            '    if (cc_number == "" || cc_number.length < ' CC_NUMBER_MIN_LENGTH ') {' "\n" .
  254.            '      error_message = error_message + "' MODULE_PAYMENT_PAYPALDP_TEXT_JS_CC_NUMBER '";' "\n" .
  255.            '      error = 1;' "\n" .
  256.            '    }' "\n" .
  257.            '    if (document.checkout_payment.paypalwpp_cc_checkcode.disabled == false && (cc_checkcode == "" || cc_checkcode.length < 3 || cc_checkcode.length > 4)) {' "\n".
  258.            '      error_message = error_message + "' MODULE_PAYMENT_PAYPALDP_TEXT_JS_CC_CVV '";' "\n" .
  259.            '      error = 1;' "\n" .
  260.            '    }' "\n" .
  261.            '  }' "\n";
  262.   }
  263.   /**
  264.    * Display Credit Card Information Submission Fields on the Checkout Payment Page
  265.    */
  266.   function selection({
  267.     global $order;
  268.     $this->cc_type_check =
  269.             'var value = document.checkout_payment.paypalwpp_cc_type.value;' .
  270.             'if (value == "Solo" || value == "Maestro" || value == "Switch") {' .
  271.             '    document.checkout_payment.paypalwpp_cc_issue_month.disabled = false;' .
  272.             '    document.checkout_payment.paypalwpp_cc_issue_year.disabled = false;' .
  273.             '    document.checkout_payment.paypalwpp_cc_checkcode.disabled = false;' .
  274.             '    if (document.checkout_payment.paypalwpp_cc_issuenumber) document.checkout_payment.paypalwpp_cc_issuenumber.disabled = false;' .
  275.             '} else {' .
  276.             '    if (document.checkout_payment.paypalwpp_cc_issuenumber) document.checkout_payment.paypalwpp_cc_issuenumber.disabled = true;' .
  277.             '    if (document.checkout_payment.paypalwpp_cc_issue_month) document.checkout_payment.paypalwpp_cc_issue_month.disabled = true;' .
  278.             '    if (document.checkout_payment.paypalwpp_cc_issue_year) document.checkout_payment.paypalwpp_cc_issue_year.disabled = true;' .
  279.             '    document.checkout_payment.paypalwpp_cc_checkcode.disabled = false;' .
  280.             '}';
  281.     if (sizeof($this->cards== 0$this->cc_type_check = '';
  282.  
  283.     /**
  284.      * since we are processing via the gateway, prepare and display the CC fields
  285.      */
  286.     $expires_month array();
  287.     $expires_year array();
  288.     $issue_year array();
  289.     for ($i 1$i 13$i++{
  290.       $expires_month[array('id' => sprintf('%02d'$i)'text' => strftime('%B - (%m)',mktime(0,0,0,$i,1,2000)));
  291.     }
  292.  
  293.     $today getdate();
  294.     for ($i $today['year']$i $today['year'15$i++{
  295.       $expires_year[array('id' => strftime('%y'mktime(0,0,0,1,1,$i))'text' => strftime('%Y',mktime(0,0,0,1,1,$i)));
  296.     }
  297.  
  298.     $onFocus ' onfocus="methodSelect(\'pmt-' $this->code . '\')"';
  299.  
  300.     $fieldsArray array();
  301.     $fieldsArray[array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_FIRSTNAME,
  302.                            'field' => zen_draw_input_field('paypalwpp_cc_firstname'$order->billing['firstname']'id="'.$this->code.'-cc-ownerf"'$onFocus ' autocomplete="off"'.
  303.                            '<script type="text/javascript">function paypalwpp_cc_type_check() { ' $this->cc_type_check . ' } </script>',
  304.                            'tag' => $this->code.'-cc-ownerf');
  305.     $fieldsArray[array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_LASTNAME,
  306.                            'field' => zen_draw_input_field('paypalwpp_cc_lastname'$order->billing['lastname']'id="'.$this->code.'-cc-ownerl"'$onFocus ' autocomplete="off"'),
  307.                            'tag' => $this->code.'-cc-ownerl');
  308.     if (sizeof($this->cards)>0$fieldsArray[array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_TYPE,
  309.                             'field' => zen_draw_pull_down_menu('paypalwpp_cc_type'$this->cards'''onchange="paypalwpp_cc_type_check();" onblur="paypalwpp_cc_type_check();"' 'id="'.$this->code.'-cc-type"'$onFocus),
  310.                            'tag' => $this->code.'-cc-type');
  311.     $fieldsArray[array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_NUMBER,
  312.                            'field' => zen_draw_input_field('paypalwpp_cc_number'$ccnum'id="'.$this->code.'-cc-number"' $onFocus ' autocomplete="off"'),
  313.                            'tag' => $this->code.'-cc-number');
  314.     $fieldsArray[array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_EXPIRES,
  315.                            'field' => zen_draw_pull_down_menu('paypalwpp_cc_expires_month'$expires_monthstrftime('%m')'id="'.$this->code.'-cc-expires-month"' $onFocus'&nbsp;' zen_draw_pull_down_menu('paypalwpp_cc_expires_year'$expires_year'''id="'.$this->code.'-cc-expires-year"' $onFocus),
  316.                            'tag' => $this->code.'-cc-expires-month');
  317.     $fieldsArray[array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_CHECKNUMBER,
  318.                            'field' => zen_draw_input_field('paypalwpp_cc_checkcode''''size="4" maxlength="4"' ' id="'.$this->code.'-cc-cvv"' $onFocus ' autocomplete="off"''&nbsp;<small>' MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_CHECKNUMBER_LOCATION '</small><script type="text/javascript">paypalwpp_cc_type_check();</script>',
  319.                            'tag' => $this->code.'-cc-cvv');
  320.  
  321.     $selection array('id' => $this->code,
  322.                        'module' => MODULE_PAYMENT_PAYPALDP_TEXT_TITLE,
  323.                        'fields' => $fieldsArray);
  324.  
  325.     if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK' && (CC_ENABLED_MAESTRO=='1' || CC_ENABLED_SWITCH=='1' || CC_ENABLED_SOLO=='1')) {
  326.       // add extra fields for Switch/Solo cards
  327.       for ($i $today['year'10$i <= $today['year']$i++{
  328.         $issue_year[array('id' => strftime('%y',mktime(0,0,0,1,1,$i))'text' => strftime('%Y',mktime(0,0,0,1,1,$i)));
  329.       }
  330.       array_splice($selection['fields']40,
  331.                    array(array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_ISSUE,
  332.                                'field' => zen_draw_pull_down_menu('paypalwpp_cc_issue_month'$expires_month'''id="'.$this->code.'-cc-issue-month"' $onFocus '&nbsp;' zen_draw_pull_down_menu('paypalwpp_cc_issue_year'$issue_year'''id="'.$this->code.'-cc-issue-year"' $onFocus),
  333.                                'tag' => $this->code.'-cc-issue-month')));
  334.       // add extra field for Maestro cards
  335.       array_splice($selection['fields']40,
  336.                    array(array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_MAESTRO_ISSUENUMBER,
  337.                                'field' => zen_draw_input_field('paypalwpp_cc_issuenumber'$maestronum'size="4" maxlength="4"' ' id="'.$this->code.'-cc-issuenumber"' $onFocus ' autocomplete="off"'),
  338.                                'tag' => $this->code.'-cc-issuenumber')));
  339.       // 3D-Secure
  340.       $selection['fields'][array('title' => '',
  341.                                'field' => '<div id="' $this->code.'-cc-securetext"><p>' .
  342.                                      '<a href="javascript:void window.open(\'vbv_learn_more.html\',\'vbv_service\',\'width=550,height=450\')">' .
  343.                                      zen_image(DIR_WS_IMAGES.'3ds/vbv_learn_more.gif''</a>' .
  344.                                      '<a href="javascript:void window.open(\'mcs_learn_more.html\',\'mcsc_service\',\'width=550,height=450\')">' .
  345.                                      zen_image(DIR_WS_IMAGES.'3ds/mcsc_learn_more.gif''</a>' .
  346.                                      '</p>' .
  347.                                      '<p>' TEXT_3DS_CARD_MAY_BE_ENROLLED '</p></div>',
  348.                                'tag' => $this->code.'-cc-securetext');
  349.     }
  350.     return $selection;
  351.   }
  352.   /**
  353.    * This is the credit card check done between checkout_payment and
  354.    * checkout_confirmation (called from checkout_confirmation).
  355.    * Evaluates the Credit Card Type for acceptance and the validity of the Credit Card Number & Expiration Date
  356.    */
  357.   function pre_confirmation_check({
  358.     global $messageStack$order;
  359.     include(DIR_WS_CLASSES 'cc_validation.php');
  360.     $cc_validation new cc_validation();
  361.     $result $cc_validation->validate($_POST['paypalwpp_cc_number'],
  362.                                        $_POST['paypalwpp_cc_expires_month']$_POST['paypalwpp_cc_expires_year'],
  363.                                        (isset($_POST['paypalwpp_cc_issue_month']$_POST['paypalwpp_cc_issue_month''')(isset($_POST['paypalwpp_cc_issue_year']$_POST['paypalwpp_cc_issue_year'''));
  364.     $error '';
  365.     switch ($result{
  366.       case 1:
  367.         break;
  368.       case -1:
  369.       $error MODULE_PAYMENT_PAYPALDP_TEXT_BAD_CARD;//sprintf(TEXT_CCVAL_ERROR_UNKNOWN_CARD, substr($cc_validation->cc_number, 0, 4));
  370.       if ($_POST['paypalwpp_cc_number'== ''$error str_replace('\n'''MODULE_PAYMENT_PAYPALDP_TEXT_JS_CC_NUMBER)// yes, those are supposed to be single-quotes.
  371.       break;
  372.       case -2:
  373.       case -3:
  374.       case -4:
  375.       $error TEXT_CCVAL_ERROR_INVALID_DATE;
  376.       break;
  377.       case false:
  378.       $error TEXT_CCVAL_ERROR_INVALID_NUMBER;
  379.       break;
  380.     }
  381.  
  382.     $_POST['paypalwpp_cc_checkcode'preg_replace('/[^0-9]/i'''$_POST['paypalwpp_cc_checkcode']);
  383.     if (isset($_POST['paypalwpp_cc_issuenumber'])) $_POST['paypalwpp_cc_issuenumber'preg_replace('/[^0-9]/i'''$_POST['paypalwpp_cc_issuenumber']);
  384.  
  385.     if (($result === false|| ($result 1) ) {
  386.       $messageStack->add_session('checkout_payment'$error '<!-- ['.$this->code.'] -->' '<!-- result: ' $result ' -->''error');
  387.       zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT'''SSL'truefalse));
  388.     }
  389.  
  390.     $this->cc_card_type $cc_validation->cc_type;
  391.     $this->cc_card_number $cc_validation->cc_number;
  392.     $this->cc_expiry_month $cc_validation->cc_expiry_month;
  393.     $this->cc_expiry_year $cc_validation->cc_expiry_year;
  394.     $this->cc_checkcode $_POST['paypalwpp_cc_checkcode'];
  395.  
  396.  
  397.     // In the case of UK cards, hook 3D-Secure if appropriate
  398.     // 3D-Secure
  399.     /**
  400.      * Checks if the card is enrolled in an authentication program and
  401.      * launches the start-authentication process if it is enrolled.
  402.      * The order may continue to authorization if a card is not enrolled
  403.      * or an error occurs.
  404.      *
  405.      * Under the Verified by Visa, MasterCard SecureCode and JCB J/Secure
  406.      * program guidelines, not all credit cards are eligible for
  407.      * participation in the payer authentication programs. Certain types of
  408.      * credit and debit cards, such as commercial and prepaid cards,
  409.      * are simply not able to participate in the programs. For this reason,
  410.      * this configuration is available to provide the option to allow
  411.      * transactions using credit and debit cards that are unable to be
  412.      * authenticated to complete and proceed with authorization.
  413.      */
  414.     if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK' && (!isset($_POST['MD']))) {
  415.       if (isset($_SESSION['3Dsecure_auth_status']&& isset($_SESSION['3Dsecure_auth_xid']&& isset($_SESSION['3Dsecure_auth_cavv']&& isset($_SESSION['3Dsecure_auth_eci'])) {
  416.         // at this point we have 3d-secure auth data
  417.       else {
  418.         // at this stage we need to prepare for checking whether 3d-secure is needed
  419.         $this->clear_3DSecure_session_vars(TRUE);
  420.         $_SESSION['3Dsecure_requires_lookup'$this->requiresLookup($_POST['paypalwpp_cc_number']);
  421.         $_SESSION['3Dsecure_card_type'$this->determineCardType($_POST['paypalwpp_cc_number']);
  422.       }
  423.       if (isset($_SESSION['3Dsecure_requires_lookup']&& $_SESSION['3Dsecure_requires_lookup'== TRUE{
  424.         $_SESSION['3Dsecure_merchantData'serialize(array('im'=>$_POST['paypalwpp_cc_issue_month']'iy'=>$_POST['paypalwpp_cc_issue_year']'in'=>$_POST['paypalwpp_cc_issuenumber']'fn'=>$_POST['paypalwpp_cc_firstname']'ln'=>$_POST['paypalwpp_cc_lastname']));
  425.         global $order_total_modules;
  426.         $calculatedOrderTotal $order_total_modules->pre_confirmation_check(TRUE);
  427.         $lookup_data_array array('currency' => $order->info['currency'],
  428.                                    'txn_amount' => $calculatedOrderTotal,
  429.                                    'order_desc' => 'Zen Cart(R) ' MODULE_PAYMENT_PAYPALDP_TEXT_TRANSACTION_FOR ' ' $_POST['paypalwpp_cc_firstname'' ' $_POST['paypalwpp_cc_lastname'],
  430.                                    'cc3d_card_number' => $_POST['paypalwpp_cc_number'],
  431.                                    'cc3d_checkcode' => $_POST['paypalwpp_cc_checkcode'],
  432.                                    'cc3d_exp_month' => $_POST['paypalwpp_cc_expires_month'],
  433.                                    'cc3d_exp_year' => $_POST['paypalwpp_cc_expires_year']  );
  434.         ////////////////////////////////////////////////////////////////////////////
  435.         // Process the enrollment lookup
  436.         ////////////////////////////////////////////////////////////////////////////
  437.         $lookup_response $this->get3DSecureLookupResponse($lookup_data_array);
  438.  
  439.         $shouldContinue $lookup_response['continue_flag'];
  440.         $errorNo $lookup_response['error_no'];
  441.         $errorDesc $lookup_response['error_desc'];
  442.         $_SESSION['3Dsecure_enrolled'$lookup_response['enrolled'];
  443.         $_SESSION['3Dsecure_transactionId'$lookup_response['transaction_id'];
  444.         $requestXML $lookup_response['requestXML'];
  445.         $rawXML $lookup_response['rawXML'];
  446.         if (isset($lookup_response['EciFlag'])) $_SESSION['3Dsecure_auth_eci'$lookup_response['EciFlag'];
  447.  
  448.         ////////////////////////////////////////////////////////////////////////////
  449.         // Assert that there was no error code returned and the Cardholder is
  450.         // enrolled in the authentication program prior to starting the
  451.         // Authentication process.
  452.         //
  453.         // If the card is not enrolled or an error was returned, check the business
  454.         // rules to determine if the order should continue.
  455.         ////////////////////////////////////////////////////////////////////////////
  456.  
  457.         if (strcasecmp('0'$errorNo== && strcasecmp('Y'$_SESSION['3Dsecure_enrolled']== 0{
  458.  
  459.           ////////////////////////////////////////////////////////////////////////
  460.           // Card is enrolled, continue to payer authentication
  461.           ////////////////////////////////////////////////////////////////////////
  462.           $_SESSION['3Dsecure_acsURL'$lookup_response['acs_url'];
  463.           $_SESSION['3Dsecure_payload'$lookup_response['payload'];
  464.           $this->form_action_url zen_href_link(FILENAME_PAYER_AUTH_FRAME'''SSL'truefalse);
  465.  
  466.         else {
  467.  
  468.           if ($shouldContinue != 'Y'{
  469.             ////////////////////////////////////////////////////////////////////
  470.             // Business rules are set to prompt for another form of payment
  471.             ////////////////////////////////////////////////////////////////////
  472.             $error$this->get_authentication_error();
  473.             if (in_array($errorNoarray('8000''8010''8020''8030'))) $error CENTINEL_PROCESSING_ERROR;
  474.             $reason $errorNo ' - ' $errorDesc;
  475.             $messageStack->add_session('checkout_payment'$error '<!-- ['.$this->code.'] -->' '<!-- result: ' $reason ' -->''error');
  476.             $errorText $error "\n\n" $reason "\n(" $this->code . ")\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  477.             zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSMODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT ' ' $reason$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  478.             zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT'''SSL'truefalse));
  479.  
  480.           else {
  481.             ////////////////////////////////////////////////////////////////////
  482.             // Business rules are set to continue to authorization
  483.             ////////////////////////////////////////////////////////////////////
  484.             if (!isset($_SESSION['3Dsecure_auth_eci']|| $_SESSION['3Dsecure_auth_eci'== ''{
  485.               // Not enrolled or error, determine the ECI value for the card number, and make it available to the payment module.
  486.               if ($_SESSION['3Dsecure_enrolled'== 'N'{
  487.                 switch($_SESSION['3Dsecure_card_type']{
  488.                   case 'VISA':
  489.                     $_SESSION['3Dsecure_auth_eci'"06";
  490.                     break;
  491.                   case 'MASTERCARD':
  492.                     $_SESSION['3Dsecure_auth_eci'"01";
  493.                     break;
  494.                   case 'JCB':
  495.                     $_SESSION['3Dsecure_auth_eci'"06";
  496.                     break;
  497.                 }
  498.               else if ('U' == $_SESSION['3Dsecure_enrolled'|| '0' != $errorNo{
  499.                 switch($_SESSION['3Dsecure_card_type']{
  500.                   case 'VISA':
  501.                     $_SESSION['3Dsecure_auth_eci'"07";
  502.                     break;
  503.                   case 'MASTERCARD':
  504.                     $_SESSION['3Dsecure_auth_eci'"01";
  505.                     break;
  506.                   case 'JCB':
  507.                     $_SESSION['3Dsecure_auth_eci'"07";
  508.                     break;
  509.                 }
  510.               }
  511.             }
  512.           }
  513.         }
  514.       }
  515.     }
  516.     // end uk/3d-secure check
  517.   }
  518.   /**
  519.    * Display Credit Card Information for review on the Checkout Confirmation Page
  520.    */
  521.   function confirmation({
  522.     $confirmation array('title' => '',
  523.                           'fields' => array(array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_FIRSTNAME,
  524.                                                   'field' => $_POST['paypalwpp_cc_firstname']),
  525.                                             array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_LASTNAME,
  526.                                                   'field' => $_POST['paypalwpp_cc_lastname']),
  527.                                             array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_TYPE,
  528.                                                   'field' => $this->cc_card_type),
  529.                                             array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_NUMBER,
  530.                                                   'field' => substr($_POST['paypalwpp_cc_number']04str_repeat('X'(strlen($_POST['paypalwpp_cc_number']8)) substr($_POST['paypalwpp_cc_number']-4)),
  531.                                             array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_CREDIT_CARD_EXPIRES,
  532.                                                   'field' => strftime('%B, %Y'mktime(0,0,0,$_POST['paypalwpp_cc_expires_month']1'20' $_POST['paypalwpp_cc_expires_year'])),
  533.                                             (isset($_POST['paypalwpp_cc_issuenumber']array('title' => MODULE_PAYMENT_PAYPALDP_TEXT_ISSUE_NUMBER,
  534.                                                   'field' => $_POST['paypalwpp_cc_issuenumber']'')
  535.                                             )));
  536.     // 3D-Secure
  537.     if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK' && $this->requiresLookup($_POST['paypalwpp_cc_number']== true{
  538.           $confirmation['fields'][count($confirmation['fields'])array(
  539.               'title' => '',
  540.               'field' => '<div id="' $this->code.'-cc-securetext"><p>' .
  541.                          '<a href="javascript:void window.open(\'vbv_learn_more.html\',\'vbv_service\',\'width=550,height=450\')">' .
  542.                          zen_image(DIR_WS_IMAGES.'3ds/vbv_learn_more.gif''</a>' .
  543.                          '<a href="javascript:void window.open(\'mcs_learn_more.html\',\'mcsc_service\',\'width=550,height=450\')">' .
  544.                          zen_image(DIR_WS_IMAGES.'3ds/mcsc_learn_more.gif''</a></p>' .
  545.                          '<p>' TEXT_3DS_CARD_MAY_BE_ENROLLED '</p></div>');
  546.     }
  547.     return $confirmation;
  548.   }
  549.   /**
  550.    * Prepare the hidden fields comprising the parameters for the Submit button on the checkout confirmation page
  551.    */
  552.   function process_button({
  553.     global $order;
  554.     $_SESSION['paypal_ec_markflow'1;
  555.     $process_button_string '';
  556.     $process_button_string .= "\n" zen_draw_hidden_field('wpp_cc_type'$_POST['paypalwpp_cc_type']"\n" .
  557.         zen_draw_hidden_field('wpp_cc_expdate_month'$_POST['paypalwpp_cc_expires_month']"\n" .
  558.         zen_draw_hidden_field('wpp_cc_expdate_year'$_POST['paypalwpp_cc_expires_year']"\n" .
  559.         zen_draw_hidden_field('wpp_cc_issuedate_month'$_POST['paypalwpp_cc_issue_month']"\n" .
  560.         zen_draw_hidden_field('wpp_cc_issuedate_year'$_POST['paypalwpp_cc_issue_year']"\n" .
  561.         zen_draw_hidden_field('wpp_cc_issuenumber'$_POST['paypalwpp_cc_issuenumber']"\n" .
  562.         zen_draw_hidden_field('wpp_cc_number'$_POST['paypalwpp_cc_number']"\n" .
  563.         zen_draw_hidden_field('wpp_cc_checkcode'$_POST['paypalwpp_cc_checkcode']"\n" .
  564.         zen_draw_hidden_field('wpp_payer_firstname'$_POST['paypalwpp_cc_firstname']"\n" .
  565.         zen_draw_hidden_field('wpp_payer_lastname'$_POST['paypalwpp_cc_lastname']"\n";
  566.     $process_button_string .= zen_draw_hidden_field(zen_session_name()zen_session_id());
  567.     return $process_button_string;
  568.   }
  569.   /**
  570.    * Prepare and submit the final authorization to PayPal via the appropriate means as configured
  571.    */
  572.   function before_process({
  573.     global $order$doPayPal$messageStack;
  574.     $options array();
  575.     $optionsShip array();
  576.     $optionsNVP array();
  577.  
  578.     $options $this->getLineItemDetails($this->selectCurrency($order->info['currency']));
  579.  
  580.     //$this->zcLog('before_process - 1', 'Have line-item details:' . "\n" . print_r($options, true));
  581.  
  582.     // Initializing DESC field: using for comments related to tax-included pricing, populated by getLineItemDetails()
  583.     $options['DESC''';
  584.  
  585.     $doPayPal $this->paypal_init();
  586.       /****************************************
  587.        * Do DP checkout
  588.        ****************************************/
  589.       $this->zcLog('before_process - DP-1''Beginning DP mode' /* . print_r($_POST, TRUE)*/);
  590.       // Set state fields depending on what PayPal wants to see for that country
  591.       $this->setStateAndCountry($order->billing);
  592.       if (zen_not_null($order->delivery['street_address'])) {
  593.         $this->setStateAndCountry($order->delivery);
  594.       }
  595.  
  596.       // Validate credit card data
  597.       include(DIR_WS_CLASSES 'cc_validation.php');
  598.       $cc_validation new cc_validation();
  599.       $response $cc_validation->validate($_POST['wpp_cc_number']$_POST['wpp_cc_expdate_month']$_POST['wpp_cc_expdate_year'],
  600.                                            $_POST['wpp_cc_issuedate_month']$_POST['wpp_cc_issuedate_year']);
  601.       $error '';
  602.       switch ($response{
  603.         case -1:
  604.           $error sprintf(TEXT_CCVAL_ERROR_UNKNOWN_CARDsubstr($cc_validation->cc_number04));
  605.           break;
  606.         case -2:
  607.         case -3:
  608.         case -4:
  609.           $error TEXT_CCVAL_ERROR_INVALID_DATE;
  610.           break;
  611.         case false:
  612.           $error TEXT_CCVAL_ERROR_INVALID_NUMBER;
  613.           break;
  614.       }
  615.  
  616.       if (($response === false|| ($response 1) ) {
  617.         $this->zcLog('before_process - DP-2''CC validation results: ' $error '(' $response ')');
  618.         $messageStack->add_session('checkout_payment'$error '<!-- ['.$this->code.'] -->' '<!-- result: ' $response ' -->''error');
  619.         zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT'''SSL'truefalse));
  620.       }
  621.       if (!in_array($cc_validation->cc_typearray('Visa''MasterCard''Switch''Solo''Discover''American Express''Maestro'))) {
  622. //        $this->zcLog('before_process - DP-3', 'CC info: ' . $cc_validation->cc_type . ' ' . substr($cc_validation->cc_number, 0, 4) . str_repeat('X', (strlen($cc_validation->cc_number) - 8)) . substr($cc_validation->cc_number, -4) . ' ' . $error);
  623.         $messageStack->add_session('checkout_payment'MODULE_PAYMENT_PAYPALDP_TEXT_BAD_CARD '<!-- [' $this->code . ' ' $cc_validation->cc_type '] -->''error');
  624.         zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT'''SSL'truefalse));
  625.       }
  626.  
  627.       // if CC validation passed, continue using the validated data
  628.       $cc_type $cc_validation->cc_type;
  629.       $cc_number $cc_validation->cc_number;
  630.       $cc_first_name ($_POST['wpp_payer_firstname'!= '' $_POST['wpp_payer_firstname'$_SESSION['customer_first_name']);
  631.       $cc_last_name ($_POST['wpp_payer_lastname'!= '' $_POST['wpp_payer_lastname'$_SESSION['customer_last_name']);
  632.       $cc_checkcode $_POST['wpp_cc_checkcode'];
  633.       $cc_expdate_month $cc_validation->cc_expiry_month;
  634.       $cc_expdate_year $cc_validation->cc_expiry_year;
  635.       $cc_issuedate_month $_POST['wpp_cc_issuedate_month'];
  636.       $cc_issuedate_year $_POST['wpp_cc_issuedate_year'];
  637.       $cc_issuenumber $_POST['wpp_cc_issuenumber'];
  638.       $cc_owner_ip current(explode(':'str_replace(','':'zen_get_ip_address())));
  639.  
  640.       // If they're still here, set some of the order object's variables.
  641.       $order->info['cc_type'$cc_type;
  642.       $order->info['cc_number'substr($cc_number04str_repeat('X'(strlen($cc_number8)) substr($cc_number-4);
  643.       $order->info['cc_owner'$cc_first_name ' ' $cc_last_name;
  644.       $order->info['cc_expires'''//$cc_expdate_month . substr($cc_expdate_year, -2);
  645.       $order->info['ip_address'$cc_owner_ip;
  646.  
  647.       // Set currency
  648.       $my_currency $this->selectCurrency($order->info['currency']'DP');
  649.  
  650.       // if CC is switch or solo, must be GBP
  651.       if (in_array($cc_typearray('Switch''Solo''Maestro'))) {
  652.         $my_currency 'GBP';
  653.       }
  654.  
  655. //      $order->info['total'] = zen_round($order->info['total'], 2);
  656.       $order_amount $this->calc_order_amount($order->info['total']$my_currency);
  657.       $display_order_amount $this->calc_order_amount($order->info['total']$my_currencyTRUE);
  658.  
  659.  
  660.       // 3D-Secure
  661.       if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK'{
  662.         // determine the card type and validate that authentication was attempted and completed if applicable
  663.         if (($_SESSION['3Dsecure_requires_lookup'|| $this->requiresLookup($_POST['wpp_cc_number']== true)) {  // authentication attempt required?
  664.           // validate an acceptable lookup result
  665.           if (isset($_SESSION['3Dsecure_enroll_lookup_attempted']== false || strcasecmp($_SESSION['3Dsecure_enroll_lookup_attempted']'Y'!= 0{
  666.             // lookup never attempted for required card, so need to redirect to payment-selection page
  667.             $reason 'Customer arrived on the order process page without attempting authentication lookup.';
  668.             $error MODULE_PAYMENT_PAYPALDP_CANNOT_BE_COMPLETED;
  669.             $messageStack->add_session('checkout_payment'$error '<!-- ['.$this->code.'] -->' '<!-- result: ' $reason ' -->''error');
  670.             $errorText $reason ."\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  671.             $errorText .= $this->code;
  672.             zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSMODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  673.             zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT'''SSL'truefalse));
  674.           }
  675.           // if enrolled, validate an acceptable authentication result
  676.           if (strcasecmp('Y'$_SESSION['3Dsecure_enrolled']== 0{
  677.             if (isset($_SESSION['3Dsecure_authentication_attempted']== false || strcasecmp($_SESSION['3Dsecure_authentication_attempted']'Y'!= 0{
  678.               $reason 'Customer arrived on the order process page without completing required authentication.';
  679.               $error MODULE_PAYMENT_PAYPALDP_CANNOT_BE_COMPLETED;
  680.               $messageStack->add_session('checkout_payment'$error '<!-- ['.$this->code.'] -->' '<!-- result: ' $reason ' -->''error');
  681.               $errorText $reason ."\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  682.               $errorText .= $this->code;
  683.               zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSMODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  684.  
  685.               // remove the lookup/auth attempted status
  686.               unset($_SESSION['3Dsecure_enroll_lookup_attempted']);
  687.               unset($_SESSION['3Dsecure_authentication_attempted']);
  688.  
  689.               // authentication result was not acceptable, redirect
  690.               zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT'''SSL'truefalse));
  691.             }
  692.           }
  693.         }
  694.         if ($cc_type != 'Solo'{  // PayPal doesn't support 3d-secure on Solo cards
  695.           if (isset($_SESSION['3Dsecure_enrolled'])) {
  696.             $options['MPIVENDOR3DS'$_SESSION['3Dsecure_enrolled'];
  697.           }
  698.           if ($_SESSION['3Dsecure_auth_eci'!= ''{
  699.             $options['ECI'$_SESSION['3Dsecure_auth_eci'];
  700.           }
  701.           if (isset($_SESSION['3Dsecure_auth_xid']and strlen($_SESSION['3Dsecure_auth_xid']0{
  702.             $options['XID'$_SESSION['3Dsecure_auth_xid'];
  703.             $options['CAVV'$_SESSION['3Dsecure_auth_cavv'];
  704.             $options['AUTHSTATUS3DS'$_SESSION['3Dsecure_auth_status'];
  705.           }
  706.         }
  707.       }
  708. ///////////////////////////
  709.  
  710.  
  711.       // Initialize the paypal caller object.
  712.       $doPayPal $this->paypal_init();
  713.       $optionsAll array_merge($options,
  714.                     array('STREET'      => $order->billing['street_address'],
  715.                           'ZIP'         => $order->billing['postcode'],
  716.                           'CITY'        => $order->billing['city'],
  717.                           'STATE'       => $order->billing['state'],
  718.                           'STREET2'     => $order->billing['suburb'],
  719.                           'COUNTRYCODE' => $order->billing['country']['iso_code_2'],
  720.                           'EXPDATE'     => $cc_expdate_month $cc_expdate_year,
  721.                           'EMAIL'       => $order->customer['email_address'],
  722.                           'PHONENUM'    => $order->customer['telephone']));
  723.  
  724.       $optionsShip array();
  725.       if (isset($order->delivery&& $order->delivery['street_address'!= ''{
  726.         $optionsShiparray('SHIPTONAME'   => ($order->delivery['name'== '' $order->delivery['firstname'' ' $order->delivery['lastname'$order->delivery['name']),
  727.                             'SHIPTOSTREET' => $order->delivery['street_address'],
  728.                             'SHIPTOSTREET2' => $order->delivery['suburb'],
  729.                             'SHIPTOCITY'   => $order->delivery['city'],
  730.                             'SHIPTOZIP'    => $order->delivery['postcode'],
  731.                             'SHIPTOSTATE'  => $order->delivery['state'],
  732.                             'SHIPTOCOUNTRYCODE'=> $order->delivery['country']['iso_code_2']);
  733.       }
  734.       // if these optional parameters are blank, remove them from transaction
  735.       if (isset($optionsShip['SHIPTOSTREET2']&& trim($optionsShip['SHIPTOSTREET2']== ''unset($optionsShip['SHIPTOSTREET2']);
  736.       if ($optionsAll['STREET2'== ''unset($optionsAll['STREET2']);
  737.       if (isset($optionsShip['SHIPTOPHONE']&& trim($optionsShip['SHIPTOPHONE']== ''unset($optionsShip['SHIPTOPHONE']);
  738.  
  739.       // if State is not supplied, repeat the city so that it's not blank, otherwise PayPal croaks
  740.       if ((!isset($optionsShip['SHIPTOSTATE']|| trim($optionsShip['SHIPTOSTATE']== ''&& isset($optionsShip['SHIPTOCITY'])) $optionsShip['SHIPTOSTATE'$optionsShip['SHIPTOCITY'];
  741.  
  742.       // Payment Transaction/Authorization Mode
  743.       $optionsNVP['PAYMENTACTION'(MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE == 'Auth Only''Authorization' 'Sale';
  744.       if (MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE == 'Auth Only'$this->order_status = $this->order_pending_status;
  745.  
  746. //      if (in_array($cc_type, array('Switch', 'Solo'))) {
  747. //        $optionsNVP['PAYMENTACTION'] = 'Authorization';
  748. //      }
  749.       $optionsAll['BUTTONSOURCE'$this->buttonSource;
  750.       $optionsAll['CURRENCY']     $my_currency;
  751.       if (strlen($cc_owner_ip7{
  752.         $optionsAll['IPADDRESS']    $cc_owner_ip;
  753.       }
  754.       if ($cc_issuedate_month && $cc_issuedate_year{
  755.         $optionsAll['CARDSTART'$cc_issuedate_month substr($cc_issuedate_year-2);
  756.       }
  757.       if (isset($_POST['wpp_cc_issuenumber'])) $optionsAll['CARDISSUE'$_POST['wpp_cc_issuenumber'];
  758.  
  759.       // Add note to track that this was an API WPP transaction:
  760.       $optionsAll['CUSTOM''DP-' . (int)$_SESSION['customer_id''-' time();
  761.  
  762.       // send the store name as transaction identifier, to help distinguish payments between multiple stores:
  763.       $optionsAll['INVNUM'= (int)$_SESSION['customer_id''-' time('-[' substr(preg_replace('/[^a-zA-Z0-9_]/'''STORE_NAME)030']';  // (cannot send actual invoice number because it's not assigned until after payment is completed)
  764.  
  765.       if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK' || (MODULE_PAYMENT_PAYPALWPP_PFVENDOR != '' && MODULE_PAYMENT_PAYPALWPP_PFPASSWORD != '')) // Payflow params required
  766.         if (isset($optionsAll['COUNTRYCODE'])) {
  767.           $optionsAll['COUNTRY'$optionsAll['COUNTRYCODE'];
  768.           unset($optionsAll['COUNTRYCODE']);
  769.         }
  770.         if (isset($optionsShip['SHIPTOCOUNTRYCODE'])) {
  771.           $optionsShip['SHIPTOCOUNTRY'$optionsShip['SHIPTOCOUNTRYCODE'];
  772.           unset($optionsShip['SHIPTOCOUNTRYCODE']);
  773.         }
  774.         if (isset($optionsShip['SHIPTOSTREET2'])) unset($optionsShip['SHIPTOSTREET2']);
  775.         if (isset($optionsAll['STREET2'])) unset($optionsAll['STREET2']);
  776.       }
  777.       if (isset($optionsAll['DESC']&& $optionsAll['DESC'== ''unset($optionsAll['DESC']);
  778.       $this->zcLog('before_process - DP-4''options: ' print_r(array_merge($optionsAll$optionsNVP$optionsShip)true"\n" 'Rest of data: ' "\n" number_format($order_amount2' ' $cc_expdate_month ' ' substr($cc_expdate_year-2' ' $cc_first_name ' ' $cc_last_name ' ' $cc_type);
  779.  
  780.       if (!isset($optionsAll['AMT'])) $optionsAll['AMT'number_format($order_amount2'.''');
  781.       $response $doPayPal->DoDirectPayment($cc_number,
  782.                                            $cc_checkcode,
  783.                                            $cc_expdate_month substr($cc_expdate_year-2),
  784.                                            $cc_first_name$cc_last_name,
  785.                                            $cc_type,
  786.                                            $optionsAllarray_merge($optionsNVP$optionsShip));
  787.  
  788.       $this->zcLog('before_process - DP-5''resultset:' "\n" urldecode(print_r($responsetrue)));
  789.  
  790.       // CHECK RESPONSE
  791.       $error $this->_errorHandler($response'DoDirectPayment');
  792.  
  793.       if ($this->fmfResponse != ''{
  794.         $this->order_status = $this->order_pending_status;
  795.       }
  796.  
  797.       $this->feeamt '';
  798.       $this->taxamt '';
  799.       $this->pendingreason '';
  800.       $this->reasoncode '';
  801.       $this->numitems sizeof($order->products);
  802.       $this->responsedata $response;
  803.  
  804.       if ($response['PNREF']{
  805.       // PNREF only comes from payflow mode
  806.         $this->payment_type MODULE_PAYMENT_PAYPALDP_PF_TEXT_TYPE;
  807.         $this->transaction_id $response['PNREF'];
  808.         $this->payment_status (MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE == 'Auth Only''Authorization' 'Completed';
  809.         $this->avs 'AVSADDR: ' $response['AVSADDR'', AVSZIP: ' $response['AVSZIP'', IAVS: ' $response['IAVS'];
  810.         $this->cvv2 $response['CVV2MATCH'];
  811.         $this->amt $display_order_amount ' ' $my_currency;
  812.         $this->payment_time date('Y-m-d h:i:s');
  813.         $this->responsedata['CURRENCYCODE'$my_currency;
  814.         $this->responsedata['EXCHANGERATE'$order->info['currency_value'];
  815.         $this->auth_code $this->response['AUTHCODE'];
  816.       else {
  817.         // here we're in NVP mode
  818.         $this->transaction_id $response['TRANSACTIONID'];
  819.         $this->payment_type MODULE_PAYMENT_PAYPALDP_DP_TEXT_TYPE;
  820.         $this->payment_status (MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE == 'Auth Only''Authorization' 'Completed';
  821.         $this->pendingreason (MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE == 'Auth Only''authorization' '';
  822.         $this->avs $response['AVSCODE'];
  823.         $this->cvv2 $response['CVV2MATCH'];
  824.         $this->correlationid $response['CORRELATIONID'];
  825.         $this->payment_time urldecode($response['TIMESTAMP']);
  826.         $this->amt urldecode($response['AMT'' ' $response['CURRENCYCODE']);
  827.         $this->auth_code (isset($this->response['AUTHCODE'])) $this->response['AUTHCODE'$this->response['TOKEN'];
  828.         $this->transactiontype 'cart';
  829.       }
  830.   }
  831.   /**
  832.    * When the order returns from the processor, this stores the results in order-status-history and logs data for subsequent use
  833.    */
  834.   function after_process({
  835.     global $insert_id$db$order;
  836.     // FMF
  837.     if ($this->fmfResponse != ''{
  838.       $detailedMessage $insert_id "\n" $this->fmfResponse . "\n" MODULES_PAYMENT_PAYPALDP_TEXT_EMAIL_FMF_INTRO "\n" print_r($this->fmfErrorsTRUE);
  839.       zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSMODULES_PAYMENT_PAYPALDP_TEXT_EMAIL_FMF_SUBJECT ' (' $insert_id ')'$detailedMessageSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($detailedMessage))'paymentalert');
  840.     }
  841.  
  842.     // add a new OSH record for this order's PP details
  843.     $commentString "Transaction ID: :transID: " .
  844.                      (isset($this->responsedata['PPREF']"\nPPRef: " $this->responsedata['PPREF'"".
  845.                      (isset($this->responsedata['AUTHCODE'])"\nAuthCode: " $this->responsedata['AUTHCODE'"".
  846.                                  "\nPayment Type: :pmtType: " .
  847.                      ($this->payment_time != '' "\nTimestamp: :pmtTime: " "".
  848.                                  "\nPayment Status: :pmtStatus: " .
  849.                      (isset($this->responsedata['auth_exp']"\nAuth-Exp: " $this->responsedata['auth_exp'"".
  850.                      ($this->avs != 'N/A' "\nAVS Code: ".$this->avs."\nCVV2 Code: ".$this->cvv2 ''.
  851.                      (trim($this->amt!= '' "\nAmount: :orderAmt: " "");
  852.     $commentString $db->bindVars($commentString':transID:'$this->transaction_id'noquotestring');
  853.     $commentString $db->bindVars($commentString':pmtType:'$this->payment_type'noquotestring');
  854.     $commentString $db->bindVars($commentString':pmtTime:'$this->payment_time'noquotestring');
  855.     $commentString $db->bindVars($commentString':pmtStatus:'$this->payment_status'noquotestring');
  856.     $commentString $db->bindVars($commentString':orderAmt:'$this->amt'noquotestring');
  857.  
  858.     $sql_data_arrayarray(array('fieldName'=>'orders_id''value'=>$insert_id'type'=>'integer'),
  859.                            array('fieldName'=>'orders_status_id''value'=>$order->info['order_status']'type'=>'integer'),
  860.                            array('fieldName'=>'date_added''value'=>'now()''type'=>'noquotestring'),
  861.                            array('fieldName'=>'customer_notified''value'=>0'type'=>'integer'),
  862.                            array('fieldName'=>'comments''value'=>$commentString'type'=>'string'));
  863.     $db->perform(TABLE_ORDERS_STATUS_HISTORY$sql_data_array);
  864.  
  865.     // 3D-Secure
  866.     if ($this->requiresLookup($order->info['cc_type']== true{
  867.       // CardinalCommerce Liability Protection Status
  868.       // Inserts 'PROTECTED' or 'NOT PROTECTED' status, ECI, CAVV values in the order status history comments
  869.       $auth_proc_status $this->determine3DSecureProtection($order->info['cc_type']$_SESSION['3Dsecure_auth_eci']);
  870.       $commentString "3D-Secure: " $auth_proc_status "\n" 'ECI Value = ' $_SESSION['3Dsecure_auth_eci'"\n" 'CAVV Value = ' $_SESSION['3Dsecure_auth_cavv'];
  871.       $sql_data_arrayarray(array('fieldName'=>'orders_id''value'=> $insert_id'type'=>'integer'),
  872.                              array('fieldName'=>'orders_status_id''value'=> $order->info['order_status']'type'=>'integer'),
  873.                              array('fieldName'=>'date_added''value'=>'now()''type'=>'noquotestring'),
  874.                              array('fieldName'=>'customer_notified''value'=> -1'type'=>'integer'),
  875.                              array('fieldName'=>'comments''value'=> $commentString'type'=>'string'));
  876.       $db->perform(TABLE_ORDERS_STATUS_HISTORY$sql_data_array);
  877.     }
  878.  
  879.     // store the PayPal order meta data -- used for later matching and back-end processing activities
  880.     $paypal_order array('order_id' => $insert_id,
  881.                           'txn_type' => $this->transactiontype,
  882.                           'module_name' => $this->code,
  883.                           'module_mode' => MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY,
  884.                           'reason_code' => $this->reasoncode,
  885.                           'payment_type' => $this->payment_type,
  886.                           'payment_status' => $this->payment_status,
  887.                           'pending_reason' => $this->pendingreason,
  888.                           'invoice' => urldecode($_SESSION['paypal_ec_token'$this->responsedata['PPREF']),
  889.                           'first_name' => $_SESSION['paypal_ec_payer_info']['payer_firstname'],
  890.                           'last_name' => $_SESSION['paypal_ec_payer_info']['payer_lastname'],
  891.                           'payer_business_name' => $_SESSION['paypal_ec_payer_info']['payer_business'],
  892.                           'address_name' => $_SESSION['paypal_ec_payer_info']['ship_name'],
  893.                           'address_street' => $_SESSION['paypal_ec_payer_info']['ship_street_1'],
  894.                           'address_city' => $_SESSION['paypal_ec_payer_info']['ship_city'],
  895.                           'address_state' => $_SESSION['paypal_ec_payer_info']['ship_state'],
  896.                           'address_zip' => $_SESSION['paypal_ec_payer_info']['ship_postal_code'],
  897.                           'address_country' => $_SESSION['paypal_ec_payer_info']['ship_country'],
  898.                           'address_status' => $_SESSION['paypal_ec_payer_info']['ship_address_status'],
  899.                           'payer_email' => $_SESSION['paypal_ec_payer_info']['payer_email'],
  900.                           'payer_id' => $_SESSION['paypal_ec_payer_id'],
  901.                           'payer_status' => $_SESSION['paypal_ec_payer_info']['payer_status'],
  902.                           'payment_date' => trim(preg_replace('/[^0-9-:]/'' '$this->payment_time)),
  903.                           'business' => '',
  904.                           'receiver_email' => (MODULE_PAYMENT_PAYPALWPP_PFVENDOR != '' MODULE_PAYMENT_PAYPALWPP_PFVENDOR str_replace('_api1'''MODULE_PAYMENT_PAYPALWPP_APIUSERNAME)),
  905.                           'receiver_id' => '',
  906.                           'txn_id' => $this->transaction_id,
  907.                           'parent_txn_id' => '',
  908.                           'num_cart_items' => (float)$this->numitems,
  909.                           'mc_gross' => (float)$this->amt,
  910.                           'mc_fee' => (float)urldecode($this->feeamt),
  911.                           'mc_currency' => $this->responsedata['CURRENCYCODE'],
  912.                           'settle_amount' => (float)urldecode($this->responsedata['SETTLEAMT']),
  913.                           'settle_currency' => $this->responsedata['CURRENCYCODE'],
  914.                           'exchange_rate' => (urldecode($this->responsedata['EXCHANGERATE']urldecode($this->responsedata['EXCHANGERATE']1.0),
  915.                           'notify_version' => '0',
  916.                           'verify_sign' =>'',
  917.                           'date_added' => 'now()',
  918.                           'memo' => (sizeof($this->fmfErrors'FMF Details ' print_r($this->fmfErrorsTRUE'{Record generated by payment module}'),
  919.                          );
  920.     zen_db_perform(TABLE_PAYPAL$paypal_order);
  921.  
  922.     // Unregister the paypal session variables, making it necessary to start again for another purchase
  923.     unset($_SESSION['paypal_ec_temp']);
  924.     unset($_SESSION['paypal_ec_token']);
  925.     unset($_SESSION['paypal_ec_payer_id']);
  926.     unset($_SESSION['paypal_ec_payer_info']);
  927.     unset($_SESSION['paypal_ec_final']);
  928.     unset($_SESSION['paypal_ec_markflow']);
  929.     $this->clear_3DSecure_session_vars(TRUE);
  930.   }
  931.   /**
  932.     * Build admin-page components
  933.     *
  934.     * @param int $zf_order_id 
  935.     * @return string 
  936.     */
  937.   function admin_notification($zf_order_id{
  938.     if (!defined('MODULE_PAYMENT_PAYPALDP_STATUS')) return '';
  939.     global $db;
  940.     $module $this->code;
  941.     $output '';
  942.     $response $this->_GetTransactionDetails($zf_order_id);
  943.     //$response = $this->_TransactionSearch('2006-12-01T00:00:00Z', $zf_order_id);
  944.     $sql "SELECT * from " TABLE_PAYPAL " WHERE order_id = :orderID
  945.             AND parent_txn_id = '' AND order_id > 0
  946.             ORDER BY paypal_ipn_id DESC LIMIT 1";
  947.     $sql $db->bindVars($sql':orderID'$zf_order_id'integer');
  948.     $ipn $db->Execute($sql);
  949.     if ($ipn->RecordCount(== 0$ipn->fields array();
  950.     if (file_exists(DIR_FS_CATALOG DIR_WS_MODULES 'payment/paypal/paypalwpp_admin_notification.php')) require(DIR_FS_CATALOG DIR_WS_MODULES 'payment/paypal/paypalwpp_admin_notification.php');
  951.     return $output;
  952.   }
  953.   /**
  954.    * Used to read details of an existing transaction.  FOR FUTURE USE.
  955.    */
  956.   function _GetTransactionDetails($oID{
  957.     if ($oID == '' || $oID 1return FALSE;
  958.     global $db$messageStack$doPayPal;
  959.     $doPayPal $this->paypal_init();
  960.     // look up history on this order from PayPal table
  961.     $sql "select * from " TABLE_PAYPAL " where order_id = :orderID order by last_modified DESC, date_added DESC, parent_txn_id DESC, paypal_ipn_id DESC LIMIT 2";
  962.     $sql $db->bindVars($sql':orderID'$oID'integer');
  963.     $zc_ppHist $db->Execute($sql);
  964.     if ($zc_ppHist->RecordCount(== 0return false;
  965.     $txnID $zc_ppHist->fields['txn_id'];
  966.     if ($txnID == '' || $txnID === 0return FALSE;
  967.     /**
  968.      * Read data from PayPal
  969.      */
  970.     $response $doPayPal->GetTransactionDetails($txnID);
  971.     if (isset($response['RESULT']&& $response['RESULT'== '7' && $zc_ppHist->RecordCount(1{
  972.       $sql "select * from " TABLE_PAYPAL " where order_id = :orderID and txn_id != :condition: order by last_modified ASC, date_added ASC, paypal_ipn_id ASC LIMIT 1";
  973.       $sql $db->bindVars($sql':orderID'$oID'integer');
  974.       $sql $db->bindVars($sql':condition:'$zc_ppHist->fields['txn_id']'integer');
  975.       $zc_ppHist $db->Execute($sql);
  976.       if ($zc_ppHist->RecordCount(== 0return false;
  977.       $txnID $zc_ppHist->fields['txn_id'];
  978.       if ($txnID == '' || $txnID === 0return FALSE;
  979.       $response $doPayPal->GetTransactionDetails($txnID);
  980.     }
  981.  
  982.     $error $this->_errorHandler($response'GetTransactionDetails'10007);
  983.     if ($error === true{
  984.       return false;
  985.     else {
  986.       return $response;
  987.     }
  988.   }
  989.   /**
  990.    * Used to read details of existing transactions.  FOR FUTURE USE.
  991.    */
  992.   function _TransactionSearch($startDate ''$oID ''$criteria ''{
  993.     global $db$messageStack$doPayPal;
  994.     $doPayPal $this->paypal_init();
  995.     // look up history on this order from PayPal table
  996.     $sql "select * from " TABLE_PAYPAL " where order_id = :orderID  AND parent_txn_id = '' ";
  997.     $sql $db->bindVars($sql':orderID'$oID'integer');
  998.     $zc_ppHist $db->Execute($sql);
  999.     if ($zc_ppHist->RecordCount(== 0return false;
  1000.     $txnID $zc_ppHist->fields['txn_id'];
  1001.     $startDate $zc_ppHist->fields['payment_date'];
  1002.     $timeval time();
  1003.     if ($startDate == ''$startDate date('Y-m-d'$timeval'T' date('h:i:s'$timeval'Z';
  1004.     /**
  1005.      * Read data from PayPal
  1006.      */
  1007.     $response $doPayPal->TransactionSearch($startDate$txnID$email$criteria);
  1008.  
  1009.     $error $this->_errorHandler($response'TransactionSearch');
  1010.     if ($error === false{
  1011.       return false;
  1012.     else {
  1013.       return $response;
  1014.     }
  1015.   }
  1016.   /**
  1017.    * Evaluate installation status of this module. Returns true if the status key is found.
  1018.    */
  1019.   function check({
  1020.     global $db;
  1021.     if (!isset($this->_check)) {
  1022.       $check_query $db->Execute("select configuration_value from " TABLE_CONFIGURATION " where configuration_key = 'MODULE_PAYMENT_PAYPALDP_STATUS'");
  1023.       $this->_check !$check_query->EOF;
  1024.     }
  1025.     return $this->_check;
  1026.   }
  1027.   /**
  1028.    * Installs all the configuration keys for this module
  1029.    */
  1030.   function install({
  1031.     global $db$messageStack;
  1032.     if (defined('MODULE_PAYMENT_PAYPALDP_STATUS')) {
  1033.       $messageStack->add_session('Website Payments Pro module already installed.''error');
  1034.       zen_redirect(zen_href_link(FILENAME_MODULES'set=payment&module=paypaldp''NONSSL'));
  1035.       return 'failed';
  1036.     }
  1037.     // cannot install DP if EC not already enabled:
  1038.     if (!defined('MODULE_PAYMENT_PAYPALWPP_STATUS'|| MODULE_PAYMENT_PAYPALWPP_STATUS != 'True'{
  1039.       $messageStack->add_session('<strong>Sorry, you must install and configure PayPal Express Checkout first.</strong> Website Payments Pro requires that you offer Express Checkout to your customers.<br /><a href="' zen_href_link('modules.php?set=payment&module=paypalwpp''''NONSSL''">Click here to set up Express Checkout.</a>' 'error');
  1040.       zen_redirect(zen_href_link(FILENAME_MODULES'set=payment&module=paypaldp''NONSSL'));
  1041.       return 'failed';
  1042.     }
  1043.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable this Payment Module', 'MODULE_PAYMENT_PAYPALDP_STATUS', 'True', 'Do you want to enable this payment module?', '6', '25', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
  1044.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Live or Sandbox', 'MODULE_PAYMENT_PAYPALDP_SERVER', 'live', '<strong>Live: </strong> Used to process Live transactions<br><strong>Sandbox: </strong>For developers and testing', '6', '25', 'zen_cfg_select_option(array(\'live\', \'sandbox\'), ', now())");
  1045.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort order of display.', 'MODULE_PAYMENT_PAYPALDP_SORT_ORDER', '0', 'Sort order of display. Lowest is displayed first.', '6', '25', now())");
  1046.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Payment Zone', 'MODULE_PAYMENT_PAYPALDP_ZONE', '0', 'If a zone is selected, only enable this payment method for that zone.', '6', '25', 'zen_get_zone_class_title', 'zen_cfg_pull_down_zone_classes(', now())");
  1047.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Order Status', 'MODULE_PAYMENT_PAYPALDP_ORDER_STATUS_ID', '2', 'Set the status of orders paid with this payment module to this value. <br /><strong>Recommended: Processing[2]</strong>', '6', '25', 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())");
  1048.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Unpaid Order Status', 'MODULE_PAYMENT_PAYPALDP_ORDER_PENDING_STATUS_ID', '1', 'Set the status of unpaid orders made with this payment module to this value. <br /><strong>Recommended: Pending[1]</strong>', '6', '25', 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())");
  1049.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Refund Order Status', 'MODULE_PAYMENT_PAYPALDP_REFUNDED_STATUS_ID', '1', 'Set the status of refunded orders to this value. <br /><strong>Recommended: Pending[1]</strong>', '6', '25', 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())");
  1050.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Payment Action', 'MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE', 'Final Sale', 'How do you want to obtain payment?<br /><strong>Default: Final Sale</strong>', '6', '25', 'zen_cfg_select_option(array(\'Auth Only\', \'Final Sale\'), ',  now())");
  1051.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Transaction Currency', 'MODULE_PAYMENT_PAYPALDP_CURRENCY',  'Selected Currency', 'Which currency should the order be sent to PayPal as? <br />NOTE: if an unsupported currency is sent to PayPal, it will be auto-converted to USD (or GBP if using UK account)<br /><strong>Default: Selected Currency</strong>', '6', '25', 'zen_cfg_select_option(array(\'Selected Currency\', \'Only USD\', \'Only AUD\', \'Only CAD\', \'Only EUR\', \'Only GBP\', \'Only CHF\', \'Only CZK\', \'Only DKK\', \'Only HKD\', \'Only HUF\', \'Only JPY\', \'Only NOK\', \'Only NZD\', \'Only PLN\', \'Only SEK\', \'Only SGD\'), ', now())");
  1052.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Fraud Mgmt Filters - FMF', 'MODULE_PAYMENT_PAYPALDP_EC_RETURN_FMF_DETAILS', 'No', 'If you have enabled FMF support in your PayPal account and wish to utilize it in your transactions, set this to yes. Otherwise, leave it at No.', '6', '25','zen_cfg_select_option(array(\'No\', \'Yes\'), ', now())");
  1053.  
  1054.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Merchant Country', 'MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY', 'USA', 'Which country is your PayPal Account registered to? <br /><u>Choices:</u><br /><font color=green>You will need to supply <strong>API Settings</strong> in the Express Checkout module.</font><br /><strong>USA and Canada merchants</strong> need PayPal API credentials and a Website Payments Pro account.<br /><strong>UK merchants</strong> need to supply <strong>PAYFLOW settings</strong> (and have a Payflow account)', '6', '25',  'zen_cfg_select_option(array(\'USA\', \'UK\', \'Canada\'), ', now())");
  1055.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Debug Mode', 'MODULE_PAYMENT_PAYPALDP_DEBUGGING', 'Off', 'Would you like to enable debug mode?  A complete detailed log of failed transactions will be emailed to the store owner.', '6', '25', 'zen_cfg_select_option(array(\'Off\', \'Alerts Only\', \'Log File\', \'Log and Email\'), ', now())");
  1056.  
  1057.     // 3D-Secure
  1058.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Cardinal Processor ID', 'MODULE_PAYMENT_PAYPALDP_CARDINAL_PROCESSOR', '134-01', 'The processor ID for the Cardinal Centinel service. ', '6', '25', now())");
  1059.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Cardinal Merchant ID', 'MODULE_PAYMENT_PAYPALDP_CARDINAL_MERCHANT', 'enter value', 'The merchant ID for the Cardinal Centinel service. ', '6', '25', now())");
  1060.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, set_function, use_function) values ('Cardinal Transaction Password', 'MODULE_PAYMENT_PAYPALDP_CARDINAL_PASSWORD', '', 'Enter your Cardinal Transaction Password from your Cardinal Merchant Admin console. This is used to secure and verify that the transaction originated from your store legitimately.', '6', '25', now(), 'zen_cfg_password_input(', 'zen_cfg_password_display')");
  1061.     $db->Execute("insert into " TABLE_CONFIGURATION " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Only Accept Chargeback-Protected Orders via Cardinal?', 'MODULE_PAYMENT_PAYPALDP_CARDINAL_AUTHENTICATE_REQ', 'No', 'Only proceed with authorization when the Cardinal authentication result provides chargeback protection? ', '6', '25', 'zen_cfg_select_option(array(\'Yes\', \'No\'), ', now())");
  1062.  
  1063.     $this->notify('NOTIFY_PAYMENT_PAYPALDP_INSTALLED');
  1064.   }
  1065.  
  1066.   function keys({
  1067.     $keys_list array('MODULE_PAYMENT_PAYPALDP_STATUS''MODULE_PAYMENT_PAYPALDP_SORT_ORDER''MODULE_PAYMENT_PAYPALDP_ZONE''MODULE_PAYMENT_PAYPALDP_ORDER_STATUS_ID''MODULE_PAYMENT_PAYPALDP_ORDER_PENDING_STATUS_ID''MODULE_PAYMENT_PAYPALDP_REFUNDED_STATUS_ID''MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE''MODULE_PAYMENT_PAYPALDP_CURRENCY''MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY''MODULE_PAYMENT_PAYPALDP_EC_RETURN_FMF_DETAILS''MODULE_PAYMENT_PAYPALDP_SERVER''MODULE_PAYMENT_PAYPALDP_DEBUGGING');
  1068.     if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK'{
  1069.       $keys_list array_merge($keys_listarray('MODULE_PAYMENT_PAYPALDP_CARDINAL_PROCESSOR','MODULE_PAYMENT_PAYPALDP_CARDINAL_MERCHANT','MODULE_PAYMENT_PAYPALDP_CARDINAL_PASSWORD','MODULE_PAYMENT_PAYPALDP_CARDINAL_AUTHENTICATE_REQ'));
  1070.     }
  1071.     return $keys_list;
  1072.   }
  1073.   /**
  1074.    * De-install this module
  1075.    */
  1076.   function remove({
  1077.     global $db;
  1078.     $db->Execute("delete from " TABLE_CONFIGURATION " where configuration_key LIKE 'MODULE\_PAYMENT\_PAYPALDP\_%'");
  1079.     $this->notify('NOTIFY_PAYMENT_PAYPALDP_UNINSTALLED');
  1080.   }
  1081.   /**
  1082.    * Check settings and conditions to determine whether we are in an Express Checkout phase or not
  1083.    */
  1084.   function in_special_checkout({
  1085.     if ((defined('MODULE_PAYMENT_PAYPALDP_STATUS'&& MODULE_PAYMENT_PAYPALDP_STATUS == 'True'&&
  1086.              !empty($_SESSION['paypal_ec_token']&&
  1087.              !empty($_SESSION['paypal_ec_payer_id']&&
  1088.              !empty($_SESSION['paypal_ec_payer_info'])) {
  1089.       return true;
  1090.     }
  1091.   }
  1092.   /**
  1093.    * Debug Logging support
  1094.    */
  1095.   function zcLog($stage$message{
  1096.     static $tokenHash;
  1097.     if ($tokenHash == ''$tokenHash '_' zen_create_random_value(4);
  1098.     if (MODULE_PAYMENT_PAYPALDP_DEBUGGING == 'Log and Email' || MODULE_PAYMENT_PAYPALDP_DEBUGGING == 'Log File'{
  1099.       $token (isset($_SESSION['paypal_ec_token'])) $_SESSION['paypal_ec_token'preg_replace('/[^0-9.A-Z\-]/'''$_GET['token']);
  1100.       $token ($token == ''date('m-d-Y-H-i'$token// or time()
  1101.       $token .= $tokenHash;
  1102.       $file $this->_logDir . '/' $this->code . '_Paypal_Action_' $token '.log';
  1103.       if (defined('PAYPAL_DEV_MODE'&& PAYPAL_DEV_MODE == 'true'$file $this->_logDir . '/' $this->code . '_Paypal_Debug_' $token '.log';
  1104.       $fp @fopen($file'a');
  1105.       @fwrite($fpdate('M-d-Y H:i:s'' (' time(')' "\n" $stage "\n" $message "\n=================================\n\n");
  1106.       @fclose($fp);
  1107.     }
  1108.     $this->_doDebug($stage$messagefalse);
  1109.   }
  1110.   /**
  1111.    * Debug Emailing support
  1112.    */
  1113.   function _doDebug($subject 'PayPal debug data'$data$useSession true{
  1114.     if (MODULE_PAYMENT_PAYPALDP_DEBUGGING == 'Log and Email'{
  1115.       $data =  urldecode($data"\n\n";
  1116.       if ($useSession$data .= "\nSession data: " print_r($_SESSIONtrue);
  1117.       zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESS$subject$this->code . "\n" $dataSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($this->code . "\n" $data))'debug');
  1118.     }
  1119.   }
  1120.   /**
  1121.    * Initialize the PayPal/PayflowPro object for communication to the processing gateways
  1122.    */
  1123.   function paypal_init({
  1124.     $nvp (MODULE_PAYMENT_PAYPALWPP_APIPASSWORD != '' && MODULE_PAYMENT_PAYPALWPP_APISIGNATURE != ''true false;
  1125.     $ec ($nvp && $_GET['type'== 'ec'true false;
  1126.     if (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK' && !$ec{
  1127.       $doPayPal new paypal_curl(array('mode' => 'payflow',
  1128.                                         'user' =>   trim(MODULE_PAYMENT_PAYPALWPP_PFUSER),
  1129.                                         'vendor' => trim(MODULE_PAYMENT_PAYPALWPP_PFVENDOR),
  1130.                                         'partner'=> trim(MODULE_PAYMENT_PAYPALWPP_PFPARTNER),
  1131.                                         'pwd' =>    trim(MODULE_PAYMENT_PAYPALWPP_PFPASSWORD),
  1132.                                         'server' => MODULE_PAYMENT_PAYPALDP_SERVER));
  1133.       $doPayPal->_endpoints array('live'    => 'https://payflowpro.paypal.com/transaction',
  1134.                                     'sandbox' => 'https://pilot-payflowpro.paypal.com/transaction');
  1135.     else {
  1136.       $doPayPal new paypal_curl(array('mode' => 'nvp',
  1137.                                         'user' => trim(MODULE_PAYMENT_PAYPALWPP_APIUSERNAME),
  1138.                                         'pwd' =>  trim(MODULE_PAYMENT_PAYPALWPP_APIPASSWORD),
  1139.                                         'signature' => trim(MODULE_PAYMENT_PAYPALWPP_APISIGNATURE),
  1140.                                         'version' => '61.0',
  1141.                                         'server' => MODULE_PAYMENT_PAYPALDP_SERVER));
  1142.       $doPayPal->_endpoints array('live'    => 'https://api-3t.paypal.com/nvp',
  1143.                                     'sandbox' => 'https://api.sandbox.paypal.com/nvp');
  1144.     }
  1145.  
  1146.     // set logging options
  1147.     $doPayPal->_logDir $this->_logDir;
  1148.     $doPayPal->_logLevel $this->_logLevel;
  1149.  
  1150.     // set proxy options if configured
  1151.     if (CURL_PROXY_REQUIRED == 'True' && CURL_PROXY_SERVER_DETAILS != ''{
  1152.       $proxy_tunnel_flag (defined('CURL_PROXY_TUNNEL_FLAG'&& strtoupper(CURL_PROXY_TUNNEL_FLAG== 'FALSE'false true;
  1153.       $doPayPal->setCurlOption(CURLOPT_HTTPPROXYTUNNEL$proxy_tunnel_flag);
  1154.       $doPayPal->setCurlOption(CURLOPT_PROXYTYPECURLPROXY_HTTP);
  1155.       $doPayPal->setCurlOption(CURLOPT_PROXYCURL_PROXY_SERVER_DETAILS);
  1156.     }
  1157.  
  1158.     // transaction processing mode
  1159.     $doPayPal->_trxtype (MODULE_PAYMENT_PAYPALDP_TRANSACTION_MODE == 'Auth Only''A' 'S';
  1160.  
  1161.     return $doPayPal;
  1162.   }
  1163.   /**
  1164.    * Determine which PayPal URL to direct the customer's browser to when needed
  1165.    */
  1166.   function getPayPalLoginServer({
  1167.     if (MODULE_PAYMENT_PAYPALDP_SERVER == 'live'{
  1168.       // live url
  1169.       $paypal_url 'https://www.paypal.com/cgi-bin/webscr';
  1170.     else {
  1171.       // sandbox url
  1172.       $paypal_url 'https://www.sandbox.paypal.com/cgi-bin/webscr';
  1173.     }
  1174.     return $paypal_url;
  1175.   }
  1176.   /**
  1177.    * Used to submit a refund for a given transaction.  FOR FUTURE USE.
  1178.    */
  1179.   function _doRefund($oID$amount 'Full'$note ''{
  1180.     global $db$doPayPal$messageStack;
  1181.     $new_order_status = (int)MODULE_PAYMENT_PAYPALDP_REFUNDED_STATUS_ID;
  1182.     $orig_order_amount 0;
  1183.     $doPayPal $this->paypal_init();
  1184.     $proceedToRefund false;
  1185.     $refundNote strip_tags(zen_db_input($_POST['refnote']));
  1186.     if (isset($_POST['fullrefund']&& $_POST['fullrefund'== MODULE_PAYMENT_PAYPAL_ENTRY_REFUND_BUTTON_TEXT_FULL{
  1187.       $refundAmt 'Full';
  1188.       if (isset($_POST['reffullconfirm']&& $_POST['reffullconfirm'== 'on'{
  1189.         $proceedToRefund true;
  1190.       else {
  1191.         $messageStack->add_session(MODULE_PAYMENT_PAYPALDP_TEXT_REFUND_FULL_CONFIRM_ERROR'error');
  1192.       }
  1193.     }
  1194.     if (isset($_POST['partialrefund']&& $_POST['partialrefund'== MODULE_PAYMENT_PAYPAL_ENTRY_REFUND_BUTTON_TEXT_PARTIAL{
  1195.       $refundAmt = (float)$_POST['refamt'];
  1196.       $proceedToRefund true;
  1197.       if ($refundAmt == 0{
  1198.         $messageStack->add_session(MODULE_PAYMENT_PAYPALDP_TEXT_INVALID_REFUND_AMOUNT'error');
  1199.         $proceedToRefund false;
  1200.       }
  1201.     }
  1202.  
  1203.     // look up history on this order from PayPal table
  1204.     $sql "select * from " TABLE_PAYPAL " where order_id = :orderID  AND parent_txn_id = '' ";
  1205.     $sql $db->bindVars($sql':orderID'$oID'integer');
  1206.     $zc_ppHist $db->Execute($sql);
  1207.     if ($zc_ppHist->RecordCount(== 0return false;
  1208.     $txnID $zc_ppHist->fields['txn_id'];
  1209.     $curCode $zc_ppHist->fields['mc_currency'];
  1210.     $PFamt $zc_ppHist->fields['mc_gross'];
  1211.     if ($doPayPal->_mode == 'payflow' && $refundAmt == 'Full'$refundAmt $PFamt;
  1212.  
  1213.     /**
  1214.      * Submit refund request to PayPal
  1215.      */
  1216.     if ($proceedToRefund{
  1217.        $response $doPayPal->RefundTransaction($oID$txnID$refundAmt$refundNote$curCode);
  1218.       $error $this->_errorHandler($response'DoRefund');
  1219.       $new_order_status ($new_order_status $new_order_status 1);
  1220.       if (!$error{
  1221.         if (!isset($response['GROSSREFUNDAMT'])) $response['GROSSREFUNDAMT'$refundAmt;
  1222.         // Success, so save the results
  1223.         $sql_data_array array('orders_id' => $oID,
  1224.                                 'orders_status_id' => (int)$new_order_status,
  1225.                                 'date_added' => 'now()',
  1226.                                 'comments' => 'REFUND INITIATED. Trans ID:' $response['REFUNDTRANSACTIONID'$response['PNREF']"\n" /*' Net Refund Amt:' . urldecode($response['NETREFUNDAMT']) . "\n" . ' Fee Refund Amt: ' . urldecode($response['FEEREFUNDAMT']) . "\n" . */' Gross Refund Amt: ' urldecode($response['GROSSREFUNDAMT'](isset($response['PPREF']"\nPPRef: " $response['PPREF'''"\n" $refundNote,
  1227.                                 'customer_notified' => 0
  1228.                              );
  1229.         zen_db_perform(TABLE_ORDERS_STATUS_HISTORY$sql_data_array);
  1230.         $db->Execute("update " TABLE_ORDERS  "
  1231.                       set orders_status = '" . (int)$new_order_status "'
  1232.                       where orders_id = '" . (int)$oID "'");
  1233.         $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALDP_TEXT_REFUND_INITIATEDurldecode($response['GROSSREFUNDAMT'])urldecode($response['REFUNDTRANSACTIONID'])$response['PNREF'])'success');
  1234.         return true;
  1235.       }
  1236.     }
  1237.   }
  1238.   /**
  1239.    * Used to capture part or all of a given previously-authorized transaction.  FOR FUTURE USE.
  1240.    * (alt value for $captureType = 'NotComplete')
  1241.    */
  1242.   function _doCapt($oID$captureType 'Complete'$amt 0$currency 'USD'$note ''{
  1243.     global $db$doPayPal$messageStack;
  1244.     $doPayPal $this->paypal_init();
  1245.  
  1246.     //@TODO: Read current order status and determine best status to set this to
  1247.     $new_order_status = (int)MODULE_PAYMENT_PAYPALDP_ORDER_STATUS_ID;
  1248.  
  1249.     $orig_order_amount 0;
  1250.     $doPayPal $this->paypal_init();
  1251.     $proceedToCapture false;
  1252.     $captureNote strip_tags(zen_db_input($_POST['captnote']));
  1253.     if (isset($_POST['captfullconfirm']&& $_POST['captfullconfirm'== 'on'{
  1254.       $proceedToCapture true;
  1255.     else {
  1256.       $messageStack->add_session(MODULE_PAYMENT_PAYPALDP_TEXT_CAPTURE_FULL_CONFIRM_ERROR'error');
  1257.     }
  1258.     if (isset($_POST['captfinal']&& $_POST['captfinal'== 'on'{
  1259.       $captureType 'Complete';
  1260.     else {
  1261.       $captureType 'NotComplete';
  1262.     }
  1263.     if (isset($_POST['btndocapture']&& $_POST['btndocapture'== MODULE_PAYMENT_PAYPAL_ENTRY_CAPTURE_BUTTON_TEXT_FULL{
  1264.       $captureAmt = (float)$_POST['captamt'];
  1265.       if ($captureAmt == 0{
  1266.         $messageStack->add_session(MODULE_PAYMENT_PAYPALDP_TEXT_INVALID_CAPTURE_AMOUNT'error');
  1267.         $proceedToCapture false;
  1268.       }
  1269.     }
  1270.     // look up history on this order from PayPal table
  1271.     $sql "select * from " TABLE_PAYPAL " where order_id = :orderID  AND parent_txn_id = '' ";
  1272.     $sql $db->bindVars($sql':orderID'$oID'integer');
  1273.     $zc_ppHist $db->Execute($sql);
  1274.     if ($zc_ppHist->RecordCount(== 0return false;
  1275.     $txnID $zc_ppHist->fields['txn_id'];
  1276.     /**
  1277.      * Submit capture request to PayPal
  1278.      */
  1279.     if ($proceedToCapture{
  1280.       $response $doPayPal->DoCapture($txnID$captureAmt$currency$captureType''$captureNote);
  1281.       $error $this->_errorHandler($response'DoCapture');
  1282.       $new_order_status ($new_order_status $new_order_status 1);
  1283.       if (!$error{
  1284.         if (isset($response['PNREF'])) {
  1285.           if (!isset($response['AMT'])) $response['AMT'$captureAmt;
  1286.           if (!isset($response['ORDERTIME'])) $response['ORDERTIME'date("M-d-Y h:i:s");
  1287.         }
  1288.         // Success, so save the results
  1289.         $sql_data_array array('orders_id' => (int)$oID,
  1290.                                 'orders_status_id' => (int)$new_order_status,
  1291.                                 'date_added' => 'now()',
  1292.                                 'comments' => 'FUNDS COLLECTED. Trans ID: ' urldecode($response['TRANSACTIONID']$response['PNREF']"\n" ' Amount: ' urldecode($response['AMT']' ' $currency "\n" 'Time: ' urldecode($response['ORDERTIME']"\n" (isset($response['RECEIPTID']'Receipt ID: ' urldecode($response['RECEIPTID']'Auth Code: ' $response['AUTHCODE'](isset($response['PPREF']"\nPPRef: " $response['PPREF'''"\n" $captureNote,
  1293.                                 'customer_notified' => 0
  1294.                              );
  1295.         zen_db_perform(TABLE_ORDERS_STATUS_HISTORY$sql_data_array);
  1296.         $db->Execute("update " TABLE_ORDERS  "
  1297.                       set orders_status = '" . (int)$new_order_status "'
  1298.                       where orders_id = '" . (int)$oID "'");
  1299.         $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALDP_TEXT_CAPT_INITIATEDurldecode($response['AMT'])urldecode($response['RECEIPTID'$response['AUTHCODE'])$response['PNREF'])'success');
  1300.         return true;
  1301.       }
  1302.     }
  1303.   }
  1304.   /**
  1305.    * Used to void a given previously-authorized transaction.  FOR FUTURE USE.
  1306.    */
  1307.   function _doVoid($oID$note ''{
  1308.     global $db$doPayPal$messageStack;
  1309.     $new_order_status = (int)MODULE_PAYMENT_PAYPALDP_REFUNDED_STATUS_ID;
  1310.     $doPayPal $this->paypal_init();
  1311.     $voidNote strip_tags(zen_db_input($_POST['voidnote']));
  1312.     $voidAuthID trim(strip_tags(zen_db_input($_POST['voidauthid'])));
  1313.     if (isset($_POST['ordervoid']&& $_POST['ordervoid'== MODULE_PAYMENT_PAYPAL_ENTRY_VOID_BUTTON_TEXT_FULL{
  1314.       if (isset($_POST['voidconfirm']&& $_POST['voidconfirm'== 'on'{
  1315.         $proceedToVoid true;
  1316.       else {
  1317.         $messageStack->add_session(MODULE_PAYMENT_PAYPALDP_TEXT_VOID_CONFIRM_ERROR'error');
  1318.       }
  1319.     }
  1320.     // look up history on this order from PayPal table
  1321.     $sql "select * from " TABLE_PAYPAL " where order_id = :orderID  AND parent_txn_id = '' ";
  1322.     $sql $db->bindVars($sql':orderID'$oID'integer');
  1323.     $sql $db->bindVars($sql':transID'$voidAuthID'string');
  1324.     $zc_ppHist $db->Execute($sql);
  1325.     if ($zc_ppHist->RecordCount(== 0return false;
  1326.     $txnID $zc_ppHist->fields['txn_id'];
  1327.     /**
  1328.      * Submit void request to PayPal
  1329.      */
  1330.     if ($proceedToVoid{
  1331.       $response $doPayPal->DoVoid($voidAuthID$voidNote);
  1332.       $error $this->_errorHandler($response'DoVoid');
  1333.       $new_order_status ($new_order_status $new_order_status 1);
  1334.       if (!$error{
  1335.         // Success, so save the results
  1336.         $sql_data_array array('orders_id' => (int)$oID,
  1337.                                 'orders_status_id' => (int)$new_order_status,
  1338.                                 'date_added' => 'now()',
  1339.                                 'comments' => 'VOIDED. Trans ID: ' urldecode($response['AUTHORIZATIONID'])$response['PNREF'(isset($response['PPREF']"\nPPRef: " $response['PPREF'''"\n" $voidNote,
  1340.                                 'customer_notified' => 0
  1341.                              );
  1342.         zen_db_perform(TABLE_ORDERS_STATUS_HISTORY$sql_data_array);
  1343.         $db->Execute("update " TABLE_ORDERS  "
  1344.                       set orders_status = '" . (int)$new_order_status "'
  1345.                       where orders_id = '" . (int)$oID "'");
  1346.         $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALDP_TEXT_VOID_INITIATEDurldecode($response['AUTHORIZATIONID']$response['PNREF'])'success');
  1347.         return true;
  1348.       }
  1349.     }
  1350.   }
  1351.  
  1352.   /**
  1353.    * Set the currency code -- use defaults if active currency is not a currency accepted by PayPal
  1354.    */
  1355.   function selectCurrency($val ''{
  1356.     $ec_currencies array('CAD''EUR''GBP''JPY''USD''AUD''CHF''CZK''DKK''HKD''HUF''NOK''NZD''PLN''SEK''SGD''THB''MXN''ILS''PHP''TWD''BRL''MYR''TKD');
  1357.     $dp_currencies array('CAD''EUR''GBP''JPY''USD''AUD''CHF''CZK''DKK''HKD''HUF''NOK''NZD''PLN''SEK''SGD');
  1358.     $dpus_currencies array('CAD''EUR''GBP''JPY''USD''AUD');
  1359.  
  1360.     // in USA, only 6 currencies are supported. But UK and Canada support 16 currencies (as of Jan 2011):
  1361.     $paypalSupportedCurrencies (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK' || MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'Canada'$dp_currencies $dpus_currencies;
  1362.  
  1363.     $my_currency substr(MODULE_PAYMENT_PAYPALDP_CURRENCY5);
  1364.     if (MODULE_PAYMENT_PAYPALDP_CURRENCY == 'Selected Currency'{
  1365.       $my_currency ($val == ''$_SESSION['currency'$val;
  1366.     }
  1367.  
  1368.     if (!in_array($my_currency$paypalSupportedCurrencies)) {
  1369.       $my_currency (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'UK''GBP' (MODULE_PAYMENT_PAYPALDP_MERCHANT_COUNTRY == 'Canada' 'CAD' 'USD');
  1370.     }
  1371.     return $my_currency;
  1372.   }
  1373.   /**
  1374.    * Calculate the amount based on acceptable currencies
  1375.    */
  1376.   function calc_order_amount($amount$paypalCurrency$applyFormatting false{
  1377.     global $currencies;
  1378.     $amount ($amount $currencies->get_value($paypalCurrency));
  1379.     if ($paypalCurrency == 'JPY' || (int)$currencies->get_decimal_places($paypalCurrency== 0{
  1380.       $amount = (int)$amount;
  1381.       $applyFormatting FALSE;
  1382.     }
  1383.     return ($applyFormatting number_format($amount$currencies->get_decimal_places($paypalCurrency)) $amount);
  1384.   }
  1385.   /**
  1386.    * Set the state field depending on what PayPal requires for that country.
  1387.    */
  1388.   function setStateAndCountry(&$info{
  1389.     global $db$messageStack;
  1390.     switch ($info['country']['iso_code_2']{
  1391.       case 'AU':
  1392.       case 'US':
  1393.       case 'CA':
  1394.       // Paypal only accepts two character state/province codes for some countries.
  1395.       if (strlen($info['state']2{
  1396.         $sql "SELECT zone_code FROM " TABLE_ZONES " WHERE zone_name = :zoneName";
  1397.         $sql $db->bindVars($sql':zoneName'$info['state']'string');
  1398.         $state $db->Execute($sql);
  1399.         if (!$state->EOF{
  1400.           $info['state'$state->fields['zone_code'];
  1401.         else {
  1402.           $messageStack->add_session('header'MODULE_PAYMENT_PAYPALDP_TEXT_STATE_ERROR'error');
  1403.         }
  1404.       }
  1405.       break;
  1406.       case 'AT':
  1407.       case 'BE':
  1408.       case 'FR':
  1409.       case 'DE':
  1410.       case 'CH':
  1411.       $info['state''';
  1412.       break;
  1413.       case 'GB':
  1414.       break;
  1415.       default:
  1416.       $info['state''';
  1417.     }
  1418.   }
  1419.   /**
  1420.    * Prepare subtotal and line-item detail content to send to PayPal
  1421.    */
  1422.   function getLineItemDetails($restrictedCurrency{
  1423.     global $order$currencies$order_totals$order_total_modules;
  1424.  
  1425.     // if not default currency, do not send subtotals or line-item details
  1426.     if (DEFAULT_CURRENCY != $order->info['currency'|| $restrictedCurrency != DEFAULT_CURRENCY{
  1427.       $this->zcLog('getLineItemDetails 1''Not using default currency. Thus, no line-item details can be submitted.');
  1428.       return array();
  1429.     }
  1430.     if ($currencies->currencies[$_SESSION['currency']]['value'!= || $currencies->currencies[$order->info['currency']]['value'!= 1{
  1431.       $this->zcLog('getLineItemDetails 2''currency val not equal to 1.0000 - cannot proceed without coping with currency conversions. Aborting line-item details.');
  1432.       return array();
  1433.     }
  1434.  
  1435.     $optionsST array();
  1436.     $optionsLI array();
  1437.     $optionsNB array();
  1438.     $numberOfLineItemsProcessed 0;
  1439.     $creditsApplied 0;
  1440.     $surcharges 0;
  1441.     $sumOfLineItems 0;
  1442.     $sumOfLineTax 0;
  1443.     $optionsST['AMT'0;
  1444.     $optionsST['ITEMAMT'0;
  1445.     $optionsST['TAXAMT'0;
  1446.     $optionsST['SHIPPINGAMT'0;
  1447.     $optionsST['SHIPDISCAMT'0;
  1448.     $optionsST['HANDLINGAMT'0;
  1449.     $optionsST['INSURANCEAMT'0;
  1450.     $flagSubtotalsUnknownYet true;
  1451.     $subTotalLI 0;
  1452.     $subTotalTax 0;
  1453.     $subTotalShipping 0;
  1454.     $subtotalPRE array('no data');
  1455.     $discountProblemsFlag FALSE;
  1456.     $flag_treat_as_partial FALSE;
  1457.  
  1458.     if (sizeof($order_totals)) {
  1459.       // prepare subtotals
  1460.       for ($i=0$n=sizeof($order_totals)$i<$n$i++{
  1461.         if ($order_totals[$i]['code'== ''continue;
  1462.         if (in_array($order_totals[$i]['code']array('ot_total','ot_subtotal','ot_tax','ot_shipping')) || strstr($order_totals[$i]['code']'insurance')) {
  1463.           if ($order_totals[$i]['code'== 'ot_shipping'$optionsST['SHIPPINGAMT'round($order_totals[$i]['value'],2);
  1464.           if ($order_totals[$i]['code'== 'ot_total')    $optionsST['AMT']         round($order_totals[$i]['value'],2);
  1465.           if ($order_totals[$i]['code'== 'ot_tax')      $optionsST['TAXAMT']     += strval(round($order_totals[$i]['value'],2));
  1466.           if ($order_totals[$i]['code'== 'ot_subtotal'$optionsST['ITEMAMT']     round($order_totals[$i]['value'],2);
  1467.           if (strstr($order_totals[$i]['code']'insurance')) $optionsST['INSURANCEAMT'+= round($order_totals[$i]['value'],2);
  1468.           //$optionsST['SHIPDISCAMT'] = '';  // Not applicable
  1469.         else {
  1470.           // handle other order totals:
  1471.           global $$order_totals[$i]['code'];
  1472.           if ((substr($order_totals[$i]['text']01== '-'|| (isset($$order_totals[$i]['code']->credit_class&& $$order_totals[$i]['code']->credit_class == true)) {
  1473.             // handle credits
  1474.             $creditsApplied += round($order_totals[$i]['value']2);
  1475.           else {
  1476.             // treat all other OT's as if they're related to handling fees or other extra charges to be added/included
  1477.             $surcharges += $order_totals[$i]['value'];
  1478.           }
  1479.         }
  1480.       }
  1481.  
  1482.       if ($creditsApplied 0$optionsST['ITEMAMT'-= $creditsApplied;
  1483.       if ($surcharges 0$optionsST['ITEMAMT'+= $surcharges;
  1484.  
  1485.       // Handle tax-included scenario
  1486.       if (DISPLAY_PRICE_WITH_TAX == 'true'$optionsST['TAXAMT'0;
  1487.  
  1488.       $subtotalPRE $optionsST;
  1489.       // Move shipping tax amount from Tax subtotal into Shipping subtotal for submission to PayPal, since PayPal applies tax to each line-item individually
  1490.       $module substr($_SESSION['shipping']['id']0strpos($_SESSION['shipping']['id']'_'));
  1491.       if (zen_not_null($order->info['shipping_method']&& DISPLAY_PRICE_WITH_TAX != 'true'{
  1492.         if ($GLOBALS[$module]->tax_class 0{
  1493.           $shipping_tax_basis (!isset($GLOBALS[$module]->tax_basis)) STORE_SHIPPING_TAX_BASIS $GLOBALS[$module]->tax_basis;
  1494.           $shippingOnBilling zen_get_tax_rate($GLOBALS[$module]->tax_class$order->billing['country']['id']$order->billing['zone_id']);
  1495.           $shippingOnDelivery zen_get_tax_rate($GLOBALS[$module]->tax_class$order->delivery['country']['id']$order->delivery['zone_id']);
  1496.           if ($shipping_tax_basis == 'Billing'{
  1497.             $shipping_tax $shippingOnBilling;
  1498.           elseif ($shipping_tax_basis == 'Shipping'{
  1499.             $shipping_tax $shippingOnDelivery;
  1500.           else {
  1501.             if (STORE_ZONE == $order->billing['zone_id']{
  1502.               $shipping_tax $shippingOnBilling;
  1503.             elseif (STORE_ZONE == $order->delivery['zone_id']{
  1504.               $shipping_tax $shippingOnDelivery;
  1505.             else {
  1506.               $shipping_tax 0;
  1507.             }
  1508.           }
  1509.           $taxAdjustmentForShipping zen_round(zen_calculate_tax($order->info['shipping_cost']$shipping_tax)$currencies->currencies[$_SESSION['currency']]['decimal_places']);
  1510.           $optionsST['SHIPPINGAMT'+= $taxAdjustmentForShipping;
  1511.           $optionsST['TAXAMT'-= $taxAdjustmentForShipping;
  1512.         }
  1513.       }
  1514.       $flagSubtotalsUnknownYet (($optionsST['SHIPPINGAMT'$optionsST['SHIPDISCAMT'$optionsST['AMT'$optionsST['TAXAMT'$optionsST['ITEMAMT'$optionsST['INSURANCEAMT']== 0);
  1515.     else {
  1516.       // if we get here, we don't have any order-total information yet because the customer has clicked Express before starting normal checkout flow
  1517.       // thus, we must make a note to manually calculate subtotals, rather than relying on the more robust order-total infrastructure
  1518.       $flagSubtotalsUnknownYet TRUE;
  1519.     }
  1520.  
  1521.     $decimals $currencies->get_decimal_places($_SESSION['currency']);
  1522.  
  1523.     // loop thru all products to prepare details of quantity and price.
  1524.     for ($i=0$n=sizeof($order->products)$k=-1$i<$n$i++{
  1525.       // PayPal is inconsistent in how it handles zero-value line-items, so skip this entry if price is zero
  1526.       if ($order->products[$i]['final_price'== 0{
  1527.         continue;
  1528.       else {
  1529.         $k++;
  1530.       }
  1531.  
  1532.       $optionsLI["L_NUMBER$k"$order->products[$i]['model'];
  1533.       $optionsLI["L_NAME$k"]   $order->products[$i]['name'' [' . (int)$order->products[$i]['id'']';
  1534.       // Append *** if out-of-stock.
  1535.       $optionsLI["L_NAME$k"]  .= ((zen_get_products_stock($order->products[$i]['id']$order->products[$i]['qty']STOCK_MARK_PRODUCT_OUT_OF_STOCK '');
  1536.       // if there are attributes, loop thru them and add to description
  1537.       if (isset($order->products[$i]['attributes']&& sizeof($order->products[$i]['attributes']{
  1538.         for ($j=0$n2=sizeof($order->products[$i]['attributes'])$j<$n2$j++{
  1539.           $optionsLI["L_NAME$k".= "\n " $order->products[$i]['attributes'][$j]['option'.
  1540.                                         ': ' $order->products[$i]['attributes'][$j]['value'];
  1541.         // end loop
  1542.       // endif attribute-info
  1543.  
  1544.       // PayPal can't handle fractional-quantity values, so convert it to qty 1 here
  1545.       if ($order->products[$i]['qty'&& ($order->products[$i]['qty'!= (int)$order->products[$i]['qty'|| $flag_treat_as_partial)) {
  1546.         $optionsLI["L_NAME$k"'('.$order->products[$i]['qty'].' x ) ' $optionsLI["L_NAME$k"];
  1547.         // zen_add_tax already handles whether DISPLAY_PRICES_WITH_TAX is set
  1548.         $optionsLI["L_AMT$k"zen_round(zen_round(zen_add_tax($order->products[$i]['final_price']$order->products[$i]['tax'])$decimals$order->products[$i]['qty']$decimals);
  1549.         $optionsLI["L_QTY$k"1;
  1550.         // no line-item tax component
  1551.       else {
  1552.         $optionsLI["L_QTY$k"$order->products[$i]['qty'];
  1553.         $optionsLI["L_AMT$k"zen_round(zen_add_tax($order->products[$i]['final_price']$order->products[$i]['tax'])$decimals);
  1554.       }
  1555.  
  1556.       $subTotalLI += ($optionsLI["L_QTY$k"$optionsLI["L_AMT$k"]);
  1557. //      $subTotalTax += ($optionsLI["L_QTY$k"] * $optionsLI["L_TAXAMT$k"]);
  1558.  
  1559.       // add line-item for one-time charges on this product
  1560.       if ($order->products[$i]['onetime_charges'!= {
  1561.         $k++;
  1562.         $optionsLI["L_NAME$k"]   MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_ONETIME_CHARGES_PREFIX substr(htmlentities($order->products[$i]['name']ENT_QUOTES'UTF-8')0120);
  1563.         $optionsLI["L_AMT$k"]    zen_round(zen_add_tax($order->products[$i]['onetime_charges']$order->products[$i]['tax'])$decimals);
  1564.         $optionsLI["L_QTY$k"]    1;
  1565. //        $optionsLI["L_TAXAMT$k"] = zen_round(zen_calculate_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']), $decimals);
  1566.         $subTotalLI += $optionsLI["L_AMT$k"];
  1567. //        $subTotalTax += $optionsLI["L_TAXAMT$k"];
  1568.       }
  1569.       $numberOfLineItemsProcessed $k;
  1570.     }  // end for loopthru all products
  1571.  
  1572.     // add line items for any surcharges added by order-total modules
  1573.     if ($surcharges 0{
  1574.       $numberOfLineItemsProcessed++;
  1575.       $k $numberOfLineItemsProcessed;
  1576.       $optionsLI["L_NAME$k"MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_SURCHARGES_LONG;
  1577.       $optionsLI["L_AMT$k"]  $surcharges;
  1578.       $optionsLI["L_QTY$k"]  1;
  1579.       $subTotalLI += $surcharges;
  1580.     }
  1581.  
  1582.     // add line items for discounts such as gift certificates and coupons
  1583.     if ($creditsApplied 0{
  1584.       $numberOfLineItemsProcessed++;
  1585.       $k $numberOfLineItemsProcessed;
  1586.       $optionsLI["L_NAME$k"]   MODULES_PAYMENT_PAYPALWPP_LINEITEM_TEXT_DISCOUNTS_LONG;
  1587.       $optionsLI["L_AMT$k"]    (-$creditsApplied);
  1588.       $optionsLI["L_QTY$k"]    1;
  1589.       $subTotalLI -= $creditsApplied;
  1590.     }
  1591.  
  1592.     // Reformat properly
  1593.     // Replace & and = and % with * if found.
  1594.     // reformat properly according to API specs
  1595.     // Remove HTML markup from name if found
  1596.     for ($k=0$n=$numberOfLineItemsProcessed+1$k<$n$k++{
  1597.       $optionsLI["L_NAME$k"str_replace(array('&','=','%')'*'$optionsLI["L_NAME$k"]);
  1598.       $optionsLI["L_NAME$k"zen_clean_html($optionsLI["L_NAME$k"]'strong');
  1599.       $optionsLI["L_NAME$k"]   substr($optionsLI["L_NAME$k"]0127);
  1600.       $optionsLI["L_AMT$k"round($optionsLI["L_AMT$k"]2);
  1601.  
  1602.       if (isset($optionsLI["L_NUMBER$k"])) {
  1603.         if ($optionsLI["L_NUMBER$k"== ''{
  1604.           unset($optionsLI["L_NUMBER$k"]);
  1605.         else {
  1606.           $optionsLI["L_NUMBER$k"str_replace(array('&','=','%')'*'$optionsLI["L_NUMBER$k"]);
  1607.           $optionsLI["L_NUMBER$k"substr($optionsLI["L_NUMBER$k"]0127);
  1608.         }
  1609.       }
  1610.  
  1611. //      if (isset($optionsLI["L_TAXAMT$k"]) && ($optionsLI["L_TAXAMT$k"] != '' || $optionsLI["L_TAXAMT$k"] > 0)) {
  1612. //        $optionsLI["L_TAXAMT$k"] = round($optionsLI["L_TAXAMT$k"], 2);
  1613. //      }
  1614.     }
  1615.  
  1616.     // Sanity Check of line-item subtotals
  1617.     for ($j=0$j<$k$j++{
  1618.       $itemAMT $optionsLI["L_AMT$j"];
  1619.       $itemQTY $optionsLI["L_QTY$j"];
  1620.       $itemTAX (isset($optionsLI["L_TAXAMT$j"]$optionsLI["L_TAXAMT$j"0);
  1621.       $sumOfLineItems += ($itemQTY $itemAMT);
  1622.       $sumOfLineTax += ($itemQTY $itemTAX);
  1623.     }
  1624.     $sumOfLineItems round($sumOfLineItems2);
  1625.     $sumOfLineTax round($sumOfLineTax2);
  1626.  
  1627.     if ($sumOfLineItems == 0{
  1628.       $sumOfLineTax 0;
  1629.       $optionsLI array();
  1630.       $discountProblemsFlag TRUE;
  1631.       if ($optionsST['SHIPPINGAMT'== $optionsST['AMT']{
  1632.         $optionsST['SHIPPINGAMT'0;
  1633.       }
  1634.     }
  1635.  
  1636. //    // Sanity check -- if tax-included pricing is causing problems, remove the numbers and put them in a comment instead:
  1637. //    $stDiffTaxOnly = (strval($sumOfLineItems - $sumOfLineTax - round($optionsST['AMT'], 2)) + 0);
  1638. //    $this->zcLog('tax sanity check', 'stDiffTaxOnly: ' . $stDiffTaxOnly . "\nsumOfLineItems: " . $sumOfLineItems . "\nsumOfLineTax: " . $sumOfLineTax . ' ' . $subTotalTax . ' ' . print_r(array_merge($optionsST, $optionsLI), true));
  1639. //    if (DISPLAY_PRICE_WITH_TAX == 'true' && $stDiffTaxOnly == 0 && ($optionsST['TAXAMT'] != 0 && $sumOfLineTax != 0)) {
  1640. //      $optionsNB['DESC'] = 'Tax included in prices: ' . $sumOfLineTax . ' (' . $optionsST['TAXAMT'] . ') ';
  1641. //      $optionsST['TAXAMT'] = 0;
  1642. //      for ($k=0, $n=$numberOfLineItemsProcessed+1; $k<$n; $k++) {
  1643. //        if (isset($optionsLI["L_TAXAMT$k"])) unset($optionsLI["L_TAXAMT$k"]);
  1644. //      }
  1645. //    }
  1646.  
  1647. //    // Do sanity check -- if any of the line-item subtotal math doesn't add up properly, skip line-item details,
  1648. //    // so that the order can go through even though PayPal isn't being flexible to handle Zen Cart's diversity
  1649. //    if ((strval($subTotalTax) - strval($sumOfLineTax)) > 0.02) {
  1650. //      $this->zcLog('getLineItemDetails 3', 'Tax Subtotal does not match sum of taxes for line-items. Tax details are being removed from line-item submission data.' . "\n" . $sumOfLineTax . ' ' . $subTotalTax . print_r(array_merge($optionsST, $optionsLI), true));
  1651. //      for ($k=0, $n=$numberOfLineItemsProcessed+1; $k<$n; $k++) {
  1652. //        if (isset($optionsLI["L_TAXAMT$k"])) unset($optionsLI["L_TAXAMT$k"]);
  1653. //      }
  1654. //      $subTotalTax = 0;
  1655. //      $sumOfLineTax = 0;
  1656. //    }
  1657.  
  1658. //    // If coupons exist and there's a calculation problem, then it's likely that taxes are incorrect, so reset L_TAXAMTn values
  1659. //    if ($creditsApplied > 0 && (strval($optionsST['TAXAMT']) != strval($sumOfLineTax))) {
  1660. //      $pre = $optionsLI;
  1661. //      for ($k=0, $n=$numberOfLineItemsProcessed+1; $k<$n; $k++) {
  1662. //        if (isset($optionsLI["L_TAXAMT$k"])) unset($optionsLI["L_TAXAMT$k"]);
  1663. //      }
  1664. //      $this->zcLog('getLineItemDetails 4', 'Coupons/Discounts have affected tax calculations, so tax details are being removed from line-item submission data.' . "\n" . $sumOfLineTax . ' ' . $optionsST['TAXAMT'] . "\n" . print_r(array_merge($optionsST, $pre, $optionsNB), true) . "\nAFTER:" . print_r(array_merge($optionsST, $optionsLI, $optionsNB), TRUE));
  1665. //      $subTotalTax = 0;
  1666. //      $sumOfLineTax = 0;
  1667. //    }
  1668.  
  1669.     // disable line-item tax details, leaving only TAXAMT subtotal as tax indicator
  1670.     for ($k=0$n=$numberOfLineItemsProcessed+1$k<$n$k++{
  1671.       if (isset($optionsLI["L_TAXAMT$k"])) unset($optionsLI["L_TAXAMT$k"]);
  1672.     }
  1673.     // if ITEMAMT >0 and subTotalLI > 0 and they're not equal ... OR subTotalLI minus sumOfLineItems isn't 0
  1674.     // check subtotals
  1675.     if ((strval($optionsST['ITEMAMT']&& strval($subTotalLI&& strval($subTotalLI!= strval($optionsST['ITEMAMT'])) || strval($subTotalLIstrval($sumOfLineItems!= 0{
  1676.       $this->zcLog('getLineItemDetails 5''Line-item subtotals do not add up properly. Line-item-details skipped.' "\n" strval($sumOfLineItems' ' strval($subTotalLI' ' print_r(array_merge($optionsST$optionsLI)true));
  1677.       $optionsLI array();
  1678.       $optionsLI["L_NAME0"MODULES_PAYMENT_PAYPALWPP_AGGREGATE_CART_CONTENTS;
  1679.       $optionsLI["L_AMT0"]  $sumOfLineItems $subTotalLI $optionsST['ITEMAMT'];
  1680.     }
  1681.  
  1682.     // check whether discounts are causing a problem
  1683.     if (strval($optionsST['ITEMAMT']0{
  1684.       $pre (array_merge($optionsST$optionsLI));
  1685.       $optionsST['ITEMAMT'$optionsST['AMT'];
  1686.       $optionsLI array();
  1687.       $optionsLI["L_NAME0"MODULES_PAYMENT_PAYPALWPP_AGGREGATE_CART_CONTENTS;
  1688.       $optionsLI["L_AMT0"]  $sumOfLineItems $subTotalLI $optionsST['ITEMAMT'];
  1689.       if ($optionsST['AMT'$optionsST['TAXAMT']$optionsST['TAXAMT'0;
  1690.       if ($optionsST['AMT'$optionsST['SHIPPINGAMT']$optionsST['SHIPPINGAMT'0;
  1691.       $discountProblemsFlag TRUE;
  1692.       $this->zcLog('getLineItemDetails 6''Discounts have caused the subtotal to calculate incorrectly. Line-item-details cannot be submitted.' "\nBefore:" print_r($preTRUE"\nAfter:" print_r(array_merge($optionsST$optionsLI)true));
  1693.     }
  1694.  
  1695.     // if AMT or ITEMAMT values are 0 (ie: certain OT modules disabled) or we've started express checkout without going through normal checkout flow, we have to get subtotals manually
  1696.     if ((!isset($optionsST['AMT']|| $optionsST['AMT'== || $flagSubtotalsUnknownYet == TRUE || $optionsST['ITEMAMT'== 0&& $discountProblemsFlag != TRUE{
  1697.       $optionsST['ITEMAMT'$sumOfLineItems;
  1698.       $optionsST['TAXAMT'$sumOfLineTax;
  1699.       if ($subTotalShipping 0$optionsST['SHIPPINGAMT'$subTotalShipping;
  1700.       $optionsST['AMT'$sumOfLineItems $optionsST['TAXAMT'$optionsST['SHIPPINGAMT'];
  1701.     }
  1702.     $this->zcLog('getLineItemDetails 7 - subtotal comparisons''BEFORE line-item calcs: ' print_r($subtotalPREtrue($flagSubtotalsUnknownYet == TRUE 'Subtotals Unknown Yet - ' '''AFTER doing line-item calcs: ' print_r(array_merge($optionsST$optionsLI$optionsNB)true));
  1703.  
  1704.     // if subtotals are not adding up correctly, then skip sending any line-item or subtotal details to PayPal
  1705.     $stAll round(strval($optionsST['ITEMAMT']strval($optionsST['TAXAMT']strval($optionsST['SHIPPINGAMT']strval($optionsST['SHIPDISCAMT']strval($optionsST['HANDLINGAMT']strval($optionsST['INSURANCEAMT'])2);
  1706.     $stDiff strval($optionsST['AMT'$stAll);
  1707.     $stDiffRounded (strval($stAll round($optionsST['AMT']2)) 0);
  1708.  
  1709.     // unset any subtotal values that are zero
  1710.     if (isset($optionsST['ITEMAMT']&& $optionsST['ITEMAMT'== 0unset($optionsST['ITEMAMT']);
  1711.     if (isset($optionsST['TAXAMT']&& $optionsST['TAXAMT'== 0unset($optionsST['TAXAMT']);
  1712.     if (isset($optionsST['SHIPPINGAMT']&& $optionsST['SHIPPINGAMT'== 0unset($optionsST['SHIPPINGAMT']);
  1713.     if (isset($optionsST['SHIPDISCAMT']&& $optionsST['SHIPDISCAMT'== 0unset($optionsST['SHIPDISCAMT']);
  1714.     if (isset($optionsST['HANDLINGAMT']&& $optionsST['HANDLINGAMT'== 0unset($optionsST['HANDLINGAMT']);
  1715.     if (isset($optionsST['INSURANCEAMT']&& $optionsST['INSURANCEAMT'== 0unset($optionsST['INSURANCEAMT']);
  1716.  
  1717.     // tidy up all values so that they comply with proper format (number_format(xxxx,2) for PayPal US use )
  1718.     if (!defined('PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING'|| PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING != 'true' || in_array($order->info['currency']array('JPY''NOK''HUF'))) {
  1719.       if (is_array($optionsST)) foreach ($optionsST as $key=>$value{
  1720.         $optionsST[$keynumber_format($value((int)$currencies->get_decimal_places($restrictedCurrency== 2));
  1721.       }
  1722.       if (is_array($optionsLI)) foreach ($optionsLI as $key=>$value{
  1723.         if (substr($key08== 'L_TAXAMT' && ($optionsLI[$key== '' || $optionsLI[$key== 0)) {
  1724.           unset($optionsLI[$key]);
  1725.         else {
  1726.           if (strstr($key'AMT')) $optionsLI[$keynumber_format($value((int)$currencies->get_decimal_places($restrictedCurrency== 2));
  1727.         }
  1728.       }
  1729.     }
  1730.  
  1731.     $this->zcLog('getLineItemDetails 8''checking subtotals... ' "\n" print_r(array_merge(array('calculated total'=>number_format($stAll((int)$currencies->get_decimal_places($restrictedCurrency== 2)))$optionsST)true"\n-------------------\ndifference: " ($stDiff 0'  (abs+rounded: ' ($stDiffRounded 0')');
  1732.  
  1733.     if $stDiffRounded != 0{
  1734.       $this->zcLog('getLineItemDetails 9''Subtotals Bad. Skipping line-item/subtotal details');
  1735.       return array();
  1736.     }
  1737.  
  1738.     $this->zcLog('getLineItemDetails 10''subtotals balance - okay');
  1739.  
  1740.     // Send Subtotal and LineItem results back to be submitted to PayPal
  1741.     return array_merge($optionsST$optionsLI$optionsNB);
  1742.   }
  1743.   /**
  1744.    * If the account was created only for temporary purposes to place the PayPal order, delete it.
  1745.    */
  1746.   function ec_delete_user($cid{
  1747.     global $db;
  1748.     unset($_SESSION['customer_id']);
  1749.     unset($_SESSION['customer_default_address_id']);
  1750.     unset($_SESSION['customer_first_name']);
  1751.     unset($_SESSION['customer_country_id']);
  1752.     unset($_SESSION['customer_zone_id']);
  1753.     unset($_SESSION['comments']);
  1754.     unset($_SESSION['customer_guest_id']);
  1755.   }
  1756.   /**
  1757.    * If the EC flow has to be interrupted for any reason, this does the appropriate cleanup and displays status/error messages.
  1758.    */
  1759.   function terminateEC($error_msg ''$kill_sess_vars false$goto_page ''{
  1760.     global $messageStack$order$order_total_modules;
  1761.     $error_msg trim($error_msg);
  1762.     if (substr($error_msg-1== '-'$error_msg trim(substr($error_msg0strlen($error_msg1));
  1763.     $stackAlert 'checkout_payment';
  1764.  
  1765.     // debug
  1766.     $this->_doDebug('PayPal test Log - terminateEC-A'"goto page: " $goto_page "\nerror_msg: " $error_msg "\n\nSession data: " print_r($_SESSIONtrue));
  1767.  
  1768.     if ($kill_sess_vars{
  1769.       if (!empty($_SESSION['paypal_ec_temp'])) {
  1770.         $this->ec_delete_user($_SESSION['customer_id']);
  1771.       }
  1772.       // Unregister the paypal session variables, making the user start over.
  1773.       unset($_SESSION['paypal_ec_temp']);
  1774.       unset($_SESSION['paypal_ec_token']);
  1775.       unset($_SESSION['paypal_ec_payer_id']);
  1776.       unset($_SESSION['paypal_ec_payer_info']);
  1777.       unset($_SESSION['paypal_ec_final']);
  1778.       unset($_SESSION['paypal_ec_markflow']);
  1779.       // debug
  1780.       $this->zcLog('termEC-1''Killed the session vars as requested');
  1781.     }
  1782.  
  1783.     $this->zcLog('termEC-2''BEFORE: Token Data:' $_SESSION['paypal_ec_token']);
  1784.  
  1785.     if ($error_msg{
  1786.       $messageStack->add_session($stackAlert$error_msg'error');
  1787.     }
  1788.     // debug
  1789.     $this->zcLog('termEC-10''Redirecting to ' $goto_page ' - Stack: ' $stackAlert "\n" 'Message: ' $error_msg "\nSession Data: " print_r($_SESSIONtrue));
  1790.     zen_redirect(zen_href_link($goto_page'''SSL'truefalse));
  1791.   }
  1792.   /**
  1793.    * Error / exception handling
  1794.    */
  1795.   function _errorHandler($response$operation ''$ignore_codes ''{
  1796.     global $messageStack$doPayPal;
  1797.     $gateway_mode (isset($response['PNREF']&& $response['PNREF'!= '');
  1798.     $basicError (!$response || (isset($response['RESULT']&& $response['RESULT'!= 0|| (isset($response['ACK']&& !strstr($response['ACK']'Success')) || (!isset($response['RESULT']&& !isset($response['ACK'])));
  1799.     $ignoreList explode(','str_replace(' '''$ignore_codes));
  1800.     foreach($ignoreList as $key=>$value{
  1801.       if ($value != '' && $response['L_ERRORCODE0'== $value$basicError false;
  1802.     }
  1803.     /** Handle FMF Scenarios **/
  1804.     if (in_array($operationarray('DoExpressCheckoutPayment''DoDirectPayment')) && $response['PAYMENTSTATUS'== 'Pending' && $response['L_ERRORCODE0'== 11610{
  1805.       $this->fmfResponse = urldecode($response['L_SHORTMESSAGE0']);
  1806.       $this->fmfErrors = array();
  1807.       if ($response['ACK'== 'SuccessWithWarning' && isset($response['L_FMFPENDINGID0'])) {
  1808.         for ($i=0$i<20$i++{
  1809.           $this->fmfErrors[array('key' => $response['L_FMFPENDINGID' $i]'status' => $response['L_FMFPENDINGID' $i]'desc' => $response['L_FMFPENDINGDESCRIPTION' $i]);
  1810.         }
  1811.       }
  1812.       return (sizeof($this->fmfErrors)>0$this->fmfErrors : FALSE;
  1813.     }
  1814.     //echo '<br />basicError='.$basicError.'<br />' . urldecode(print_r($response,true)); die('halted');
  1815.     if (!isset($response['L_SHORTMESSAGE0']&& isset($response['RESPMSG']&& $response['RESPMSG'!= ''$response['L_SHORTMESSAGE0'$response['RESPMSG'];
  1816.     $errorInfo "\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with PayPal Website Payments Pro.';
  1817.  
  1818.     switch($operation{
  1819.       case 'DoDirectPayment':
  1820.         if ($basicError ||
  1821.            ((isset($_SESSION['paypal_ec_token']&& isset($response['TOKEN'])) && $_SESSION['paypal_ec_token'!= urldecode($response['TOKEN'])) ) {
  1822.             // Error, so send the store owner a complete dump of the transaction.
  1823.           if ($this->enableDebugging{
  1824.             $this->_doDebug('PayPal Error Log - before_process() - DP'"In function: before_process() - Direct Payment \r\nDid first contact attempt return error? " ($error_occurred "Yes" "No"" \r\n\r\nValue List:\r\n" str_replace('&',"\r\n"urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList)))) "\r\n\r\nResponse:\r\n" urldecode(print_r($responsetrue)));
  1825.           }
  1826.           $errorText MODULE_PAYMENT_PAYPALDP_INVALID_RESPONSE;
  1827.           $errorNum urldecode($response['L_ERRORCODE0'' ' $response['RESULT'' <!-- ' $response['RESPMSG'' -->');
  1828.           if ($response['RESULT'== 25$errorText MODULE_PAYMENT_PAYPALDP_TEXT_NOT_WPP_ACCOUNT_ERROR;
  1829.           if ($response['L_ERRORCODE0'== 10500 || $response['L_ERRORCODE0'== 10501$errorText MODULE_PAYMENT_PAYPALDP_TEXT_NOT_US_WPP_ACCOUNT_ERROR;
  1830.           if ($response['HOSTCODE'== 10500 || $response['HOSTCODE'== 10501$errorText MODULE_PAYMENT_PAYPALDP_TEXT_NOT_UKWPP_ACCOUNT_ERROR;
  1831.           if ($response['HOSTCODE'== 10558$errorText MODULE_PAYMENT_PAYPALDP_TEXT_CANNOT_USE_THIS_CURRENCY_ERROR;
  1832.           if ($response['L_ERRORCODE0'== 10002$errorText MODULE_PAYMENT_PAYPALDP_TEXT_SANDBOX_VS_LIVE_ERROR;
  1833.           if ($response['L_ERRORCODE0'== 10565{
  1834.             $errorText MODULE_PAYMENT_PAYPALDP_TEXT_WPP_BAD_COUNTRY_ERROR;
  1835.             $_SESSION['payment''';
  1836.           }
  1837.           if ($response['L_ERRORCODE0'== 10566$errorText MODULE_PAYMENT_PAYPALDP_TEXT_CARD_TYPE_NOT_SUPPORTED;
  1838.           if ($response['L_ERRORCODE0'== 10736$errorText MODULE_PAYMENT_PAYPALDP_TEXT_ADDR_ERROR;
  1839.           if ($response['L_ERRORCODE0'== 10752{
  1840.             $errorText MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED;
  1841.             $errorNum '10752';
  1842.           }
  1843.           if ($response['L_ERRORCODE0'== 15012// Mastercard CE agreement not signed between merchant and PayPal. Thus cannot accept mastercard.
  1844.             $errorText MODULE_PAYMENT_PAYPALDP_TEXT_CARD_TYPE_NOT_SUPPORTED;
  1845.             $errorNum '15012';
  1846.           }
  1847.           if ($response['L_ERRORCODE0'== 15005{
  1848.             $errorText 'Card rejected by the bank. Your IP address has been recorded.';
  1849.             $errorNum '15005';
  1850.           }
  1851.           if ($response['RESPMSG'!= ''$errorText MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED ' ' $errorText;
  1852.  
  1853.           $detailedMessage ($errorText == MODULE_PAYMENT_PAYPALDP_INVALID_RESPONSE || $errorText == MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED || $this->enableDebugging || $response['CURL_ERRORS'!= '' || $this->emailAlerts(isset($response['RESULT']&& $response['RESULT'!= MODULE_PAYMENT_PAYPALDP_CANNOT_BE_COMPLETED ' (' $errorNum ')' $errorNum' ' urldecode(' ' $response['L_SHORTMESSAGE0'' - ' $response['L_LONGMESSAGE0'' ' $response['CURL_ERRORS']'';
  1854.           $explain "\n\nProblem occurred while customer #" $_SESSION['customer_id'' -- ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' -- was attempting checkout.' "\n";
  1855.           $detailedEmailMessage MODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_MESSAGE urldecode($response['L_ERRORCODE0']  ' ' $response['RESPMSG']"\n" $response['L_SHORTMESSAGE0'"\n" $response['L_LONGMESSAGE0'$response['L_ERRORCODE1'"\n" $response['L_SHORTMESSAGE1'"\n" $response['L_LONGMESSAGE1'$response['L_ERRORCODE2'"\n" $response['L_SHORTMESSAGE2'"\n" $response['L_LONGMESSAGE2'($response['CURL_ERRORS'!= '' "\n" $response['CURL_ERRORS'''"\n\n" 'Zen Cart message: ' $detailedMessage "\n\n" $errorInfo "\n\n" 'Transaction Response Details: ' print_r($responsetrue"\n\n" 'Transaction Submission: ' urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList)true)));
  1856.           $detailedEmailMessage .= $explain;
  1857.           if (!isset($response['L_ERRORCODE0']&& isset($response['RESULT'])) $detailedEmailMessage .= "\n\n" print_r($responseTRUE);
  1858.           zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSMODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT ' (' zen_uncomment($errorNum')'zen_uncomment($detailedEmailMessage)STORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br(zen_uncomment($detailedEmailMessage)))'paymentalert');
  1859.           if ($response['L_ERRORCODE0'== 15012$detailedEmailMessage '';
  1860.           $this->terminateEC(($detailedEmailMessage == '' $errorText ' (' $errorNum ') ' $detailedMessage)($gateway_mode true false)FILENAME_CHECKOUT_PAYMENT);
  1861.           return true;
  1862.         }
  1863.         break;
  1864.       case 'DoRefund':
  1865.         if ($basicError || (!isset($response['RESPMSG']&& !isset($response['REFUNDTRANSACTIONID']))) {
  1866.           // if error, display error message. If debug options enabled, email dump to store owner
  1867.           if ($this->enableDebugging{
  1868.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1869.           }
  1870.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_REFUND_ERROR;
  1871.           if ($response['L_ERRORCODE0'== 10009$errorText MODULE_PAYMENT_PAYPALDP_TEXT_REFUNDFULL_ERROR;
  1872.           if ($response['RESULT'== 105 || isset($response['RESPMSG'])) $response['L_SHORTMESSAGE0'$response['RESULT'' ' $response['RESPMSG'];
  1873.           if (urldecode($response['L_LONGMESSAGE0']== 'This transaction has already been fully refunded'$response['L_SHORTMESSAGE0'urldecode($response['L_LONGMESSAGE0']);
  1874.           if (urldecode($response['L_LONGMESSAGE0']== 'Can not do a full refund after a partial refund'$response['L_SHORTMESSAGE0'urldecode($response['L_LONGMESSAGE0']);
  1875.           if (urldecode($response['L_LONGMESSAGE0']== 'The partial refund amount must be less than or equal to the remaining amount'$response['L_SHORTMESSAGE0'urldecode($response['L_LONGMESSAGE0']);
  1876.           if (urldecode($response['L_LONGMESSAGE0']== 'You can not refund this type of transaction'$response['L_SHORTMESSAGE0'urldecode($response['L_LONGMESSAGE0']);
  1877.           $errorText .= ' (' urldecode($response['L_SHORTMESSAGE0']') ' $response['L_ERRORCODE0'];
  1878.           $messageStack->add_session($errorText'error');
  1879.           return true;
  1880.         }
  1881.         break;
  1882.       case 'DoAuthorization':
  1883.       case 'DoReauthorization':
  1884.         if ($basicError{
  1885.           // if error, display error message. If debug options enabled, email dump to store owner
  1886.           if ($this->enableDebugging{
  1887.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1888.           }
  1889.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_AUTH_ERROR;
  1890.           $errorText .= ' (' urldecode($response['L_SHORTMESSAGE0']') ' $response['L_ERRORCODE0'];
  1891.           $messageStack->add_session($errorText'error');
  1892.           return true;
  1893.         }
  1894.         break;
  1895.       case 'DoCapture':
  1896.         if ($basicError{
  1897.           // if error, display error message. If debug options enabled, email dump to store owner
  1898.           if ($this->enableDebugging{
  1899.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1900.           }
  1901.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_CAPT_ERROR;
  1902.           if ($response['RESULT'== 111$response['L_SHORTMESSAGE0'$response['RESULT'' ' $response['RESPMSG'];
  1903.           $errorText .= ' (' urldecode($response['L_SHORTMESSAGE0']') ' $response['L_ERRORCODE0'];
  1904.           $messageStack->add_session($errorText'error');
  1905.           return true;
  1906.         }
  1907.         break;
  1908.       case 'DoVoid':
  1909.         if ($basicError{
  1910.           // if error, display error message. If debug options enabled, email dump to store owner
  1911.           if ($this->enableDebugging{
  1912.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1913.           }
  1914.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_VOID_ERROR;
  1915.           if ($response['RESULT'== 12$response['L_SHORTMESSAGE0'$response['RESULT'' ' $response['RESPMSG'];
  1916.           if ($response['RESULT'== 108$response['L_SHORTMESSAGE0'$response['RESULT'' ' $response['RESPMSG'];
  1917.           $errorText .= ' (' urldecode($response['L_SHORTMESSAGE0']') ' $response['L_ERRORCODE0'];
  1918.           $messageStack->add_session($errorText'error');
  1919.           return true;
  1920.         }
  1921.         break;
  1922.       case 'GetTransactionDetails':
  1923.         if ($basicError{
  1924.           if (isset($response['RESPMSG']&& $response['RESPMSG'== 'Field format error: ORIGID missing'{
  1925.             return FALSE;
  1926.           }
  1927.           // if error, display error message. If debug options enabled, email dump to store owner
  1928.           if ($this->enableDebugging{
  1929.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1930.           }
  1931.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_GETDETAILS_ERROR;
  1932.           $errorText .= ' (' urldecode($response['L_SHORTMESSAGE0']') ' $response['L_ERRORCODE0'];
  1933.           $messageStack->add_session($errorText'error');
  1934.           return true;
  1935.         }
  1936.         break;
  1937.       case 'TransactionSearch':
  1938.         if ($basicError{
  1939.           // if error, display error message. If debug options enabled, email dump to store owner
  1940.           if ($this->enableDebugging{
  1941.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1942.           }
  1943.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_TRANSSEARCH_ERROR;
  1944.           $errorText .= ' (' urldecode($response['L_SHORTMESSAGE0']') ' $response['L_ERRORCODE0'];
  1945.           $messageStack->add_session($errorText'error');
  1946.           return true;
  1947.         }
  1948.         break;
  1949.  
  1950.       default:
  1951.         if ($basicError{
  1952.           // if error, display error message. If debug options enabled, email dump to store owner
  1953.           if ($this->enableDebugging{
  1954.             $this->_doDebug('PayPal Error Log - ' $operation"Value List:\r\n" str_replace('&',"\r\n"$doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) "\r\n\r\nResponse:\r\n" print_r($responsetrue));
  1955.           }
  1956.           $errorText MODULE_PAYMENT_PAYPALDP_TEXT_GEN_API_ERROR;
  1957.           $errorNum .= ' (' urldecode($response['L_SHORTMESSAGE0'' <!-- ' $response['RESPMSG']' -->) ' $response['L_ERRORCODE0'];
  1958.           $detailedMessage ($errorText == MODULE_PAYMENT_PAYPALDP_TEXT_GEN_API_ERROR || $errorText == MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED || $this->enableDebugging || $response['CURL_ERRORS'!= '' || $this->emailAlertsurldecode(' ' $response['L_SHORTMESSAGE0'' - ' $response['L_LONGMESSAGE0'' ' $response['CURL_ERRORS']'';
  1959.           $explain "\n\nProblem occurred while customer #" $_SESSION['customer_id'' -- ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' -- was attempting checkout.' "\n";
  1960.           $detailedEmailMessage ($detailedMessage == '''' MODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_MESSAGE ' ' $response['RESPMSG'urldecode($response['L_ERRORCODE0'"\n" $response['L_SHORTMESSAGE0'"\n" $response['L_LONGMESSAGE0'$response['L_ERRORCODE1'"\n" $response['L_SHORTMESSAGE1'"\n" $response['L_LONGMESSAGE1'$response['L_ERRORCODE2'"\n" $response['L_SHORTMESSAGE2'"\n" $response['L_LONGMESSAGE2'($response['CURL_ERRORS'!= '' "\n" $response['CURL_ERRORS'''"\n\n" 'Zen Cart message: ' $detailedMessage "\n\n" $errorInfo "\n\n" 'Transaction Response Details: ' print_r($responsetrue"\n\n" 'Transaction Submission: ' urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList)true)));
  1961.           if ($detailedEmailMessage != ''$detailedEmailMessage .= $explain;
  1962.           if ($detailedEmailMessage != ''zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSMODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT ' (' zen_uncomment($errorNum')'zen_uncomment($detailedMessage $explain)STORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br(zen_uncomment($detailedEmailMessage)))'paymentalert');
  1963.           $messageStack->add_session($errorText $errorNum $detailedMessage'error');
  1964.           return true;
  1965.         }
  1966.         break;
  1967.     }
  1968.   }
  1969.  
  1970.   function tableCheckup({
  1971.     global $db$sniffer;
  1972.     $fieldOkay1 (method_exists($sniffer'field_type')) $sniffer->field_type(TABLE_PAYPAL'txn_id''varchar(20)'true: -1;
  1973.     $fieldOkay2 ($sniffer->field_exists(TABLE_PAYPAL'module_name')) true : -1;
  1974.     $fieldOkay3 ($sniffer->field_exists(TABLE_PAYPAL'order_id')) true : -1;
  1975.  
  1976.     if ($fieldOkay1 == -1{
  1977.       $sql "show fields from " TABLE_PAYPAL;
  1978.       $result $db->Execute($sql);
  1979.       while (!$result->EOF{
  1980.         if  ($result->fields['Field'== 'txn_id'{
  1981.           if  ($result->fields['Type'== 'varchar(20)'{
  1982.             $fieldOkay1 true// exists and matches required type, so skip to other checkup
  1983.           else {
  1984.             $fieldOkay1 $result->fields['Type']// doesn't match, so return what it "is"
  1985.             break;
  1986.           }
  1987.         }
  1988.         $result->MoveNext();
  1989.       }
  1990.     }
  1991.  
  1992.     if ($fieldOkay1 !== true{
  1993.       // temporary fix to table structure for v1.3.7.x -- may remove in later release
  1994.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE payment_type payment_type varchar(40) NOT NULL default ''");
  1995.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE txn_type txn_type varchar(40) NOT NULL default ''");
  1996.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE payment_status payment_status varchar(32) NOT NULL default ''");
  1997.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE reason_code reason_code varchar(40) default NULL");
  1998.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE pending_reason pending_reason varchar(32) default NULL");
  1999.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE invoice invoice varchar(128) default NULL");
  2000.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE payer_business_name payer_business_name varchar(128) default NULL");
  2001.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE address_name address_name varchar(64) default NULL");
  2002.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE address_street address_street varchar(254) default NULL");
  2003.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE address_city address_city varchar(120) default NULL");
  2004.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE address_state address_state varchar(120) default NULL");
  2005.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE payer_email payer_email varchar(128) NOT NULL default ''");
  2006.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE business business varchar(128) NOT NULL default ''");
  2007.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE receiver_email receiver_email varchar(128) NOT NULL default ''");
  2008.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE txn_id txn_id varchar(20) NOT NULL default ''");
  2009.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE parent_txn_id parent_txn_id varchar(20) default NULL");
  2010.     }
  2011.     if ($fieldOkay2 !== true{
  2012.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " ADD COLUMN module_name varchar(40) NOT NULL default '' after txn_type");
  2013.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " ADD COLUMN module_mode varchar(40) NOT NULL default '' after module_name");
  2014.     }
  2015.     if ($fieldOkay3 !== true{
  2016.       $db->Execute("ALTER TABLE " TABLE_PAYPAL " CHANGE zen_order_id order_id int(11) NOT NULL default '0'");
  2017.     }
  2018.  
  2019.   }
  2020.  
  2021.   /****************************************************************************************************************************
  2022.    * ADDED CODE FOR 3D-SECURE SUPPORT PROVIDED BY CARDINALCOMMERCE FOR PAYPAL-UK
  2023.    */
  2024.  
  2025.   /**
  2026.    * reset session vars related to 3D-Secure processing
  2027.    */
  2028.   function clear_3DSecure_session_vars($thorough FALSE{
  2029.     if ($thorough{
  2030.       if (isset($_SESSION['3Dsecure_requires_lookup'])) unset($_SESSION['3Dsecure_requires_lookup']);
  2031.       if (isset($_SESSION['3Dsecure_card_type'])) unset($_SESSION['3Dsecure_card_type']);
  2032.     }
  2033.     if (isset($_SESSION['3Dsecure_merchantData'])) unset($_SESSION['3Dsecure_merchantData']);
  2034.     if (isset($_SESSION['3Dsecure_enroll_lookup_attempted'])) unset($_SESSION['3Dsecure_enroll_lookup_attempted']);
  2035.     if (isset($_SESSION['3Dsecure_authentication_attempted'])) unset($_SESSION['3Dsecure_authentication_attempted']);
  2036.     if (isset($_SESSION['3Dsecure_transactionId'])) unset($_SESSION['3Dsecure_transactionId']);
  2037.     if (isset($_SESSION['3Dsecure_enrolled'])) unset($_SESSION['3Dsecure_enrolled']);
  2038.     if (isset($_SESSION['3Dsecure_acsURL'])) unset($_SESSION['3Dsecure_acsURL']);
  2039.     if (isset($_SESSION['3Dsecure_payload'])) unset($_SESSION['3Dsecure_payload']);
  2040.     if (isset($_SESSION['3Dsecure_auth_status'])) unset($_SESSION['3Dsecure_auth_status']);
  2041.     if (isset($_SESSION['3Dsecure_sig_status'])) unset($_SESSION['3Dsecure_sig_status']);
  2042.     if (isset($_SESSION['3Dsecure_auth_xid'])) unset($_SESSION['3Dsecure_auth_xid']);
  2043.     if (isset($_SESSION['3Dsecure_auth_cavv'])) unset($_SESSION['3Dsecure_auth_cavv']);
  2044.     if (isset($_SESSION['3Dsecure_auth_eci'])) unset($_SESSION['3Dsecure_auth_eci']);
  2045.     if (isset($_SESSION['3Dsecure_term_url'])) unset($_SESSION['3Dsecure_term_url']);
  2046.     if (isset($_SESSION['3Dsecure_auth_url'])) unset($_SESSION['3Dsecure_auth_url']);
  2047.   }
  2048.  
  2049.  
  2050.   function determine3DSecureProtection($cardType$ECI{
  2051.     $resultStatus "NOT PROTECTED";
  2052.     if (strcasecmp($cardType"VISA"== 0){
  2053.       if ((strcasecmp($ECI"05"== 0|| (strcasecmp($ECI"06"== 0)) {
  2054.         $resultStatus "PROTECTED";
  2055.       else {
  2056.         $resultStatus "NOT PROTECTED";
  2057.       }
  2058.     else if (strcasecmp($cardType"MASTERCARD"== 0){
  2059.       if (strcasecmp($ECI"02"== 0{
  2060.         $resultStatus "PROTECTED";
  2061.       else {
  2062.         $resultStatus "NOT PROTECTED";
  2063.       }
  2064.     else if (strcasecmp($cardType"JCB"== 0){
  2065.       if ((strcasecmp($ECI"05"== 0|| (strcasecmp($ECI"06"== 0)) {
  2066.         $resultStatus "PROTECTED";
  2067.       else {
  2068.         $resultStatus "NOT PROTECTED";
  2069.       }
  2070.     }
  2071.     return $resultStatus;
  2072.   }
  2073.  
  2074.   /**
  2075.    * 3D-Secure lookup
  2076.    *
  2077.    * @param array $lookup_data_array 
  2078.    * @return array 
  2079.    */
  2080.   function get3DSecureLookupResponse($lookup_data_array{
  2081.     // Set some defaults
  2082.     if (!isset($lookup_data_array['order_desc']|| $lookup_data_array['order_desc'== ''$lookup_data_array['order_desc''Zen Cart(R) Transaction';
  2083.     if (!isset($lookup_data_array['order_number']|| $lookup_data_array['order_number'== ''$lookup_data_array['order_number'zen_session_id();
  2084.     // format the card expiration
  2085.     $lookup_data_array['cc3d_exp_year'(strlen($lookup_data_array['cc3d_exp_year']== '20' ''$lookup_data_array['cc3d_exp_year'];
  2086.     // get the ISO 4217 currency
  2087.     $iso_currency $this->getISOCurrency($lookup_data_array['currency']);
  2088.     // format the transaction amounts
  2089.     $raw_amount $this->formatRawAmount($lookup_data_array['txn_amount']$iso_currency);
  2090.     // determine the appropriate product code for submission
  2091.     $prodCode FALSE;
  2092.     if (isset($_SESSION['cart'])) {
  2093.       if ($_SESSION['cart']->get_cart_type == 'virtual'{
  2094.         $prodCode 'DIG';
  2095.       else {
  2096.         $prodCode 'PHY';
  2097.       }
  2098.     }
  2099.  
  2100.     // DEBUG ONLY: $this->zcLog(__FILE__ . '->' . __LINE__, 'session details: ' . print_r(array_merge($_POST, $_SESSION), true));
  2101.  
  2102.     // Build the XML cmpi_lookup message
  2103.     $data '<CardinalMPI>';
  2104.     $data .= '<MsgType>cmpi_lookup</MsgType>';
  2105.     $data .= '<Version>1.7</Version>';
  2106.     $data .= '<ProcessorId>' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_PROCESSOR'</ProcessorId>';
  2107.     $data .= '<MerchantId><![CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_MERCHANT']]></MerchantId>';
  2108.     $data .= '<TransactionPwd><![CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_PASSWORD']]></TransactionPwd>';
  2109.     $data .= '<TransactionType>CC</TransactionType>';
  2110.     $data .= '<TransactionMode>S</TransactionMode>';
  2111.     $data .= '<OrderNumber>' $this->escapeXML($lookup_data_array['order_number']'</OrderNumber>';
  2112.     $data .= '<OrderDescription>' $this->escapeXML($lookup_data_array['order_desc']'</OrderDescription>';
  2113.     $data .= '<Amount>' $this->escapeXML($raw_amount'</Amount>';
  2114.     $data .= '<CurrencyCode>' $this->escapeXML($iso_currency'</CurrencyCode>';
  2115.     $data .= '<CardNumber>' $this->escapeXML($lookup_data_array['cc3d_card_number']'</CardNumber>';
  2116.     $data .= '<Cvv>' $this->escapeXML($lookup_data_array['cc3d_checkcode']'</Cvv>';
  2117.     $data .= '<CardCode>' $this->escapeXML($lookup_data_array['cc3d_checkcode']'</CardCode>';
  2118.     $data .= '<CardExpMonth>' $this->escapeXML($lookup_data_array['cc3d_exp_month']'</CardExpMonth>';
  2119.     $data .= '<CardExpYear>' $this->escapeXML($lookup_data_array['cc3d_exp_year']'</CardExpYear>';
  2120.     $data .= '<UserAgent>' $this->escapeXML($_SERVER["HTTP_USER_AGENT"]'</UserAgent>';
  2121.     $ipAddress current(explode(':'str_replace(','':'zen_get_ip_address())));
  2122.     $data .= '<IPAddress>' $this->escapeXML($ipAddress'</IPAddress>';
  2123.     $data .= '<BrowserHeader>' $this->escapeXML($_SERVER["HTTP_ACCEPT"]'</BrowserHeader>';
  2124.     $data .= '<OrderChannel>' $this->escapeXML('MARK''</OrderChannel>';
  2125.     if (isset($lookup_data_array['merchantData'])) $data .= '<MerchantData>' $this->escapeXML($lookup_data_array['merchantData']'</MerchantData>';
  2126.     if ($prodCode !== FALSE && $prodCode != ''$data .= '<ProductCode>' $this->escapeXML($prodCode'</ProductCode>';
  2127.     $data .= '</CardinalMPI>';
  2128.     $debugData str_replace(array('[CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_MERCHANT']]''[CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_PASSWORD']]'$this->escapeXML($lookup_data_array['cc3d_card_number'])$this->escapeXML($lookup_data_array['cc3d_checkcode']))'********'$data);
  2129.  
  2130.     if (MODULE_PAYMENT_CARDINAL_CENTINEL_DEBUGGING !== FALSE{
  2131.       $this->zcLog('Cardinal Lookup 1''[' zen_session_id('] Cardinal Centinel - cmpi_lookup request (' MODULE_PAYMENT_PAYPALDP_CARDINAL_TXN_URL ') - ' $debugData);
  2132.     }
  2133.  
  2134.     $responseString $this->send3DSecureHttp(MODULE_PAYMENT_PAYPALDP_CARDINAL_TXN_URL$data$debugData);
  2135.  
  2136.     if (MODULE_PAYMENT_CARDINAL_CENTINEL_DEBUGGING !== FALSE{
  2137.       $this->zcLog('Cardinal Lookup 2''[' zen_session_id('] Cardinal Centinel - cmpi_lookup response - ' $responseString);
  2138.     }
  2139.  
  2140.     // parse the XML
  2141.     $parser new CardinalXMLParser;
  2142.     $parser->deserializeXml($responseString);
  2143.  
  2144.     $errorNo $parser->deserializedResponse['ErrorNo'];
  2145.     $errorDesc $parser->deserializedResponse['ErrorDesc'];
  2146.     $enrolled $parser->deserializedResponse['Enrolled'];
  2147.  
  2148.     if ($errorNo != 0{
  2149.       $this->zcLog('Cardinal Lookup 3''[' zen_session_id('] Cardinal Centinel - cmpi_lookup error - ' $errorNo ' - ' $errorDesc);
  2150.       $errorText 'Cardinal Lookup 3' '[' zen_session_id('] Cardinal Centinel - cmpi_lookup error - ' $errorNo ' - ' $errorDesc;
  2151.       $errorText .= "\n\n" 'There are 3 steps to configuring your Cardinal 3D-Secure service properly: ' "\n1-Login to the Cardinal Merchant Admin URL supplied in your welcome package (NOT the test URL), and accept the license agreement.\n2-Set a transaction password.\n3-Copy your Cardinal Merchant ID and Cardinal Transaction Password into your ZC PayPal module.\n\nFor specific help, please contact implement@cardinalcommerce.com to sort out your account configuration issues.";
  2152.       $errorText .= "\n\nProblem observed while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication. THEIR PURCHASE WAS NOT SUCCESSFUL. Please resolve this matter to enable future checkouts.';
  2153.       zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSsubstr($errorDesc075' (' $errorNo ')'$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  2154.     }
  2155.  
  2156.     // default the continue flag to 'N'
  2157.     $continue_flag 'N';
  2158.  
  2159.     // determine whether the transaction should continue or fail based upon
  2160.     // the enrollment lookup results
  2161.     if (strcasecmp(MODULE_PAYMENT_PAYPALDP_CARDINAL_AUTHENTICATE_REQ'No'== 0{
  2162.       $continue_flag 'Y';
  2163.     else if (strcmp($errorNo'0'== 0{
  2164.       if (strcasecmp($enrolled'Y'== 0{
  2165.         $continue_flag 'Y';
  2166.       else if (strcasecmp($enrolled'N'== 0{
  2167.         $cardType $this->determineCardType($this->cc_card_number);
  2168.         if (strcasecmp($cardType'VISA'== || strcasecmp($cardType'JCB'== 0{
  2169.           $continue_flag 'Y';
  2170.         }
  2171.       }
  2172.     else if ($errorNo == 1001// merchant has an account configuration problem to fix
  2173.       $errorText CENTINEL_ERROR_CODE_1001 ' - ' CENTINEL_ERROR_CODE_1001_DESC;
  2174.       $errorText .= "\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  2175.       zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSCENTINEL_ERROR_CODE_1001_DESC ' (' CENTINEL_ERROR_CODE_1001 ')'$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  2176.       $continue_flag 'Y';
  2177.     }
  2178.  
  2179.     if (strcasecmp('Y'$continue_flag== 0{
  2180.       // For validation/security purposes, mark the session that the lookup result was acceptable.
  2181.       $_SESSION['3Dsecure_enroll_lookup_attempted''Y';
  2182.     else {
  2183.       // For validation/security purposes, mark the session that the lookup result was not acceptable.
  2184.       unset($_SESSION['3Dsecure_enroll_lookup_attempted']);
  2185.     }
  2186.  
  2187.     $result array('continue_flag' => $continue_flag,
  2188.                     'enrolled' => $enrolled,
  2189.                     'transaction_id' => $parser->deserializedResponse['TransactionId'],
  2190.                     'error_no' => $errorNo,
  2191.                     'error_desc' => $errorDesc,
  2192.                     'acs_url' => $parser->deserializedResponse['ACSUrl'],
  2193.                     'spa_hidden_fields' => $parser->deserializedResponse['SPAHiddenFields'],
  2194.                     'payload' => $parser->deserializedResponse['Payload'],
  2195.                     'cc3d_card_number' => $parser->deserializedResponse['CardNumber'],
  2196.                     'cc3d_checkcode' => $parser->deserializedResponse['CardCode'],
  2197.                     'cc3d_exp_month' => $parser->deserializedResponse['CardExpMonth'],
  2198.                     'cc3d_exp_year' => $parser->deserializedResponse['CardExpYear'],
  2199.                     'EciFlag' => $parser->deserializedResponse['EciFlag'],
  2200.                     'cc3d_merchantdata' => $parser->deserializedResponse['MerchantData']);
  2201.     return $result;
  2202.   }
  2203.   /**
  2204.    * 3D-Secure Authenticate
  2205.    * @param array $authenticate_data_array 
  2206.    * @return array 
  2207.    */
  2208.   function get3DSecureAuthenticateResponse($authenticate_data_array{
  2209.     // Build the XML cmpi_authenticate message
  2210.     $data '<CardinalMPI>';
  2211.     $data .= '<MsgType>cmpi_authenticate</MsgType>';
  2212.     $data .= '<Version>1.7</Version>';
  2213.     $data .= '<ProcessorId>' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_PROCESSOR'</ProcessorId>';
  2214.     $data .= '<MerchantId><![CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_MERCHANT']]></MerchantId>';
  2215.     $data .= '<TransactionType>CC</TransactionType>';
  2216.     $data .= '<TransactionPwd><![CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_PASSWORD']]></TransactionPwd>';
  2217.     $data .= '<TransactionId>' $this->escapeXML($authenticate_data_array['transaction_id']'</TransactionId>';
  2218.     $data .= '<PAResPayload>' $this->escapeXML($authenticate_data_array['payload']'</PAResPayload>';
  2219.     if (isset($authenticate_data_array['merchantData'])) $data .= '<MerchantData>' $this->escapeXML($authenticate_data_array['merchantData']'</MerchantData>';
  2220.     $data .= '</CardinalMPI>';
  2221.     $debugData str_replace(array('[CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_MERCHANT']]''[CDATA[' $this->escapeXML(MODULE_PAYMENT_PAYPALDP_CARDINAL_PASSWORD']]')'********'$data);
  2222.  
  2223.     if (MODULE_PAYMENT_CARDINAL_CENTINEL_DEBUGGING !== FALSE{
  2224.       $this->zcLog('Cardinal Auth 1''[' zen_session_id('] Cardinal Centinel - cmpi_authenticate request (' MODULE_PAYMENT_PAYPALDP_CARDINAL_TXN_URL ') - ' $debugData);
  2225.     }
  2226.  
  2227.     $responseString $this->send3DSecureHttp(MODULE_PAYMENT_PAYPALDP_CARDINAL_TXN_URL$data$debugData);
  2228.  
  2229.     if (MODULE_PAYMENT_CARDINAL_CENTINEL_DEBUGGING !== FALSE{
  2230.       $this->zcLog('Cardinal Auth 2''[' zen_session_id('] Cardinal Centinel - cmpi_authenticate response - ' $responseString);
  2231.     }
  2232.  
  2233.     // parse the XML
  2234.     $parser new CardinalXMLParser;
  2235.     $parser->deserializeXml($responseString);
  2236.  
  2237.     $errorNo $parser->deserializedResponse['ErrorNo'];
  2238.     $errorDesc $parser->deserializedResponse['ErrorDesc'];
  2239.     $authStatus $parser->deserializedResponse['PAResStatus'];
  2240.     $sigStatus $parser->deserializedResponse['SignatureVerification'];
  2241.     $xid $parser->deserializedResponse['Xid'];
  2242.     $cavv $parser->deserializedResponse['Cavv'];
  2243.     $eci $parser->deserializedResponse['EciFlag'];
  2244.  
  2245.     // default the continue flag to 'N'
  2246.     $continue_flag 'N';
  2247.  
  2248.     if ($errorNo == 0{
  2249.       if (strcasecmp($authStatus'Y'== || strcasecmp($authStatus'A'== 0{
  2250.         $continue_flag 'Y';
  2251.       else if (strcasecmp($authStatus'N'== 0{
  2252.         $continue_flag 'N';
  2253.       else if (strcasecmp($authStatus'U'== 0{
  2254.         if (strcasecmp(MODULE_PAYMENT_PAYPALDP_CARDINAL_AUTHENTICATE_REQ'No'== 0{
  2255.           $this->zcLog('Cardinal Auth 3''Business rule in effect (not requiring chargeback protection), so setting to continue to Y');
  2256.           $continue_flag 'Y';
  2257.         }
  2258.       }
  2259.     else {
  2260.       $this->zcLog('Cardinal Auth 4''[' zen_session_id('] Cardinal Centinel - cmpi_authenticate returned an error - ' $errorNo ' - ' $errorDesc);
  2261.       $continue_flag 'N';
  2262.     }
  2263.  
  2264.     if ($continue_flag =='Y' && strcasecmp($sigStatus'N'== 0{
  2265.       // Signature status is 'N', do not continue
  2266.       $continue_flag 'N';
  2267.     }
  2268.  
  2269.     if ($continue_flag == 'Y'{
  2270.       // For validation/security purposes, mark the session that the
  2271.       // authentication result was acceptable.
  2272.       $_SESSION['3Dsecure_authentication_attempted''Y';
  2273.     else {
  2274.       // For validation/security purposes, mark the session that the
  2275.       // authentication result was not acceptable.
  2276.       unset($_SESSION['3Dsecure_authentication_attempted']);
  2277.     }
  2278.  
  2279.     $result array('continue_flag' => $continue_flag,
  2280.                     'auth_status' => $authStatus,
  2281.                     'sig_status' => $sigStatus,
  2282.                     'error_no' => $errorNo,
  2283.                     'error_desc' => $errorDesc,
  2284.                     'auth_xid' => $xid,
  2285.                     'auth_cavv' => $cavv,
  2286.                     'auth_eci' => $eci,
  2287.                     'cc3d_card_number' => $parser->deserializedResponse['CardNumber'],
  2288.                     'cc3d_checkcode' => $parser->deserializedResponse['CardCode'],
  2289.                     'cc3d_exp_month' => $parser->deserializedResponse['CardExpMonth'],
  2290.                     'cc3d_exp_year' => $parser->deserializedResponse['CardExpYear'],
  2291.                     'cc3d_merchantdata' => $parser->deserializedResponse['MerchantData']);
  2292.     return $result;
  2293.   }
  2294.  
  2295.   /////////////////////////////////////////////////////////////////////////////////////////////
  2296.   // Function sendHttp(url, data)
  2297.   //
  2298.   // HTTP POST the form payload to the url using cURL.
  2299.   // form payload according to the Centinel XML Message APIs. The form payload is returned from
  2300.   // the function.
  2301.   /////////////////////////////////////////////////////////////////////////////////////////////
  2302.  
  2303.   function send3DSecureHttp($url$data$debugData{
  2304.       // verify that the URL uses a supported protocol.
  2305.     if ((strpos($url"http://")=== 0|| (strpos($url"https://")=== 0)) {
  2306.  
  2307.       // create a new cURL resource and set params
  2308.       $ch curl_init($url);
  2309.       curl_setopt($chCURLOPT_POST,1);
  2310.       curl_setopt($chCURLOPT_POSTFIELDS"cmpi_msg=".urlencode($data));
  2311.       curl_setopt($chCURLOPT_SSL_VERIFYHOST,  2);
  2312.       curl_setopt($chCURLOPT_RETURNTRANSFER,1);
  2313.       curl_setopt($chCURLOPT_SSL_VERIFYPEERFALSE);
  2314.       curl_setopt($chCURLOPT_TIMEOUT8);
  2315.  
  2316.       // Execute the request.
  2317.       $result curl_exec($ch);
  2318.       $succeeded  curl_errno($ch== true false;
  2319.       $error curl_errno($ch'-' curl_error($ch);
  2320.  
  2321.       // close cURL resource, and free up system resources
  2322.       curl_close($ch);
  2323.  
  2324.       // If Communication was not successful set error result
  2325.       if (!$succeeded{
  2326.         $this->zcLog('Cardinal Send 1''[' zen_session_id('] Cardinal Centinel - ' CENTINEL_ERROR_CODE_8030_DESC);
  2327.         $this->zcLog('Cardinal Send 2''[' zen_session_id('] Centinel Request:  ' $debugData);
  2328.         $this->zcLog('Cardinal Send 3''[' zen_session_id('] Centinel Response: ' $result);
  2329.         $result $this->setErrorResponse(CENTINEL_ERROR_CODE_8030CENTINEL_ERROR_CODE_8030_DESC);
  2330.  
  2331.         $errorText CENTINEL_ERROR_CODE_8030 ' - ' CENTINEL_ERROR_CODE_8030_DESC;
  2332.         $errorText .= "\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  2333.         if ($error != '-'$errorText .= "\n\nCURL error: " $error;
  2334.         zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSCENTINEL_ERROR_CODE_8030_DESC ' (' CENTINEL_ERROR_CODE_8030 ')'$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  2335.       else if (strpos($result"<CardinalMPI>"=== false{
  2336.         // Assert that we received an expected Centinel Message in response.
  2337.         $this->zcLog('Cardinal Send 4''[' zen_session_id('] Cardinal Centinel - ' CENTINEL_ERROR_CODE_8010_DESC);
  2338.         $this->zcLog('Cardinal Send 5''[' zen_session_id('] Centinel Request:  ' $debugData);
  2339.         $this->zcLog('Cardinal Send 6''[' zen_session_id('] Centinel Response: ' $result);
  2340.         $result $this->setErrorResponse(CENTINEL_ERROR_CODE_8010CENTINEL_ERROR_CODE_8010_DESC);
  2341.         $errorText CENTINEL_ERROR_CODE_8010 ' - ' CENTINEL_ERROR_CODE_8010_DESC;
  2342.         $errorText .= "\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  2343.         zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSCENTINEL_ERROR_CODE_8010_DESC ' (' CENTINEL_ERROR_CODE_8010 ')'$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  2344.       else {
  2345.         // Check whether the merchant has a properly configured 3D-Secure account
  2346.         if (strpos($result"<ErrorNo>4243"0{
  2347.           $this->zcLog('Cardinal Send 4''[' zen_session_id('] Cardinal Centinel - ' CENTINEL_ERROR_CODE_4243_DESC);
  2348.           $this->zcLog('Cardinal Send 5''[' zen_session_id('] Centinel Request:  ' $debugData);
  2349.           $this->zcLog('Cardinal Send 6''[' zen_session_id('] Centinel Response: ' $result);
  2350.           $result $this->setErrorResponse(CENTINEL_ERROR_CODE_4243CENTINEL_ERROR_CODE_4243_DESC);
  2351.           $errorText CENTINEL_ERROR_CODE_4243 ' - ' CENTINEL_ERROR_CODE_4243_DESC;
  2352.           $errorText .= "\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  2353.           zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSCENTINEL_ERROR_CODE_4243_DESC ' (' CENTINEL_ERROR_CODE_4243 ')'$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  2354.         }
  2355.       }
  2356.     else {
  2357.       $this->zcLog('Cardinal Send 7''[' zen_session_id('] Cardinal Centinel - ' CENTINEL_ERROR_CODE_8000_DESC ' - ' $url);
  2358.       $result $this->setErrorResponse(CENTINEL_ERROR_CODE_8000CENTINEL_ERROR_CODE_8000_DESC);
  2359.       $errorText CENTINEL_ERROR_CODE_8000 ' - ' CENTINEL_ERROR_CODE_8000_DESC;
  2360.       $errorText .= "\n\nProblem occurred while customer " $_SESSION['customer_id'' ' $_SESSION['customer_first_name'' ' $_SESSION['customer_last_name'' was attempting checkout with 3D-Secure authentication.';
  2361.       zen_mail(STORE_NAMESTORE_OWNER_EMAIL_ADDRESSCENTINEL_ERROR_CODE_8000_DESC ' (' CENTINEL_ERROR_CODE_8000 ')'$errorTextSTORE_OWNERSTORE_OWNER_EMAIL_ADDRESSarray('EMAIL_MESSAGE_HTML'=>nl2br($errorText))'paymentalert');
  2362.     }
  2363.  
  2364.     return $result;
  2365.   }
  2366.  
  2367.   /////////////////////////////////////////////////////////////////////////////////////////////
  2368.   // Function escapeXML(value)
  2369.   //
  2370.   // Escaped string converting all '&' to '&amp;' and all '<' to '&lt'. Return the escaped value.
  2371.   /////////////////////////////////////////////////////////////////////////////////////////////
  2372.   function escapeXML($elementValue){
  2373.     $escapedValue str_replace("&""&amp;"trim($elementValue));
  2374.     $escapedValue str_replace("<""&lt;"$escapedValue);
  2375.     return $escapedValue;
  2376.   }
  2377.  
  2378.   /////////////////////////////////////////////////////////////////////////////////////////////
  2379.   // Function setErrorResponse(errorNo, errorDesc)
  2380.   //
  2381.   // Initialize an Error response to ensure that parsing will be handled properly.
  2382.   /////////////////////////////////////////////////////////////////////////////////////////////
  2383.   function setErrorResponse($errorNo$errorDesc{
  2384.     $resultText  "<CardinalMPI>";
  2385.     $resultText $resultText."<ErrorNo>".($errorNo)."</ErrorNo>" ;
  2386.     $resultText $resultText."<ErrorDesc>".($errorDesc)."</ErrorDesc>" ;
  2387.     $resultText  $resultText."</CardinalMPI>";
  2388.     return $resultText;
  2389.   }
  2390.  
  2391.   function get_authentication_error({
  2392.     $this->clear_3DSecure_session_vars();
  2393.     return CENTINEL_AUTHENTICATION_ERROR;
  2394.   }
  2395.  
  2396.   // Convert Currency to ISO4217 3 digit code
  2397.   //   If curr is char code will convert to digit code
  2398.   //   If curr is digits less than 3, will pad with leading zeros
  2399.   //   If we are unable to format curr, curr is returned unformatted.
  2400.   //   MAPs will return the appropriate error code.
  2401.   function getISOCurrency($curr{
  2402.     $out "";
  2403.     if(ctype_digit($curr|| is_int($curr)) {
  2404.       $numCurr $curr 0;
  2405.       if($numCurr 10{
  2406.         $out "00" $numCurr;
  2407.       else if ($numCurr 100{
  2408.         $out "0" $numCurr;
  2409.       else {
  2410.         //Assume 3 digits (if greater let MAPs handle error)
  2411.         $out "" $numCurr;
  2412.       }
  2413.     else {
  2414.       // Convert char to digit
  2415.       $curCode Array();
  2416.       $curCode["AUD"]="036";
  2417.       $curCode["CAD"]="124";
  2418.       $curCode["CHF"]="756";
  2419.       $curCode["CZK"]="203";
  2420.       $curCode["DKK"]="208";
  2421.       $curCode["EUR"]="978";
  2422.       $curCode["GBP"]="826";
  2423.       $curCode["HUF"]="348";
  2424.       $curCode["JPY"]="392";
  2425.       $curCode["NOK"]="578";
  2426.       $curCode["NZD"]="554";
  2427.       $curCode["PLN"]="985";
  2428.       $curCode["SEK"]="752";
  2429.       $curCode["SGD"]="702";
  2430.       $curCode["USD"]="840";
  2431.       $out $curCode[$curr];
  2432.     }
  2433.  
  2434.     return $out;
  2435.   }
  2436.  
  2437.   // Format Amount to rawamount
  2438.   //   Rawamount does not contain a decimal and is rounded and padded
  2439.   //   based on the currency exponenet value
  2440.   //   amount - Double floating point
  2441.   //   curr - ISO4217 Currency code, 3char or 3digit
  2442.   function formatRawAmount($amount$curr{
  2443.     $dblAmount $amount 0.0;
  2444.  
  2445.     // Build Currency format table
  2446.     $curFormat Array();
  2447.     $curFormat["036"]=2;
  2448.     $curFormat["124"]=2;
  2449.     $curFormat["203"]=2;
  2450.     $curFormat["208"]=2;
  2451.     $curFormat["348"]=2;
  2452.     $curFormat["392"]=0;
  2453.     $curFormat["554"]=2;
  2454.     $curFormat["578"]=2;
  2455.     $curFormat["702"]=2;
  2456.     $curFormat["752"]=2;
  2457.     $curFormat["756"]=2;
  2458.     $curFormat["826"]=2;
  2459.     $curFormat["840"]=2;
  2460.     $curFormat["978"]=2;
  2461.     $curFormat["985"]=2;
  2462.  
  2463.     $digCurr $this->getISOCurrency("" $curr);
  2464.     $exponent $curFormat[$digCurr];
  2465.     $strAmount "" Round($dblAmount$exponent);
  2466.     $strRetVal "" $strAmount;
  2467.  
  2468.     // decimal position
  2469.     $curpos strpos($strRetVal".");
  2470.  
  2471.     // Pad with zeros
  2472.     if($curpos == true{
  2473.       $padCount $exponent (strlen($strRetVal$curpos 1);
  2474.       for($i=0;$i<$padCount;$i++{
  2475.         $strRetVal .= "0";
  2476.       }
  2477.     else {
  2478.       $padCount $exponent;
  2479.       for($i=0;$i<$padCount;$i++{
  2480.         $strRetVal .= "0";
  2481.       }
  2482.     }
  2483.  
  2484.     if($curpos !== false{
  2485.       $strRetVal substr($strRetVal0$curpossubstr($strRetVal$curpos+1);
  2486.     }
  2487.     return $strRetVal;
  2488.   }
  2489.  
  2490.   function requiresLookup($info{
  2491.     if (is_numeric($info)) {
  2492.       $cardType $this->determineCardType($info);
  2493.     else {
  2494.       $cardType $info;
  2495.     }
  2496.     if (in_array(strtoupper($cardType)array('VISA''MASTERCARD''JCB''MAESTRO'))) {
  2497.       return true;
  2498.     else {
  2499.       return false;
  2500.     }
  2501.   }
  2502.  
  2503.   function determineCardType($cardNumber{
  2504.     $cardNumber preg_replace('/[^0-9]/'''$cardNumber);
  2505.     // NOTE: We check Solo before Maestro, and Maestro/Switch *before* we check Visa/Mastercard, so we don't have to rule-out numerous types from V/MC matching rules.
  2506.     if (preg_match('/^(6334[5-9][0-9]|6767[0-9]{2})[0-9]{10}([0-9]{2,3}?)?$/'$cardNumber)) {
  2507.       $cardType "SOLO";
  2508.     else if (preg_match('/^(49369[8-9]|490303|6333[0-4][0-9]|6759[0-9]{2}|5[0678][0-9]{4}|6[0-9][02-9][02-9][0-9]{2})[0-9]{6,13}?$/'$cardNumber)) {
  2509.       $cardType "MAESTRO";
  2510.     else if (preg_match('/^(49030[2-9]|49033[5-9]|4905[0-9]{2}|49110[1-2]|49117[4-9]|49918[0-2]|4936[0-9]{2}|564182|6333[0-4][0-9])[0-9]{10}([0-9]{2,3}?)?$/'$cardNumber)) {
  2511.       $cardType "MAESTRO"// SWITCH is now Maestro
  2512.     elseif (preg_match('/^4[0-9]{12}([0-9]{3})?$/'$cardNumber)) {
  2513.       $cardType 'VISA';
  2514.     elseif (preg_match('/^5[1-5][0-9]{14}$/'$cardNumber)) {
  2515.       $cardType 'MASTERCARD';
  2516.     elseif (preg_match('/^3[47][0-9]{13}$/'$cardNumber)) {
  2517.       $cardType 'AMEX';
  2518.     elseif (preg_match('/^3(0[0-5]|[68][0-9])[0-9]{11}$/'$cardNumber)) {
  2519.       $cardType 'DINERS CLUB';
  2520.     elseif (preg_match('/^(6011[0-9]{12}|622[1-9][0-9]{12}|64[4-9][0-9]{13}|65[0-9]{14})$/'$cardNumber)) {
  2521.       $cardType 'DISCOVER';
  2522.     elseif (preg_match('/^(35(28|29|[3-8][0-9])[0-9]{12}|2131[0-9]{11}|1800[0-9]{11})$/'$cardNumber)) {
  2523.       $cardType "JCB";
  2524.     else {
  2525.       $cardType "UNKNOWN";
  2526.     }
  2527.     return $cardType;
  2528.   }
  2529.  
  2530. }
  2531.  
  2532.  
  2533.  
  2534.  
  2535.   var $xml_parser;
  2536.   var $elementName;
  2537.   var $elementValue;
  2538.  
  2539.   /////////////////////////////////////////////////////////////////////////////////////////////
  2540.   // Function CardinalXMLParser()
  2541.   //
  2542.   // Initialize the XML parser.
  2543.   /////////////////////////////////////////////////////////////////////////////////////////////
  2544.  
  2545.   function CardinalXMLParser({
  2546.     $this->xml_parser = xml_parser_create();
  2547.   }
  2548.  
  2549.   /////////////////////////////////////////////////////////////////////////////////////////////
  2550.   // Function startElement(parser, name, attribute)
  2551.   //
  2552.   // Start Tag Element Handler
  2553.   /////////////////////////////////////////////////////////////////////////////////////////////
  2554.  
  2555.   function startElement($parser$name$attrs=''{
  2556.     $this->elementName = $name;
  2557.   }
  2558.  
  2559.   /////////////////////////////////////////////////////////////////////////////////////////////
  2560.   // Function elementData(parser, data)
  2561.   //
  2562.   // Element Data Handler
  2563.   /////////////////////////////////////////////////////////////////////////////////////////////
  2564.  
  2565.   function elementData($parser$data{
  2566.     $this->elementValue .= $data;
  2567.   }
  2568.  
  2569.   /////////////////////////////////////////////////////////////////////////////////////////////
  2570.   // Function endElement(name, value)
  2571.   //
  2572.   // End Tag Element Handler
  2573.   /////////////////////////////////////////////////////////////////////////////////////////////
  2574.  
  2575.   function endElement($parser$name{
  2576.     if (substr($this->elementValue01== "\n"$this->elementValue = substr($this->elementValue1);
  2577.     $this->deserializedResponse[$this->elementName]$this->elementValue;
  2578.     $this->elementName = "";
  2579.     $this->elementValue = "";
  2580.   }
  2581.  
  2582.   /////////////////////////////////////////////////////////////////////////////////////////////
  2583.   // Function deserialize(xmlString)
  2584.   //
  2585.   // Deserialize the XML reponse message and add each element to the deserializedResponse collection.
  2586.   // Once complete, then each element reference will be available using the getValue function.
  2587.   /////////////////////////////////////////////////////////////////////////////////////////////
  2588.  
  2589.   function deserializeXml($responseString{
  2590.     xml_set_object($this->xml_parser$this);
  2591.     xml_parser_set_option($this->xml_parser,XML_OPTION_CASE_FOLDING,FALSE);
  2592.     xml_set_element_handler($this->xml_parser"startElement""endElement");
  2593.     xml_set_character_data_handler($this->xml_parser"elementData");
  2594.  
  2595.     if (!xml_parse($this->xml_parser$responseString)) {
  2596.       $this->deserializedResponse["ErrorNo"]CENTINEL_ERROR_CODE_8020;
  2597.       $this->deserializedResponse["ErrorDesc"]CENTINEL_ERROR_CODE_8020_DESC;
  2598.     }
  2599.  
  2600.     xml_parser_free($this->xml_parser);
  2601.   }
  2602. }

Documentation generated on Sat, 19 Nov 2011 20:42:27 -0500 by phpDocumentor 1.4.3