/** * Process post-sale requestion from the Postfinance payment server. * * @access public * @return void */ public function processPostSale() { if ($this->getRequestData('NCERROR') > 0) { $this->log('Order ID "' . $this->getRequestData('orderID') . '" has NCERROR ' . $this->getRequestData('NCERROR'), __METHOD__, TL_ERROR); return; } $objOrder = new IsotopeOrder(); if (!$objOrder->findBy('id', $this->getRequestData('orderID'))) { $this->log('Order ID "' . $this->getRequestData('orderID') . '" not found', __METHOD__, TL_ERROR); return; } if (!$this->validateSHASign()) { $this->log('Received invalid postsale data for order ID "' . $objOrder->id . '"', __METHOD__, TL_ERROR); return; } // Validate payment data (see #2221) if ($objOrder->currency != $this->getRequestData('currency') || $objOrder->grandTotal != $this->getRequestData('amount')) { $this->log('Postsale checkout manipulation in payment for Order ID ' . $objOrder->id . '!', __METHOD__, TL_ERROR); return; } if (!$objOrder->checkout()) { $this->log('Post-Sale checkout for Order ID "' . $objOrder->id . '" failed', __METHOD__, TL_ERROR); return; } $objOrder->date_paid = time(); $objOrder->save(); }
/** * Process PayPal Instant Payment Notifications (IPN) * * @access public * @return void */ public function processPostSale() { $arrData = array(); foreach ($_POST as $k => $v) { $arrData[] = $k . '=' . urlencode($v); } $objRequest = new Request(); $objRequest->send('https://www.' . ($this->debug ? 'sandbox.' : '') . 'paypal.com/cgi-bin/webscr?cmd=_notify-validate', implode('&', $arrData), 'post'); if ($objRequest->hasError()) { $this->log('Request Error: ' . $objRequest->error, __METHOD__, TL_ERROR); exit; } elseif ($objRequest->response == 'VERIFIED' && ($this->Input->post('receiver_email', true) == $this->paypal_account || $this->debug)) { $objOrder = new IsotopeOrder(); if (!$objOrder->findBy('id', $this->Input->post('invoice'))) { $this->log('Order ID "' . $this->Input->post('invoice') . '" not found', __METHOD__, TL_ERROR); return; } // Validate payment data (see #2221) if ($objOrder->currency != $this->Input->post('mc_currency') || $objOrder->grandTotal != $this->Input->post('mc_gross')) { $this->log('IPN manipulation in payment from "' . $this->Input->post('payer_email') . '" !', __METHOD__, TL_ERROR); return; } if (!$objOrder->checkout()) { $this->log('IPN checkout for Order ID "' . $this->Input->post('invoice') . '" failed', __METHOD__, TL_ERROR); return; } // Load / initialize data $arrPayment = deserialize($objOrder->payment_data, true); // Store request data in order for future references $arrPayment['POSTSALE'][] = $_POST; $arrData = $objOrder->getData(); $arrData['old_payment_status'] = $arrPayment['status']; $arrPayment['status'] = $this->Input->post('payment_status'); $arrData['new_payment_status'] = $arrPayment['status']; // array('pending','processing','complete','on_hold', 'cancelled'), switch ($arrPayment['status']) { case 'Completed': $objOrder->date_paid = time(); break; case 'Canceled_Reversal': case 'Denied': case 'Expired': case 'Failed': case 'Voided': $objOrder->date_paid = ''; if ($objOrder->status == 'complete') { $objOrder->status = 'on_hold'; } break; case 'In-Progress': case 'Partially_Refunded': case 'Pending': case 'Processed': case 'Refunded': case 'Reversed': break; } $objOrder->payment_data = $arrPayment; $objOrder->save(); $this->log('PayPal IPN: data accepted', __METHOD__, TL_GENERAL); } else { $this->log('PayPal IPN: data rejected (' . $objRequest->response . ')', __METHOD__, TL_ERROR); } header('HTTP/1.1 200 OK'); exit; }
/** * Generate module * @return void */ protected function compile() { // Order has been completed (postsale request) if ($this->strCurrentStep == 'complete' && $this->Input->get('uid') != '') { $objOrder = new IsotopeOrder(); if ($objOrder->findBy('uniqid', $this->Input->get('uid'))) { // Order is complete, forward to confirmation page if ($objOrder->complete()) { IsotopeFrontend::clearTimeout(); $this->redirect(IsotopeFrontend::addQueryStringToUrl('uid=' . $objOrder->uniqid, $this->orderCompleteJumpTo)); } // Order is not complete, wait for it if (IsotopeFrontend::setTimeout()) { $this->Template = new FrontendTemplate('mod_message'); $this->Template->type = 'processing'; $this->Template->message = $GLOBALS['TL_LANG']['MSC']['payment_processing']; return; } } } // Return error message if cart is empty if (!$this->Isotope->Cart->items) { $this->Template = new FrontendTemplate('mod_message'); $this->Template->type = 'empty'; $this->Template->message = $GLOBALS['TL_LANG']['MSC']['noItemsInCart']; return; } // Insufficient cart subtotal if ($this->Isotope->Config->cartMinSubtotal > 0 && $this->Isotope->Config->cartMinSubtotal > $this->Isotope->Cart->subTotal) { $this->Template = new FrontendTemplate('mod_message'); $this->Template->type = 'error'; $this->Template->message = sprintf($GLOBALS['TL_LANG']['ERR']['cartMinSubtotal'], $this->Isotope->formatPriceWithCurrency($this->Isotope->Config->cartMinSubtotal)); return; } // Redirect to login page if not logged in if ($this->iso_checkout_method == 'member' && FE_USER_LOGGED_IN !== true) { $objPage = $this->Database->prepare("SELECT id,alias FROM tl_page WHERE id=?")->limit(1)->execute($this->iso_login_jumpTo); if (!$objPage->numRows) { $this->Template = new FrontendTemplate('mod_message'); $this->Template->type = 'error'; $this->Template->message = $GLOBALS['TL_LANG']['ERR']['isoLoginRequired']; return; } $this->redirect($this->generateFrontendUrl($objPage->row())); } elseif ($this->iso_checkout_method == 'guest' && FE_USER_LOGGED_IN === true) { $this->Template = new FrontendTemplate('mod_message'); $this->Template->type = 'error'; $this->Template->message = 'User checkout not allowed'; return; } if (!$this->iso_forward_review && !strlen($this->Input->get('step'))) { $this->redirectToNextStep(); } // Default template settings. Must be set at beginning so they can be overwritten later (eg. trough callback) $this->Template->action = ampersand($this->Environment->request, ENCODE_AMPERSANDS); $this->Template->formId = $this->strFormId; $this->Template->formSubmit = $this->strFormId; $this->Template->enctype = 'application/x-www-form-urlencoded'; $this->Template->previousLabel = specialchars($GLOBALS['TL_LANG']['MSC']['previousStep']); $this->Template->nextLabel = specialchars($GLOBALS['TL_LANG']['MSC']['nextStep']); $this->Template->nextClass = 'next'; $this->Template->showPrevious = true; $this->Template->showNext = true; $this->Template->showForm = true; // Remove shipping step if no items are shipped if (!$this->Isotope->Cart->requiresShipping) { unset($GLOBALS['ISO_CHECKOUT_STEPS']['shipping']); // Remove payment step if items are free of charge. We need to do this here because shipping might have a price. if (!$this->Isotope->Cart->requiresPayment) { unset($GLOBALS['ISO_CHECKOUT_STEPS']['payment']); } } if ($this->strCurrentStep == 'failed') { $this->Database->prepare("UPDATE tl_iso_orders SET status='on_hold' WHERE cart_id=?")->execute($this->Isotope->Cart->id); $this->Template->mtype = 'error'; $this->Template->message = strlen($this->Input->get('reason')) ? $this->Input->get('reason') : $GLOBALS['TL_LANG']['ERR']['orderFailed']; $this->strCurrentStep = 'review'; } // Run trough all steps until we find the current one or one reports failure foreach ($GLOBALS['ISO_CHECKOUT_STEPS'] as $step => $arrCallbacks) { // Step could be removed while looping if (!isset($GLOBALS['ISO_CHECKOUT_STEPS'][$step])) { continue; } $this->strFormId = 'iso_mod_checkout_' . $step; $this->Template->formId = $this->strFormId; $this->Template->formSubmit = $this->strFormId; $strBuffer = ''; foreach ($arrCallbacks as $callback) { if ($callback[0] == 'ModuleIsotopeCheckout') { $strBuffer .= $this->{$callback[1]}(); } else { $this->import($callback[0]); $strBuffer .= $this->{$callback[0]}->{$callback[1]}($this); } // the user wanted to proceed but the current step is not completed yet if ($this->doNotSubmit && $step != $this->strCurrentStep) { $this->redirect($this->addToUrl('step=' . $step, true)); } } if ($step == $this->strCurrentStep) { break; } } if ($this->strCurrentStep == 'process') { $this->writeOrder(); $strBuffer = $this->Isotope->Cart->hasPayment ? $this->Isotope->Cart->Payment->checkoutForm($this) : false; if ($strBuffer === false) { $this->redirect($this->addToUrl('step=complete', true)); } $this->Template->showForm = false; $this->doNotSubmit = true; } if ($this->strCurrentStep == 'complete') { $strBuffer = $this->Isotope->Cart->hasPayment ? $this->Isotope->Cart->Payment->processPayment() : true; if ($strBuffer === true) { $objOrder = new IsotopeOrder(); // If checkout is successful, complete order and redirect to confirmation page if ($objOrder->findBy('cart_id', $this->Isotope->Cart->id) && $objOrder->checkout($this->Isotope->Cart) && $objOrder->complete()) { $this->redirect(IsotopeFrontend::addQueryStringToUrl('uid=' . $objOrder->uniqid, $this->orderCompleteJumpTo)); } // Checkout failed, show error message $this->redirect($this->addToUrl('step=failed', true)); } elseif ($strBuffer === false) { $this->redirect($this->addToUrl('step=failed', true)); } else { $this->Template->showNext = false; $this->Template->showPrevious = false; } } $this->Template->fields = $strBuffer; if (!strlen($this->strCurrentStep)) { $this->strCurrentStep = $step; } // Show checkout steps $arrStepKeys = array_keys($GLOBALS['ISO_CHECKOUT_STEPS']); $blnPassed = true; $total = count($arrStepKeys) - 1; $arrSteps = array(); if ($this->strCurrentStep != 'process' && $this->strCurrentStep != 'complete') { foreach ($arrStepKeys as $i => $step) { if ($this->strCurrentStep == $step) { $blnPassed = false; } $blnActive = $this->strCurrentStep == $step ? true : false; $arrSteps[] = array('isActive' => $blnActive, 'class' => 'step_' . $i . ($i == 0 ? ' first' : '') . ($i == $total ? ' last' : '') . ($blnActive ? ' active' : '') . ($blnPassed ? ' passed' : '') . (!$blnPassed && !$blnActive ? ' upcoming' : '') . ' ' . $step, 'label' => strlen($GLOBALS['TL_LANG']['ISO']['checkout_' . $step]) ? $GLOBALS['TL_LANG']['ISO']['checkout_' . $step] : $step, 'href' => $blnPassed ? $this->addToUrl('step=' . $step, true) : '', 'title' => specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['checkboutStepBack'], strlen($GLOBALS['TL_LANG']['ISO']['checkout_' . $step]) ? $GLOBALS['TL_LANG']['ISO']['checkout_' . $step] : $step))); } } $this->Template->steps = $arrSteps; $this->Template->activeStep = $GLOBALS['ISO_LANG']['MSC']['activeStep']; // Hide back buttons it this is the first step if (array_search($this->strCurrentStep, $arrStepKeys) === 0) { $this->Template->showPrevious = false; } elseif (array_search($this->strCurrentStep, $arrStepKeys) === $total) { $this->Template->nextClass = 'confirm'; $this->Template->nextLabel = specialchars($GLOBALS['TL_LANG']['MSC']['confirmOrder']); } // User pressed "back" button if (strlen($this->Input->post('previousStep'))) { $this->redirectToPreviousStep(); } elseif ($this->Input->post('FORM_SUBMIT') == $this->strFormId && !$this->doNotSubmit) { $this->redirectToNextStep(); } }