Home > Article > Backend Development > PHP modify zen-cart order and payment process to prevent missed orders
Anyone who has used zen-cart knows that the ordering steps in zen-cart are as follows (the expressions in [] are not necessary):
1. Shopping cart (shopping cart)
2. [delivery method]
3. Payment method
4. Order confirmation
5. [Third-party website payment]
6. Order processing (checkout process) - This step is more important because the information in the shopping cart will be written into the order here
7. Order successful (checkout success)
There is no problem with this process under normal circumstances. However, in the process from step 5 to step 6, the user may think that the payment is successful and close the web page directly, or the user may not be able to jump to the checkout_process page normally due to network reasons. The consequences of this are very serious, because the order Cannot be created normally.
Based on the above analysis, we hope to change the process slightly, that is, the order has been created before payment, so that even if we cannot jump back from the third-party payment website during payment, we will not There may be situations where users make successful payments but have no orders in the background. The modified blueprint is basically as follows:
1. After confirming the order on the checkour_confirmation page, you will directly proccess and enter the checkour_success page, where you can enter the payment page. As shown in the picture below:
2. If the customer fails to pay at that time, he can also enter his backend to pay for historical orders. As shown in the picture below:
Let’s take a look at how to implement the above functions step by step.
1. First we need to transform the existing payment module. You need to add a field paynow_action_url to the payment method class to represent the page URL for payment. You also need to add a function, paynow_button($order_id), to obtain the parameter hidden field code of the payment form.
To add the paynow_action_url field, please add the following code at the end of the constructor of the payment class:
if ( (zen_not_null($module)) && (in_array($module.'.php', $this->modules)) && (isset($GLOBALS[$module]->paynow_action_url)) ) { $this->paynow_action_url = $GLOBALS[$module]->paynow_action_url; }
To add the paynow_button ($order_id) function, please add it after the last function of the payment class The following code:
function paynow_button($order_id){ if (is_array($this->modules)) { if (is_object($GLOBALS[$this->selected_module])) { return $GLOBALS[$this->selected_module]->paynow_button($order_id); } } }
2. Take the paypal payment method as an example to explain how to implement it. In order not to destroy the original code of paypal, we will make a copy of the paypal.php file, name it paypalsimple.php, and make appropriate modifications to the code inside. The code is as shown below. You can see that the specification of form_action_url is removed here and paynow_action_url is given. Because we hope that the user will directly enter the checkout_process after clicking "Confirm Order", so if form_action_url is not specified, then the form to confirm the order will be It is submitted directly to the checkout_process page, and paynow_action_url is the value of the previous form_action_url. The implementation of the paynow_button function is also very simple. Here we just cut the content of the original process_button() function, but we do not use the global $order variable, but use $order = new order($order_id) to re- An object constructed in preparation for displaying the pay now button in historical orders.
paypalsimple.php
<?php /** * @package paypalsimple payment module * @copyright Copyright 2003-2006 Zen Cart Development Team * @copyright Portions Copyright 2003 osCommerce * @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0 * @version $Id: paypalsimple.php 4960 2009-12-29 11:46:46Z gary $ */ // ensure dependencies are loaded include_once((IS_ADMIN_FLAG === true ? DIR_FS_CATALOG_MODULES : DIR_WS_MODULES) . 'payment/paypal/paypal_functions.php'); class paypalsimple { var $code, $title, $description, $enabled; // class constructor function paypalsimple() { global $order; $this->code = 'paypalsimple'; $this->title = MODULE_PAYMENT_PAYPAL_SIMPLE_TEXT_TITLE; if(IS_ADMIN_FLAG === true){ $this->title = MODULE_PAYMENT_PAYPAL_SIMPLE_TEXT_ADMIN_TITLE; } $this->description = MODULE_PAYMENT_PAYPAL_SIMPLE_TEXT_DESCRIPTION; $this->sort_order = MODULE_PAYMENT_PAYPAL_SIMPLE_SORT_ORDER; $this->enabled = ((MODULE_PAYMENT_PAYPAL_SIMPLE_STATUS == 'True') ? true : false); if ((int)MODULE_PAYMENT_PAYPAL_SIMPLE_ORDER_STATUS_ID > 0) { $this->order_status = MODULE_PAYMENT_PAYPAL_SIMPLE_ORDER_STATUS_ID; } $this->paynow_action_url = 'https://' . MODULE_PAYMENT_PAYPAL_SIMPLE_HANDLER; if (is_object($order)) $this->update_status(); } // class methods function update_status() { global $order, $db; if ( ($this->enabled == true) && ((int)MODULE_PAYMENT_PAYPAL_SIMPLE_ZONE > 0) ) { $check_flag = false; $check = $db->Execute("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_PAYMENT_PAYPAL_SIMPLE_ZONE . "' and zone_country_id = '" . $order->billing['country']['id'] . "' order by zone_id"); while (!$check->EOF) { if ($check->fields['zone_id'] < 1) { $check_flag = true; break; } elseif ($check->fields['zone_id'] == $order->billing['zone_id']) { $check_flag = true; break; } $check->MoveNext(); } if ($check_flag == false) { $this->enabled = false; } } } function javascript_validation() { return false; } function selection() { $text = MODULE_PAYMENT_SIMPLE_PAYPAL_TEXT_CATALOG_LOGO.' '.MODULE_PAYMENT_PAYPAL_SIMPLE_TEXT_TITLE . '<br/><br/> <span class="smallText">' . MODULE_PAYMENT_PAYPAL_SIMPLE_ACCEPTANCE_MARK_TEXT . '</span><br/><br/>'; return array('id' => $this->code, 'module' => $text ); } function pre_confirmation_check() { return false; } function confirmation() { return false; } function process_button() { return false; } function before_process() { return false; } function after_process() { return false; } function get_error() { return false; } function check() { global $db; if (!isset($this->_check)) { $check_query = $db->Execute("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_PAYPAL_SIMPLE_STATUS'"); $this->_check = $check_query->RecordCount(); } return $this->_check; } function install() { global $db; $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 PayPal-Simple Module', 'MODULE_PAYMENT_PAYPAL_SIMPLE_STATUS', 'True', 'Do you want to accept PayPal-Simple payments?', '6', '0', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())"); $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_PAYPAL_SIMPLE_SORT_ORDER', '0', 'Sort order of display. Lowest is displayed first.', '6', '8', now())"); $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_PAYPAL_SIMPLE_ZONE', '0', 'If a zone is selected, only enable this payment method for that zone.', '6', '2', 'zen_get_zone_class_title', 'zen_cfg_pull_down_zone_classes(', now())"); $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_PAYPAL_SIMPLE_ORDER_STATUS_ID', '0', 'Set the status of orders made with this payment module to this value', '6', '0', 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())"); $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Mode for PayPal web services<br /><br />Default:<br /><code>www.paypal.com/cgi-bin/webscr</code><br />or<br /><code>www.paypal.com/us/cgi-bin/webscr</code><br />or for the UK,<br /><code>www.paypal.com/uk/cgi-bin/webscr</code>', 'MODULE_PAYMENT_PAYPAL_SIMPLE_HANDLER', 'www.paypal.com/cgi-bin/webscr', 'Choose the URL for PayPal live processing', '6', '73', '', now())"); } function remove() { global $db; $db->Execute("delete from " . TABLE_CONFIGURATION . " where configuration_key in ('" . implode("', '", $this->keys()) . "')"); } function keys() { return array('MODULE_PAYMENT_PAYPAL_SIMPLE_STATUS','MODULE_PAYMENT_PAYPAL_SIMPLE_SORT_ORDER','MODULE_PAYMENT_PAYPAL_SIMPLE_ZONE','MODULE_PAYMENT_PAYPAL_SIMPLE_ORDER_STATUS_ID', 'MODULE_PAYMENT_PAYPAL_SIMPLE_HANDLER'); } function paynow_button($order_id){ global $db, $order, $currencies, $currency; require_once(DIR_WS_CLASSES . 'order.php'); $order = new order($order_id); $options = array(); $optionsCore = array(); $optionsPhone = array(); $optionsShip = array(); $optionsLineItems = array(); $optionsAggregate = array(); $optionsTrans = array(); $buttonArray = array(); $this->totalsum = $order->info['total']; // save the session stuff permanently in case paypal loses the session $_SESSION['ppipn_key_to_remove'] = session_id(); $db->Execute("delete from " . TABLE_PAYPAL_SESSION . " where session_id = '" . zen_db_input($_SESSION['ppipn_key_to_remove']) . "'"); $sql = "insert into " . TABLE_PAYPAL_SESSION . " (session_id, saved_session, expiry) values ( '" . zen_db_input($_SESSION['ppipn_key_to_remove']) . "', '" . base64_encode(serialize($_SESSION)) . "', '" . (time() + (1*60*60*24*2)) . "')"; $db->Execute($sql); $my_currency = select_pp_currency(); $this->transaction_currency = $my_currency; $this->transaction_amount = ($this->totalsum * $currencies->get_value($my_currency)); $telephone = preg_replace('/\D/', '', $order->customer['telephone']); if ($telephone != '') { $optionsPhone['H_PhoneNumber'] = $telephone; if (in_array($order->customer['country']['iso_code_2'], array('US','CA'))) { $optionsPhone['night_phone_a'] = substr($telephone,0,3); $optionsPhone['night_phone_b'] = substr($telephone,3,3); $optionsPhone['night_phone_c'] = substr($telephone,6,4); $optionsPhone['day_phone_a'] = substr($telephone,0,3); $optionsPhone['day_phone_b'] = substr($telephone,3,3); $optionsPhone['day_phone_c'] = substr($telephone,6,4); } else { $optionsPhone['night_phone_b'] = $telephone; $optionsPhone['day_phone_b'] = $telephone; } } $optionsCore = array( 'charset' => CHARSET, 'lc' => $order->customer['country']['iso_code_2'], 'page_style' => MODULE_PAYMENT_PAYPAL_PAGE_STYLE, 'custom' => zen_session_name() . '=' . zen_session_id(), 'business' => MODULE_PAYMENT_PAYPAL_BUSINESS_ID, 'return' => zen_href_link(FILENAME_PAY_SUCCESS, 'referer=paypal', 'SSL'), 'cancel_return' => zen_href_link(FILENAME_PAY_FAILED, '', 'SSL'), 'shopping_url' => zen_href_link(FILENAME_SHOPPING_CART, '', 'SSL'), 'notify_url' => zen_href_link('ipn_main_handler.php', '', 'SSL',false,false,true), 'redirect_cmd' => '_xclick', 'rm' => 2, 'bn' => 'zencart', 'mrb' => 'R-6C7952342H795591R', 'pal' => '9E82WJBKKGPLQ', ); $optionsCust = array( 'first_name' => replace_accents($order->customer['firstname']), 'last_name' => replace_accents($order->customer['lastname']), 'address1' => replace_accents($order->customer['street_address']), 'city' => replace_accents($order->customer['city']), 'state' => zen_get_zone_code($order->customer['country']['id'], $order->customer['zone_id'], $order->customer['zone_id']), 'zip' => $order->customer['postcode'], 'country' => $order->customer['country']['iso_code_2'], 'email' => $order->customer['email_address'], ); if ($order->customer['suburb'] != '') $optionsCust['address2'] = $order->customer['suburb']; if (MODULE_PAYMENT_PAYPAL_ADDRESS_REQUIRED == 2) $optionsCust = array( 'address_name' => replace_accents($order->customer['firstname'] . ' ' . $order->customer['lastname']), 'address_street' => replace_accents($order->customer['street_address']), 'address_city' => replace_accents($order->customer['city']), 'address_state' => zen_get_zone_code($order->customer['country']['id'], $order->customer['zone_id'], $order->customer['zone_id']), 'address_zip' => $order->customer['postcode'], 'address_country' => $order->customer['country']['title'], 'address_country_code' => $order->customer['country']['iso_code_2'], 'payer_email' => $order->customer['email_address'], ); $optionsShip = array( //'address_override' => MODULE_PAYMENT_PAYPAL_ADDRESS_OVERRIDE, 'no_shipping' => MODULE_PAYMENT_PAYPAL_ADDRESS_REQUIRED, ); if (MODULE_PAYMENT_PAYPAL_DETAILED_CART == 'Yes') $optionsLineItems = ipn_getLineItemDetails(); if (sizeof($optionsLineItems) > 0) { $optionsLineItems['cmd'] = '_cart'; // $optionsLineItems['num_cart_items'] = sizeof($order->products); if (isset($optionsLineItems['shipping'])) { $optionsLineItems['shipping_1'] = $optionsLineItems['shipping']; unset($optionsLineItems['shipping']); } if (isset($optionsLineItems['handling'])) { $optionsLineItems['handling_1'] = $optionsLineItems['handling']; unset($optionsLineItems['handling']); } unset($optionsLineItems['subtotal']); // if line-item details couldn't be kept due to calculation mismatches or discounts etc, default to aggregate mode if (!isset($optionsLineItems['item_name_1'])) $optionsLineItems = array(); //if ($optionsLineItems['amount'] != $this->transaction_amount) $optionsLineItems = array(); ipn_debug_email('Line Item Details (if blank, this means there was a data mismatch, and thus bypassed): ' . "\n" . print_r($optionsLineItems, true)); } $products_name_display = ""; /* for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { if(i > 0) { $products_name_display.= ', '; } $products_name_display.= $order->products[$i]['name']. '('. $order->products[$i]['qty'] .','.$order->products[$i]['dhisys_web_order_number'].')'; }*/ $optionsAggregate = array( 'cmd' => '_ext-enter', 'item_name' => $products_name_display, 'item_number' => $order_id, 'num_cart_items' => sizeof($order->products), 'amount' => number_format($this->transaction_amount, $currencies->get_decimal_places($my_currency)), 'shipping' => '0.00', ); if (MODULE_PAYMENT_PAYPAL_TAX_OVERRIDE == 'true') $optionsAggregate['tax'] = '0.00'; if (MODULE_PAYMENT_PAYPAL_TAX_OVERRIDE == 'true') $optionsAggregate['tax_cart'] = '0.00'; $optionsTrans = array( 'upload' => (int)(sizeof($order->products) > 0), 'currency_code' => $my_currency, // 'paypal_order_id' => $paypal_order_id, //'no_note' => '1', //'invoice' => '', ); // if line-item info is invalid, use aggregate: if (sizeof($optionsLineItems) > 0) $optionsAggregate = $optionsLineItems; // prepare submission $options = array_merge($optionsCore, $optionsCust, $optionsPhone, $optionsShip, $optionsTrans, $optionsAggregate); ipn_debug_email('Keys for submission: ' . print_r($options, true)); if(sizeof($order->products) > 0){ $options['cmd'] = '_cart'; for ($i=0, $n=sizeof($order->products); $i<$n; $i++) { $options['item_name_'. (string)($i+1)] = $order->products[$i]['name']; $options['item_number_'. (string)($i+1)] = $order->products[$i]['dhisys_web_order_number']; $options['amount_'. (string)($i+1)] = number_format((float)$order->products[$i]['final_price'],2); $options['quantity_'. (string)($i+1)] = $order->products[$i]['qty']; } } // build the button fields foreach ($options as $name => $value) { // remove quotation marks $value = str_replace('"', '', $value); // check for invalid chars if (preg_match('/[^a-zA-Z_0-9]/', $name)) { ipn_debug_email('datacheck - ABORTING - preg_match found invalid submission key: ' . $name . ' (' . $value . ')'); break; } // do we need special handling for & and = symbols? //if (strpos($value, '&') !== false || strpos($value, '=') !== false) $value = urlencode($value); $buttonArray[] = zen_draw_hidden_field($name, $value); } $_SESSION['paypal_transaction_info'] = array($this->transaction_amount, $this->transaction_currency); $process_button_string = implode("\n", $buttonArray) . "\n"; return $process_button_string; } } ?>
3. Display the pay now button on the checkout_success page. Open the file "includes/modules/pages/checkout_success/header.php" and add the following code at the end of the file (if you have mastered the notifier/observer mode in zen-cart and do not want to break the zen-cart core code If so, you can also create an observation class to listen for NOTIFY_HEADER_END_CHECKOUT_SUCCESS).
require_once(DIR_WS_CLASSES . 'order.php'); require_once(DIR_WS_CLASSES . 'payment.php'); $payment_modules = new payment($orders->fields['payment_module_code']);
Open the file "includes/modules/templates/template_default/templates/tpl_checkout_success_default.php" and add the following code in the appropriate position. Here, a judgment is made on the status of the order. When there is only the order This button is only displayed when the status is unpaid,
<div id="pay_now"> <?php //&& $orders->fields['orders_status'] == '1' if(isset($payment_modules->paynow_action_url) && $payment_modules->paynow_action_url != ''&& $orders->fields['orders_status'] == '1'){ echo('<fieldset id="csNotifications">'); echo('<legend>'.TEXT_PAYNOW.'</legend>'); echo zen_draw_form('checkout_paynow', $payment_modules->paynow_action_url, 'post', 'id="checkout_confirmation" onsubmit="submitonce();"'); $selection = $payment_modules->selection(); echo('<div class="buttonRow payment_method">'.$selection[0]['module'].'</div>'); echo('<div class="buttonRow forward paynow">'); if (is_array($payment_modules->modules)) { echo $payment_modules->paynow_button($orders_id); } echo(zen_image_submit(BUTTON_IMAGE_PAYNOW, BUTTON_IMAGE_PAYNOW_ALT, 'name="btn_paynow" id="btn_paynow"')); echo('</div>'); echo('</form>'); echo('</fieldset>'); } ?> </div>
4. 在历史订单中显示pay now按钮。需要显示pay now按钮的页面有三个:account, account_history,account_history_info,这里的实现和checkout_success页面的实现大同小异,只是传给$payment_modules的函数paynow_button的参数不一样而已,这里就不再赘述。
总结:
经过上面的修改,我们的流程如下:
1. 购物车(shopping cart)
2. [货运方式(delivery method)]
3. 支付方式(payment method)
4. 订单确认(confirmation)
5. 订单处理(checkout process)
6. 下单成功(checkout success)
7. [第三方网站支付]
因为从订单确认到订单处理,都是在我们自己的网站完成的,并且进入支付网站之前,订单已经存在了,这样就不会出现掉单的情况了。
更多php 修改zen-cart下单和付款流程以防止漏单相关文章请关注PHP中文网!