zen-cartを利用したことのある方はご存知かと思いますが、zen-cartでの注文手順は以下の通りです([]内の表現は不要です):
1. ショッピングカート
2. 【配送方法】
3.お支払い方法
4. 注文確認(確認)
5. [第三者ウェブサイトでの支払い]
6. 注文処理(チェックアウトプロセス) - このステップ ショッピングカート内の情報が書き込まれるため、より重要です。ご注文はこちら
7. 注文成功(チェックアウト成功)
通常の状況ではこのプロセスに問題はありません。ただし、ステップ 5 からステップ 6 までのプロセスでは、ユーザーが支払いが成功したと考えて Web ページを直接閉じたり、ネットワーク上の理由によりユーザーが正常に checkout_process ページにジャンプできない可能性があります。注文を正常に作成できないため、これは非常に深刻です。
上記の分析に基づいて、私たちはプロセスを少し変更したいと考えています。つまり、注文は支払い前に作成されており、たとえ支払いがサードパーティの支払い Web サイトからジャンプバックできなくても、ユーザーが正常に支払いに失敗した場合、バックグラウンドで注文は行われません。変更されたブループリントは基本的に次のとおりです:
1. checkour_confirmation ページで注文を確認した後、直接処理して checkour_success ページに入り、そこで支払いページに入ることができます。下の図に示すように:
2. 顧客がその時点で支払いを怠った場合は、独自のバックエンドに入って過去の注文の支払いを行うこともできます。下の図に示すように:
上記の関数を実装する方法を段階的に見てみましょう。
1. まず、既存の支払いモジュールを変換する必要があります。支払い用のページ URL を表すフィールド paynow_action_url を支払いメソッド クラスに追加する必要があります。さらに、支払いフォームのパラメータ隠しフィールド コードを取得するために関数 paynow_button($order_id) を追加する必要があります。
paynow_action_url フィールドを追加するには、支払いクラスのコンストラクターの最後に次のコードを追加してください:
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; }
paynow_button($order_id) 関数を追加するには、支払いクラスの最後の関数の後に次のコードを追加してください。
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. PayPal 支払い方法を例として、その実装方法を説明します。 paypal の元のコードを破壊しないように、paypal.php ファイルのコピーを作成し、paypalsimple.php という名前を付け、内部のコードに適切な変更を加えます。コードは以下のとおりです。ここでは、form_action_url の指定が削除され、paynow_action_url が指定されていることがわかります。これは、ユーザーが「注文の確認」をクリックした後に直接 checkout_process に入ってほしいためです。そのため、form_action_url が指定されていない場合は、注文を確認するためのフォームは checkout_process ページに直接送信され、paynow_action_url は前の form_action_url の値です。 paynow_button 関数の実装も非常に簡単です。ここでは、元の process_button() 関数の内容を切り取っているだけですが、グローバル $order 変数を使用せず、$order = new order($order_id) を使用して再実行します。過去の注文で今すぐ支払うボタンを表示する準備として構築されたオブジェクト。
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. checkout_success ページに [今すぐ支払う] ボタンを表示します。ファイル「includes/modules/pages/checkout_success/header.php」を開き、ファイルの最後に次のコードを追加します (zen-cart のノーティファイア/オブザーバー モードをマスターしていて、zen-cart の機能を中断したくない場合)カート コア コード その場合、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']);
「includes/modules/templates/template_default/templates/tpl_checkout_success_default.php」ファイルを開き、ここでは注文のステータスのみを判断します。注文は未払いです ボタンはステータスが
の場合にのみ表示されます<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中文网!