PageRenderTime 84ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 1ms

/includes/modules/payment/paypalwpp.php

https://github.com/happyxlq/lt_svn
PHP | 2970 lines | 2068 code | 275 blank | 627 comment | 718 complexity | 9f0912611febbb9394825e30cebc86de MD5 | raw file
Possible License(s): AGPL-1.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * paypalwpp.php payment module class for Paypal Express Checkout / Website Payments Pro / Payflow Pro payment methods
  4. *
  5. * @package paymentMethod
  6. * @copyright Copyright 2003-2007 Zen Cart Development Team
  7. * @copyright Portions Copyright 2003 osCommerce
  8. * @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
  9. * @version $Id: paypalwpp.php 7620 2007-12-11 19:12:46Z drbyte $
  10. */
  11. /**
  12. * load the communications layer code
  13. */
  14. require_once(DIR_FS_CATALOG . DIR_WS_MODULES . 'payment/paypal/paypal_curl.php');
  15. /**
  16. * the PayPal payment module with Express Checkout
  17. *
  18. * currently supports US-NVP
  19. *
  20. */
  21. class paypalwpp extends base {
  22. /**
  23. * name of this module
  24. *
  25. * @var string
  26. */
  27. var $code;
  28. /**
  29. * displayed module title
  30. *
  31. * @var string
  32. */
  33. var $title;
  34. /**
  35. * displayed module description
  36. *
  37. * @var string
  38. */
  39. var $description;
  40. /**
  41. * module status - set based on various config and zone criteria
  42. *
  43. * @var string
  44. */
  45. var $enabled;
  46. /**
  47. * the zone to which this module is restricted for use
  48. *
  49. * @var string
  50. */
  51. var $zone;
  52. /**
  53. * array holding accepted DP/gateway card types
  54. *
  55. * @var array
  56. */
  57. var $cards = array();
  58. /**
  59. * JS code used for gateway/DP mode
  60. *
  61. * @var string
  62. */
  63. var $cc_type_javascript = '';
  64. /**
  65. * JS code used for gateway/DP mode
  66. *
  67. * @var string
  68. */
  69. var $cc_type_check = '';
  70. /**
  71. * debugging flag
  72. *
  73. * @var boolean
  74. */
  75. var $enableDebugging = false;
  76. /**
  77. * is DP enabled ?
  78. *
  79. * @var boolean
  80. */
  81. var $enableDirectPayment = false;
  82. /**
  83. * Determines whether payment page is displayed or not
  84. *
  85. * @var boolean
  86. */
  87. var $showPaymentPage = false;
  88. var $flagDisablePaymentAddressChange = false;
  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
  97. *
  98. * @var string
  99. */
  100. var $buttonSourceEC = 'ZenCart-EC_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. * class constructor
  120. */
  121. function paypalwpp() {
  122. include_once(zen_get_file_directory(DIR_FS_CATALOG . DIR_WS_LANGUAGES . $_SESSION['language'] . '/modules/payment/', 'paypalwpp.php', 'false'));
  123. global $order;
  124. $this->code = 'paypalwpp';
  125. $this->codeTitle = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_EC;
  126. $this->codeVersion = '1.3.8a';
  127. $this->enableDirectPayment = (MODULE_PAYMENT_PAYPALWPP_DIRECT_ENABLED == 'True');
  128. $this->enabled = (MODULE_PAYMENT_PAYPALWPP_STATUS == 'True');
  129. // Set the title & description text based on the mode we're in ... EC vs DP vs admin
  130. if (IS_ADMIN_FLAG === true) {
  131. $this->description = sprintf(MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_DESCRIPTION, ' (rev' . $this->codeVersion . ')');
  132. switch (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE) {
  133. case ('PayPal'):
  134. if (MODULE_PAYMENT_PAYPALWPP_DIRECT_ENABLED == 'True') {
  135. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_WPP;
  136. } else {
  137. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_EC;
  138. }
  139. break;
  140. case ('Payflow-UK'):
  141. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_PRO20;
  142. break;
  143. case ('Payflow-US'):
  144. if (defined('MODULE_PAYMENT_PAYPALWPP_PAYFLOW_EC') && MODULE_PAYMENT_PAYPALWPP_PAYFLOW_EC == 'Yes') {
  145. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_PF_EC;
  146. } else {
  147. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_PF_GATEWAY;
  148. }
  149. break;
  150. default:
  151. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_ADMIN_TITLE_EC;
  152. }
  153. if ($this->enabled) {
  154. if ( (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'PayPal' && (MODULE_PAYMENT_PAYPALWPP_APISIGNATURE == '' || MODULE_PAYMENT_PAYPALWPP_APIUSERNAME == '' || MODULE_PAYMENT_PAYPALWPP_APIPASSWORD == ''))
  155. || (substr(MODULE_PAYMENT_PAYPALWPP_MODULE_MODE,0,7) == 'Payflow' && (MODULE_PAYMENT_PAYPALWPP_PFPARTNER == '' || MODULE_PAYMENT_PAYPALWPP_PFVENDOR == '' || MODULE_PAYMENT_PAYPALWPP_PFUSER == '' || MODULE_PAYMENT_PAYPALWPP_PFPASSWORD == ''))
  156. ) $this->title .= '<span class="alert"><strong> NOT CONFIGURED YET</strong></span>';
  157. if (MODULE_PAYMENT_PAYPALWPP_SERVER =='sandbox') $this->title .= '<strong><span class="alert"> (sandbox active)</span></strong>';
  158. if (MODULE_PAYMENT_PAYPALWPP_DEBUGGING =='Log File' || MODULE_PAYMENT_PAYPALWPP_DEBUGGING =='Log and Email') $this->title .= '<strong> (Debug)</strong>';
  159. if (!function_exists('curl_init')) $this->title .= '<strong><span class="alert"> CURL NOT FOUND. Cannot Use.</span></strong>';
  160. }
  161. } else {
  162. $this->description = MODULE_PAYMENT_PAYPALWPP_TEXT_DESCRIPTION;
  163. $this->title = MODULE_PAYMENT_PAYPALWPP_EC_TEXT_TITLE; //pp
  164. if (!$this->in_special_checkout() && $this->enableDirectPayment == true) {
  165. $this->title = MODULE_PAYMENT_PAYPALWPP_TEXT_TITLE; //cc
  166. }
  167. }
  168. if ((!defined('PAYPAL_OVERRIDE_CURL_WARNING') || (defined('PAYPAL_OVERRIDE_CURL_WARNING') && PAYPAL_OVERRIDE_CURL_WARNING != 'True')) && !function_exists('curl_init')) $this->enabled = false;
  169. $this->enableDebugging = (MODULE_PAYMENT_PAYPALWPP_DEBUGGING == 'Log File' || MODULE_PAYMENT_PAYPALWPP_DEBUGGING =='Log and Email');
  170. $this->emailAlerts = (MODULE_PAYMENT_PAYPALWPP_DEBUGGING == 'Log File' || MODULE_PAYMENT_PAYPALWPP_DEBUGGING =='Log and Email' || MODULE_PAYMENT_PAYPALWPP_DEBUGGING == 'Alerts Only');
  171. $this->doDPonly = (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE =='Payflow-US' && !(defined('MODULE_PAYMENT_PAYPALWPP_PAYFLOW_EC') && MODULE_PAYMENT_PAYPALWPP_PAYFLOW_EC == 'Yes'));
  172. $this->showPaymentPage = (MODULE_PAYMENT_PAYPALWPP_SKIP_PAYMENT_PAGE == 'No') ? true : false;
  173. $this->sort_order = MODULE_PAYMENT_PAYPALWPP_SORT_ORDER;
  174. $this->buttonSourceEC = 'ZenCart-EC_us';
  175. $this->buttonSourceDP = 'ZenCart-DP_us';
  176. if (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-UK') {
  177. $this->buttonSourceEC = 'ZenCart-EC_uk';
  178. $this->buttonSourceDP = 'ZenCart-DP_uk';
  179. }
  180. if (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-US') {
  181. $this->buttonSourceEC = 'ZenCart-ECGW_us';
  182. $this->buttonSourceDP = 'ZenCart-GW_us';
  183. }
  184. $this->order_pending_status = MODULE_PAYMENT_PAYPALWPP_ORDER_PENDING_STATUS_ID;
  185. if ((int)MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID > 0) {
  186. $this->order_status = MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID;
  187. }
  188. $this->new_acct_notify = MODULE_PAYMENT_PAYPALWPP_NEW_ACCT_NOTIFY;
  189. $this->zone = (int)MODULE_PAYMENT_PAYPALWPP_ZONE;
  190. if (is_object($order)) $this->update_status();
  191. if (PROJECT_VERSION_MAJOR != '1' && substr(PROJECT_VERSION_MINOR, 0, 3) != '3.8') $this->enabled = false;
  192. // offer credit card choices for pull-down menu -- only needed for UK version
  193. $this->cards = array();
  194. if (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-UK') {
  195. if (CC_ENABLED_VISA=='1') $this->cards[] = array('id' => 'Visa', 'text' => 'Visa');
  196. if (CC_ENABLED_MC=='1') $this->cards[] = array('id' => 'MasterCard', 'text' => 'MasterCard');
  197. if (CC_ENABLED_MAESTRO=='1') $this->cards[] = array('id' => 'Maestro', 'text' => 'Maestro');
  198. if (CC_ENABLED_SWITCH=='1') $this->cards[] = array('id' => 'Switch', 'text' => 'Switch');
  199. if (CC_ENABLED_SOLO=='1') $this->cards[] = array('id' => 'Solo', 'text' => 'Solo');
  200. }
  201. // if operating in markflow mode, start EC process when submitting order
  202. if (!$this->in_special_checkout() && $this->enableDirectPayment == false) {
  203. $this->form_action_url = zen_href_link('ipn_main_handler.php', 'type=ec&markflow=1&clearSess=1&stage=final', 'SSL', true, true, true);
  204. }
  205. // debug setup
  206. if (!@is_writable($this->_logDir)) $this->_logDir = DIR_FS_CATALOG . $this->_logDir;
  207. if (!@is_writable($this->_logDir)) $this->_logDir = DIR_FS_SQL_CACHE;
  208. // Regular mode:
  209. if ($this->enableDebugging) $this->_logLevel = PEAR_LOG_INFO;
  210. // DEV MODE:
  211. if (defined('PAYPAL_DEV_MODE') && PAYPAL_DEV_MODE == 'true') $this->_logLevel = PEAR_LOG_DEBUG;
  212. if (IS_ADMIN_FLAG === true) $this->tableCheckup();
  213. }
  214. /**
  215. * Sets payment module status based on zone restrictions etc
  216. */
  217. function update_status() {
  218. global $order, $db;
  219. if ($this->enabled && (int)$this->zone > 0) {
  220. $check_flag = false;
  221. $sql = "SELECT zone_id
  222. FROM " . TABLE_ZONES_TO_GEO_ZONES . "
  223. WHERE geo_zone_id = :zoneId
  224. AND zone_country_id = :countryId
  225. ORDER BY zone_id";
  226. $sql = $db->bindVars($sql, ':zoneId', $this->zone, 'integer');
  227. $sql = $db->bindVars($sql, ':countryId', $order->billing['country']['id'], 'integer');
  228. $check = $db->Execute($sql);
  229. while (!$check->EOF) {
  230. if ($check->fields['zone_id'] < 1) {
  231. $check_flag = true;
  232. break;
  233. } elseif ($check->fields['zone_id'] == $order->billing['zone_id']) {
  234. $check_flag = true;
  235. break;
  236. }
  237. $check->MoveNext();
  238. }
  239. if (!$check_flag) {
  240. $this->enabled = false;
  241. }
  242. // module cannot be used for purchase > $10,000 USD
  243. $order_amount = $this->calc_order_amount($order->info['total'], 'USD');
  244. if ($order_amount > 10000) $this->enabled = false;
  245. }
  246. }
  247. /**
  248. * Validate the credit card information via javascript (Number, Owner, and CVV Lengths)
  249. */
  250. function javascript_validation() {
  251. if ($this->in_special_checkout() || $this->enableDirectPayment == false) {
  252. // if we are in express-checkout flow or if DirectPayment is disabled (ie: just mark flow) then no JS validation req'd
  253. return false;
  254. }
  255. return ' if (payment_value == "' . $this->code . '") {' . "\n" .
  256. ' var cc_firstname = document.checkout_payment.paypalec_cc_firstname.value;' . "\n" .
  257. ' var cc_lastname = document.checkout_payment.paypalec_cc_lastname.value;' . "\n" .
  258. ' var cc_number = document.checkout_payment.paypalec_cc_number.value;' . "\n" .
  259. ' var cc_checkcode = document.checkout_payment.paypalwpp_cc_checkcode.value;' . "\n" .
  260. ' if (cc_firstname == "" || cc_lastname == "" || eval(cc_firstname.length) + eval(cc_lastname.length) < ' . CC_OWNER_MIN_LENGTH . ') {' . "\n" .
  261. ' error_message = error_message + "' . MODULE_PAYMENT_PAYPALWPP_TEXT_JS_CC_OWNER . '";' . "\n" .
  262. ' error = 1;' . "\n" .
  263. ' }' . "\n" .
  264. ' if (cc_number == "" || cc_number.length < ' . CC_NUMBER_MIN_LENGTH . ') {' . "\n" .
  265. ' error_message = error_message + "' . MODULE_PAYMENT_PAYPALWPP_TEXT_JS_CC_NUMBER . '";' . "\n" .
  266. ' error = 1;' . "\n" .
  267. ' }' . "\n" .
  268. ' }' . "\n";
  269. }
  270. /**
  271. * Display Credit Card Information Submission Fields on the Checkout Payment Page
  272. */
  273. function selection() {
  274. global $order;
  275. $this->cc_type_check =
  276. 'var value = document.checkout_payment.paypalec_cc_type.value;' .
  277. 'if (value == "Switch" || value == "Solo") {' .
  278. ' document.checkout_payment.paypalec_cc_issue_month.disabled = false;' .
  279. ' document.checkout_payment.paypalec_cc_issue_year.disabled = false;' .
  280. ' document.checkout_payment.paypalec_cc_checkcode.disabled = true;' .
  281. ' if (document.checkout_payment.paypalec_cc_issuenumber) document.checkout_payment.paypalec_cc_issuenumber.disabled = true;' .
  282. '} else if (value == "Maestro") {' .
  283. ' document.checkout_payment.paypalec_cc_issuenumber.disabled = false;' .
  284. ' if (document.checkout_payment.paypalec_cc_issue_month) document.checkout_payment.paypalec_cc_issue_month.disabled = true;' .
  285. ' if (document.checkout_payment.paypalec_cc_issue_year) document.checkout_payment.paypalec_cc_issue_year.disabled = true;' .
  286. ' document.checkout_payment.paypalec_cc_checkcode.disabled = false;' .
  287. '} else {' .
  288. ' if (document.checkout_payment.paypalec_cc_issuenumber) document.checkout_payment.paypalec_cc_issuenumber.disabled = true;' .
  289. ' document.checkout_payment.paypalec_cc_checkcode.disabled = false;' .
  290. '}';
  291. if (sizeof($this->cards) == 0 || $this->enableDirectPayment == false) $this->cc_type_check = '';
  292. /**
  293. * if we are NOT processing via the gateway, we will only display MarkFlow payment option, and no CC fields
  294. */
  295. if ($this->enableDirectPayment == false) {
  296. return array('id' => $this->code,
  297. 'module' => '<img align="absmiddle" src="' . MODULE_PAYMENT_PAYPALWPP_MARK_BUTTON_IMG . '" alt="' . MODULE_PAYMENT_PAYPALWPP_MARK_BUTTON_TXT . '" />' . MODULE_PAYMENT_PAYPALWPP_MARK_BUTTON_TXT,
  298. 'info'=>'<li>If you <b>have paypal account</b>, you can pay your order by your paypal account.</li>
  299. <li>If you <b>don\'t have paypal account</b>, it doesn\'t matter. You canalso pay via paypal with you credit card or bank debit card.</li>
  300. <li>Payment can be submitted in any currency.</li>
  301. <li>Our paypal account is : <b>'.MODULE_PAYMENT_PAYPALWPP_APIUSERNAME.'</b></li>
  302. <a onclick="javascript:window.open(\'https://www.paypal.com/us/cgi-bin/webscr?cmd=xpt/cps/popup/OLCWhatIsPayPal-outside\',\'olcwhatispaypal\',\'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=400, height=350\');" class="hand">
  303. <img border="0" alt="Solution Graphics" src="https://www.paypal.com/en_US/i/bnr/horizontal_solution_PP.gif"/></a>
  304. <a onclick="javascript:window.open(\'https://www.paypal.com/verified/pal='.MODULE_PAYMENT_PAYPALWPP_APIUSERNAME.'\',\'olcwhatispaypal\',\'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=400, height=350\');" class="hand">
  305. <img border="0" src="includes/templates/chanelwatches/images/logo/PaypalVerify.gif"/>
  306. </a>'
  307. );
  308. }
  309. /**
  310. * if we ARE processing via the gateway, prepare and display both the CC fields and the PP option
  311. */
  312. $expires_month = array();
  313. $expires_year = array();
  314. $issue_year = array();
  315. for ($i = 1; $i < 13; $i++) {
  316. $expires_month[] = array('id' => sprintf('%02d', $i), 'text' => strftime('%B - (%m)',mktime(0,0,0,$i,1,2000)));
  317. }
  318. $today = getdate();
  319. for ($i = $today['year']; $i < $today['year'] + 10; $i++) {
  320. $expires_year[] = array('id' => strftime('%y', mktime(0,0,0,1,1,$i)), 'text' => strftime('%Y',mktime(0,0,0,1,1,$i)));
  321. }
  322. $onFocus = ' onfocus="methodSelect(\'pmt-' . $this->code . '\')"';
  323. $fieldsArray = array();
  324. $fieldsArray[] = array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_FIRSTNAME,
  325. 'field' => zen_draw_input_field('paypalec_cc_firstname', $order->billing['firstname'], 'id="'.$this->code.'-cc-ownerf"'. $onFocus) .
  326. '<script type="text/javascript">function paypalec_cc_type_check() { ' . $this->cc_type_check . ' } </script>',
  327. 'tag' => $this->code.'-cc-ownerf');
  328. $fieldsArray[] = array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_LASTNAME,
  329. 'field' => zen_draw_input_field('paypalec_cc_lastname', $order->billing['lastname'], 'id="'.$this->code.'-cc-ownerl"'. $onFocus),
  330. 'tag' => $this->code.'-cc-ownerl');
  331. if (sizeof($this->cards)>0) $fieldsArray[] = array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_TYPE,
  332. 'field' => zen_draw_pull_down_menu('paypalec_cc_type', $this->cards, '', 'onchange="paypalec_cc_type_check();" onblur="paypalec_cc_type_check();"' . 'id="'.$this->code.'-cc-type"'. $onFocus),
  333. 'tag' => $this->code.'-cc-type');
  334. $fieldsArray[] = array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_NUMBER,
  335. 'field' => zen_draw_input_field('paypalec_cc_number', $ccnum, 'id="'.$this->code.'-cc-number"' . $onFocus),
  336. 'tag' => $this->code.'-cc-number');
  337. $fieldsArray[] = array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_EXPIRES,
  338. 'field' => zen_draw_pull_down_menu('paypalec_cc_expires_month', $expires_month, '', 'id="'.$this->code.'-cc-expires-month"' . $onFocus) . '&nbsp;' . zen_draw_pull_down_menu('paypalec_cc_expires_year', $expires_year, '', 'id="'.$this->code.'-cc-expires-year"' . $onFocus),
  339. 'tag' => $this->code.'-cc-expires-month');
  340. $fieldsArray[] = array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_CHECKNUMBER,
  341. 'field' => zen_draw_input_field('paypalec_cc_checkcode', '', 'size="4" maxlength="4"' . ' id="'.$this->code.'-cc-cvv"' . $onFocus) . '&nbsp;<small>' . MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_CHECKNUMBER_LOCATION . '</small><script type="text/javascript">paypalec_cc_type_check();</script>',
  342. 'tag' => $this->code.'-cc-cvv');
  343. if (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'PayPal') $fieldsArray[] = array('title' => '<br /><img src="' . MODULE_PAYMENT_PAYPALWPP_MARK_BUTTON_IMG . '" alt="' . MODULE_PAYMENT_PAYPALWPP_MARK_BUTTON_TXT . '" /><span style="font-size:11px; font-family: Arial, Verdana;"> ' . MODULE_PAYMENT_PAYPALWPP_MARK_BUTTON_TXT . '</span>');
  344. $selection = array('id' => $this->code,
  345. 'module' => MODULE_PAYMENT_PAYPALWPP_TEXT_TITLE,
  346. 'fields' => $fieldsArray);
  347. if (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-UK' && (CC_ENABLED_SOLO=='1' || CC_ENABLED_SWITCH=='1')) {
  348. // add extra fields for Switch/Solo cards
  349. for ($i = $today['year'] - 10; $i <= $today['year']; $i++) {
  350. $issue_year[] = array('id' => strftime('%y',mktime(0,0,0,1,1,$i)), 'text' => strftime('%Y',mktime(0,0,0,1,1,$i)));
  351. }
  352. array_splice($selection['fields'], 4, 0,
  353. array(array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_ISSUE,
  354. 'field' => zen_draw_pull_down_menu('paypalec_cc_issue_month', $expires_month, '', 'id="'.$this->code.'-cc-issue-month"' . $onFocus ) . '&nbsp;' . zen_draw_pull_down_menu('paypalec_cc_issue_year', $issue_year, '', 'id="'.$this->code.'-cc-issue-year"' . $onFocus),
  355. 'tag' => $this->code.'-cc-issue-month')));
  356. }
  357. /* @TODO -- convert this to handle Issue Number
  358. if (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-UK' && CC_ENABLED_MAESTRO=='1') {
  359. // add extra field for Maestro cards
  360. array_splice($selection['fields'], 4, 0,
  361. array(array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_MAESTRO_ISSUENUMBER,
  362. 'field' => zen_draw_pull_down_menu('paypalec_cc_issuenumber', $expires_month, '', 'id="'.$this->code.'-cc-issue-month"' . $onFocus ),
  363. 'tag' => $this->code.'-cc-issue-month')));
  364. }
  365. */
  366. return $selection;
  367. }
  368. /**
  369. * This is the credit card check done between checkout_payment and
  370. * checkout_confirmation (called from checkout_confirmation).
  371. * Evaluates the Credit Card Type for acceptance and the validity of the Credit Card Number & Expiration Date
  372. */
  373. function pre_confirmation_check() {
  374. // If this is an EC checkout, do nothing.
  375. if ($this->in_special_checkout() || $this->enableDirectPayment == false) {
  376. return false;
  377. }
  378. include(DIR_WS_CLASSES . 'cc_validation.php');
  379. $cc_validation = new cc_validation();
  380. $result = $cc_validation->validate($_POST['paypalec_cc_number'],
  381. $_POST['paypalec_cc_expires_month'], $_POST['paypalec_cc_expires_year'],
  382. $_POST['paypalec_cc_issue_month'], $_POST['paypalec_cc_issue_year']);
  383. $error = '';
  384. switch ($result) {
  385. case -1:
  386. $error = sprintf(TEXT_CCVAL_ERROR_UNKNOWN_CARD, substr($cc_validation->cc_number, 0, 4));
  387. break;
  388. case -2:
  389. case -3:
  390. case -4:
  391. $error = TEXT_CCVAL_ERROR_INVALID_DATE;
  392. break;
  393. case false:
  394. $error = TEXT_CCVAL_ERROR_INVALID_NUMBER;
  395. break;
  396. }
  397. $_POST['paypalec_cc_checkcode'] = preg_replace('/[^0-9]/i', '', $_POST['paypalec_cc_checkcode']);
  398. $_POST['paypalec_cc_issuenumber'] = preg_replace('/[^0-9]/i', '', $_POST['paypalec_cc_issuenumber']);
  399. if (($result === false) || ($result < 1) ) {
  400. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_CARD_ERROR . '<br />' . $error, false, FILENAME_CHECKOUT_PAYMENT);
  401. }
  402. $this->cc_card_type = $cc_validation->cc_type;
  403. $this->cc_card_number = $cc_validation->cc_number;
  404. $this->cc_expiry_month = $cc_validation->cc_expiry_month;
  405. $this->cc_expiry_year = $cc_validation->cc_expiry_year;
  406. $this->cc_checkcode = $_POST['paypalec_cc_checkcode'];
  407. }
  408. /**
  409. * Display Credit Card Information for review on the Checkout Confirmation Page
  410. */
  411. function confirmation() {
  412. if ($this->in_special_checkout() || $this->enableDirectPayment == false) {
  413. $confirmation = array('title' => '', 'fields' => array());
  414. } else {
  415. $confirmation = array('title' => '',
  416. 'fields' => array(array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_FIRSTNAME,
  417. 'field' => $_POST['paypalec_cc_firstname']),
  418. array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_LASTNAME,
  419. 'field' => $_POST['paypalec_cc_lastname']),
  420. array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_TYPE,
  421. 'field' => $this->cc_card_type),
  422. array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_NUMBER,
  423. 'field' => substr($_POST['paypalec_cc_number'], 0, 4) . str_repeat('X', (strlen($_POST['paypalec_cc_number']) - 8)) . substr($_POST['paypalec_cc_number'], -4)),
  424. array('title' => MODULE_PAYMENT_PAYPALWPP_TEXT_CREDIT_CARD_EXPIRES,
  425. 'field' => strftime('%B, %Y', mktime(0,0,0,$_POST['paypalec_cc_expires_month'], 1, '20' . $_POST['paypalec_cc_expires_year'])))));
  426. }
  427. return $confirmation;
  428. }
  429. /**
  430. * Prepare the hidden fields comprising the parameters for the Submit button on the checkout confirmation page
  431. */
  432. function process_button() {
  433. if ($this->in_special_checkout() || $this->enableDirectPayment == false) {
  434. $process_button_string = '';
  435. } else {
  436. $_SESSION['paypal_ec_markflow'] = 1;
  437. $process_button_string = zen_draw_hidden_field('ec_cc_type', $_POST['paypalec_cc_type']) .
  438. zen_draw_hidden_field('ec_cc_expdate_month', $_POST['paypalec_cc_expires_month']) .
  439. zen_draw_hidden_field('ec_cc_expdate_year', $_POST['paypalec_cc_expires_year']) .
  440. zen_draw_hidden_field('ec_cc_issuedate_month', $_POST['paypalec_cc_issue_month']) .
  441. zen_draw_hidden_field('ec_cc_issuedate_year', $_POST['paypalec_cc_issue_year']) .
  442. zen_draw_hidden_field('ec_cc_number', $_POST['paypalec_cc_number']) .
  443. zen_draw_hidden_field('ec_cc_checkcode', $_POST['paypalec_cc_checkcode']) .
  444. zen_draw_hidden_field('ec_payer_firstname', $_POST['paypalec_cc_firstname']) .
  445. zen_draw_hidden_field('ec_payer_lastname', $_POST['paypalec_cc_lastname']);
  446. }
  447. return $process_button_string;
  448. }
  449. /**
  450. * Prepare and submit the final authorization to PayPal via the appropriate means as configured
  451. */
  452. function before_process() {
  453. global $order, $doPayPal;
  454. $options = array();
  455. $optionsShip = array();
  456. $optionsNVP = array();
  457. $options = $this->getLineItemDetails();
  458. //$this->zcLog('before_process - 1', 'Have line-item details:' . "\n" . print_r($options, true));
  459. $doPayPal = $this->paypal_init();
  460. if ($this->in_special_checkout() || $this->enableDirectPayment == false) {
  461. $this->zcLog('before_process - EC-1', 'Beginning EC mode');
  462. /****************************************
  463. * Do EC checkout
  464. ****************************************/
  465. // do not allow blank address to be sent to PayPal
  466. if ($_SESSION['paypal_ec_payer_info']['ship_street_1'] != '' && $_SESSION['paypal_ec_payer_info']['ship_address_status'] != 'None') {
  467. $options = array_merge($options,
  468. array('SHIPTONAME' => $_SESSION['paypal_ec_payer_info']['ship_name'],
  469. 'SHIPTOSTREET' => $_SESSION['paypal_ec_payer_info']['ship_street_1'],
  470. 'SHIPTOSTREET2'=> $_SESSION['paypal_ec_payer_info']['ship_street_2'],
  471. 'SHIPTOCITY' => $_SESSION['paypal_ec_payer_info']['ship_city'],
  472. 'SHIPTOSTATE' => $_SESSION['paypal_ec_payer_info']['ship_state'],
  473. 'SHIPTOZIP' => $_SESSION['paypal_ec_payer_info']['ship_postal_code'],
  474. 'SHIPTOCOUNTRYCODE'=> $_SESSION['paypal_ec_payer_info']['ship_country_code'],
  475. ));
  476. $this->zcLog('before_process - EC-2', 'address overrides added:' . "\n" . print_r($options, true));
  477. }
  478. $this->zcLog('before_process - EC-3', 'address info added:' . "\n" . print_r($options, true));
  479. // If the customer has changed their shipping address,
  480. // override the shipping address in PayPal with the shipping
  481. // address that is selected in Zen Cart.
  482. if ($order->delivery['street_address'] != $_SESSION['paypal_ec_payer_info']['ship_street_1'] && $_SESSION['paypal_ec_payer_info']['ship_street_1'] != '') {
  483. $_GET['markflow'] = 2;
  484. if (($address_arr = $this->getOverrideAddress()) !== false) {
  485. // set the override var
  486. $options['ADDROVERRIDE'] = 1;
  487. // set the address info
  488. $options['SHIPTONAME'] = $address_arr['entry_firstname'] . ' ' . $address_arr['entry_lastname'];
  489. $options['SHIPTOSTREET'] = $address_arr['entry_street_address'];
  490. if ($address_arr['entry_suburb'] != '') $options['SHIPTOSTREET2'] = $address_arr['entry_suburb'];
  491. $options['SHIPTOCITY'] = $address_arr['entry_city'];
  492. $options['SHIPTOZIP'] = $address_arr['entry_postcode'];
  493. $options['SHIPTOSTATE'] = $address_arr['zone_code'];
  494. $options['SHIPTOCOUNTRYCODE'] = $address_arr['countries_iso_code_2'];
  495. }
  496. }
  497. // if these optional parameters are blank, remove them from transaction
  498. if (isset($options['SHIPTOSTREET2']) && trim($options['SHIPTOSTREET2']) == '') unset($options['SHIPTOSTREET2']);
  499. if (isset($options['SHIPTOPHONE']) && trim($options['SHIPTOPHONE']) == '') unset($options['SHIPTOPHONE']);
  500. // if State is not supplied, repeat the city so that it's not blank, otherwise PayPal croaks
  501. if ((!isset($options['SHIPTOSTATE']) || trim($options['SHIPTOSTATE']) == '') && $options['SHIPTOCITY'] != '') $options['SHIPTOSTATE'] = $options['SHIPTOCITY'];
  502. $options['BUTTONSOURCE'] = $this->buttonSourceEC;
  503. $options['CURRENCY'] = $this->selectCurrency($order->info['currency']);
  504. $order_amount = $this->calc_order_amount($order->info['total'], $options['CURRENCY']);
  505. // unused at present:
  506. // $options['CUSTOM'] = '';
  507. // $options['INVNUM'] = '';
  508. // $options['DESC'] = '';
  509. // debug output
  510. $this->zcLog('before_process - EC-4', 'info being submitted:' . "\n" . $_SESSION['paypal_ec_token'] . ' ' . $_SESSION['paypal_ec_payer_id'] . ' ' . number_format($order_amount, 2) . "\n" . print_r($options, true));
  511. $response = $doPayPal->DoExpressCheckoutPayment($_SESSION['paypal_ec_token'],
  512. $_SESSION['paypal_ec_payer_id'],
  513. number_format((isset($options['AMT']) ? $options['AMT'] : $order_amount), 2),
  514. $options);
  515. $this->zcLog('before_process - EC-5', 'resultset:' . "\n" . urldecode(print_r($response, true)));
  516. // CHECK RESPONSE -- if error, actions are taken in the errorHandler
  517. $error = $this->_errorHandler($response, 'DoExpressCheckoutPayment');
  518. // SUCCESS
  519. $this->payment_type = MODULE_PAYMENT_PAYPALWPP_EC_TEXT_TYPE;
  520. $this->responsedata = $response;
  521. if ($response['PAYMENTTYPE'] != '') $this->payment_type .= ' (' . urldecode($response['PAYMENTTYPE']) . ')';
  522. $this->transaction_id = trim($response['PNREF'] . ' ' . $response['TRANSACTIONID']);
  523. if (empty($response['PENDINGREASON']) ||
  524. $response['PENDINGREASON'] == 'none' ||
  525. $response['PENDINGREASON'] == 'completed' ||
  526. $response['PAYMENTSTATUS'] == 'Completed') {
  527. $this->payment_status = 'Completed';
  528. if ($this->order_status > 0) $order->info['order_status'] = $this->order_status;
  529. } else {
  530. $this->payment_status = 'Pending (' . $response['PENDINGREASON'] . ')';
  531. $order->info['order_status'] = $this->order_pending_status;
  532. }
  533. $this->avs = 'N/A';
  534. $this->cvv2 = 'N/A';
  535. $this->correlationid = $response['CORRELATIONID'];
  536. $this->transactiontype = $response['TRANSACTIONTYPE'];
  537. $this->payment_time = urldecode($response['ORDERTIME']);
  538. $this->feeamt = urldecode($response['FEEAMT']);
  539. $this->taxamt = urldecode($response['TAXAMT']);
  540. $this->pendingreason = $response['PENDINGREASON'];
  541. $this->reasoncode = $response['REASONCODE'];
  542. // $this->numitems = $_SESSION['cart']->count_contents();
  543. $this->numitems = sizeof($order->products);
  544. $this->amt = urldecode($response['AMT'] . ' ' . $response['CURRENCYCODE']);
  545. $this->auth_code = (isset($this->response['AUTHCODE'])) ? $this->response['AUTHCODE'] : $this->response['TOKEN'];
  546. } else {
  547. /****************************************
  548. * Do DP checkout
  549. ****************************************/
  550. $this->zcLog('before_process - DP-1', 'Beginning DP mode');
  551. // Set state fields depending on what PayPal wants to see for that country
  552. $this->setStateAndCountry($order->billing);
  553. if (zen_not_null($order->delivery['street_address'])) {
  554. $this->setStateAndCountry($order->delivery);
  555. }
  556. // Validate credit card data
  557. include(DIR_WS_CLASSES . 'cc_validation.php');
  558. $cc_validation = new cc_validation();
  559. $response = $cc_validation->validate($_POST['ec_cc_number'], $_POST['ec_cc_expdate_month'], $_POST['ec_cc_expdate_year'],
  560. $_POST['ec_cc_issuedate_month'], $_POST['ec_cc_issuedate_year']);
  561. $error = '';
  562. switch ($response) {
  563. case -1:
  564. $error = sprintf(TEXT_CCVAL_ERROR_UNKNOWN_CARD, substr($cc_validation->cc_number, 0, 4));
  565. break;
  566. case -2:
  567. case -3:
  568. case -4:
  569. $error = TEXT_CCVAL_ERROR_INVALID_DATE;
  570. break;
  571. case false:
  572. $error = TEXT_CCVAL_ERROR_INVALID_NUMBER;
  573. break;
  574. }
  575. $this->zcLog('before_process - DP-2', 'CC validation results: ' . $error . '(' . $response . ')');
  576. if ($response == false || $response < 1) {
  577. $this->terminateEC($error, false, FILENAME_CHECKOUT_PAYMENT);
  578. }
  579. if (!in_array($cc_validation->cc_type, array('Visa', 'MasterCard', 'Switch', 'Solo', 'Discover', 'American Express', 'Maestro'))) {
  580. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_BAD_CARD, false, FILENAME_CHECKOUT_PAYMENT);
  581. }
  582. $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));
  583. // if CC validation passed, continue using the validated data
  584. $cc_type = $cc_validation->cc_type;
  585. $cc_number = $cc_validation->cc_number;
  586. $cc_first_name = $_POST['ec_payer_firstname'];
  587. $cc_last_name = $_POST['ec_payer_lastname'];
  588. $cc_checkcode = $_POST['ec_cc_checkcode'];
  589. $cc_expdate_month = $cc_validation->cc_expiry_month;
  590. $cc_expdate_year = $cc_validation->cc_expiry_year;
  591. $cc_issuedate_month = $_POST['ec_cc_issuedate_month'];
  592. $cc_issuedate_year = $_POST['ec_cc_issuedate_year'];
  593. $cc_owner_ip = zen_get_ip_address();
  594. // If they're still here, set some of the order object's variables.
  595. $order->info['cc_type'] = $cc_type;
  596. $order->info['cc_number'] = substr($cc_number, 0, 4) . str_repeat('X', (strlen($cc_number) - 8)) . substr($cc_number, -4);
  597. $order->info['cc_owner'] = $cc_first_name . ' ' . $cc_last_name;
  598. $order->info['cc_expires'] = $cc_expdate_month . substr($cc_expdate_year, -2);
  599. $order->info['ip_address'] = $cc_owner_ip;
  600. // Set currency
  601. $my_currency = $this->selectCurrency($order->info['currency'], 'DP');
  602. /*
  603. // if CC is switch or solo, must be GBP
  604. if (in_array($cc_type, array('Switch', 'Solo', 'Maestro'))) {
  605. $my_currency = 'GBP';
  606. }
  607. */
  608. $order_amount = $this->calc_order_amount($order->info['total'], $my_currency);
  609. // Initialize the paypal caller object.
  610. $doPayPal = $this->paypal_init();
  611. $optionsAll = array_merge($options,
  612. array('STREET' => $order->billing['street_address'],
  613. 'ZIP' => $order->billing['postcode']));
  614. $optionsNVP = array('CITY' => $order->billing['city'],
  615. 'STATE' => $order->billing['state'],
  616. 'COUNTRYCODE' => $order->billing['country']['iso_code_2'],
  617. 'EXPDATE' => $cc_expdate_month . $cc_expdate_year );
  618. $optionsShip = array();
  619. if (isset($order->delivery) && $order->delivery['street_address'] != '') {
  620. $optionsShip= array('SHIPTONAME' => ($order->delivery['name'] == '' ? $order->delivery['firstname'] . ' ' . $order->delivery['lastname'] : $order->delivery['name']),
  621. 'SHIPTOSTREET' => $order->delivery['street_address'],
  622. 'SHIPTOSTREET2'=> $order->delivery['suburb'],
  623. 'SHIPTOCITY' => $order->delivery['city'],
  624. 'SHIPTOZIP' => $order->delivery['postcode'],
  625. 'SHIPTOSTATE' => $order->delivery['state'],
  626. 'SHIPTOCOUNTRYCODE'=> $order->delivery['country']['iso_code_2']);
  627. }
  628. // if these optional parameters are blank, remove them from transaction
  629. if (isset($optionsShip['SHIPTOSTREET2']) && trim($optionsShip['SHIPTOSTREET2']) == '') unset($optionsShip['SHIPTOSTREET2']);
  630. if (isset($optionsShip['SHIPTOPHONE']) && trim($optionsShip['SHIPTOPHONE']) == '') unset($optionsShip['SHIPTOPHONE']);
  631. // if State is not supplied, repeat the city so that it's not blank, otherwise PayPal croaks
  632. if (!isset($optionsShip['SHIPTOSTATE']) || trim($optionsShip['SHIPTOSTATE']) == '') $optionsShip['SHIPTOSTATE'] = $optionsShip['SHIPTOCITY'];
  633. // Payment Transaction/Authorization Mode
  634. $optionsNVP['PAYMENTACTION'] = (MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE == 'Auth Only') ? 'Authorization' : 'Sale';
  635. // if (in_array($cc_type, array('Switch', 'Solo'))) {
  636. // $optionsNVP['PAYMENTACTION'] = 'Authorization';
  637. // }
  638. $optionsAll['BUTTONSOURCE'] = $this->buttonSourceDP;
  639. $optionsAll['CURRENCY'] = $my_currency;
  640. $optionsAll['IPADDRESS'] = $cc_owner_ip;
  641. if ($cc_issuedate_month && $cc_issuedate_year) {
  642. $optionsAll['CARDSTART'] = $cc_issuedate_month . substr($cc_issuedate_year, -2);
  643. }
  644. // unused at present:
  645. // $options['CUSTOM'] = '';
  646. // $options['INVNUM'] = '';
  647. // $options['DESC'] = '';
  648. $this->zcLog('before_process - DP-4', 'optionsAll: ' . print_r($optionsAll, true) . "\n" . 'optionsNVP: ' . print_r($optionsNVP, true) . "\n" . 'optionsShip' . print_r($optionsShip, true) . "\n" . 'Rest of data: ' . "\n" . number_format($order_amount, 2) . ' ' . $cc_expdate_month . ' ' . substr($cc_expdate_year, -2) . ' ' . $cc_first_name . ' ' . $cc_last_name . ' ' . $cc_type);
  649. $response = $doPayPal->DoDirectPayment(number_format($order_amount, 2),
  650. $cc_number,
  651. $cc_checkcode,
  652. $cc_expdate_month . substr($cc_expdate_year, -2),
  653. $cc_first_name, $cc_last_name,
  654. $cc_type,
  655. $optionsAll, array_merge($optionsNVP, $optionsShip));
  656. $this->zcLog('before_process - DP-5', 'resultset:' . "\n" . print_r($response, true));
  657. // CHECK RESPONSE
  658. $error = $this->_errorHandler($response, 'DoDirectPayment');
  659. $this->feeamt = '';
  660. $this->taxamt = '';
  661. $this->pendingreason = '';
  662. $this->reasoncode = '';
  663. $this->numitems = sizeof($order->products);
  664. $this->responsedata = $response;
  665. if ($response['PNREF']) {
  666. // PNREF only comes from payflow mode
  667. $this->payment_type = MODULE_PAYMENT_PAYPALWPP_PF_TEXT_TYPE;
  668. $this->transaction_id = $response['PNREF'];
  669. $this->payment_status = (MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE == 'Auth Only') ? 'Authorization' : 'Completed';
  670. $this->avs = 'AVSADDR: ' . $response['AVSADDR'] . ', AVSZIP: ' . $response['AVSZIP'] . ', IAVS: ' . $response['IAVS'];
  671. $this->cvv2 = $response['CVV2MATCH'];
  672. $this->amt = $order_amount . ' ' . $my_currency;
  673. $this->payment_time = date('Y-m-d h:i:s');
  674. $this->responsedata['CURRENCYCODE'] = $my_currency;
  675. $this->responsedata['EXCHANGERATE'] = $order->info['currency_value'];
  676. $this->auth_code = $this->response['AUTHCODE'];
  677. } else {
  678. // here we're in NVP mode
  679. $this->transaction_id = $response['TRANSACTIONID'];
  680. $this->payment_type = MODULE_PAYMENT_PAYPALWPP_DP_TEXT_TYPE;
  681. $this->payment_status = (MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE == 'Auth Only') ? 'Authorization' : 'Completed';
  682. $this->pendingreason = (MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE == 'Auth Only') ? 'authorization' : '';
  683. $this->avs = $response['AVSCODE'];
  684. $this->cvv2 = $response['CVV2MATCH'];
  685. $this->correlationid = $response['CORRELATIONID'];
  686. $this->payment_time = urldecode($response['TIMESTAMP']);
  687. $this->amt = urldecode($response['AMT'] . ' ' . $response['CURRENCYCODE']);
  688. $this->auth_code = (isset($this->response['AUTHCODE'])) ? $this->response['AUTHCODE'] : $this->response['TOKEN'];
  689. $this->transactiontype = 'cart';
  690. }
  691. }
  692. }
  693. /**
  694. * When the order returns from the processor, this stores the results in order-status-history and logs data for subsequent use
  695. */
  696. function after_process() {
  697. global $insert_id, $db, $order;
  698. // add a new OSH record for this order's PP details
  699. $commentString = "Transaction ID: :transID: " .
  700. (isset($this->responsedata['PPREF']) ? "\nPPRef: " . $this->responsedata['PPREF'] : "") .
  701. (isset($this->responsedata['AUTHCODE'])? "\nAuthCode: " . $this->responsedata['AUTHCODE'] : "") .
  702. "\nPayment Type: :pmtType: " .
  703. "\nTimestamp: :pmtTime: " .
  704. "\nPayment Status: :pmtStatus: " .
  705. ($this->avs != 'N/A' ? "\nAVS Code: ".$this->avs."\nCVV2 Code: ".$this->cvv2 : '') .
  706. "\nAmount: :orderAmt: ";
  707. $commentString = $db->bindVars($commentString, ':transID:', $this->transaction_id, 'noquotestring');
  708. $commentString = $db->bindVars($commentString, ':pmtType:', $this->payment_type, 'noquotestring');
  709. $commentString = $db->bindVars($commentString, ':pmtTime:', $this->payment_time, 'noquotestring');
  710. $commentString = $db->bindVars($commentString, ':pmtStatus:', $this->payment_status, 'noquotestring');
  711. $commentString = $db->bindVars($commentString, ':orderAmt:', $this->amt, 'noquotestring');
  712. $sql_data_array= array(array('fieldName'=>'orders_id', 'value'=>$insert_id, 'type'=>'integer'),
  713. array('fieldName'=>'orders_status_id', 'value'=>$order->info['order_status'], 'type'=>'integer'),
  714. array('fieldName'=>'date_added', 'value'=>'now()', 'type'=>'noquotestring'),
  715. array('fieldName'=>'customer_notified', 'value'=>0, 'type'=>'integer'),
  716. array('fieldName'=>'comments', 'value'=>$commentString, 'type'=>'string'));
  717. $db->perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
  718. // store the PayPal order meta data -- used for later matching and back-end processing activities
  719. $paypal_order = array('order_id' => $insert_id,
  720. 'txn_type' => $this->transactiontype,
  721. 'module_name' => $this->code,
  722. 'module_mode' => MODULE_PAYMENT_PAYPALWPP_MODULE_MODE,
  723. 'reason_code' => $this->reasoncode,
  724. 'payment_type' => $this->payment_type,
  725. 'payment_status' => $this->payment_status,
  726. 'pending_reason' => $this->pendingreason,
  727. 'invoice' => urldecode($_SESSION['paypal_ec_token'] . $this->responsedata['PPREF']),
  728. 'first_name' => $_SESSION['paypal_ec_payer_info']['payer_firstname'],
  729. 'last_name' => $_SESSION['paypal_ec_payer_info']['payer_lastname'],
  730. 'payer_business_name' => $_SESSION['paypal_ec_payer_info']['payer_business'],
  731. 'address_name' => $_SESSION['paypal_ec_payer_info']['ship_name'],
  732. 'address_street' => $_SESSION['paypal_ec_payer_info']['ship_street_1'],
  733. 'address_city' => $_SESSION['paypal_ec_payer_info']['ship_city'],
  734. 'address_state' => $_SESSION['paypal_ec_payer_info']['ship_state'],
  735. 'address_zip' => $_SESSION['paypal_ec_payer_info']['ship_postal_code'],
  736. 'address_country' => $_SESSION['paypal_ec_payer_info']['ship_country'],
  737. 'address_status' => $_SESSION['paypal_ec_payer_info']['ship_address_status'],
  738. 'payer_email' => $_SESSION['paypal_ec_payer_info']['payer_email'],
  739. 'payer_id' => $_SESSION['paypal_ec_payer_id'],
  740. 'payer_status' => $_SESSION['paypal_ec_payer_info']['payer_status'],
  741. 'payment_date' => trim(preg_replace('/[^0-9-:]/', ' ', $this->payment_time)),
  742. 'business' => '',
  743. 'receiver_email' => (substr(MODULE_PAYMENT_PAYPALWPP_MODULE_MODE,0,7) == 'Payflow' ? MODULE_PAYMENT_PAYPALWPP_PFVENDOR : str_replace('_api1', '', MODULE_PAYMENT_PAYPALWPP_APIUSERNAME)),
  744. 'receiver_id' => '',
  745. 'txn_id' => $this->transaction_id,
  746. 'parent_txn_id' => '',
  747. 'num_cart_items' => (float)$this->numitems,
  748. 'mc_gross' => (float)$this->amt,
  749. 'mc_fee' => (float)urldecode($this->feeamt),
  750. 'mc_currency' => $this->responsedata['CURRENCYCODE'],
  751. 'settle_amount' => (float)urldecode($this->responsedata['SETTLEAMT']),
  752. 'settle_currency' => $this->responsedata['CURRENCYCODE'],
  753. 'exchange_rate' => (urldecode($this->responsedata['EXCHANGERATE']) > 0 ? urldecode($this->responsedata['EXCHANGERATE']) : 1.0),
  754. 'notify_version' => '0',
  755. 'verify_sign' =>'',
  756. 'date_added' => 'now()',
  757. 'memo' => '{Record generated by payment module}'
  758. );
  759. zen_db_perform(TABLE_PAYPAL, $paypal_order);
  760. // Unregister the paypal session variables, making it necessary to start again for another purchase
  761. unset($_SESSION['paypal_ec_temp']);
  762. unset($_SESSION['paypal_ec_token']);
  763. unset($_SESSION['paypal_ec_payer_id']);
  764. unset($_SESSION['paypal_ec_payer_info']);
  765. unset($_SESSION['paypal_ec_final']);
  766. unset($_SESSION['paypal_ec_markflow']);
  767. }
  768. /**
  769. * Build admin-page components
  770. *
  771. * @param int $zf_order_id
  772. * @return string
  773. */
  774. function admin_notification($zf_order_id) {
  775. global $db;
  776. $module = $this->code;
  777. $output = '';
  778. $response = $this->_GetTransactionDetails($zf_order_id);
  779. //$response = $this->_TransactionSearch('2006-12-01T00:00:00Z', $zf_order_id);
  780. $sql = "SELECT * from " . TABLE_PAYPAL . " WHERE order_id = :orderID
  781. AND parent_txn_id = '' AND order_id > 0
  782. ORDER BY paypal_ipn_id DESC LIMIT 1";
  783. $sql = $db->bindVars($sql, ':orderID', $zf_order_id, 'integer');
  784. $ipn = $db->Execute($sql);
  785. if ($ipn->RecordCount() == 0) $ipn->fields = array();
  786. 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');
  787. return $output;
  788. }
  789. /**
  790. * Used to read details of an existing transaction. FOR FUTURE USE.
  791. */
  792. function _GetTransactionDetails($oID) {
  793. global $db, $messageStack, $doPayPal;
  794. $doPayPal = $this->paypal_init();
  795. // look up history on this order from PayPal table
  796. $sql = "select * from " . TABLE_PAYPAL . " where order_id = :orderID AND parent_txn_id = '' ";
  797. $sql = $db->bindVars($sql, ':orderID', $oID, 'integer');
  798. $zc_ppHist = $db->Execute($sql);
  799. if ($zc_ppHist->RecordCount() == 0) return false;
  800. $txnID = $zc_ppHist->fields['txn_id'];
  801. /**
  802. * Read data from PayPal
  803. */
  804. $response = $doPayPal->GetTransactionDetails($txnID);
  805. $error = $this->_errorHandler($response, 'GetTransactionDetails', 10007);
  806. if ($error === false) {
  807. return false;
  808. } else {
  809. return $response;
  810. }
  811. }
  812. /**
  813. * Used to read details of existing transactions. FOR FUTURE USE.
  814. */
  815. function _TransactionSearch($startDate = '', $oID = '', $criteria = '') {
  816. global $db, $messageStack, $doPayPal;
  817. $doPayPal = $this->paypal_init();
  818. // look up history on this order from PayPal table
  819. $sql = "select * from " . TABLE_PAYPAL . " where order_id = :orderID AND parent_txn_id = '' ";
  820. $sql = $db->bindVars($sql, ':orderID', $oID, 'integer');
  821. $zc_ppHist = $db->Execute($sql);
  822. if ($zc_ppHist->RecordCount() == 0) return false;
  823. $txnID = $zc_ppHist->fields['txn_id'];
  824. $startDate = $zc_ppHist->fields['payment_date'];
  825. $timeval = time();
  826. if ($startDate == '') $startDate = date('Y-m-d', $timeval) . 'T' . date('h:i:s', $timeval) . 'Z';
  827. /**
  828. * Read data from PayPal
  829. */
  830. $response = $doPayPal->TransactionSearch($startDate, $txnID, $email, $criteria);
  831. $error = $this->_errorHandler($response, 'TransactionSearch');
  832. if ($error === false) {
  833. return false;
  834. } else {
  835. return $response;
  836. }
  837. }
  838. /**
  839. * Display appropriate error message when needed
  840. */
  841. function get_error() {
  842. include_once(zen_get_file_directory(DIR_FS_CATALOG . DIR_WS_LANGUAGES . $_SESSION['language'] . '/modules/payment/', 'paypalwpp.php', 'false'));
  843. $error = array('title' => MODULE_PAYMENT_PAYPALWPP_ERROR_HEADING,
  844. 'error' => ((isset($_GET['error'])) ? stripslashes(urldecode($_GET['error'])) : MODULE_PAYMENT_PAYPALWPP_TEXT_CARD_ERROR));
  845. return $error;
  846. }
  847. /**
  848. * Evaluate installation status of this module. Returns true if the status key is found.
  849. */
  850. function check() {
  851. global $db;
  852. if (!isset($this->_check)) {
  853. $check_query = $db->Execute("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_PAYPALWPP_STATUS'");
  854. $this->_check = !$check_query->EOF;
  855. }
  856. return $this->_check;
  857. }
  858. /**
  859. * Installs all the configuration keys for this module
  860. */
  861. function install() {
  862. global $db;
  863. $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_PAYPALWPP_STATUS', 'True', 'Do you want to enable this payment module?', '6', '25', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
  864. $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 Direct Payment', 'MODULE_PAYMENT_PAYPALWPP_DIRECT_ENABLED', 'False', 'Would you like to enable credit card payments through PayPal DIRECTLY on your website? <br />(<strong>NOTE:</strong> You need to be subscribed to Website Payments Pro or Payflow Pro to use this feature.)', '6', '25', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
  865. $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_PAYPALWPP_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())");
  866. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Express Checkout: Require Confirmed Address', 'MODULE_PAYMENT_PAYPALWPP_CONFIRMED_ADDRESS', 'No', 'Do you want to require that your customers use a *confirmed* address when choosing their shipping address in PayPal?', '6', '25', 'zen_cfg_select_option(array(\'Yes\', \'No\'), ', now())");
  867. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Express Checkout: Select Cheapest Shipping Automatically', 'MODULE_PAYMENT_PAYPALWPP_AUTOSELECT_CHEAPEST_SHIPPING', 'Yes', 'When customer returns from PayPal, do we want to automatically select the Cheapest shipping method and skip the shipping page? (making it more *express*)<br />Note: enabling this means the customer does *not* have easy access to select an alternate shipping method (without going back to the Checkout-Step-1 page)', '6', '25', 'zen_cfg_select_option(array(\'Yes\', \'No\'), ', now())");
  868. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Express Checkout: Skip Payment Page', 'MODULE_PAYMENT_PAYPALWPP_SKIP_PAYMENT_PAGE', 'Yes', 'If the customer is checking out with Express Checkout, do you want to skip the checkout payment page, making things more *express*? <br /><strong>(NOTE: The Payment Page will auto-display regardless of this setting if you have Coupons or Gift Certificates enabled in your store.)</strong>', '6', '25', 'zen_cfg_select_option(array(\'Yes\', \'No\'), ', now())");
  869. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Express Checkout: Automatic Account Creation', 'MODULE_PAYMENT_PAYPALWPP_NEW_ACCT_NOTIFY', 'Yes', 'If a visitor is not an existing customer, a Zen Cart account is created for them. Would you like make it a permanent account and send them an email containing their login information?<br />NOTE: Permanent accounts are auto-created if the customer purchases downloads or gift certificates, regardless of this setting.', '6', '25', 'zen_cfg_select_option(array(\'Yes\', \'No\'), ', now())");
  870. $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_PAYPALWPP_SORT_ORDER', '0', 'Sort order of display. Lowest is displayed first.', '6', '25', now())");
  871. $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_PAYPALWPP_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())");
  872. $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_PAYPALWPP_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())");
  873. $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_PAYPALWPP_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())");
  874. $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_PAYPALWPP_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())");
  875. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('PayPal Page Style', 'MODULE_PAYMENT_PAYPALWPP_PAGE_STYLE', 'Primary', 'The page-layout style you want customers to see when they visit the PayPal site. You can configure your <strong>Custom Page Styles</strong> in your PayPal Profile settings. This value is case-sensitive.', '6', '25', now())");
  876. $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_PAYPALWPP_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())");
  877. $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_PAYPALWPP_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\', \'Only THB\'), ', now())");
  878. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('API Signature -- Username', 'MODULE_PAYMENT_PAYPALWPP_APIUSERNAME', '', 'The API Username from your PayPal API Signature settings under *API Access*. This value typically looks like an email address and is case-sensitive.', '6', '25', now())");
  879. $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 ('API Signature -- Password', 'MODULE_PAYMENT_PAYPALWPP_APIPASSWORD', '', 'The API Password from your PayPal API Signature settings under *API Access*. This value is a 16-character code and is case-sensitive.', '6', '25', now(), 'zen_cfg_password_input(', 'zen_cfg_password_display')");
  880. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('API Signature -- Signature Code', 'MODULE_PAYMENT_PAYPALWPP_APISIGNATURE', '', 'The API Signature from your PayPal API Signature settings under *API Access*. This value is a 56-character code, and is case-sensitive.', '6', '25', now())");
  881. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('PAYFLOW: User', 'MODULE_PAYMENT_PAYPALWPP_PFUSER', '', 'If you set up one or more additional users on the account, this value is the ID of the user authorized to process transactions. Otherwise it should be the same value as VENDOR. This value is case-sensitive.', '6', '25', now())");
  882. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('PAYFLOW: Partner', 'MODULE_PAYMENT_PAYPALWPP_PFPARTNER', 'ZenCart', 'Your Payflow Partner linked to your Payflow account. This value is case-sensitive.<br />Typical values: <strong>PayPal</strong> or <strong>ZenCart</strong>', '6', '25', now())");
  883. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('PAYFLOW: Vendor', 'MODULE_PAYMENT_PAYPALWPP_PFVENDOR', '', 'Your merchant login ID that you created when you registered for the Payflow Pro account. This value is case-sensitive.', '6', '25', now())");
  884. $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 ('PAYFLOW: Password', 'MODULE_PAYMENT_PAYPALWPP_PFPASSWORD', '', 'The 6- to 32-character password that you defined while registering for the account. This value is case-sensitive.', '6', '25', now(), 'zen_cfg_password_input(', 'zen_cfg_password_display')");
  885. $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('PayPal Mode', 'MODULE_PAYMENT_PAYPALWPP_MODULE_MODE', 'PayPal', 'Which PayPal API system should be used for processing? <br /><u>Choices:</u><br /><font color=green>For choice #1, you need to supply <strong>API Settings</strong> above.</font><br /><strong>1. PayPal</strong> = Express Checkout with a regular PayPal account<br />or<br /><font color=green>for choices 2 &amp; 3 you need to supply <strong>PAYFLOW settings</strong>, below (and have a Payflow account)</font><br /><strong>2. Payflow-UK</strong> = Website Payments Pro UK Payflow Edition<br /><strong>3. Payflow-US</strong> = Payflow Pro Gateway only<!--<br /><strong>4. PayflowUS+EC</strong> = Payflow Pro with Express Checkout-->', '6', '25', 'zen_cfg_select_option(array(\'PayPal\', \'Payflow-UK\', \'Payflow-US\'), ', now())");
  886. $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_PAYPALWPP_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())");
  887. $this->notify('NOTIFY_PAYMENT_PAYPALWPP_INSTALLED');
  888. }
  889. function keys() {
  890. $keys_list = array('MODULE_PAYMENT_PAYPALWPP_STATUS', /*'MODULE_PAYMENT_PAYPALWPP_DIRECT_ENABLED', */'MODULE_PAYMENT_PAYPALWPP_SORT_ORDER', 'MODULE_PAYMENT_PAYPALWPP_ZONE', 'MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID', 'MODULE_PAYMENT_PAYPALWPP_ORDER_PENDING_STATUS_ID', 'MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID', 'MODULE_PAYMENT_PAYPALWPP_CONFIRMED_ADDRESS', 'MODULE_PAYMENT_PAYPALWPP_AUTOSELECT_CHEAPEST_SHIPPING', 'MODULE_PAYMENT_PAYPALWPP_SKIP_PAYMENT_PAGE', 'MODULE_PAYMENT_PAYPALWPP_NEW_ACCT_NOTIFY', 'MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE', 'MODULE_PAYMENT_PAYPALWPP_CURRENCY', 'MODULE_PAYMENT_PAYPALWPP_PAGE_STYLE', 'MODULE_PAYMENT_PAYPALWPP_APIUSERNAME', 'MODULE_PAYMENT_PAYPALWPP_APIPASSWORD', 'MODULE_PAYMENT_PAYPALWPP_APISIGNATURE', 'MODULE_PAYMENT_PAYPALWPP_MODULE_MODE', /*'MODULE_PAYMENT_PAYPALWPP_PFPARTNER', 'MODULE_PAYMENT_PAYPALWPP_PFVENDOR', 'MODULE_PAYMENT_PAYPALWPP_PFUSER', 'MODULE_PAYMENT_PAYPALWPP_PFPASSWORD', */'MODULE_PAYMENT_PAYPALWPP_SERVER', 'MODULE_PAYMENT_PAYPALWPP_DEBUGGING');
  891. if (IS_ADMIN_FLAG === true && (PAYPAL_DEV_MODE == 'true' || strstr(MODULE_PAYMENT_PAYPALWPP_MODULE_MODE, 'Payflow'))) {
  892. $keys_list = array_merge($keys_list, array('MODULE_PAYMENT_PAYPALWPP_PFPARTNER', 'MODULE_PAYMENT_PAYPALWPP_PFVENDOR', 'MODULE_PAYMENT_PAYPALWPP_PFUSER', 'MODULE_PAYMENT_PAYPALWPP_PFPASSWORD'));
  893. }
  894. if (IS_ADMIN_FLAG === true && ((isset($_GET['debug']) && $_GET['debug']=='on') || PAYPAL_DEV_MODE == 'true')) {
  895. $keys_list[]='MODULE_PAYMENT_PAYPALWPP_DIRECT_ENABLED';
  896. }
  897. return $keys_list;
  898. }
  899. /**
  900. * De-install this module
  901. */
  902. function remove() {
  903. global $messageStack;
  904. // cannot remove EC if DP installed:
  905. if (defined('MODULE_PAYMENT_PAYPALDP_STATUS')) {
  906. $messageStack->add_session('<strong>Sorry, you must remove Website Payments Pro (paypaldp) 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=paypaldp', '', 'NONSSL') . '">Click here to edit or remove your Website Payments Pro module.</a>' , 'error');
  907. zen_redirect(zen_href_link(FILENAME_MODULES, 'set=payment&module=paypalwpp', 'NONSSL'));
  908. return 'failed';
  909. }
  910. global $db;
  911. $db->Execute("delete from " . TABLE_CONFIGURATION . " where configuration_key LIKE 'MODULE\_PAYMENT\_PAYPALWPP\_%'");
  912. $this->notify('NOTIFY_PAYMENT_PAYPALWPP_UNINSTALLED');
  913. }
  914. /**
  915. * Check settings and conditions to determine whether we are in an Express Checkout phase or not
  916. */
  917. function in_special_checkout() {
  918. if ((defined('MODULE_PAYMENT_PAYPALWPP_STATUS') && MODULE_PAYMENT_PAYPALWPP_STATUS == 'True') &&
  919. !empty($_SESSION['paypal_ec_token']) &&
  920. !empty($_SESSION['paypal_ec_payer_id']) &&
  921. !empty($_SESSION['paypal_ec_payer_info'])) {
  922. $this->flagDisablePaymentAddressChange = true;
  923. return true;
  924. }
  925. }
  926. /**
  927. * Determine whether the shipping-edit button should be displayed or not
  928. */
  929. function alterShippingEditButton() {
  930. return false;
  931. if ($this->in_special_checkout() && empty($_SESSION['paypal_ec_markflow'])) {
  932. return zen_href_link('ipn_main_handler.php', 'type=ec&clearSess=1', 'SSL', true,true, true);
  933. }
  934. }
  935. /**
  936. * Debug Logging support
  937. */
  938. function zcLog($stage, $message) {
  939. static $tokenHash;
  940. if ($tokenHash == '') $tokenHash = '_' . zen_create_random_value(4);
  941. if (MODULE_PAYMENT_PAYPALWPP_DEBUGGING == 'Log and Email' || MODULE_PAYMENT_PAYPALWPP_DEBUGGING == 'Log File') {
  942. $token = (isset($_SESSION['paypal_ec_token'])) ? $_SESSION['paypal_ec_token'] : preg_replace('/[^0-9.A-Z\-]/', '', $_GET['token']);
  943. $token = ($token == '') ? date('m-d-Y-h-i') : $token; // or time()
  944. $token .= $tokenHash;
  945. $file = $this->_logDir . '/' . 'Paypal_Action_' . $token . '.log';
  946. if (defined('PAYPAL_DEV_MODE') && PAYPAL_DEV_MODE == 'true') $file = $this->_logDir . '/' . $this->code . '_Paypal_Debug_' . $token . '.log';
  947. $fp = @fopen($file, 'a');
  948. @fwrite($fp, date('M-d-Y h:i:s') . "\n" . $stage . "\n" . $message . "\n=================================\n\n");
  949. @fclose($fp);
  950. }
  951. $this->_doDebug($stage, $message, false);
  952. }
  953. /**
  954. * Debug Emailing support
  955. */
  956. function _doDebug($subject = 'PayPal debug data', $data, $useSession = true) {
  957. if (MODULE_PAYMENT_PAYPALWPP_DEBUGGING == 'Log and Email') {
  958. $data = urldecode($data) . "\n\n";
  959. if ($useSession) $data .= "\nSession data: " . print_r($_SESSION, true);
  960. zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, $subject, $data, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br($this->code . "\n" . $data)), 'debug');
  961. }
  962. }
  963. /**
  964. * Initialize the PayPal/PayflowPro object for communication to the processing gateways
  965. */
  966. function paypal_init() {
  967. $ec_uses_gateway = (defined('MODULE_PAYMENT_PAYPALWPP_PRO20_EC_METHOD') && MODULE_PAYMENT_PAYPALWPP_PRO20_EC_METHOD == 'Payflow') ? true : false;
  968. $nvp = (!($ec_uses_gateway) && MODULE_PAYMENT_PAYPALWPP_APIPASSWORD != '' && MODULE_PAYMENT_PAYPALWPP_APISIGNATURE != '') ? true : false;
  969. $ec = ($nvp && ($this->in_special_checkout() || $_GET['type'] == 'ec')) ? true : false;
  970. if (substr(MODULE_PAYMENT_PAYPALWPP_MODULE_MODE,0,7) == 'Payflow'/* && !$ec*/) {
  971. $doPayPal = new paypal_curl(array('mode' => 'payflow',
  972. 'user' => trim(MODULE_PAYMENT_PAYPALWPP_PFUSER),
  973. 'vendor' => trim(MODULE_PAYMENT_PAYPALWPP_PFVENDOR),
  974. 'partner'=> trim(MODULE_PAYMENT_PAYPALWPP_PFPARTNER),
  975. 'pwd' => trim(MODULE_PAYMENT_PAYPALWPP_PFPASSWORD),
  976. 'server' => MODULE_PAYMENT_PAYPALWPP_SERVER));
  977. $doPayPal->_endpoints = array('live' => 'https://payflowpro.verisign.com/transaction',
  978. 'sandbox' => 'https://pilot-payflowpro.verisign.com/transaction');
  979. } else {
  980. $doPayPal = new paypal_curl(array('mode' => 'nvp',
  981. 'user' => trim(MODULE_PAYMENT_PAYPALWPP_APIUSERNAME),
  982. 'pwd' => trim(MODULE_PAYMENT_PAYPALWPP_APIPASSWORD),
  983. 'signature' => trim(MODULE_PAYMENT_PAYPALWPP_APISIGNATURE),
  984. 'version' => '3.2',
  985. 'server' => MODULE_PAYMENT_PAYPALWPP_SERVER));
  986. $doPayPal->_endpoints = array('live' => 'https://api-3t.paypal.com/nvp',
  987. 'sandbox' => 'https://api.sandbox.paypal.com/nvp');
  988. }
  989. // set logging options
  990. $doPayPal->_logDir = $this->_logDir;
  991. // $doPayPal->_logLevel = $this->_logLevel;
  992. // set proxy options if configured
  993. if (CURL_PROXY_REQUIRED == 'True' && CURL_PROXY_SERVER_DETAILS != '') {
  994. $proxy_tunnel_flag = (defined('CURL_PROXY_TUNNEL_FLAG') && strtoupper(CURL_PROXY_TUNNEL_FLAG) == 'FALSE') ? false : true;
  995. $doPayPal->setCurlOption(CURLOPT_HTTPPROXYTUNNEL, $proxy_tunnel_flag);
  996. $doPayPal->setCurlOption(CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  997. $doPayPal->setCurlOption(CURLOPT_PROXY, CURL_PROXY_SERVER_DETAILS);
  998. }
  999. // transaction processing mode
  1000. $doPayPal->_trxtype = (in_array(MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE, array('Auth Only', 'Order'))) ? 'A' : 'S';
  1001. // $this->zcLog('comm details', 'Comm Details: ' . "\n" . print_r($doPayPal, true) . "\n\n" . 'MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE = ' . MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE . "\ndoPayPal->_trxtype = " . $doPayPal->_trxtype . "\n");
  1002. return $doPayPal;
  1003. }
  1004. /**
  1005. * Determine which PayPal URL to direct the customer's browser to when needed
  1006. */
  1007. function getPayPalLoginServer() {
  1008. if (MODULE_PAYMENT_PAYPALWPP_SERVER == 'live') {
  1009. // live url
  1010. $paypal_url = 'https://www.paypal.com/cgi-bin/webscr';
  1011. } else {
  1012. // sandbox url
  1013. $paypal_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
  1014. // for UK sandbox -- NOTE: this system is intermittently flakey ... and if it's down, odd redirects occur.
  1015. if (substr(MODULE_PAYMENT_PAYPALWPP_MODULE_MODE,0,7) == 'Payflow') {
  1016. // $paypal_url = 'https://test-expresscheckout.paypal.com/cgi-bin/webscr';
  1017. }
  1018. }
  1019. return $paypal_url;
  1020. }
  1021. /**
  1022. * Used to submit a refund for a given transaction. FOR FUTURE USE.
  1023. */
  1024. function _doRefund($oID, $amount = 'Full', $note = '') {
  1025. global $db, $doPayPal, $messageStack;
  1026. $new_order_status = MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID;
  1027. $orig_order_amount = 0;
  1028. $doPayPal = $this->paypal_init();
  1029. $proceedToRefund = false;
  1030. $refundNote = strip_tags(zen_db_input($_POST['refnote']));
  1031. if (isset($_POST['fullrefund']) && $_POST['fullrefund'] == MODULE_PAYMENT_PAYPAL_ENTRY_REFUND_BUTTON_TEXT_FULL) {
  1032. $refundAmt = 'Full';
  1033. if (isset($_POST['reffullconfirm']) && $_POST['reffullconfirm'] == 'on') {
  1034. $proceedToRefund = true;
  1035. } else {
  1036. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_REFUND_FULL_CONFIRM_ERROR, 'error');
  1037. }
  1038. }
  1039. if (isset($_POST['partialrefund']) && $_POST['partialrefund'] == MODULE_PAYMENT_PAYPAL_ENTRY_REFUND_BUTTON_TEXT_PARTIAL) {
  1040. $refundAmt = (float)$_POST['refamt'];
  1041. $new_order_status = MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID;
  1042. $proceedToRefund = true;
  1043. if ($refundAmt == 0) {
  1044. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_INVALID_REFUND_AMOUNT, 'error');
  1045. $proceedToRefund = false;
  1046. }
  1047. }
  1048. // look up history on this order from PayPal table
  1049. $sql = "select * from " . TABLE_PAYPAL . " where order_id = :orderID AND parent_txn_id = '' ";
  1050. $sql = $db->bindVars($sql, ':orderID', $oID, 'integer');
  1051. $zc_ppHist = $db->Execute($sql);
  1052. if ($zc_ppHist->RecordCount() == 0) return false;
  1053. $txnID = $zc_ppHist->fields['txn_id'];
  1054. $PFamt = $zc_ppHist->fields['mc_gross'];
  1055. if ($doPayPal->_mode == 'payflow' && $refundAmt == 'Full') $refundAmt = $PFamt;
  1056. /**
  1057. * Submit refund request to PayPal
  1058. */
  1059. if ($proceedToRefund) {
  1060. $response = $doPayPal->RefundTransaction($oID, $txnID, $refundAmt, $refundNote);
  1061. $error = $this->_errorHandler($response, 'DoRefund');
  1062. if (!$error) {
  1063. if (!isset($response['GROSSREFUNDAMT'])) $response['GROSSREFUNDAMT'] = $refundAmt;
  1064. // Success, so save the results
  1065. $sql_data_array = array('orders_id' => $oID,
  1066. 'orders_status_id' => (int)$new_order_status,
  1067. 'date_added' => 'now()',
  1068. '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,
  1069. 'customer_notified' => 0
  1070. );
  1071. zen_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
  1072. $db->Execute("update " . TABLE_ORDERS . "
  1073. set orders_status = '" . (int)$new_order_status . "'
  1074. where orders_id = '" . (int)$oID . "'");
  1075. $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALWPP_TEXT_REFUND_INITIATED, urldecode($response['GROSSREFUNDAMT']), urldecode($response['REFUNDTRANSACTIONID']). $response['PNREF']), 'success');
  1076. return true;
  1077. }
  1078. }
  1079. }
  1080. /**
  1081. * Used to authorize part of a given previously-initiated transaction. FOR FUTURE USE.
  1082. */
  1083. function _doAuth($oID, $amt, $currency = 'USD') {
  1084. global $db, $doPayPal, $messageStack;
  1085. $doPayPal = $this->paypal_init();
  1086. $authAmt = $amt;
  1087. $new_order_status = MODULE_PAYMENT_PAYPALWPP_ORDER_PENDING_STATUS_ID;
  1088. if (isset($_POST['orderauth']) && $_POST['orderauth'] == MODULE_PAYMENT_PAYPAL_ENTRY_AUTH_BUTTON_TEXT_PARTIAL) {
  1089. $authAmt = (float)$_POST['authamt'];
  1090. $new_order_status = MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID;
  1091. if (isset($_POST['authconfirm']) && $_POST['authconfirm'] == 'on') {
  1092. $proceedToAuth = true;
  1093. } else {
  1094. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_AUTH_CONFIRM_ERROR, 'error');
  1095. $proceedToAuth = false;
  1096. }
  1097. if ($authAmt == 0) {
  1098. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_INVALID_AUTH_AMOUNT, 'error');
  1099. $proceedToAuth = false;
  1100. }
  1101. }
  1102. // look up history on this order from PayPal table
  1103. $sql = "select * from " . TABLE_PAYPAL . " where order_id = :orderID AND parent_txn_id = '' ";
  1104. $sql = $db->bindVars($sql, ':orderID', $oID, 'integer');
  1105. $zc_ppHist = $db->Execute($sql);
  1106. if ($zc_ppHist->RecordCount() == 0) return false;
  1107. $txnID = $zc_ppHist->fields['txn_id'];
  1108. /**
  1109. * Submit auth request to PayPal
  1110. */
  1111. if ($proceedToAuth) {
  1112. $response = $doPayPal->DoAuthorization($txnID, $authAmt, $currency);
  1113. $error = $this->_errorHandler($response, 'DoAuthorization');
  1114. if (!$error) {
  1115. // Success, so save the results
  1116. $sql_data_array = array('orders_id' => (int)$oID,
  1117. 'orders_status_id' => (int)$new_order_status,
  1118. 'date_added' => 'now()',
  1119. 'comments' => 'AUTHORIZATION ADDED. Trans ID: ' . urldecode($response['TRANSACTIONID']) . "\n" . ' Amount:' . urldecode($response['AMT']) . ' ' . $currency,
  1120. 'customer_notified' => 0
  1121. );
  1122. zen_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
  1123. $db->Execute("update " . TABLE_ORDERS . "
  1124. set orders_status = '" . (int)$new_order_status . "'
  1125. where orders_id = '" . (int)$oID . "'");
  1126. $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALWPP_TEXT_AUTH_INITIATED, urldecode($response['AMT'])), 'success');
  1127. return true;
  1128. }
  1129. }
  1130. }
  1131. /**
  1132. * Used to capture part or all of a given previously-authorized transaction. FOR FUTURE USE.
  1133. */
  1134. function _doCapt($oID, $captureType = 'Complete', $amt = 0, $currency = 'USD', $note = '') {
  1135. global $db, $doPayPal, $messageStack;
  1136. $doPayPal = $this->paypal_init();
  1137. // alt value for $captureType = 'NotComplete';
  1138. //@TODO: Read current order status and determine best status to set this to
  1139. $new_order_status = MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID;
  1140. $orig_order_amount = 0;
  1141. $doPayPal = $this->paypal_init();
  1142. $proceedToCapture = false;
  1143. $captureNote = strip_tags(zen_db_input($_POST['captnote']));
  1144. if (isset($_POST['captfullconfirm']) && $_POST['captfullconfirm'] == 'on') {
  1145. $proceedToCapture = true;
  1146. } else {
  1147. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_CAPTURE_FULL_CONFIRM_ERROR, 'error');
  1148. }
  1149. if (isset($_POST['captfinal']) && $_POST['captfinal'] == 'on') {
  1150. $captureType = 'Complete';
  1151. } else {
  1152. $captureType = 'NotComplete';
  1153. }
  1154. if (isset($_POST['btndocapture']) && $_POST['btndocapture'] == MODULE_PAYMENT_PAYPAL_ENTRY_CAPTURE_BUTTON_TEXT_FULL) {
  1155. $captureAmt = (float)$_POST['captamt'];
  1156. if ($captureAmt == 0) {
  1157. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_INVALID_CAPTURE_AMOUNT, 'error');
  1158. $proceedToCapture = false;
  1159. }
  1160. }
  1161. // look up history on this order from PayPal table
  1162. $sql = "select * from " . TABLE_PAYPAL . " where order_id = :orderID AND parent_txn_id = '' ";
  1163. $sql = $db->bindVars($sql, ':orderID', $oID, 'integer');
  1164. $zc_ppHist = $db->Execute($sql);
  1165. if ($zc_ppHist->RecordCount() == 0) return false;
  1166. $txnID = $zc_ppHist->fields['txn_id'];
  1167. /**
  1168. * Submit capture request to PayPal
  1169. */
  1170. if ($proceedToCapture) {
  1171. $response = $doPayPal->DoCapture($txnID, $captureAmt, $currency, $captureType, '', $captureNote);
  1172. $error = $this->_errorHandler($response, 'DoCapture');
  1173. if (!$error) {
  1174. if (isset($response['PNREF'])) {
  1175. if (!isset($response['AMT'])) $response['AMT'] = $captureAmt;
  1176. if (!isset($response['ORDERTIME'])) $response['ORDERTIME'] = date("M-d-Y h:i:s");
  1177. }
  1178. // Success, so save the results
  1179. $sql_data_array = array('orders_id' => (int)$oID,
  1180. 'orders_status_id' => (int)$new_order_status,
  1181. 'date_added' => 'now()',
  1182. '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,
  1183. 'customer_notified' => 0
  1184. );
  1185. zen_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
  1186. $db->Execute("update " . TABLE_ORDERS . "
  1187. set orders_status = '" . (int)$new_order_status . "'
  1188. where orders_id = '" . (int)$oID . "'");
  1189. $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALWPP_TEXT_CAPT_INITIATED, urldecode($response['AMT']), urldecode($response['RECEIPTID'] . $response['AUTHCODE']). $response['PNREF']), 'success');
  1190. return true;
  1191. }
  1192. }
  1193. }
  1194. /**
  1195. * Used to void a given previously-authorized transaction. FOR FUTURE USE.
  1196. */
  1197. function _doVoid($oID, $note = '') {
  1198. global $db, $doPayPal, $messageStack;
  1199. $new_order_status = MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID;
  1200. $doPayPal = $this->paypal_init();
  1201. $voidNote = strip_tags(zen_db_input($_POST['voidnote']));
  1202. $voidAuthID = trim(strip_tags(zen_db_input($_POST['voidauthid'])));
  1203. if (isset($_POST['ordervoid']) && $_POST['ordervoid'] == MODULE_PAYMENT_PAYPAL_ENTRY_VOID_BUTTON_TEXT_FULL) {
  1204. if (isset($_POST['voidconfirm']) && $_POST['voidconfirm'] == 'on') {
  1205. $proceedToVoid = true;
  1206. } else {
  1207. $messageStack->add_session(MODULE_PAYMENT_PAYPALWPP_TEXT_VOID_CONFIRM_ERROR, 'error');
  1208. }
  1209. }
  1210. // look up history on this order from PayPal table
  1211. $sql = "select * from " . TABLE_PAYPAL . " where order_id = :orderID AND parent_txn_id = '' ";
  1212. $sql = $db->bindVars($sql, ':orderID', $oID, 'integer');
  1213. $sql = $db->bindVars($sql, ':transID', $voidAuthID, 'string');
  1214. $zc_ppHist = $db->Execute($sql);
  1215. if ($zc_ppHist->RecordCount() == 0) return false;
  1216. $txnID = $zc_ppHist->fields['txn_id'];
  1217. /**
  1218. * Submit void request to PayPal
  1219. */
  1220. if ($proceedToVoid) {
  1221. $response = $doPayPal->DoVoid($voidAuthID, $voidNote);
  1222. $error = $this->_errorHandler($response, 'DoVoid');
  1223. if (!$error) {
  1224. // Success, so save the results
  1225. $sql_data_array = array('orders_id' => (int)$oID,
  1226. 'orders_status_id' => (int)$new_order_status,
  1227. 'date_added' => 'now()',
  1228. 'comments' => 'VOIDED. Trans ID: ' . urldecode($response['AUTHORIZATIONID']). $response['PNREF'] . (isset($response['PPREF']) ? "\nPPRef: " . $response['PPREF'] : '') . "\n" . $voidNote,
  1229. 'customer_notified' => 0
  1230. );
  1231. zen_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
  1232. $db->Execute("update " . TABLE_ORDERS . "
  1233. set orders_status = '" . (int)$new_order_status . "'
  1234. where orders_id = '" . (int)$oID . "'");
  1235. $messageStack->add_session(sprintf(MODULE_PAYMENT_PAYPALWPP_TEXT_VOID_INITIATED, urldecode($response['AUTHORIZATIONID']) . $response['PNREF']), 'success');
  1236. return true;
  1237. }
  1238. }
  1239. }
  1240. /**
  1241. * Determine the language to use when visiting the PayPal site
  1242. */
  1243. function getLanguageCode() {
  1244. $lang_code = '';
  1245. $storeISO = zen_get_countries(STORE_COUNTRY, true);
  1246. if (in_array(strtoupper($storeISO['countries_iso_code_2']), array('US', 'AU', 'DE', 'FR', 'IT', 'GB', 'ES'))) {
  1247. $lang_code = strtoupper($storeISO['countries_iso_code_2']);
  1248. } elseif (in_array(strtoupper($_SESSION['languages_code']), array('EN', 'US', 'AU', 'DE', 'FR', 'IT', 'GB', 'ES'))) {
  1249. $lang_code = $_SESSION['languages_code'];
  1250. if (strtoupper($lang_code) == 'EN') $lang_code = 'US';
  1251. }
  1252. return strtoupper($lang_code);
  1253. }
  1254. /**
  1255. * Set the currency code -- use defaults if active currency is not a currency accepted by PayPal
  1256. */
  1257. function selectCurrency($val = '', $subset = 'EC') {
  1258. $ec_currencies = array('CAD', 'EUR', 'GBP', 'JPY', 'USD', 'AUD', 'CHF', 'CZK', 'DKK', 'HKD', 'HUF', 'NOK', 'NZD', 'PLN', 'SEK', 'SGD', 'THB');
  1259. $dp_currencies = array('CAD', 'EUR', 'GBP', 'JPY', 'USD', 'AUD');
  1260. $paypalSupportedCurrencies = ($subset == 'EC') ? $ec_currencies : $dp_currencies;
  1261. // if using Pro 2.0 (UK), only the 6 currencies are supported.
  1262. $paypalSupportedCurrencies = (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-UK') ? $dp_currencies : $paypalSupportedCurrencies;
  1263. $my_currency = substr(MODULE_PAYMENT_PAYPALWPP_CURRENCY, 5);
  1264. if (MODULE_PAYMENT_PAYPALWPP_CURRENCY == 'Selected Currency') {
  1265. $my_currency = ($val == '') ? $_SESSION['currency'] : $val;
  1266. }
  1267. if (!in_array($my_currency, $paypalSupportedCurrencies)) {
  1268. $my_currency = (MODULE_PAYMENT_PAYPALWPP_MODULE_MODE == 'Payflow-UK') ? 'GBP' : 'USD';
  1269. }
  1270. return $my_currency;
  1271. }
  1272. /**
  1273. * Calculate the amount based on acceptable currencies
  1274. */
  1275. function calc_order_amount($amount, $paypalCurrency, $applyFormatting = false) {
  1276. global $currencies;
  1277. $amount = ($amount) * $currencies->get_value($paypalCurrency);
  1278. return ($applyFormatting ? number_format($amount, $currencies->get_decimal_places($paypalCurrency)) : $amount);
  1279. }
  1280. /**
  1281. * Set the state field depending on what PayPal requires for that country.
  1282. */
  1283. function setStateAndCountry(&$info) {
  1284. global $db;
  1285. switch ($info['country']['iso_code_2']) {
  1286. case 'AU':
  1287. case 'US':
  1288. case 'CA':
  1289. // Paypal only accepts two character state/province codes for some countries.
  1290. if (strlen($info['state']) > 2) {
  1291. $sql = "SELECT zone_code FROM " . TABLE_ZONES . " WHERE zone_name = :zoneName";
  1292. $sql = $db->bindVars($sql, ':zoneName', $info['state'], 'string');
  1293. $state = $db->Execute($sql);
  1294. if (!$state->EOF) {
  1295. $info['state'] = $state->fields['zone_code'];
  1296. } else {
  1297. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_STATE_ERROR);
  1298. }
  1299. }
  1300. break;
  1301. case 'AT':
  1302. case 'BE':
  1303. case 'FR':
  1304. case 'DE':
  1305. case 'CH':
  1306. $info['state'] = '';
  1307. break;
  1308. default:
  1309. $info['state'] = '';
  1310. }
  1311. }
  1312. /**
  1313. * Prepare subtotal and line-item detail content to send to PayPal
  1314. */
  1315. function getLineItemDetails() {
  1316. global $order, $currencies, $order_totals, $order_total_modules;
  1317. $optionsST = array();
  1318. $optionsLI = array();
  1319. $onetimeSum = 0;
  1320. $onetimeTax = 0;
  1321. $creditsApplied = 0;
  1322. $creditsTax_applied = 0;
  1323. $sumOfLineItems = 0;
  1324. $sumOfLineTax = 0;
  1325. // prepare subtotals
  1326. for ($i=0, $n=sizeof($order_totals); $i<$n; $i++) {
  1327. if ($order_totals[$i]['code'] == 'ot_subtotal') $optionsST['ITEMAMT'] = round($order_totals[$i]['value'],2);
  1328. if ($order_totals[$i]['code'] == 'ot_tax') $optionsST['TAXAMT'] = round($order_totals[$i]['value'],2);
  1329. if ($order_totals[$i]['code'] == 'ot_shipping') $optionsST['SHIPPINGAMT'] = round($order_totals[$i]['value'],2);
  1330. if ($order_totals[$i]['code'] == 'ot_total') $optionsST['AMT'] = round($order_totals[$i]['value'],2);
  1331. $optionsST['HANDLINGAMT'] = 0;
  1332. global $$order_totals[$i]['code'];
  1333. if (isset($$order_totals[$i]['code']->credit_class) && $$order_totals[$i]['code']->credit_class == true) $creditsApplied += round($order_totals[$i]['value'],2);
  1334. // treat all other OT's as if they're related to handling fees
  1335. if (!in_array($order_totals[$i]['code'], array('ot_total','ot_subtotal','ot_tax','ot_shipping'))
  1336. && !(isset($$order_totals[$i]['code']->credit_class) && $$order_totals[$i]['code']->credit_class == true)) {
  1337. $optionsST['HANDLINGAMT'] += $order_totals[$i]['value'];
  1338. }
  1339. }
  1340. // Move shipping tax amount from Tax subtotal into Shipping subtotal for submission to PayPal
  1341. $module = substr($_SESSION['shipping']['id'], 0, strpos($_SESSION['shipping']['id'], '_'));
  1342. if (zen_not_null($order->info['shipping_method'])) {
  1343. if ($GLOBALS[$module]->tax_class > 0) {
  1344. $shipping_tax_basis = (!isset($GLOBALS[$module]->tax_basis)) ? STORE_SHIPPING_TAX_BASIS : $GLOBALS[$module]->tax_basis;
  1345. $shippingOnBilling = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->billing['country']['id'], $order->billing['zone_id']);
  1346. $shippingOnDelivery = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']);
  1347. if ($shipping_tax_basis == 'Billing') {
  1348. $shipping_tax = $shippingOnBilling;
  1349. } elseif ($shipping_tax_basis == 'Shipping') {
  1350. $shipping_tax = $shippingOnDelivery;
  1351. } else {
  1352. if (STORE_ZONE == $order->billing['zone_id']) {
  1353. $shipping_tax = $shippingOnBilling;
  1354. } elseif (STORE_ZONE == $order->delivery['zone_id']) {
  1355. $shipping_tax = $shippingOnDelivery;
  1356. } else {
  1357. $shipping_tax = 0;
  1358. }
  1359. }
  1360. $taxAdjustmentForShipping = zen_calculate_tax($order->info['shipping_cost'], $shipping_tax);
  1361. $optionsST['SHIPPINGAMT'] += $taxAdjustmentForShipping;
  1362. $optionsST['TAXAMT'] -= $taxAdjustmentForShipping;
  1363. }
  1364. }
  1365. // loop thru all products to display quantity and price. Appends *** if out-of-stock.
  1366. for ($i=0, $n=sizeof($order->products), $k=0; $i<$n; $i++, $k++) {
  1367. $optionsLI["L_NUMBER$k"] = $order->products[$i]['model'];
  1368. $optionsLI["L_QTY$k"] = (int)$order->products[$i]['qty'];
  1369. $optionsLI["L_NAME$k"] = $order->products[$i]['name'];
  1370. $optionsLI["L_NAME$k"] .= (zen_get_products_stock($order->products[$i]['id']) - $order->products[$i]['qty'] < 0 ? STOCK_MARK_PRODUCT_OUT_OF_STOCK : '');
  1371. // if there are attributes, loop thru them and add to description
  1372. if (isset($order->products[$i]['attributes']) && sizeof($order->products[$i]['attributes']) > 0 ) {
  1373. for ($j=0, $n2=sizeof($order->products[$i]['attributes']); $j<$n2; $j++) {
  1374. $optionsLI["L_NAME$k"] .= "\n " . $order->products[$i]['attributes'][$j]['option'] .
  1375. ': ' . $order->products[$i]['attributes'][$j]['value'];
  1376. } // end loop
  1377. } // endif attribute-info
  1378. $optionsLI["L_AMT$k"] = $order->products[$i]['final_price'];
  1379. $optionsLI["L_TAXAMT$k"] = zen_calculate_tax($order->products[$i]['final_price'], $order->products[$i]['tax']);
  1380. // track one-time charges
  1381. if ($order->products[$i]['onetime_charges'] != 0 ) {
  1382. $onetimeSum += $order->products[$i]['onetime_charges'];
  1383. $onetimeTax += zen_calculate_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']);
  1384. }
  1385. // Replace & and = with * if found.
  1386. $optionsLI["L_NAME$k"] = str_replace(array('&','='), '*', $optionsLI["L_NAME$k"]);
  1387. $optionsLI["L_NAME$k"] = zen_clean_html($optionsLI["L_NAME$k"], 'strong');
  1388. // reformat properly
  1389. $optionsLI["L_NUMBER$k"] = substr($optionsLI["L_NUMBER$k"], 0, 127);
  1390. $optionsLI["L_NAME$k"] = substr($optionsLI["L_NAME$k"], 0, 127);
  1391. $optionsLI["L_AMT$k"] = $optionsLI["L_AMT$k"];
  1392. $optionsLI["L_TAXAMT$k"] = round($optionsLI["L_TAXAMT$k"],2);
  1393. } // end for loopthru all products
  1394. if ($onetimeSum > 0) {
  1395. $i++; $k++;
  1396. $optionsLI["L_NUMBER$k"] = $k;
  1397. $optionsLI["L_NAME$k"] = 'One-Time Charges';
  1398. $optionsLI["L_AMT$k"] = $onetimeSum;
  1399. $optionsLI["L_TAXAMT$k"] = $onetimeTax;
  1400. $optionsLI["L_QTY$k"] = 1;
  1401. }
  1402. // handle discounts such as gift certificates and coupons
  1403. if ($creditsApplied > 0) {
  1404. $optionsST['HANDLINGAMT'] -= $creditsApplied;
  1405. }
  1406. // add all one-time charges
  1407. $optionsST['ITEMAMT'] += $onetimeSum;
  1408. //ensure things are not negative
  1409. $optionsST['HANDLINGAMT'] = abs(strval($optionsST['HANDLINGAMT']));
  1410. // ensure all numbers are non-negative
  1411. if (is_array($optionsST)) foreach ($optionsST as $key=>$value) {
  1412. $optionsST[$key] = abs(strval($value));
  1413. }
  1414. if (is_array($optionsLI)) foreach ($optionsLI as $key=>$value) {
  1415. if (strstr($key, 'AMT')) $optionsLI[$key] = abs(strval($value));
  1416. }
  1417. // subtotals have to add up to AMT
  1418. // Thus, if there is a discrepancy, make adjustment to HANDLINGAMT:
  1419. $st = $optionsST['ITEMAMT'] + $optionsST['TAXAMT'] + $optionsST['SHIPPINGAMT'] + $optionsST['HANDLINGAMT'];
  1420. if ($st != $optionsST['AMT']) $optionsST['HANDLINGAMT'] += strval($optionsST['AMT'] - $st);
  1421. /* //PayPal API spec contradicts itself ... and apparently neither of these "requirements" are enforced.
  1422. //Thus skipping this section for now:
  1423. // according to API specs, these cannot be set if they contain zero values, so unset if they are zero:
  1424. if ($optionsST['TAXAMT'] == 0) unset($optionsST['TAXAMT']);
  1425. if ($optionsST['SHIPPINGAMT'] == 0) unset($optionsST['SHIPPINGAMT']);
  1426. if ($optionsST['HANDLINGAMT'] == 0) unset($optionsST['HANDLINGAMT']);
  1427. // set missing subtotals if they are zero values, since all must be submitted
  1428. if (!isset($optionsST['TAXAMT'])) $optionsST['TAXAMT'] = 0;
  1429. if (!isset($optionsST['SHIPPINGAMT'])) $optionsST['SHIPPINGAMT'] = 0;
  1430. if (!isset($optionsST['HANDLINGAMT'])) $optionsST['HANDLINGAMT'] = 0;
  1431. */
  1432. // Since the PayPal spec cannot handle mathematically mismatched values caused by one-time charges,
  1433. // must drop line-item details if any one-time charges apply to this order:
  1434. // if there are any discounts in this order, do NOT supply line-item details
  1435. if ($onetimeSum > 0) $optionsLI = array();
  1436. // Do sanity check -- if any of the line-item subtotal math doesn't add up properly, skip line-item details,
  1437. // so that the order can go through even though PayPal isn't being flexible to handle Zen Cart's diversity
  1438. for ($j=0; $j<$k; $j++) {
  1439. $itemAMT = $optionsLI["L_AMT$j"];
  1440. $itemTAX = $optionsLI["L_TAXAMT$j"];
  1441. $itemQTY = $optionsLI["L_QTY$j"];
  1442. $sumOfLineItems += ($itemQTY * $itemAMT);
  1443. $sumOfLineTax += round(($itemQTY * $itemTAX),2);
  1444. }
  1445. if ((float)$optionsST['ITEMAMT'] != (float)strval($sumOfLineItems)) {
  1446. $optionsLI = array();
  1447. $this->zcLog('getLineItemDetails 1', 'Order Subtotal does not match sum of line-item prices. Line-item-details skipped.' . "\n" . (float)$optionsST['ITEMAMT'] . ' ' . (float)$sumOfLineItems);
  1448. //die('ITEMAMT != $sumOfLineItems ' . $optionsST['ITEMAMT'] . ' ' . $sumOfLineItems);
  1449. }
  1450. if ((float)$optionsST['TAXAMT'] != (float)strval($sumOfLineTax)) {
  1451. $optionsLI = array();
  1452. $this->zcLog('getLineItemDetails 2', 'Tax Subtotal does not match sum of taxes for line-items. Line-item-details skipped.' . "\n" . $optionsST['TAXAMT'] . ' ' . $sumOfLineTax);
  1453. //die('TAXAMT != $sumofLineTax ' . $optionsST['TAXAMT'] . ' ' . $sumOfLineTax);
  1454. }
  1455. $this->zcLog('getLineItemDetails 3', 'LineItemDetails: ' . "\n" . ($creditsApplied ? 'Credits apply to this order, so all line-item details are NOT being submitted. Thus, the following data is REDUNDANT' . "\n" : '') . 'Details:' . print_r(array_merge($optionsST, $optionsLI), true) . "\n\n" . 'DEFAULT_CURRENCY = ' . DEFAULT_CURRENCY . "\nSESSION['currency'] = " . $_SESSION['currency'] . "\n" . "order->info['currency'] = " . $order->info['currency'] . "\n\$currencies->currencies[\$_SESSION['currency']]['value'] = " . $currencies->currencies[$_SESSION['currency']]['value'] . "\n" . print_r($currencies, true));
  1456. // if not default currency, do not send subtotals or line-item details
  1457. if (DEFAULT_CURRENCY != $order->info['currency']) {
  1458. $this->zcLog('getLineItemDetails 4', 'Not using default currency. Thus, no line-item details can be submitted.');
  1459. return array();
  1460. }
  1461. if ($currencies->currencies[$_SESSION['currency']]['value'] != 1) {
  1462. $this->zcLog('getLineItemDetails 5', 'currency val not equal to 1.0000 - cannot proceed without coping with currency conversions. Aborting line-item details.');
  1463. return array();
  1464. }
  1465. // if there are any discounts in this order, do not supply subtotals or line-item details
  1466. if (strval($creditsApplied) > 0) return array();
  1467. //$this->zcLog('getLineItemDetails 6', 'no credits - okay');
  1468. // if subtotals are not adding up correctly, then skip sending any line-item or subtotal details to PayPal
  1469. $st = round(strval($optionsST['ITEMAMT'] + $optionsST['TAXAMT'] + $optionsST['SHIPPINGAMT'] + $optionsST['HANDLINGAMT']),2);
  1470. $stDiff = strval($optionsST['AMT'] - $st);
  1471. $stDiffRounded = strval(abs($st) - abs(round($optionsST['AMT'],2)));
  1472. // tidy up all values so that they comply with proper format (number_format(xxxx,2) for PayPal US use )
  1473. if (!defined('PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING') || PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING != 'true') {
  1474. if (is_array($optionsST)) foreach ($optionsST as $key=>$value) {
  1475. $optionsST[$key] = number_format(abs($value), 2);
  1476. }
  1477. if (is_array($optionsLI)) foreach ($optionsLI as $key=>$value) {
  1478. if (strstr($key, 'AMT')) $optionsLI[$key] = number_format(abs($value), 2);
  1479. }
  1480. }
  1481. $this->zcLog('getLineItemDetails 7', 'checking subtotals... '. "\nitemamt: " . $optionsST['ITEMAMT'] . "\ntaxamt: " . $optionsST['TAXAMT'] . "\nshippingamt: " . $optionsST['SHIPPINGAMT'] . "\nhandlingamt: " . $optionsST['HANDLINGAMT'] . "\n-------------------\nsubtotal: " . number_format($st, 2) . "\nAMT: " . $optionsST['AMT'] . "\n-------------------\ndifference: " . $stDiff . ' (abs+rounded: ' . $stDiffRounded . ')');
  1482. if ( $stDiffRounded != 0) return array(); //die('bad subtotals'); //return array();
  1483. $this->zcLog('getLineItemDetails 8', 'subtotals balance - okay');
  1484. if (abs($optionsST['HANDLINGAMT']) == 0) unset($optionsST['HANDLINGAMT']);
  1485. // Send Subtotal and LineItem results back to be submitted to PayPal
  1486. return array_merge($optionsST, $optionsLI);
  1487. }
  1488. /**
  1489. * This method sends the user to PayPal's site
  1490. * There, they will log in to their account, choose a funding source and shipping method
  1491. * and then return to our site with an EC token
  1492. */
  1493. function ec_step1() {
  1494. global $order, $db, $doPayPal;
  1495. // if cart is empty due to timeout on login or shopping cart page, go to timeout screen
  1496. if ($_SESSION['cart']->count_contents() == 0) {
  1497. zen_redirect(zen_href_link(FILENAME_TIME_OUT, '', 'SSL'));
  1498. }
  1499. // init new order object
  1500. require(DIR_WS_CLASSES . 'order.php');
  1501. $order = new order;
  1502. // load the selected shipping module so that shipping taxes can be assessed
  1503. require(DIR_WS_CLASSES . 'shipping.php');
  1504. $shipping_modules = new shipping($_SESSION['shipping']);
  1505. // load OT modules so that discounts and taxes can be assessed
  1506. require(DIR_WS_CLASSES . 'order_total.php');
  1507. $order_total_modules = new order_total;
  1508. $order_totals = $order_total_modules->pre_confirmation_check();
  1509. $order_totals = $order_total_modules->process();
  1510. $doPayPal = $this->paypal_init();
  1511. $options = array();
  1512. // unused at present:
  1513. // $options['CUSTOM'] = '';
  1514. // $options['INVNUM'] = '';
  1515. // Determine the language to use when visiting the PP site
  1516. $lc_code = $this->getLanguageCode();
  1517. if ($lc_code != '') $options['LOCALECODE'] = $lc_code;
  1518. // Set currency and amount
  1519. $options['CURRENCY'] = $this->selectCurrency();
  1520. $order_amount = $this->calc_order_amount($order->info['total'], $options['CURRENCY']);
  1521. // Payment Transaction/Authorization Mode
  1522. $options['PAYMENTACTION'] = (MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE == 'Auth Only') ? 'Authorization' : 'Sale';
  1523. // for future:
  1524. if (MODULE_PAYMENT_PAYPALWPP_TRANSACTION_MODE == 'Order') $options['PAYMENTACTION'] = 'Order';
  1525. // Set the return URL if they click "Submit" on PayPal site
  1526. $return_url = zen_href_link('ipn_main_handler.php', 'type=ec', 'SSL', true, true, true);
  1527. // Select the return URL if they click "cancel" on PayPal site or click to return without making payment or login
  1528. $cancel_url = zen_href_link(($_SESSION['customer_first_name'] != '' && $_SESSION['customer_id'] != '' ? FILENAME_CHECKOUT_SHIPPING :FILENAME_LOGIN), 'ec_cancel=1', 'SSL');
  1529. // debug
  1530. $this->zcLog('ec_step1 - 1', 'Checking to see if we are in markflow' . "\n" . 'cart contents: ' . $_SESSION['cart']->get_content_type() . "\n\nNOTE: " . '$this->showPaymentPage = ' . (int)$this->showPaymentPage . "\nCustomer ID: " . (int)$_SESSION['customer_id'] . "\nSession Data: " . print_r($_SESSION, true));
  1531. /**
  1532. * Check whether shipping is required on this order or not.
  1533. * If not, tell PayPal to skip all shipping options
  1534. * ie: don't ask for any shipping info if cart content is strictly virtual and customer is already logged-in
  1535. * (if not logged in, we need address information only to build the customer record)
  1536. */
  1537. if ($_SESSION['cart']->get_content_type() == 'virtual' && isset($_SESSION['customer_id']) && $_SESSION['customer_id'] > 0) {
  1538. $this->zcLog('ec-step1-addr_check', "cart contents is virtual and customer is logged in ... therefore options['NOSHIPPING']=1");
  1539. $options['NOSHIPPING'] = 1;
  1540. } else {
  1541. $this->zcLog('ec-step1-addr_check', "cart contents is not all virtual or customer is not logged in ... therefore will be submitting address details");
  1542. // If we are in a "mark" flow and the customer has a usable address, set the addressoverride variable to 1. This will
  1543. // override the shipping address in PayPal with the shipping address that is selected in Zen Cart.
  1544. if (($address_arr = $this->getOverrideAddress()) !== false) {
  1545. $address_error = false;
  1546. foreach(array('entry_firstname','entry_lastname','entry_street_address','entry_city','entry_postcode','zone_code','countries_iso_code_2') as $key) {
  1547. if ($address_arr[$key] == '') $address_error = true;
  1548. if ($address_error == true) $this->zcLog('ec-step1-addr_check2', '$address_error = true because ' .$key . ' is blank.');
  1549. }
  1550. if ($address_error == false) {
  1551. // set the override var
  1552. $options['ADDROVERRIDE'] = 1;
  1553. // set the address info
  1554. $options['SHIPTONAME'] = $address_arr['entry_firstname'] . ' ' . $address_arr['entry_lastname'];
  1555. $options['SHIPTOSTREET'] = $address_arr['entry_street_address'];
  1556. if ($address_arr['entry_suburb'] != '') $options['SHIPTOSTREET2'] = $address_arr['entry_suburb'];
  1557. $options['SHIPTOCITY'] = $address_arr['entry_city'];
  1558. $options['SHIPTOZIP'] = $address_arr['entry_postcode'];
  1559. $options['SHIPTOSTATE'] = $address_arr['zone_code'];
  1560. $options['SHIPTOCOUNTRYCODE'] = $address_arr['countries_iso_code_2'];
  1561. }
  1562. }
  1563. $this->zcLog('ec-step1-addr_check3', 'address details from override check:'.print_r($address_arr, true));
  1564. // Do we require a "confirmed" shipping address ?
  1565. if (MODULE_PAYMENT_PAYPALWPP_CONFIRMED_ADDRESS == 'Yes') {
  1566. $options['REQCONFIRMSHIPPING'] = 1;
  1567. }
  1568. }
  1569. // if we know customer's email address, supply it, so as to pre-fill the signup box at PayPal (for new PayPal accounts only)
  1570. if (!empty($_SESSION['customer_first_name']) && !empty($_SESSION['customer_id'])) {
  1571. $sql = "select * from " . TABLE_CUSTOMERS . " where customers_id = :custID ";
  1572. $sql = $db->bindVars($sql, ':custID', $_SESSION['customer_id'], 'integer');
  1573. $zc_getemail = $db->Execute($sql);
  1574. if ($zc_getemail->RecordCount() > 0 && $zc_getemail->fields['customers_email_address'] != '') {
  1575. $options['EMAIL'] = $zc_getemail->fields['customers_email_address'];
  1576. }
  1577. if ($zc_getemail->RecordCount() > 0 && $zc_getemail->fields['customers_telephone'] != '') {
  1578. $options['PHONENUM'] = $zc_getemail->fields['customers_telephone'];
  1579. }
  1580. }
  1581. // alter PayPal login page:
  1582. $options['SOLUTIONTYPE'] = 'SOLE';
  1583. // debug
  1584. $this->zcLog('ec_step1 - 2 -submit', print_r(array_merge(array('AMT' => number_format($order_amount, 2), 'RETURNURL' => $return_url, 'CANCELURL' => $cancel_url), $options), true));
  1585. /**
  1586. * Ask PayPal for the token with which to initiate communications
  1587. */
  1588. $response = $doPayPal->SetExpressCheckout(number_format($order_amount, 2), $return_url, $cancel_url, $options);
  1589. /**
  1590. * Determine result of request for token -- if error occurred, the errorHandler will redirect accordingly
  1591. */
  1592. $error = $this->_errorHandler($response, 'SetExpressCheckout');
  1593. // Success, so read the EC token
  1594. $_SESSION['paypal_ec_token'] = preg_replace('/[^0-9.A-Z\-]/', '', urldecode($response['TOKEN']));
  1595. // prepare to redirect to PayPal so the customer can log in and make their selections
  1596. $paypal_url = $this->getPayPalLoginServer();
  1597. // Set the name of the displayed "continue" button on the PayPal site.
  1598. // 'commit' = "Pay Now" || 'continue' = "Review Payment"
  1599. $orderReview = true;
  1600. if ($_SESSION['paypal_ec_markflow'] == 1) $orderReview = false;
  1601. $userActionKey = "&useraction=" . ((int)$orderReview == false ? 'commit' : 'continue');
  1602. // This is where we actually redirect the customer's browser to PayPal. Upon return, they go to ec_step2
  1603. header("HTTP/1.1 302 Object Moved");
  1604. zen_redirect($paypal_url . "?cmd=_express-checkout&token=" . $_SESSION['paypal_ec_token'] . $userActionKey);
  1605. }
  1606. /**
  1607. * This method is for step 2 of the express checkout option. This
  1608. * retrieves from PayPal the data set by step one and sets the Zen Cart
  1609. * data accordingly depending on admin settings.
  1610. */
  1611. function ec_step2() {
  1612. // Visitor just came back from PayPal and so we collect all
  1613. // the info returned, create an account if necessary, then log
  1614. // them in, and then send them to the appropriate page.
  1615. if (empty($_SESSION['paypal_ec_token'])) {
  1616. // see if the token is set -- if not, we cannot continue -- ideally the token should match the session token
  1617. if (isset($_GET['token'])) {
  1618. // we have a token, so we will proceed
  1619. $_SESSION['paypal_ec_token'] = $_GET['token'];
  1620. // sanitize this
  1621. $_SESSION['paypal_ec_token'] = preg_replace('/[^0-9.A-Z\-]/', '', $_GET['token']);
  1622. } else {
  1623. // no token -- not ready for this step -- send them back to checkout page with error
  1624. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_INVALID_RESPONSE, true);
  1625. }
  1626. }
  1627. // debug
  1628. $this->zcLog('PayPal test Log - ec_step2 $_REQUEST data', "In function: ec_step2()\r\nData in \$_REQUEST = \r\n" . print_r($_REQUEST, true));
  1629. // Initialize the paypal caller object.
  1630. global $doPayPal;
  1631. $doPayPal = $this->paypal_init();
  1632. // with the token we retrieve the data about this user
  1633. $response = $doPayPal->GetExpressCheckoutDetails($_SESSION['paypal_ec_token']);
  1634. /**
  1635. * Determine result of request for data -- if error occurred, the errorHandler will redirect accordingly
  1636. */
  1637. $error = $this->_errorHandler($response, 'GetExpressCheckoutDetails');
  1638. // Alert customer that they've selected an unconfirmed address at PayPal, and must go back and choose a Confirmed one
  1639. if (MODULE_PAYMENT_PAYPALWPP_CONFIRMED_ADDRESS == 'Yes' && $response['ADDRESSSTATUS'] != 'Confirmed') {
  1640. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_CONFIRMEDADDR_ERROR, true, FILENAME_CHECKOUT_SHIPPING);
  1641. }
  1642. // will we be creating an account for this customer? We must if the cart contents are virtual, so can login to download etc.
  1643. if ($_SESSION['cart']->get_content_type('true') > 0 || in_array($_SESSION['cart']->content_type, array('mixed', 'virtual'))) $this->new_acct_notify = 'Yes';
  1644. // get the payer_id from the customer's info as returned from PayPal
  1645. $_SESSION['paypal_ec_payer_id'] = $response['PAYERID'];
  1646. $gender = '';
  1647. if (urldecode($response['SALUTATION'] == 'Mr')) $gender = 'm';
  1648. if (in_array(urldecode($response['SALUTATION']), array('Ms', 'Mrs'))) $gender = 'f';
  1649. // prepare the information to pass to the ec_step2_finish() function, which does the account creation, address build, etc
  1650. $step2_payerinfo = array('payer_id' => $response['PAYERID'],
  1651. 'payer_email' => urldecode($response['EMAIL']),
  1652. 'payer_salutation'=> urldecode($response['SALUTATION']),
  1653. 'payer_gender' => $gender,
  1654. 'payer_firstname' => urldecode($response['FIRSTNAME']),
  1655. 'payer_lastname' => urldecode($response['LASTNAME']),
  1656. 'payer_business' => urldecode($response['BUSINESS']),
  1657. 'payer_status' => $response['PAYERSTATUS'],
  1658. 'ship_country_code' => urldecode($response['COUNTRYCODE']),
  1659. 'ship_address_status' => urldecode($response['ADDRESSSTATUS']),
  1660. 'ship_phone' => urldecode($response['PHONENUM']));
  1661. if ($response['ADDRESSSTATUS'] == 'None') {
  1662. $step2_shipto = array();
  1663. } else {
  1664. // accomodate PayPal bug which repeats 1st line of address for 2nd line if 2nd line is empty.
  1665. if ($response['SHIPTOSTREET2'] == $response['SHIPTOSTREET1']) $response['SHIPTOSTREET2'] = '';
  1666. // accomodate PayPal bug which incorrectly treats 'Yukon Territory' as YK instead of ISO standard of YT.
  1667. if ($response['SHIPTOSTATE'] == 'YK') $response['SHIPTOSTATE'] = 'YT';
  1668. // same with Newfoundland
  1669. if ($response['SHIPTOSTATE'] == 'NF') $response['SHIPTOSTATE'] = 'NL';
  1670. // process address details supplied
  1671. $step2_shipto = array('ship_name' => urldecode($response['SHIPTONAME']),
  1672. 'ship_street_1' => urldecode($response['SHIPTOSTREET']),
  1673. 'ship_street_2' => urldecode($response['SHIPTOSTREET2']),
  1674. 'ship_city' => urldecode($response['SHIPTOCITY']),
  1675. 'ship_state' => (isset($response['SHIPTOSTATE']) && $response['SHIPTOSTATE'] !='' ? urldecode($response['SHIPTOSTATE']) : urldecode($response['SHIPTOCITY'])),
  1676. 'ship_postal_code' => urldecode($response['SHIPTOZIP']),
  1677. 'ship_country_code' => urldecode($response['SHIPTOCOUNTRYCODE']),
  1678. 'ship_country_name' => urldecode($response['SHIPTOCOUNTRYNAME']));
  1679. }
  1680. // reset all previously-selected shipping choices, because cart contents may have been changed
  1681. if (!(isset($_SESSION['paypal_ec_markflow']) && $_SESSION['paypal_ec_markflow'] == 1)) unset($_SESSION['shipping']);
  1682. // send data off to build account, log in, set addresses, place order
  1683. $this->ec_step2_finish(array_merge($step2_payerinfo, $step2_shipto), $this->new_acct_notify);
  1684. }
  1685. /**
  1686. * Complete the step2 phase by creating accounts if needed, linking data, placing order, etc.
  1687. */
  1688. function ec_step2_finish($paypal_ec_payer_info, $new_acct_notify) {
  1689. global $db, $order;
  1690. // register the payer_info in the session
  1691. $_SESSION['paypal_ec_payer_info'] = $paypal_ec_payer_info;
  1692. // debug
  1693. $this->zcLog('ec_step2_finish - 1', 'START: paypal_ec_payer_info= ' . print_r($_SESSION['paypal_ec_payer_info'], true));
  1694. /**
  1695. * Building customer zone/address from returned data
  1696. */
  1697. // set some defaults, which will be updated later:
  1698. $country_id = '223';
  1699. $address_format_id = 2;
  1700. $state_id = 0;
  1701. $acct_exists = false;
  1702. // store default address id for later use/reference
  1703. $original_default_address_id = $_SESSION['customer_default_address_id'];
  1704. // Get the customer's country ID based on name or ISO code
  1705. $sql = "SELECT countries_id, address_format_id, countries_iso_code_2, countries_iso_code_3
  1706. FROM " . TABLE_COUNTRIES . "
  1707. WHERE countries_iso_code_2 = :countryId
  1708. OR countries_name = :countryId
  1709. LIMIT 1";
  1710. $sql1 = $db->bindVars($sql, ':countryId', $paypal_ec_payer_info['ship_country_name'], 'string');
  1711. $country1 = $db->Execute($sql1);
  1712. $sql2 = $db->bindVars($sql, ':countryId', $paypal_ec_payer_info['ship_country_code'], 'string');
  1713. $country2 = $db->Execute($sql2);
  1714. // see if we found a record, if yes, then use it instead of default American format
  1715. if ($country1->RecordCount() > 0) {
  1716. $country_id = $country1->fields['countries_id'];
  1717. if (!isset($paypal_ec_payer_info['ship_country_code']) || $paypal_ec_payer_info['ship_country_code'] == '') $paypal_ec_payer_info['ship_country_code'] = $country1->fields['countries_iso_code_2'];
  1718. $country_code3 = $country1->fields['countries_iso_code_3'];
  1719. $address_format_id = (int)$country1->fields['address_format_id'];
  1720. } elseif ($country2->RecordCount() > 0) {
  1721. // if didn't find it based on name, check using ISO code (ie: in case of no-shipping-address required/supplied)
  1722. $country_id = $country2->fields['countries_id'];
  1723. $country_code3 = $country2->fields['countries_iso_code_3'];
  1724. $address_format_id = (int)$country2->fields['address_format_id'];
  1725. }
  1726. // Need to determine zone, based on zone name first, and then zone code if name fails check. Otherwise uses 0.
  1727. $sql = "SELECT zone_id
  1728. FROM " . TABLE_ZONES . "
  1729. WHERE zone_country_id = :zCountry
  1730. AND zone_code = :zoneCode
  1731. OR zone_name = :zoneCode
  1732. LIMIT 1";
  1733. $sql = $db->bindVars($sql, ':zCountry', $country_id, 'integer');
  1734. $sql = $db->bindVars($sql, ':zoneCode', $paypal_ec_payer_info['ship_state'], 'string');
  1735. $states = $db->Execute($sql);
  1736. if ($states->RecordCount() > 0) {
  1737. $state_id = $states->fields['zone_id'];
  1738. }
  1739. /**
  1740. * Using the supplied data from PayPal, set the data into the order record
  1741. */
  1742. // customer
  1743. $order->customer['name'] = $paypal_ec_payer_info['payer_firstname'] . ' ' . $paypal_ec_payer_info['payer_lastname'];
  1744. $order->customer['company'] = $paypal_ec_payer_info['payer_business'];
  1745. $order->customer['street_address'] = $paypal_ec_payer_info['ship_street_1'];
  1746. $order->customer['suburb'] = $paypal_ec_payer_info['ship_street_2'];
  1747. $order->customer['city'] = $paypal_ec_payer_info['ship_city'];
  1748. $order->customer['postcode'] = $paypal_ec_payer_info['ship_postal_code'];
  1749. $order->customer['state'] = $paypal_ec_payer_info['ship_state'];
  1750. $order->customer['country'] = array('id' => $country_id, 'title' => $paypal_ec_payer_info['ship_country_name'], 'iso_code_2' => $paypal_ec_payer_info['ship_country_code'], 'iso_code_3' => $country_code3);
  1751. $order->customer['country']['id'] = $country_id;
  1752. $order->customer['country']['iso_code_2'] = $paypal_ec_payer_info['ship_country_code'];
  1753. $order->customer['format_id'] = $address_format_id;
  1754. $order->customer['email_address'] = $paypal_ec_payer_info['payer_email'];
  1755. $order->customer['telephone'] = $paypal_ec_payer_info['ship_phone'];
  1756. $order->customer['zone_id'] = $state_id;
  1757. // billing
  1758. $order->billing['name'] = $paypal_ec_payer_info['payer_firstname'] . ' ' . $paypal_ec_payer_info['payer_lastname'];
  1759. $order->billing['company'] = $paypal_ec_payer_info['payer_business'];
  1760. $order->billing['street_address'] = $paypal_ec_payer_info['ship_street_1'];
  1761. $order->billing['suburb'] = $paypal_ec_payer_info['ship_street_2'];
  1762. $order->billing['city'] = $paypal_ec_payer_info['ship_city'];
  1763. $order->billing['postcode'] = $paypal_ec_payer_info['ship_postal_code'];
  1764. $order->billing['state'] = $paypal_ec_payer_info['ship_state'];
  1765. $order->billing['country'] = array('id' => $country_id, 'title' => $paypal_ec_payer_info['ship_country_name'], 'iso_code_2' => $paypal_ec_payer_info['ship_country_code'], 'iso_code_3' => $country_code3);
  1766. $order->billing['country']['id'] = $country_id;
  1767. $order->billing['country']['iso_code_2'] = $paypal_ec_payer_info['ship_country_code'];
  1768. $order->billing['format_id'] = $address_format_id;
  1769. $order->billing['zone_id'] = $state_id;
  1770. // delivery
  1771. if ($_SESSION['paypal_ec_payer_info']['ship_address_status'] != 'None') {
  1772. $order->delivery['name'] = $paypal_ec_payer_info['payer_firstname'] . ' ' . $paypal_ec_payer_info['payer_lastname'];
  1773. $order->delivery['company'] = $paypal_ec_payer_info['payer_business'];
  1774. $order->delivery['street_address']= $paypal_ec_payer_info['ship_street_1'];
  1775. $order->delivery['suburb'] = $paypal_ec_payer_info['ship_street_2'];
  1776. $order->delivery['city'] = $paypal_ec_payer_info['ship_city'];
  1777. $order->delivery['postcode'] = $paypal_ec_payer_info['ship_postal_code'];
  1778. $order->delivery['state'] = $paypal_ec_payer_info['ship_state'];
  1779. $order->delivery['country'] = array('id' => $country_id, 'title' => $paypal_ec_payer_info['ship_country_name'], 'iso_code_2' => $paypal_ec_payer_info['ship_country_code'], 'iso_code_3' => $country_code3);
  1780. $order->delivery['country_id'] = $country_id;
  1781. $order->delivery['format_id'] = $address_format_id;
  1782. $order->delivery['zone_id'] = $state_id;
  1783. }
  1784. // debug
  1785. $this->zcLog('ec_step2_finish - 2',
  1786. 'country_id = ' . $country_id . ' ' . $paypal_ec_payer_info['ship_country_name'] . ' ' . $paypal_ec_payer_info['ship_country_code'] ."\naddress_format_id = " . $address_format_id . "\nstate_id = " . $state_id . ' (original state tested: ' . $paypal_ec_payer_info['ship_state'] . ')' . "\ncountry1->fields['countries_id'] = " . $country1->fields['countries_id'] . "\ncountry2->fields['countries_id'] = " . $country2->fields['countries_id'] . "\n" . '$order = ' . print_r($order, true));
  1787. // check to see whether PayPal should still be offered to this customer, based on the zone of their address:
  1788. $this->update_status();
  1789. if (!$this->enabled) {
  1790. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_INVALID_ZONE_ERROR, true, FILENAME_SHOPPING_CART);
  1791. }
  1792. // see if the user is logged in
  1793. if (!empty($_SESSION['customer_first_name']) && !empty($_SESSION['customer_id'])) {
  1794. // They're logged in, so forward them straight to checkout stages, depending on address needs etc
  1795. $order->customer['id'] = $_SESSION['customer_id'];
  1796. // set the session value for express checkout temp
  1797. $_SESSION['paypal_ec_temp'] = false;
  1798. // if no address required for shipping, leave shipping portion alone
  1799. if ($_SESSION['paypal_ec_payer_info']['ship_address_status'] != 'None' && $_SESSION['paypal_ec_payer_info']['ship_street_1'] != '') {
  1800. // set the session info for the sendto
  1801. $_SESSION['sendto'] = $_SESSION['customer_default_address_id'];
  1802. // This is the address matching section
  1803. // try to match it first
  1804. // note: this is by no means 100%
  1805. $address_book_id = $this->findMatchingAddressBookEntry($_SESSION['customer_id'], $order->delivery);
  1806. // no match, so add the record
  1807. if (!$address_book_id) {
  1808. $address_book_id = $this->addAddressBookEntry($_SESSION['customer_id'], $order->delivery, false);
  1809. }
  1810. // set the address for use
  1811. $_SESSION['sendto'] = $address_book_id;
  1812. }
  1813. // set the users billto information (default address)
  1814. if (!isset($_SESSION['billto'])) {
  1815. $_SESSION['billto'] = $_SESSION['customer_default_address_id'];
  1816. }
  1817. // debug
  1818. $this->zcLog('ec_step2_finish - 3', 'Exiting ec_step2_finish logged-in mode.' . "\n" . 'Selected address: ' . $address_book_id . "\nOriginal was: " . $original_default_address_id);
  1819. // select a shipping method, based on cheapest available option
  1820. if (MODULE_PAYMENT_PAYPALWPP_AUTOSELECT_CHEAPEST_SHIPPING == 'Yes') $this->setShippingMethod();
  1821. // send the user on
  1822. if ($_SESSION['paypal_ec_markflow'] == 1) {
  1823. $this->terminateEC('', false, FILENAME_CHECKOUT_PROCESS);
  1824. } else {
  1825. $this->terminateEC('', false, FILENAME_CHECKOUT_CONFIRMATION);
  1826. }
  1827. } else {
  1828. // They're not logged in. Create an account if necessary, and then log them in.
  1829. // First, see if they're an existing customer, and log them in automatically
  1830. // If Paypal didn't supply us an email address, something went wrong
  1831. if (trim($paypal_ec_payer_info['payer_email']) == '') $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_INVALID_RESPONSE, true);
  1832. // attempt to obtain the user information using the payer_email from the info returned from PayPal, via email address
  1833. $sql = "SELECT customers_id, customers_firstname, customers_lastname, customers_paypal_payerid, customers_paypal_ec
  1834. FROM " . TABLE_CUSTOMERS . "
  1835. WHERE customers_email_address = :emailAddress ";
  1836. $sql = $db->bindVars($sql, ':emailAddress', $paypal_ec_payer_info['payer_email'], 'string');
  1837. $check_customer = $db->Execute($sql);
  1838. // debug
  1839. $this->zcLog('ec_step2_finish - 4', 'Not logged in. Looking for account.' . "\n" . $sql . "\n" . print_r($check_customer, true));
  1840. if (!$check_customer->EOF) {
  1841. $acct_exists = true;
  1842. // see if this was only a temp account -- if so, remove it
  1843. if ($check_customer->fields['customers_paypal_ec'] == '1') {
  1844. // Delete the existing temporary account
  1845. $this->ec_delete_user($check_customer->fields['customers_id']);
  1846. $acct_exists = false;
  1847. // debug
  1848. $this->zcLog('ec_step2_finish - 5', 'Found temporary account - deleting it.');
  1849. }
  1850. }
  1851. // Create an account, if the account does not exist
  1852. if (!$acct_exists) {
  1853. // debug
  1854. $this->zcLog('ec_step2_finish - 6', 'No ZC account found for this customer. Creating new account.' . "\n" . '$this->new_acct_notify =' . $this->new_acct_notify);
  1855. // Generate a random 8-char password
  1856. $password = zen_create_random_value(8);
  1857. $sql_data_array = array();
  1858. // set the customer information in the array for the table insertion
  1859. $sql_data_array = array(
  1860. 'customers_firstname' => $paypal_ec_payer_info['payer_firstname'],
  1861. 'customers_lastname' => $paypal_ec_payer_info['payer_lastname'],
  1862. 'customers_email_address' => $paypal_ec_payer_info['payer_email'],
  1863. 'customers_telephone' => $paypal_ec_payer_info['ship_phone'],
  1864. 'customers_fax' => '',
  1865. 'customers_gender' => $paypal_ec_payer_info['payer_gender'],
  1866. 'customers_newsletter' => '0',
  1867. 'customers_password' => zen_encrypt_password($password),
  1868. 'customers_paypal_payerid' => $_SESSION['paypal_ec_payer_id']);
  1869. // insert the data
  1870. $result = zen_db_perform(TABLE_CUSTOMERS, $sql_data_array);
  1871. // grab the customer_id (last insert id)
  1872. $customer_id = $db->Insert_ID();
  1873. // set the Guest customer ID -- for PWA purposes
  1874. $_SESSION['customer_guest_id'] = $customer_id;
  1875. // set the customer address information in the array for the table insertion
  1876. $sql_data_array = array(
  1877. 'customers_id' => $customer_id,
  1878. 'entry_gender' => $paypal_ec_payer_info['payer_gender'],
  1879. 'entry_firstname' => $paypal_ec_payer_info['payer_firstname'],
  1880. 'entry_lastname' => $paypal_ec_payer_info['payer_lastname'],
  1881. 'entry_street_address' => $paypal_ec_payer_info['ship_street_1'],
  1882. 'entry_suburb' => $paypal_ec_payer_info['ship_street_2'],
  1883. 'entry_city' => $paypal_ec_payer_info['ship_city'],
  1884. 'entry_zone_id' => $state_id,
  1885. 'entry_postcode' => $paypal_ec_payer_info['ship_postal_code'],
  1886. 'entry_country_id' => $country_id);
  1887. if ($state_id > 0) {
  1888. $sql_data_array['entry_zone_id'] = $state_id;
  1889. $sql_data_array['entry_state'] = '';
  1890. } else {
  1891. $sql_data_array['entry_zone_id'] = 0;
  1892. $sql_data_array['entry_state'] = $paypal_ec_payer_info['ship_state'];
  1893. }
  1894. // insert the data
  1895. zen_db_perform(TABLE_ADDRESS_BOOK, $sql_data_array);
  1896. // grab the address_id (last insert id)
  1897. $address_id = $db->Insert_ID();
  1898. // set the address id lookup for the customer
  1899. $sql = "UPDATE " . TABLE_CUSTOMERS . "
  1900. SET customers_default_address_id = :addrID
  1901. WHERE customers_id = :custID";
  1902. $sql = $db->bindVars($sql, ':addrID', $address_id, 'integer');
  1903. $sql = $db->bindVars($sql, ':custID', $customer_id, 'integer');
  1904. $db->Execute($sql);
  1905. // insert the new customer_id into the customers info table for consistency
  1906. $sql = "INSERT INTO " . TABLE_CUSTOMERS_INFO . "
  1907. (customers_info_id, customers_info_number_of_logons, customers_info_date_account_created)
  1908. VALUES (:custID, 0, now())";
  1909. $sql = $db->bindVars($sql, ':custID', $customer_id, 'integer');
  1910. $db->Execute($sql);
  1911. // send Welcome Email if appropriate
  1912. if ($this->new_acct_notify == 'Yes') {
  1913. // require the language file
  1914. global $language_page_directory, $template_dir;
  1915. if (!isset($language_page_directory)) $language_page_directory = DIR_WS_LANGUAGES . $_SESSION['language'] . '/';
  1916. if (file_exists($language_page_directory . $template_dir . '/create_account.php')) {
  1917. $template_dir_select = $template_dir . '/';
  1918. } else {
  1919. $template_dir_select = '';
  1920. }
  1921. require($language_page_directory . $template_dir_select . '/create_account.php');
  1922. // set the mail text
  1923. $email_text = sprintf(EMAIL_GREET_NONE, $paypal_ec_payer_info['payer_firstname']) . EMAIL_WELCOME . EMAIL_TEXT;
  1924. $email_text .= "\n\n" . EMAIL_EC_ACCOUNT_INFORMATION . "\nUsername: " . $paypal_ec_payer_info['payer_email'] . "\nPassword: " . $password . "\n\n";
  1925. $email_text .= EMAIL_CONTACT;
  1926. // send the mail
  1927. zen_mail($paypal_ec_payer_info['payer_firstname'] . " " . $paypal_ec_payer_info['payer_lastname'], $paypal_ec_payer_info['payer_email'], EMAIL_SUBJECT, $email_text, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br($email_text)), 'welcome');
  1928. // set the express checkout temp -- false means the account is no longer "only" for EC ... it'll be permanent
  1929. $_SESSION['paypal_ec_temp'] = false;
  1930. } else {
  1931. // Make it a temporary account that'll be deleted once they've checked out
  1932. $sql = "UPDATE " . TABLE_CUSTOMERS . "
  1933. SET customers_paypal_ec = 1
  1934. WHERE customers_id = :custID ";
  1935. $sql = $db->bindVars($sql, ':custID', $customer_id, 'integer');
  1936. $db->Execute($sql);
  1937. // set the boolean ec temp value since we created account strictly for EC purposes
  1938. $_SESSION['paypal_ec_temp'] = true;
  1939. }
  1940. // hook notifier class vis a vis account-creation
  1941. $this->notify('NOTIFY_LOGIN_SUCCESS_VIA_CREATE_ACCOUNT');
  1942. } else {
  1943. // set the boolean ec temp value for the account to false, since we didn't have to create one
  1944. $_SESSION['paypal_ec_temp'] = false;
  1945. }
  1946. // log the user in with the email sent back from paypal response
  1947. $this->user_login($_SESSION['paypal_ec_payer_info']['payer_email'], false);
  1948. // debug
  1949. $this->zcLog('ec_step2_finish - 7', 'Auto-Logged customer in. (' . $_SESSION['paypal_ec_payer_info']['payer_email'] . ') (' . $_SESSION['customer_id'] . ')' . "\n" . '$_SESSION[paypal_ec_temp]=' . $_SESSION['paypal_ec_temp']);
  1950. // This is the address matching section
  1951. // try to match it first
  1952. // note: this is by no means 100%
  1953. $address_book_id = $this->findMatchingAddressBookEntry($_SESSION['customer_id'], $order->delivery);
  1954. // no match add the record
  1955. if (!$address_book_id) {
  1956. $address_book_id = $this->addAddressBookEntry($_SESSION['customer_id'], $order->delivery, false);
  1957. if (!$address_book_id) {
  1958. $address_book_id = $_SESSION['customer_default_address_id'];
  1959. }
  1960. }
  1961. // set the sendto to the address
  1962. $_SESSION['sendto'] = $address_book_id;
  1963. // set billto in the session
  1964. $_SESSION['billto'] = $_SESSION['customer_default_address_id'];
  1965. // select a shipping method, based on cheapest available option
  1966. if (MODULE_PAYMENT_PAYPALWPP_AUTOSELECT_CHEAPEST_SHIPPING == 'Yes') $this->setShippingMethod();
  1967. // debug
  1968. $this->zcLog('ec_step2_finish - 8', 'Exiting via terminateEC (from originally-not-logged-in mode).' . "\n" . 'Selected address: ' . $address_book_id . "\nOriginal was: " . $original_default_address_id . "\nprepared data: " . print_r($order->delivery, true));
  1969. // send the user on
  1970. if ($_SESSION['paypal_ec_markflow'] == 1) {
  1971. $this->terminateEC('', false, FILENAME_CHECKOUT_PROCESS);
  1972. } else {
  1973. $this->terminateEC('', false, FILENAME_CHECKOUT_CONFIRMATION);
  1974. }
  1975. }
  1976. }
  1977. /**
  1978. * Determine the appropriate shipping method if applicable
  1979. * By default, selects the lowest-cost quote
  1980. */
  1981. function setShippingMethod() {
  1982. global $total_count, $total_weight;
  1983. // ensure that cart contents is calculated properly for weight and value
  1984. if (!isset($total_weight)) $total_weight = $_SESSION['cart']->show_weight();
  1985. if (!isset($total_count)) $total_count = $_SESSION['cart']->count_contents();
  1986. // set the shipping method if one is not already set
  1987. // defaults to the cheapest shipping method
  1988. if ( !$_SESSION['shipping'] || ( $_SESSION['shipping'] && ($_SESSION['shipping'] == false) && (zen_count_shipping_modules() > 1) ) ) {
  1989. require_once(DIR_WS_CLASSES . 'http_client.php');
  1990. require_once(DIR_WS_CLASSES . 'shipping.php');
  1991. $shipping_Obj = new shipping;
  1992. // generate the quotes
  1993. $shipping_Obj->quote();
  1994. // set the cheapest one
  1995. $_SESSION['shipping'] = $shipping_Obj->cheapest();
  1996. }
  1997. }
  1998. /**
  1999. * Get Override Address (uses sendto if set, otherwise uses customer's primary address)
  2000. */
  2001. function getOverrideAddress() {
  2002. global $db;
  2003. // Only proceed IF *in* markflow mode AND logged-in (have to be logged in to get to markflow mode anyway)
  2004. if (!empty($_GET['markflow']) && !empty($_SESSION['customer_id'])) {
  2005. // From now on for this user we will edit addresses in Zen Cart, not by going to PayPal.
  2006. $_SESSION['paypal_ec_markflow'] = 1;
  2007. // debug
  2008. $this->zcLog('getOverrideAddress - 1', 'Now in markflow mode.' . "\n" . 'SESSION[sendto] = ' . $_SESSION['sendto']);
  2009. // find the users default address id
  2010. if (!empty($_SESSION['sendto'])) {
  2011. $address_id = $_SESSION['sendto'];
  2012. } else {
  2013. $sql = "SELECT customers_default_address_id
  2014. FROM " . TABLE_CUSTOMERS . "
  2015. WHERE customers_id = :customerId";
  2016. $sql = $db->bindVars($sql, ':customerId', $_SESSION['customer_id'], 'integer');
  2017. $default_address_id_arr = $db->Execute($sql);
  2018. if (!$default_address_id_arr->EOF) {
  2019. $address_id = $default_address_id_arr->fields['customers_default_address_id'];
  2020. } else {
  2021. // couldn't find an address.
  2022. return false;
  2023. }
  2024. }
  2025. // now grab the address from the database and set it as the overridden address
  2026. $sql = "SELECT entry_firstname, entry_lastname,
  2027. entry_street_address, entry_suburb, entry_city, entry_postcode,
  2028. entry_country_id, entry_zone_id, entry_state
  2029. FROM " . TABLE_ADDRESS_BOOK . "
  2030. WHERE address_book_id = :addressId
  2031. AND customers_id = :customerId
  2032. LIMIT 1";
  2033. $sql = $db->bindVars($sql, ':addressId', $address_id, 'integer');
  2034. $sql = $db->bindVars($sql, ':customerId', $_SESSION['customer_id'], 'integer');
  2035. $address_arr = $db->Execute($sql);
  2036. // see if we found a record, if not then we have nothing to override with
  2037. if (!$address_arr->EOF) {
  2038. // get the state/prov code
  2039. $sql = "SELECT zone_code
  2040. FROM " . TABLE_ZONES . "
  2041. WHERE zone_id = :zoneId";
  2042. $sql = $db->bindVars($sql, ':zoneId', $address_arr->fields['entry_zone_id'], 'integer');
  2043. $state_code_arr = $db->Execute($sql);
  2044. if ($state_code_arr->EOF) {
  2045. $state_code_arr->fields['zone_code'] = '';
  2046. }
  2047. if ($state_code_arr->fields['zone_code'] == '' && $address_arr->fields['entry_state'] != '') {
  2048. $state_code_arr->fields['zone_code'] = $address_arr->fields['entry_state'];
  2049. }
  2050. $address_arr->fields['zone_code'] = $state_code_arr->fields['zone_code'];
  2051. // get the country code
  2052. // ISO 3166 standard country code
  2053. $sql = "SELECT countries_iso_code_2
  2054. FROM " . TABLE_COUNTRIES . "
  2055. WHERE countries_id = :countryId";
  2056. $sql = $db->bindVars($sql, ':countryId', $address_arr->fields['entry_country_id'], 'integer');
  2057. $country_code_arr = $db->Execute($sql);
  2058. if ($country_code_arr->EOF) {
  2059. // default to US if not found
  2060. $country_code_arr->fields['countries_iso_code_2'] = 'US';
  2061. }
  2062. $address_arr->fields['countries_iso_code_2'] = $country_code_arr->fields['countries_iso_code_2'];
  2063. // debug
  2064. $this->zcLog('getOverrideAddress - 2', '$address_arr->fields = ' . print_r($address_arr->fields, true));
  2065. // return address data.
  2066. return $address_arr->fields;
  2067. }
  2068. // debug
  2069. $this->zcLog('getOverrideAddress - 3', 'no override record found');
  2070. }
  2071. // debug
  2072. $this->zcLog('getOverrideAddress - 4', 'not logged in and not in markflow mode - nothing to override');
  2073. return false;
  2074. }
  2075. /**
  2076. * This method attempts to match items in an address book, to avoid
  2077. * duplicate entries to the address book. On a successful match it
  2078. * returns the address_book_id(int) - on failure it returns false.
  2079. *
  2080. * @param int $customer_id
  2081. * @param array $address_question_arr
  2082. * @return int|boolean
  2083. */
  2084. function findMatchingAddressBookEntry($customer_id, $address_question_arr) {
  2085. global $db;
  2086. // if address is blank, don't do any matching
  2087. if ($address_question_arr['street_address'] == '') return false;
  2088. // default
  2089. $country_id = '223';
  2090. $address_format_id = 2; //2 is the American format
  2091. // first get the zone id's from the 2 digit iso codes
  2092. // country first
  2093. $sql = "SELECT countries_id, address_format_id
  2094. FROM " . TABLE_COUNTRIES . "
  2095. WHERE countries_iso_code_2 = :countryId
  2096. OR countries_name = :countryId
  2097. LIMIT 1";
  2098. $sql = $db->bindVars($sql, ':countryId', $address_question_arr['country']['title'], 'string');
  2099. $country = $db->Execute($sql);
  2100. // see if we found a record, if not default to American format
  2101. if (!$country->EOF) {
  2102. $country_id = $country->fields['countries_id'];
  2103. $address_format_id = $country->fields['address_format_id'];
  2104. }
  2105. // see if the country code has a state
  2106. $sql = "SELECT zone_id
  2107. FROM " . TABLE_ZONES . "
  2108. WHERE zone_country_id = :zoneId
  2109. LIMIT 1";
  2110. $sql = $db->bindVars($sql, ':zoneId', $country_id, 'integer');
  2111. $country_zone_check = $db->Execute($sql);
  2112. $check_zone = $country_zone_check->RecordCount();
  2113. // now try and find the zone_id (state/province code)
  2114. // use the country id above
  2115. if ($check_zone) {
  2116. $sql = "SELECT zone_id
  2117. FROM " . TABLE_ZONES . "
  2118. WHERE zone_country_id = :zoneId
  2119. AND zone_code = :zoneCode
  2120. OR zone_name = :zoneCode
  2121. LIMIT 1";
  2122. $sql = $db->bindVars($sql, ':zoneId', $country_id, 'integer');
  2123. $sql = $db->bindVars($sql, ':zoneCode', $address_question_arr['state'], 'string');
  2124. $zone = $db->Execute($sql);
  2125. if (!$zone->EOF) {
  2126. // grab the id
  2127. $zone_id = $zone->fields['zone_id'];
  2128. } else {
  2129. $check_zone = false;
  2130. $zone_id = 0;
  2131. }
  2132. }
  2133. // debug
  2134. $this->zcLog('findMatchingAddressBookEntry - 1-stats', 'country:' . print_r($country, true) . "\n" . 'country_zone_check:' . print_r($country_zone_check, true) . "\n" . 'zone_check:' . print_r($zone, true) . 'check_zone: ' . $check_zone . "\n" . 'zone:' . $zone_id);
  2135. // do a match on address suburb
  2136. $sql = "SELECT address_book_id, entry_street_address, entry_suburb
  2137. FROM " . TABLE_ADDRESS_BOOK . "
  2138. WHERE customers_id = :customerId
  2139. AND entry_country_id = :countryId";
  2140. if ($check_zone) {
  2141. $sql .= " AND entry_zone_id = :zoneId";
  2142. }
  2143. $sql = $db->bindVars($sql, ':zoneId', $zone_id, 'integer');
  2144. $sql = $db->bindVars($sql, ':countryId', $country_id, 'integer');
  2145. $sql = $db->bindVars($sql, ':customerId', $customer_id, 'integer');
  2146. $answers_arr = $db->Execute($sql);
  2147. // debug
  2148. $this->zcLog('findMatchingAddressBookEntry - 2-read for match', "\nSQL was:" . $sql . "\nRecordCount = " . $answers_arr->RecordCount());
  2149. if (!$answers_arr->EOF) {
  2150. // build a base string to compare street+suburb content
  2151. $matchQuestion = str_replace("\n", '', $address_question_arr['street_address']);
  2152. $matchQuestion = trim($matchQuestion);
  2153. $matchQuestion = $matchQuestion . str_replace("\n", '', $address_question_arr['suburb']);
  2154. $matchQuestion = str_replace("\t", '', $matchQuestion);
  2155. $matchQuestion = trim($matchQuestion);
  2156. $matchQuestion = strtolower($matchQuestion);
  2157. $matchQuestion = str_replace(' ', '', $matchQuestion);
  2158. // go through the data
  2159. while (!$answers_arr->EOF) {
  2160. // now the matching logic
  2161. // first from the db
  2162. $fromDb = '';
  2163. $fromDb = str_replace("\n", '', $answers_arr->fields['entry_street_address']);
  2164. $fromDb = trim($fromDb);
  2165. $fromDb = $fromDb . str_replace("\n", '', $answers_arr->fields['entry_suburb']);
  2166. $fromDb = str_replace("\t", '', $fromDb);
  2167. $fromDb = trim($fromDb);
  2168. $fromDb = strtolower($fromDb);
  2169. $fromDb = str_replace(' ', '', $fromDb);
  2170. // debug
  2171. $this->zcLog('findMatchingAddressBookEntry - 3a', "From PayPal:\r\n" . $matchQuestion . "\r\n\r\nFrom DB:\r\n" . $fromDb . "\r\n". print_r($answers_arr, true));
  2172. // check the strings
  2173. if (strlen($fromDb) == strlen($matchQuestion)) {
  2174. if ($fromDb == $matchQuestion) {
  2175. // exact match return the id
  2176. // debug
  2177. $this->zcLog('findMatchingAddressBookEntry - 3b', "Exact match:\n" . print_r($answers_arr->fields, true));
  2178. return $answers_arr->fields['address_book_id'];
  2179. }
  2180. } elseif (strlen($fromDb) > strlen($matchQuestion)) {
  2181. if (substr($fromDb, 0, strlen($matchQuestion)) == $matchQuestion) {
  2182. // we have a match return it (PP)
  2183. // debug
  2184. $this->zcLog('findMatchingAddressBookEntry - 3b', "partial match (PP):\n" . print_r($answers_arr->fields, true));
  2185. return $answers_arr->fields['address_book_id'];
  2186. }
  2187. } else {
  2188. if ($fromDb == substr($matchQuestion, 0, strlen($fromDb))) {
  2189. // we have a match return it (DB)
  2190. // debug
  2191. $this->zcLog('findMatchingAddressBookEntry - 3b', "partial match (DB):\n" . print_r($answers_arr->fields, true));
  2192. return $answers_arr->fields['address_book_id'];
  2193. }
  2194. }
  2195. $answers_arr->MoveNext();
  2196. }
  2197. }
  2198. // debug
  2199. $this->zcLog('findMatchingAddressBookEntry - 4', "no match");
  2200. // no matches found
  2201. return false;
  2202. }
  2203. /**
  2204. * This method adds an address book entry to the database, this allows us to add addresses
  2205. * that we get back from PayPal that are not in Zen Cart
  2206. *
  2207. * @param int $customer_id
  2208. * @param array $address_question_arr
  2209. * @return int
  2210. */
  2211. function addAddressBookEntry($customer_id, $address_question_arr, $make_default = false) {
  2212. global $db;
  2213. // debug
  2214. $this->zcLog('addAddressBookEntry - 1', 'address to add: ' . "\n" . print_r($address_question_arr, true));
  2215. // set some defaults
  2216. $country_id = '223';
  2217. $address_format_id = 2; //2 is the American format
  2218. // first get the zone id's from the 2 digit iso codes
  2219. // country first
  2220. $sql = "SELECT countries_id, address_format_id
  2221. FROM " . TABLE_COUNTRIES . "
  2222. WHERE countries_iso_code_2 = :countryId
  2223. OR countries_name = :countryId
  2224. LIMIT 1";
  2225. $sql = $db->bindVars($sql, ':countryId', $address_question_arr['country']['title'], 'string');
  2226. $country = $db->Execute($sql);
  2227. // see if we found a record, if not default to American format
  2228. if (!$country->EOF) {
  2229. $country_id = $country->fields['countries_id'];
  2230. $address_format_id = (int)$country->fields['address_format_id'];
  2231. }
  2232. // see if the country code has a state
  2233. $sql = "SELECT zone_id
  2234. FROM " . TABLE_ZONES . "
  2235. WHERE zone_country_id = :zoneId
  2236. LIMIT 1";
  2237. $sql = $db->bindVars($sql, ':zoneId', $country_id, 'integer');
  2238. $country_zone_check = $db->Execute($sql);
  2239. $check_zone = $country_zone_check->RecordCount();
  2240. // now try and find the zone_id (state/province code)
  2241. // use the country id above
  2242. if ($check_zone) {
  2243. $sql = "SELECT zone_id
  2244. FROM " . TABLE_ZONES . "
  2245. WHERE zone_country_id = :zoneId
  2246. AND zone_code = :zoneCode
  2247. OR zone_name = :zoneCode
  2248. LIMIT 1";
  2249. $sql = $db->bindVars($sql, ':zoneId', $country_id, 'integer');
  2250. $sql = $db->bindVars($sql, ':zoneCode', $address_question_arr['state'], 'string');
  2251. $zone = $db->Execute($sql);
  2252. if (!$zone->EOF) {
  2253. // grab the id
  2254. $zone_id = $zone->fields['zone_id'];
  2255. } else {
  2256. $zone_id = 0;
  2257. }
  2258. }
  2259. // now run the insert
  2260. // this isn't the best way to get fname/lname but it will get the majority of cases
  2261. list($fname, $lname) = explode(' ', $address_question_arr['name']);
  2262. $sql_data_array= array(array('fieldName'=>'entry_firstname', 'value'=>$fname, 'type'=>'string'),
  2263. array('fieldName'=>'entry_lastname', 'value'=>$lname, 'type'=>'string'),
  2264. array('fieldName'=>'entry_street_address', 'value'=>$address_question_arr['street_address'], 'type'=>'string'),
  2265. array('fieldName'=>'entry_postcode', 'value'=>$address_question_arr['postcode'], 'type'=>'string'),
  2266. array('fieldName'=>'entry_city', 'value'=>$address_question_arr['city'], 'type'=>'string'),
  2267. array('fieldName'=>'entry_country_id', 'value'=>$country_id, 'type'=>'integer'));
  2268. $sql_data_array[] = array('fieldName'=>'entry_gender', 'value'=>$address_question_arr['payer_gender'], 'type'=>'enum:m|f');
  2269. $sql_data_array[] = array('fieldName'=>'entry_suburb', 'value'=>$address_question_arr['suburb'], 'type'=>'string');
  2270. if ($zone_id > 0) {
  2271. $sql_data_array[] = array('fieldName'=>'entry_zone_id', 'value'=>$zone_id, 'type'=>'integer');
  2272. $sql_data_array[] = array('fieldName'=>'entry_state', 'value'=>'', 'type'=>'string');
  2273. } else {
  2274. $sql_data_array[] = array('fieldName'=>'entry_zone_id', 'value'=>'0', 'type'=>'integer');
  2275. $sql_data_array[] = array('fieldName'=>'entry_state', 'value'=>$address_question_arr['state'], 'type'=>'string');
  2276. }
  2277. $sql_data_array[] = array('fieldName'=>'customers_id', 'value'=>$customer_id, 'type'=>'integer');
  2278. $db->perform(TABLE_ADDRESS_BOOK, $sql_data_array);
  2279. $new_address_book_id = $db->Insert_ID();
  2280. $this->notify('NOTIFY_HEADER_ADDRESS_BOOK_ADD_ENTRY_DONE');
  2281. // make default if set, update
  2282. if ($make_default) {
  2283. $sql_data_array = array();
  2284. $sql_data_array[] = array('fieldName'=>'customers_default_address_id', 'value'=>$new_address_book_id, 'type'=>'integer');
  2285. $where_clause = "customers_id = :customersID";
  2286. $where_clause = $db->bindVars($where_clause, ':customersID', $customer_id, 'integer');
  2287. $db->perform(TABLE_CUSTOMERS, $sql_data_array, 'update', $where_clause);
  2288. $_SESSION['customer_default_address_id'] = $new_address_book_id;
  2289. }
  2290. // set the sendto
  2291. $_SESSION['sendto'] = $new_address_book_id;
  2292. // debug
  2293. $this->zcLog('addAddressBookEntry - 2', 'added address #' . $new_address_book_id. "\n" . 'SESSION[sendto] is now set to ' . $_SESSION['sendto']);
  2294. // return the address_id
  2295. return $new_address_book_id;
  2296. }
  2297. /**
  2298. * If we created an account for the customer, this logs them in and notes that the record was created for PayPal EC purposes
  2299. */
  2300. function user_login($email_address, $redirect = true) {
  2301. global $db, $order;
  2302. global $session_started;
  2303. if ($session_started == false) {
  2304. zen_redirect(zen_href_link(FILENAME_COOKIE_USAGE));
  2305. }
  2306. $sql = "SELECT * FROM " . TABLE_CUSTOMERS . "
  2307. WHERE customers_email_address = :custEmail ";
  2308. $sql = $db->bindVars($sql, ':custEmail', $email_address, 'string');
  2309. $check_customer = $db->Execute($sql);
  2310. if ($check_customer->EOF) {
  2311. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_BAD_LOGIN, true);
  2312. }
  2313. if (SESSION_RECREATE == 'True') {
  2314. zen_session_recreate();
  2315. }
  2316. $sql = "SELECT entry_country_id, entry_zone_id
  2317. FROM " . TABLE_ADDRESS_BOOK . "
  2318. WHERE customers_id = :custID
  2319. AND address_book_id = :addrID ";
  2320. $sql = $db->bindVars($sql, ':custID', $check_customer->fields['customers_id'], 'integer');
  2321. $sql = $db->bindVars($sql, ':addrID', $check_customer->fields['customers_default_address_id'], 'integer');
  2322. $check_country = $db->Execute($sql);
  2323. $_SESSION['customer_id'] = (int)$check_customer->fields['customers_id'];
  2324. $_SESSION['customer_default_address_id'] = $check_customer->fields['customers_default_address_id'];
  2325. $_SESSION['customer_first_name'] = $check_customer->fields['customers_firstname'];
  2326. $_SESSION['customer_country_id'] = $check_country->fields['entry_country_id'];
  2327. $_SESSION['customer_zone_id'] = $check_country->fields['entry_zone_id'];
  2328. $order->customer['id'] = $_SESSION['customer_id'];
  2329. $sql = "UPDATE " . TABLE_CUSTOMERS_INFO . "
  2330. SET customers_info_date_of_last_logon = now(),
  2331. customers_info_number_of_logons = customers_info_number_of_logons+1
  2332. WHERE customers_info_id = :custID ";
  2333. $sql = $db->bindVars($sql, ':custID', $_SESSION['customer_id'], 'integer');
  2334. $db->Execute($sql);
  2335. $_SESSION['cart']->restore_contents();
  2336. if ($redirect) {
  2337. $this->terminateEC();
  2338. }
  2339. return true;
  2340. }
  2341. /**
  2342. * If the account was created only for temporary purposes to place the PayPal order, delete it.
  2343. */
  2344. function ec_delete_user($cid) {
  2345. global $db;
  2346. unset($_SESSION['customer_id']);
  2347. unset($_SESSION['customer_default_address_id']);
  2348. unset($_SESSION['customer_first_name']);
  2349. unset($_SESSION['customer_country_id']);
  2350. unset($_SESSION['customer_zone_id']);
  2351. unset($_SESSION['comments']);
  2352. unset($_SESSION['customer_guest_id']);
  2353. $cid = (int)$cid;
  2354. $sql = "delete from " . TABLE_ADDRESS_BOOK . " where customers_id = " . $cid;
  2355. $db->Execute($sql);
  2356. $sql = "delete from " . TABLE_CUSTOMERS . " where customers_id = " . $cid;
  2357. $db->Execute($sql);
  2358. $sql = "delete from " . TABLE_CUSTOMERS_INFO . " where customers_info_id = " . $cid;
  2359. $db->Execute($sql);
  2360. $sql = "delete from " . TABLE_CUSTOMERS_BASKET . " where customers_id = " . $cid;
  2361. $db->Execute($sql);
  2362. $sql = "delete from " . TABLE_CUSTOMERS_BASKET_ATTRIBUTES . " where customers_id = " . $cid;
  2363. $db->Execute($sql);
  2364. $sql = "delete from " . TABLE_WHOS_ONLINE . " where customer_id = " . $cid;
  2365. $db->Execute($sql);
  2366. }
  2367. /**
  2368. * If the EC flow has to be interrupted for any reason, this does the appropriate cleanup and displays status/error messages.
  2369. */
  2370. function terminateEC($error_msg = '', $kill_sess_vars = false, $goto_page = '') {
  2371. global $messageStack, $order, $order_total_modules;
  2372. $stackAlert = 'header';
  2373. // debug
  2374. $this->_doDebug('PayPal test Log - terminateEC-A', "goto page: " . $goto_page . "\nerror_msg: " . $error_msg . "\n\nSession data: " . print_r($_SESSION, true));
  2375. if ($kill_sess_vars) {
  2376. if (!empty($_SESSION['paypal_ec_temp'])) {
  2377. $this->ec_delete_user($_SESSION['customer_id']);
  2378. }
  2379. // Unregister the paypal session variables, making the user start over.
  2380. unset($_SESSION['paypal_ec_temp']);
  2381. unset($_SESSION['paypal_ec_token']);
  2382. unset($_SESSION['paypal_ec_payer_id']);
  2383. unset($_SESSION['paypal_ec_payer_info']);
  2384. unset($_SESSION['paypal_ec_final']);
  2385. unset($_SESSION['paypal_ec_markflow']);
  2386. // debug
  2387. $this->zcLog('termEC-1', 'Killed the session vars as requested');
  2388. }
  2389. $this->zcLog('termEC-2', 'BEFORE: $this->showPaymentPage = ' . (int)$this->showPaymentPage . "\nToken Data:" . $_SESSION['paypal_ec_token']);
  2390. // force display of payment page if GV/DC active for this customer
  2391. if (MODULE_ORDER_TOTAL_INSTALLED && $this->showPaymentPage !== true && isset($_SESSION['paypal_ec_token']) ) {
  2392. require_once(DIR_WS_CLASSES . 'order.php');
  2393. $order = new order;
  2394. require_once(DIR_WS_CLASSES . 'order_total.php');
  2395. $order_total_modules = new order_total;
  2396. $order_totals = $order_total_modules->process();
  2397. $selection = $order_total_modules->credit_selection();
  2398. if (sizeof($selection)>0) $this->showPaymentPage = true;
  2399. }
  2400. // if came from Payment page, don't go back to it
  2401. if ($_SESSION['paypal_ec_markflow'] == 1) $this->showPaymentPage = false;
  2402. // if in DP mode, don't go to payment page ... we've already been there to get here
  2403. if ($goto_page == FILENAME_CHECKOUT_PROCESS) $this->showPaymentPage = false;
  2404. // debug
  2405. $this->zcLog('termEC-3', 'AFTER: $this->showPaymentPage = ' . (int)$this->showPaymentPage);
  2406. if (!empty($_SESSION['customer_first_name']) && !empty($_SESSION['customer_id'])) {
  2407. if ($this->showPaymentPage === true || $goto_page == FILENAME_CHECKOUT_PAYMENT) {
  2408. // debug
  2409. $this->zcLog('termEC-4', 'We ARE logged in, and $this->showPaymentPage === true');
  2410. // if no shipping selected or if shipping cost is < 0 goto shipping page
  2411. if ((!$_SESSION['shipping'] || $_SESSION['shipping'] == '') || $_SESSION['shipping']['cost'] < 0) {
  2412. // debug
  2413. $this->zcLog('termEC-5', 'Have no shipping method selected, or shipping < 0 so set FILENAME_CHECKOUT_SHIPPING');
  2414. $redirect_path = FILENAME_CHECKOUT_SHIPPING;
  2415. $stackAlert = 'checkout_shipping';
  2416. } else {
  2417. // debug
  2418. $this->zcLog('termEC-6', 'We DO have a shipping method selected, so goto PAYMENT');
  2419. $redirect_path = FILENAME_CHECKOUT_PAYMENT;
  2420. $stackAlert = 'checkout_payment';
  2421. }
  2422. } elseif ($goto_page) {
  2423. // debug
  2424. $this->zcLog('termEC-7', '$this->showPaymentPage NOT true, and have custom page parameter: ' . $goto_page);
  2425. $redirect_path = $goto_page;
  2426. $stackAlert = 'header';
  2427. if ($goto_page == FILENAME_SHOPPING_CART) $stackAlert = 'shopping_cart';
  2428. } else {
  2429. // debug
  2430. $this->zcLog('termEC-8', '$this->showPaymentPage NOT true, and NO custom page selected ... using SHIPPING as default');
  2431. $redirect_path = FILENAME_CHECKOUT_SHIPPING;
  2432. $stackAlert = 'checkout_shipping';
  2433. }
  2434. } else {
  2435. // debug
  2436. $this->zcLog('termEC-9', 'We are NOT logged in, so set snapshot to Shipping page, and redirect to login');
  2437. $_SESSION['navigation']->set_snapshot(FILENAME_CHECKOUT_SHIPPING);
  2438. $redirect_path = FILENAME_LOGIN;
  2439. $stackAlert = 'login';
  2440. }
  2441. if ($error_msg) {
  2442. $messageStack->add_session($stackAlert, $error_msg, 'error');
  2443. }
  2444. // debug
  2445. $this->zcLog('termEC-10', 'Redirecting to ' . $redirect_path . ' - Stack: ' . $stackAlert . "\n" . 'Message: ' . $error_msg . "\nSession Data: " . print_r($_SESSION, true));
  2446. zen_redirect(zen_href_link($redirect_path, '', 'SSL', true, false));
  2447. }
  2448. /**
  2449. * Error / exception handling
  2450. */
  2451. function _errorHandler($response, $operation = '', $ignore_codes = '') {
  2452. global $messageStack, $doPayPal;
  2453. $gateway_mode = (isset($response['PNREF']) && $response['PNREF'] != '');
  2454. $basicError = (!$response || (isset($response['RESULT']) && $response['RESULT'] != 0) || (isset($response['ACK']) && !strstr($response['ACK'], 'Success')) || (!isset($response['RESULT']) && !isset($response['ACK'])));
  2455. $ignoreList = explode(',', str_replace(' ', '', $ignore_codes));
  2456. foreach($ignoreList as $key=>$value) {
  2457. if ($value != '' && $response['L_ERRORCODE0'] == $value) $basicError = false;
  2458. }
  2459. //echo '<br />basicError='.$basicError.'<br />' . urldecode(print_r($response,true)); die('halted');
  2460. switch($operation) {
  2461. case 'SetExpressCheckout':
  2462. if ($basicError) {
  2463. // if error, display error message. If debug options enabled, email dump to store owner
  2464. if ($this->enableDebugging) {
  2465. $this->_doDebug('PayPal Error Log - ec_step1()', "In function: ec_step1()\r\n\r\nValue List:\r\n" . str_replace('&',"\r\n", $doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) . "\r\n\r\nResponse:\r\n" . print_r($response, true));
  2466. }
  2467. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_GEN_ERROR;
  2468. $errorNum = urldecode($response['L_ERRORCODE0'] . $response['RESULT']);
  2469. if ($response['RESULT'] == 25) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_NOT_WPP_ACCOUNT_ERROR;
  2470. if ($response['L_ERRORCODE0'] == 10002) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_SANDBOX_VS_LIVE_ERROR;
  2471. if ($response['L_ERRORCODE0'] == 10565) {
  2472. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_WPP_BAD_COUNTRY_ERROR;
  2473. $_SESSION['payment'] = '';
  2474. }
  2475. if ($response['L_ERRORCODE0'] == 10736) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_ADDR_ERROR;
  2476. if ($response['L_ERRORCODE0'] == 10752) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_DECLINED;
  2477. $detailedMessage = ($errorText == MODULE_PAYMENT_PAYPALWPP_TEXT_GEN_ERROR || $this->enableDebugging || $response['CURL_ERRORS'] != '' || $this->emailAlerts) ? $errorNum . ' ' . urldecode(' ' . $response['L_SHORTMESSAGE0'] . ' - ' . $response['L_LONGMESSAGE0'] . (isset($response['RESPMSG']) ? ' ' . $response['RESPMSG'] : '') . ' ' . $response['CURL_ERRORS']) : '';
  2478. $detailedEmailMessage = ($detailedMessage == '') ? '' : MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_MESSAGE . 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: ' . $errorText);
  2479. if ($detailedEmailMessage != '') zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_SUBJECT . ' (' . $errorNum . ')', $detailedMessage, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>$detailedMessage), 'paymentalert');
  2480. $this->terminateEC($errorText . ' (' . $errorNum . ') ' . $detailedMessage, true);
  2481. return true;
  2482. }
  2483. break;
  2484. case 'GetExpressCheckoutDetails':
  2485. if ($basicError || $_SESSION['paypal_ec_token'] != urldecode($response['TOKEN'])) {
  2486. // if response indicates an error, send the customer back to checkout and display the error. Debug to store owner if active.
  2487. if ($this->enableDebugging) {
  2488. $this->_doDebug('PayPal Error Log - ec_step2()', "In function: ec_step2()\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($response, true)));
  2489. }
  2490. $this->terminateEC(MODULE_PAYMENT_PAYPALWPP_TEXT_GEN_ERROR . ' (' . $response['L_ERRORCODE0'] . ' ' . urldecode($response['L_SHORTMESSAGE0'] . $response['RESULT']) . ')', true);
  2491. return true;
  2492. }
  2493. break;
  2494. case 'DoExpressCheckoutPayment':
  2495. if ($basicError || $_SESSION['paypal_ec_token'] != urldecode($response['TOKEN'])) {
  2496. // there's an error, so alert customer, and if debug is on, notify storeowner
  2497. if ($this->enableDebugging) {
  2498. $this->_doDebug('PayPal Error Log - before_process() - EC', "In function: before_process() - Express Checkout\r\n\r\nValue List:\r\n" . str_replace('&',"\r\n", $doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList))) . "\r\n\r\nResponse:\r\n" . print_r($response, true));
  2499. }
  2500. // if funding source problem occurred, must send back to re-select alternate funding source
  2501. if ($response['L_ERRORCODE0'] == 10422) {
  2502. $paypal_url = $this->getPayPalLoginServer();
  2503. zen_redirect($paypal_url . "?cmd=_express-checkout&token=" . $_SESSION['paypal_ec_token']);
  2504. die();
  2505. }
  2506. // some other error condition
  2507. $errorText = MODULE_PAYMENT_PAYPALWPP_INVALID_RESPONSE;
  2508. $errorNum = urldecode($response['L_ERRORCODE0']);
  2509. if ($response['L_ERRORCODE0'] == 10415) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_ORDER_ALREADY_PLACED_ERROR;
  2510. if ($response['L_ERRORCODE0'] == 10417) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_INSUFFICIENT_FUNDS_ERROR;
  2511. if ($response['L_ERRORCODE0'] == 10474) $errorText .= urldecode($response['L_LONGMESSAGE0']);
  2512. $detailedMessage = ($errorText == MODULE_PAYMENT_PAYPALWPP_INVALID_RESPONSE || $this->enableDebugging || $response['CURL_ERRORS'] != '' || $this->emailAlerts) ? $errorNum . ' ' . urldecode(' ' . $response['L_SHORTMESSAGE0'] . ' - ' . $response['L_LONGMESSAGE0'] . $response['RESULT'] . ' ' . $response['CURL_ERRORS']) : '';
  2513. $detailedEmailMessage = ($detailedMessage == '') ? '' : MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_MESSAGE . 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: ' . $errorText);
  2514. if ($detailedEmailMessage != '') zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_SUBJECT . ' (' . $errorNum . ')', $detailedMessage, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>$detailedMessage), 'paymentalert');
  2515. $this->terminateEC(($detailedEmailMessage == '' ? $errorText . ' (' . urldecode($response['L_SHORTMESSAGE0'] . $response['RESULT']) . ') ' : $detailedMessage), true);
  2516. return true;
  2517. }
  2518. break;
  2519. case 'DoDirectPayment':
  2520. if ($basicError ||
  2521. (isset($_SESSION['paypal_ec_token']) && $_SESSION['paypal_ec_token'] != urldecode($response['TOKEN'])) ) {
  2522. // Error, so send the store owner a complete dump of the transaction.
  2523. if ($this->enableDebugging) {
  2524. $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($response, true)));
  2525. }
  2526. $errorText = MODULE_PAYMENT_PAYPALWPP_INVALID_RESPONSE;
  2527. $errorNum = urldecode($response['L_ERRORCODE0'] . $response['RESULT'] . ' ' . $response['RESPMSG']);
  2528. if ($response['RESULT'] == 25) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_NOT_WPP_ACCOUNT_ERROR;
  2529. if ($response['L_ERRORCODE0'] == 10002) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_SANDBOX_VS_LIVE_ERROR;
  2530. if ($response['L_ERRORCODE0'] == 10565) {
  2531. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_WPP_BAD_COUNTRY_ERROR;
  2532. $_SESSION['payment'] = '';
  2533. }
  2534. if ($response['L_ERRORCODE0'] == 10736) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_ADDR_ERROR;
  2535. if ($response['L_ERRORCODE0'] == 10752) {
  2536. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_DECLINED;
  2537. $errorNum = '10752';
  2538. }
  2539. if ($response['RESPMSG'] != '') $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_DECLINED;
  2540. $detailedMessage = ($errorText == MODULE_PAYMENT_PAYPALWPP_INVALID_RESPONSE || $errorText == MODULE_PAYMENT_PAYPALWPP_TEXT_DECLINED || $this->enableDebugging || $response['CURL_ERRORS'] != '' || $this->emailAlerts) ? $errorNum . ' ' . urldecode(' ' . $response['L_SHORTMESSAGE0'] . ' - ' . $response['L_LONGMESSAGE0'] . ' ' . $response['CURL_ERRORS']) : '';
  2541. $detailedEmailMessage = ($detailedMessage == '') ? '' : MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_MESSAGE . 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" . 'Transaction Response Details: ' . print_r($response, true) . "\n\n" . 'Transaction Submission: ' . urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList), true)));
  2542. if ($detailedEmailMessage != '') zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_SUBJECT . ' (' . $errorNum . ')', $detailedMessage, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br($detailedEmailMessage)), 'paymentalert');
  2543. $this->terminateEC(($detailedEmailMessage == '' ? $errorText . ' (' . $errorNum . ') ' : $detailedMessage), ($gateway_mode ? true : false), FILENAME_CHECKOUT_PAYMENT);
  2544. return true;
  2545. }
  2546. break;
  2547. case 'DoRefund':
  2548. if ($basicError || (!isset($response['RESPMSG']) && !isset($response['REFUNDTRANSACTIONID']))) {
  2549. // if error, display error message. If debug options enabled, email dump to store owner
  2550. if ($this->enableDebugging) {
  2551. $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($response, true));
  2552. }
  2553. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_REFUND_ERROR;
  2554. if ($response['L_ERRORCODE0'] == 10009) $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_REFUNDFULL_ERROR;
  2555. if ($response['RESULT'] == 105 || isset($response['RESPMSG'])) $response['L_SHORTMESSAGE0'] = $response['RESULT'] . ' ' . $response['RESPMSG'];
  2556. if (urldecode($response['L_LONGMESSAGE0']) == 'This transaction has already been fully refunded') $response['L_SHORTMESSAGE0'] = urldecode($response['L_LONGMESSAGE0']);
  2557. if (urldecode($response['L_LONGMESSAGE0']) == 'Can not do a full refund after a partial refund') $response['L_SHORTMESSAGE0'] = urldecode($response['L_LONGMESSAGE0']);
  2558. 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']);
  2559. if (urldecode($response['L_LONGMESSAGE0']) == 'You can not refund this type of transaction') $response['L_SHORTMESSAGE0'] = urldecode($response['L_LONGMESSAGE0']);
  2560. $errorText .= ' (' . urldecode($response['L_SHORTMESSAGE0']) . ') ' . $response['L_ERRORCODE0'];
  2561. $messageStack->add_session($errorText, 'error');
  2562. return true;
  2563. }
  2564. break;
  2565. case 'DoAuthorization':
  2566. case 'DoReauthorization':
  2567. if ($basicError) {
  2568. // if error, display error message. If debug options enabled, email dump to store owner
  2569. if ($this->enableDebugging) {
  2570. $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($response, true));
  2571. }
  2572. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_AUTH_ERROR;
  2573. $errorText .= ' (' . urldecode($response['L_SHORTMESSAGE0']) . ') ' . $response['L_ERRORCODE0'];
  2574. $messageStack->add_session($errorText, 'error');
  2575. return true;
  2576. }
  2577. break;
  2578. case 'DoCapture':
  2579. if ($basicError) {
  2580. // if error, display error message. If debug options enabled, email dump to store owner
  2581. if ($this->enableDebugging) {
  2582. $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($response, true));
  2583. }
  2584. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_CAPT_ERROR;
  2585. if ($response['RESULT'] == 111) $response['L_SHORTMESSAGE0'] = $response['RESULT'] . ' ' . $response['RESPMSG'];
  2586. $errorText .= ' (' . urldecode($response['L_SHORTMESSAGE0']) . ') ' . $response['L_ERRORCODE0'];
  2587. $messageStack->add_session($errorText, 'error');
  2588. return true;
  2589. }
  2590. break;
  2591. case 'DoVoid':
  2592. if ($basicError) {
  2593. // if error, display error message. If debug options enabled, email dump to store owner
  2594. if ($this->enableDebugging) {
  2595. $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($response, true));
  2596. }
  2597. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_VOID_ERROR;
  2598. if ($response['RESULT'] == 12) $response['L_SHORTMESSAGE0'] = $response['RESULT'] . ' ' . $response['RESPMSG'];
  2599. if ($response['RESULT'] == 108) $response['L_SHORTMESSAGE0'] = $response['RESULT'] . ' ' . $response['RESPMSG'];
  2600. $errorText .= ' (' . urldecode($response['L_SHORTMESSAGE0']) . ') ' . $response['L_ERRORCODE0'];
  2601. $messageStack->add_session($errorText, 'error');
  2602. return true;
  2603. }
  2604. break;
  2605. case 'GetTransactionDetails':
  2606. if ($basicError) {
  2607. // if error, display error message. If debug options enabled, email dump to store owner
  2608. if ($this->enableDebugging) {
  2609. $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($response, true));
  2610. }
  2611. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_GETDETAILS_ERROR;
  2612. $errorText .= ' (' . urldecode($response['L_SHORTMESSAGE0']) . ') ' . $response['L_ERRORCODE0'];
  2613. $messageStack->add_session($errorText, 'error');
  2614. return true;
  2615. }
  2616. break;
  2617. case 'TransactionSearch':
  2618. if ($basicError) {
  2619. // if error, display error message. If debug options enabled, email dump to store owner
  2620. if ($this->enableDebugging) {
  2621. $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($response, true));
  2622. }
  2623. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_TRANSSEARCH_ERROR;
  2624. $errorText .= ' (' . urldecode($response['L_SHORTMESSAGE0']) . ') ' . $response['L_ERRORCODE0'];
  2625. $messageStack->add_session($errorText, 'error');
  2626. return true;
  2627. }
  2628. break;
  2629. default:
  2630. if ($basicError) {
  2631. // if error, display error message. If debug options enabled, email dump to store owner
  2632. if ($this->enableDebugging) {
  2633. $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($response, true));
  2634. }
  2635. $errorText = MODULE_PAYMENT_PAYPALWPP_TEXT_GEN_API_ERROR;
  2636. $errorNum .= ' (' . urldecode($response['L_SHORTMESSAGE0'] . ' ' . $response['RESPMSG']) . ') ' . $response['L_ERRORCODE0'];
  2637. $detailedMessage = ($errorText == MODULE_PAYMENT_PAYPALWPP_TEXT_GEN_API_ERROR || $errorText == MODULE_PAYMENT_PAYPALWPP_TEXT_DECLINED || $this->enableDebugging || $response['CURL_ERRORS'] != '' || $this->emailAlerts) ? urldecode(' ' . $response['L_SHORTMESSAGE0'] . ' - ' . $response['L_LONGMESSAGE0'] . ' ' . $response['CURL_ERRORS']) : '';
  2638. $detailedEmailMessage = ($detailedMessage == '') ? '' : MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_MESSAGE . 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" . 'Transaction Response Details: ' . print_r($response, true) . "\n\n" . 'Transaction Submission: ' . urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList), true)));
  2639. if ($detailedEmailMessage != '') zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, MODULE_PAYMENT_PAYPALWPP_TEXT_EMAIL_ERROR_SUBJECT . ' (' . $errorNum . ')', $detailedMessage, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br($detailedEmailMessage)), 'paymentalert');
  2640. $messageStack->add_session($errorText . $errorNum . $detailedMessage, 'error');
  2641. return true;
  2642. }
  2643. break;
  2644. }
  2645. }
  2646. function tableCheckup() {
  2647. global $db, $sniffer;
  2648. $fieldOkay1 = (method_exists($sniffer, 'field_type')) ? $sniffer->field_type(TABLE_PAYPAL, 'txn_id', 'varchar(20)', true) : -1;
  2649. $fieldOkay2 = ($sniffer->field_exists(TABLE_PAYPAL, 'module_name')) ? true : -1;
  2650. $fieldOkay3 = ($sniffer->field_exists(TABLE_PAYPAL, 'order_id')) ? true : -1;
  2651. if ($fieldOkay1 == -1) {
  2652. $sql = "show fields from " . TABLE_PAYPAL;
  2653. $result = $db->Execute($sql);
  2654. while (!$result->EOF) {
  2655. if ($result->fields['Field'] == 'txn_id') {
  2656. if ($result->fields['Type'] == 'varchar(20)') {
  2657. $fieldOkay1 = true; // exists and matches required type, so skip to other checkup
  2658. } else {
  2659. $fieldOkay1 = $result->fields['Type']; // doesn't match, so return what it "is"
  2660. break;
  2661. }
  2662. }
  2663. $result->MoveNext();
  2664. }
  2665. }
  2666. if ($fieldOkay1 !== true) {
  2667. // temporary fix to table structure for v1.3.7.x -- may remove in later release
  2668. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE payment_type payment_type varchar(40) NOT NULL default ''");
  2669. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE txn_type txn_type varchar(40) NOT NULL default ''");
  2670. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE payment_status payment_status varchar(32) NOT NULL default ''");
  2671. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE reason_code reason_code varchar(40) default NULL");
  2672. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE pending_reason pending_reason varchar(32) default NULL");
  2673. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE invoice invoice varchar(128) default NULL");
  2674. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE payer_business_name payer_business_name varchar(128) default NULL");
  2675. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE address_name address_name varchar(64) default NULL");
  2676. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE address_street address_street varchar(254) default NULL");
  2677. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE address_city address_city varchar(120) default NULL");
  2678. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE address_state address_state varchar(120) default NULL");
  2679. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE payer_email payer_email varchar(128) NOT NULL default ''");
  2680. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE business business varchar(128) NOT NULL default ''");
  2681. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE receiver_email receiver_email varchar(128) NOT NULL default ''");
  2682. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE txn_id txn_id varchar(20) NOT NULL default ''");
  2683. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE parent_txn_id parent_txn_id varchar(20) default NULL");
  2684. }
  2685. if ($fieldOkay2 !== true) {
  2686. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " ADD COLUMN module_name varchar(40) NOT NULL default '' after txn_type");
  2687. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " ADD COLUMN module_mode varchar(40) NOT NULL default '' after module_name");
  2688. }
  2689. if ($fieldOkay3 !== true) {
  2690. $db->Execute("ALTER TABLE " . TABLE_PAYPAL . " CHANGE zen_order_id order_id int(11) NOT NULL default '0'");
  2691. }
  2692. }
  2693. }
  2694. ?>