电商系统结账页面搭建与订单存储实现
在电商系统开发中,结账页面的创建和订单的有效存储是至关重要的环节。下面将详细介绍如何创建结账页面,以及如何对数据库进行修改以存储与客户账户相关联的订单。
1. 创建结账页面
结账页面与购物车页面相似,会显示已订购的商品,同时还会展示如 shipping address 和 credit card type 等信息。若访客没有记录 shipping 和 credit card 数据,结账按钮将被禁用。
1.1 实现 checkout_info 组件化模板
以下是具体的操作步骤:
1. 在
presentation/templates
文件夹中创建
checkout_info.tpl
文件,并添加以下代码:
{* checkout_info.tpl *}
{load_presentation_object filename="checkout_info" assign="obj"}
<form method="post" action="{$obj->mLinkToCheckout}">
<h2>Your order consists of the following items:</h2>
<table class="tss-table">
<tr>
<th>Product Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
{section name=i loop=$obj->mCartItems}
<tr>
<td>{$obj->mCartItems[i].name} ({$obj->mCartItems[i].attributes})</td>
<td>{$obj->mCartItems[i].price}</td>
<td>{$obj->mCartItems[i].quantity}</td>
<td>{$obj->mCartItems[i].subtotal}</td>
</tr>
{/section}
</table>
<p>Total amount: <font class="price">${$obj->mTotalAmount}</font></p>
{if $obj->mNoCreditCard == 'yes'}
<p class="error">No credit card details stored.</p>
{else}
<p>{$obj->mCreditCardNote}</p>
{/if}
{if $obj->mNoShippingAddress == 'yes'}
<p class="error">Shipping address required to place order.</p>
{else}
<p>
Shipping address: <br />
{$obj->mCustomerData.address_1}<br />
{if $obj->mCustomerData.address_2}
{$obj->mCustomerData.address_2}<br />
{/if}
{$obj->mCustomerData.city}<br />
{$obj->mCustomerData.region}<br />
{$obj->mCustomerData.postal_code}<br />
{$obj->mCustomerData.country}<br /><br />
Shipping region: {$obj->mShippingRegion}
</p>
{/if}
<input type="submit" name="place_order" value="Place Order"
{$obj->mOrderButtonVisible} /> |
<a href="{$obj->mLinkToCart}">Edit Shopping Cart</a> |
<a href="{$obj->mLinkToContinueShopping}">Continue Shopping</a>
</form>
-
创建
presentation/checkout_info.php文件,并添加以下代码:
<?php
// Class that supports the checkout page
class CheckoutInfo
{
// Public attributes
public $mCartItems;
public $mTotalAmount;
public $mCreditCardNote;
public $mOrderButtonVisible;
public $mNoShippingAddress = 'no';
public $mNoCreditCard = 'no';
public $mPlainCreditCard;
public $mShippingRegion;
public $mLinkToCheckout;
public $mLinkToCart;
public $mLinkToContinueShopping;
// Class constructor
public function __construct()
{
$this->mLinkToCheckout = Link::ToCheckout();
$this->mLinkToCart = Link::ToCart();
$this->mLinkToContinueShopping = $_SESSION['link_to_last_page_loaded'];
}
public function init()
{
// Set members for use in the Smarty template
$this->mCartItems = ShoppingCart::GetCartProducts(GET_CART_PRODUCTS);
$this->mTotalAmount = ShoppingCart::GetTotalAmount();
$this->mCustomerData = Customer::Get();
// If the Place Order button was clicked, save the order to database ...
if(isset ($_POST['place_order']))
{
// Create the order and get the order ID
$order_id = ShoppingCart::CreateOrder();
// This will contain the PayPal link
$redirect =
PAYPAL_URL . '&item_name=TShirtShop Order ' .
urlencode('#') . $order_id .
'&item_number=' . $order_id .
'&amount=' . $this->mTotalAmount .
'¤cy_code=' . PAYPAL_CURRENCY_CODE .
'&return=' . PAYPAL_RETURN_URL .
'&cancel_return=' . PAYPAL_CANCEL_RETURN_URL;
// Redirection to the payment page
header('Location: ' . $redirect);
exit();
}
// We allow placing orders only if we have complete customer details
if (empty ($this->mCustomerData['credit_card']))
{
$this->mOrderButtonVisible = 'disabled="disabled"';
$this->mNoCreditCard = 'yes';
}
else
{
$this->mPlainCreditCard = Customer::DecryptCreditCard(
$this->mCustomerData['credit_card']);
$this->mCreditCardNote = 'Credit card to use: ' .
$this->mPlainCreditCard['card_type'] .
'<br />Card number: ' .
$this->mPlainCreditCard['card_number_x'];
}
if (empty ($this->mCustomerData['address_1']))
{
$this->mOrderButtonVisible = 'disabled="disabled"';
$this->mNoShippingAddress = 'yes';
}
else
{
$shipping_regions = Customer::GetShippingRegions();
foreach ($shipping_regions as $item)
if ($item['shipping_region_id'] ==
$this->mCustomerData['shipping_region_id'])
$this->mShippingRegion = $item['shipping_region'];
}
}
}
?>
-
在
presentation/templates文件夹中创建checkout_not_logged.tpl文件,并添加以下代码:
{* checkout_not_logged.tpl *}
<h3>
You must be logged in to CHECKOUT <br />
If you don't have an account please register <br />
</h3>
-
打开
presentation/link.php文件,在Link类的末尾添加以下方法:
// Creates a link to the checkout page
public static function ToCheckout()
{
return self::Build('checkout/', 'https');
}
-
在同一文件中,修改
Link类的CheckRequest()方法,使其不验证结账请求:
if (isset ($_GET['Search']) || isset($_GET['SearchResults']) ||
isset ($_GET['CartAction']) || isset ($_GET['AjaxRequest']) ||
isset ($_POST['Login']) || isset ($_GET['Logout']) ||
isset ($_GET['RegisterCustomer']) ||
isset ($_GET['AddressDetails']) ||
isset ($_GET['CreditCardDetails']) ||
isset ($_GET['AccountDetails']) || isset ($_GET['Checkout']))
{
return ;
}
// Obtain proper URL for category pages
elseif (isset ($_GET['DepartmentId']) && isset ($_GET['CategoryId']))
-
打开项目根文件夹中的
.htaccess文件,添加以下重写规则:
# Rewrite account details pages
RewriteRule ^account-details/?$ index.php?AccountDetails [L]
# Rewrite checkout pages
RewriteRule ^checkout/?$ index.php?Checkout [L]
</IfModule>
# Set the default 500 page for Apache errors
ErrorDocument 500 /tshirtshop/500.php
-
打开
presentation/store_front.php文件,修改StoreFront类的init()方法,正确构建 “continue shopping” 链接:
// Build the "continue shopping" link
if (!isset ($_GET['CartAction']) && !isset($_GET['Logout']) &&
!isset($_GET['RegisterCustomer']) &&
!isset($_GET['AddressDetails']) &&
!isset($_GET['CreditCardDetails']) &&
!isset($_GET['AccountDetails']) &&
!isset($_GET['Checkout']))
$_SESSION['link_to_last_page_loaded'] = $_SESSION['link_to_store_front'];
// Build the "cancel" link for customer details pages
if (!isset($_GET['Logout']) &&
!isset($_GET['RegisterCustomer']) &&
!isset($_GET['AddressDetails']) &&
!isset($_GET['CreditCardDetails']) &&
!isset($_GET['AccountDetails']))
$_SESSION['customer_cancel_link'] = $_SESSION['link_to_store_front'];
// Load department details if visiting a department
if (isset ($_GET['DepartmentId']))
{
$this->mContentsCell = 'department.tpl';
$this->mCategoriesCell = 'categories_list.tpl';
}
-
在同一文件中,为
StoreFront类添加新成员:
// Define the template file for the cart summary cell
public $mCartSummaryCell = 'blank.tpl';
// Define the template file for the login or logged cell
public $mLoginOrLoggedCell = 'customer_login.tpl';
// Controls the visibility of the shop navigation (departments, etc)
public $mHideBoxes = false;
// Page title
public $mPageTitle;
// PayPal continue shopping link
public $mPayPalContinueShoppingLink;
-
继续修改
StoreFront类的init()方法,添加以下代码:
if (isset ($_GET['RegisterCustomer']) ||
isset ($_GET['AccountDetails']))
$this->mContentsCell = 'customer_details.tpl';
elseif (isset ($_GET['AddressDetails']))
$this->mContentsCell = 'customer_address.tpl';
elseif (isset ($_GET['CreditCardDetails']))
$this->mContentsCell = 'customer_credit_card.tpl';
if (isset ($_GET['Checkout']))
{
if (Customer::IsAuthenticated())
$this->mContentsCell = 'checkout_info.tpl';
else
$this->mContentsCell = 'checkout_not_logged.tpl';
$this->mHideBoxes = true;
}
// Load the page title
$this->mPageTitle = $this->_GetPageTitle();
}
-
修改
presentation/templates/store_front.tpl文件,在显示结账页面时,仅在左侧显示登录或已登录框:
<div class="yui-b">
{include file=$obj->mLoginOrLoggedCell}
{if !$obj->mHideBoxes}
{include file="search_box.tpl"}
{include file="departments_list.tpl"}
{include file=$obj->mCategoriesCell}
{include file=$obj->mCartSummaryCell}
{/if}
</div>
</div>
</div>
</body>
</html>
-
修改
presentation/templates/cart_details.tpl文件,将用户重定向到checkout_info页面,而不是 PayPal:
...
<table class="cart-subtotal">
<tr>
<td>
<p>
Total amount:
<font class="price">${$obj->mTotalAmount}</font>
</p>
</td>
<td align="right">
<input type="submit" name="update" value="Update" />
</td>
{if $obj->mShowCheckoutLink}
<td align="right">
<a href="{$obj->mLinkToCheckout}">Checkout</a>
</td>
{/if}
</tr>
</table>
</form>
{/if}
...
-
修改
presentation/cart_details.php文件中的CartDetails类,添加两个新的公共成员:
...
public $mRecommendations;
public $mLinkToCheckout;
public $mShowCheckoutLink = false;
// Private attributes
private $_mItemId;
...
-
更新
CartDetails类的init()方法,删除 PayPal 代码,并添加以下代码:
...
/* Calculate the total amount for the shopping cart
before applicable taxes and/or shipping */
$this->mTotalAmount = ShoppingCart::GetTotalAmount();
// Display Checkout link in the shopping cart
if ($this->mTotalAmount != 0 && Customer::IsAuthenticated())
{
$this->mLinkToCheckout = Link::ToCheckout();
$this->mShowCheckoutLink = true;
}
// Get shopping cart products
$this->mCartProducts =
ShoppingCart::GetCartProducts(GET_CART_PRODUCTS);
...
完成以上步骤后,登录网站,向购物车添加一些商品,然后点击购物车页面上的 “Checkout” 链接,即可看到效果。
1.2 checkout_info 组件化模板的工作原理
在
CheckoutInfo
类的
init()
方法中,首先检查客户是否点击了 “Place Order” 按钮。如果点击了,将订单保存到数据库,并将客户重定向到支付页面:
// If the Place Order button was clicked, save the order to database ...
if(isset ($_POST['place_order']))
{
// Create the order and get the order ID
$order_id = ShoppingCart::CreateOrder();
// This will contain the PayPal link
$redirect =
PAYPAL_URL . '&item_name=TShirtShop Order ' .
urlencode('#') . $order_id .
'&item_number=' . $order_id .
'&amount=' . $this->mTotalAmount .
'¤cy_code=' . PAYPAL_CURRENCY_CODE .
'&return=' . PAYPAL_RETURN_URL .
'&cancel_return=' . PAYPAL_CANCEL_RETURN_URL;
// Redirection to the payment page
header('Location: ' . $redirect);
exit();
}
同时,还会设置一些供 Smarty 模板使用的变量:
// Set members for use in the Smarty template
$this->mCartItems = ShoppingCart::GetCartProducts(GET_CART_PRODUCTS);
$this->mTotalAmount = ShoppingCart::GetTotalAmount();
$this->mCustomerData = Customer::Get();
若客户未输入 credit card 信息或 shipping address,将显示通知,并禁用 “Place Order” 按钮。若存在 credit card 信息,则会解密并准备显示 credit card type 和卡号的后四位:
// We allow placing orders only if we have complete customer details
if (empty ($this->mCustomerData['credit_card']))
{
$this->mOrderButtonVisible = 'disabled="disabled"';
$this->mNoCreditCard = 'yes';
}
else
{
$this->mPlainCreditCard = Customer::DecryptCreditCard(
$this->mCustomerData['credit_card']);
$this->mCreditCardNote = 'Credit card to use: ' .
$this->mPlainCreditCard['card_type'] .
'<br />Card number: ' .
$this->mPlainCreditCard['card_number_x'];
}
if (empty ($this->mCustomerData['address_1']))
{
$this->mOrderButtonVisible = 'disabled="disabled"';
$this->mNoShippingAddress = 'yes';
}
2. 强制使用 SSL 连接
在构建目录管理页面时,使用 SSL 来保护服务器和客户端浏览器之间传输的数据是很有必要的。现在客户会发送极其敏感的数据,使用 SSL 不再是可选项。
以下是具体的实现步骤:
1. 在
presentation/store_front.php
文件的
StoreFront
类末尾添加以下方法:
// Visiting a sensitive page?
private function _IsSensitivePage()
{
if (isset($_GET['RegisterCustomer']) ||
isset($_GET['AccountDetails']) ||
isset($_GET['CreditCardDetails']) ||
isset($_GET['AddressDetails']) ||
isset($_GET['Checkout']) ||
isset($_POST['Login']))
return true;
return false;
}
-
在
StoreFront类的__constructor()方法中添加以下代码:
// Class constructor
public function __construct()
{
$is_https = false;
// Is the page being accessed through an HTTPS connection?
if (getenv('HTTPS') == 'on')
$is_https = true;
// Use HTTPS when accessing sensitive pages
if ($this->_IsSensitivePage() && $is_https == false &&
USE_SSL != 'no')
{
$redirect_to =
Link::Build(str_replace(VIRTUAL_LOCATION, '', getenv('REQUEST_URI')),
'https');
header ('Location: '. $redirect_to);
exit();
}
// Don't use HTTPS for nonsensitive pages
if (!$this->_IsSensitivePage() && $is_https == true)
{
$redirect_to =
Link::Build(str_replace(VIRTUAL_LOCATION, '', getenv('REQUEST_URI')));
header ('Location: '. $redirect_to);
exit();
}
$this->mSiteUrl = Link::Build('');
}
通过以上设置,当有人尝试访问如
http://localhost/tshirtshop/credit-card-details/
这样的敏感页面时,会自动重定向到
https://localhost/tshirtshop/credit-card-details/
。
3. 存储客户订单
目前的订单跟踪系统没有将订单与下单客户的账户关联起来。接下来将对数据库进行修改,使客户能够通过其账户下单,并将订单与客户账户关联起来。
3.1 为客户账户添加订单
要实现客户通过账户下单,需要对当前的下单机制进行多项修改。具体操作如下:
1. 加载
phpMyAdmin
,选择
tshirtshop
数据库,打开新的 SQL 查询页面。
2. 使用查询页面执行以下代码,删除
order_detail
和
orders
表中的数据:
-- Delete all records from order_detail table
TRUNCATE TABLE order_detail;
-- Delete all records from orders table
TRUNCATE TABLE orders;
-
从
orders表中删除customer_name、shipping_address和customer_email字段:
-- Drop customer_name, shipping_address and customer_email fields
-- from the orders table
ALTER TABLE `orders` DROP COLUMN `customer_name`,
DROP COLUMN `shipping_address`,
DROP COLUMN `customer_email`;
-
添加新字段
customer_id、auth_code和reference,并在customer_id字段上添加新索引:
-- Adding the three new fields: customer_id, auth_code and reference.
ALTER TABLE `orders` ADD COLUMN `customer_id` INT,
ADD COLUMN `auth_code` VARCHAR(50),
ADD COLUMN `reference` VARCHAR(50);
-- Adding a new index to orders table
CREATE INDEX `idx_orders_customer_id` ON `orders` (`customer_id`);
-
删除旧的
shopping_cart_create_order存储过程,并创建新的存储过程:
-- Drop shopping_cart_create_order stored procedure
DROP PROCEDURE shopping_cart_create_order$$
-- Create shopping_cart_create_order stored procedure
CREATE PROCEDURE shopping_cart_create_order(IN inCartId CHAR(32),
IN inCustomerId INT)
BEGIN
DECLARE orderId INT;
-- Insert a new record into orders and obtain the new order ID
INSERT INTO orders (created_on, customer_id) VALUES (NOW(), inCustomerId);
-- Obtain the new Order ID
SELECT LAST_INSERT_ID() INTO orderId;
-- Insert order details in order_detail table
INSERT INTO order_detail (order_id, product_id, attributes,
product_name, quantity, unit_cost)
SELECT orderId, p.product_id, sc.attributes, p.name, sc.quantity,
COALESCE(NULLIF(p.discounted_price, 0), p.price) AS unit_cost
FROM shopping_cart sc
INNER JOIN product p
ON sc.product_id = p.product_id
WHERE sc.cart_id = inCartId AND sc.buy_now;
-- Save the order's total amount
UPDATE orders
SET total_amount = (SELECT SUM(unit_cost * quantity)
FROM order_detail
WHERE order_id = orderId)
WHERE order_id = orderId;
-- Clear the shopping cart
CALL shopping_cart_empty(inCartId);
-- Return the Order ID
SELECT orderId;
END$$
-
修改
business/shopping_cart.php文件中ShoppingCart类的CreateOrder()方法:
// Create a new order
public static function CreateOrder($customerId)
{
// Build SQL query
$sql = 'CALL shopping_cart_create_order(:cart_id, :customer_id)';
// Build the parameters array
$params = array (':cart_id' => self::GetCartId(),
':customer_id' => $customerId);
// Execute the query and return the results
return DatabaseHandler::GetOne($sql, $params);
}
-
修改
presentation/checkout_info.php文件中的init()方法:
public function init()
{
// Set members for use in the Smarty template
$this->mCartItems = ShoppingCart::GetCartProducts(GET_CART_PRODUCTS);
$this->mTotalAmount = ShoppingCart::GetTotalAmount();
$this->mCustomerData = Customer::Get();
// If the Place Order button was clicked, save the order to database ...
if(isset ($_POST['place_order']))
{
// Create the order and get the order ID
$order_id = ShoppingCart::CreateOrder(Customer::GetCurrentCustomerId());
// This will contain the PayPal link
$redirect =
PAYPAL_URL . '&item_name=TShirtShop Order ' .
urlencode('#') . $order_id .
'&item_number=' . $order_id .
'&amount=' . $this->mTotalAmount .
'¤cy_code=' . PAYPAL_CURRENCY_CODE .
'&return=' . PAYPAL_RETURN_URL .
'&cancel_return=' . PAYPAL_CANCEL_RETURN_URL;
}
}
完成以上操作后,登录网站,使用新系统下一两个订单,以验证代码是否正常工作。
通过以上步骤,我们完成了结账页面的创建和订单与客户账户的关联,为电商系统的进一步完善奠定了基础。在后续的开发中,可以基于这些功能实现更复杂的订单系统和信用卡交易功能。
电商系统结账页面搭建与订单存储实现
4. 订单管理部分的修改
为了使订单管理部分能够集成新的功能,我们需要对其进行相应的修改。虽然文档中未详细提及具体的修改内容,但我们可以推测,可能需要对订单管理页面的展示、查询和操作等功能进行更新,以适应新的订单关联机制。
5. 增加税费和运费
在电商系统中,处理税费和运费是非常重要的一部分。虽然文档中提到有多种实现方式,但这里仅介绍一种简单的方法,并为进一步的开发奠定基础。
5.1 简单的税费和运费处理思路
- 税费计算 :可以根据商品的总价和预设的税率来计算税费。例如,假设税率为 10%,商品总价为 $100,则税费为 $100 * 0.1 = $10。
- 运费计算 :可以根据商品的重量、体积或目的地等因素来计算运费。例如,假设每件商品的运费为 $5,购买了 3 件商品,则运费为 $5 * 3 = $15。
5.2 代码实现示例
以下是一个简单的 PHP 代码示例,用于计算税费和运费:
<?php
// 商品总价
$totalAmount = 100;
// 税率
$taxRate = 0.1;
// 每件商品的运费
$shippingFeePerItem = 5;
// 购买的商品数量
$quantity = 3;
// 计算税费
$tax = $totalAmount * $taxRate;
// 计算运费
$shippingFee = $shippingFeePerItem * $quantity;
// 总费用(商品总价 + 税费 + 运费)
$totalCost = $totalAmount + $tax + $shippingFee;
echo "商品总价: $" . $totalAmount . "<br>";
echo "税费: $" . $tax . "<br>";
echo "运费: $" . $shippingFee . "<br>";
echo "总费用: $" . $totalCost . "<br>";
?>
6. 总结与展望
通过以上步骤,我们完成了电商系统中结账页面的创建、订单与客户账户的关联、订单管理部分的修改以及税费和运费的处理。这些功能的实现为电商系统的进一步完善奠定了坚实的基础。
在未来的开发中,可以基于这些功能实现更复杂的订单系统,例如订单状态跟踪、订单取消和退款等功能。同时,还可以实现更安全和便捷的信用卡交易功能,提高用户体验。
以下是整个电商系统订单处理流程的 mermaid 流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([用户登录]):::startend --> B(浏览商品):::process
B --> C{是否添加商品到购物车}:::decision
C -- 是 --> D(添加商品到购物车):::process
C -- 否 --> B
D --> E{是否点击结账}:::decision
E -- 是 --> F(显示结账页面):::process
E -- 否 --> D
F --> G{是否输入完整信息}:::decision
G -- 是 --> H(点击下单):::process
G -- 否 --> I(显示错误信息):::process
I --> F
H --> J(创建订单并关联客户账户):::process
J --> K(计算税费和运费):::process
K --> L(生成支付链接):::process
L --> M(跳转到支付页面):::process
M --> N{支付是否成功}:::decision
N -- 是 --> O(订单处理完成):::process
N -- 否 --> P(显示支付失败信息):::process
P --> F
O --> Q([结束]):::startend
通过这个流程图,我们可以清晰地看到用户从登录到完成订单支付的整个流程,以及各个环节之间的逻辑关系。
综上所述,电商系统的开发是一个不断完善和优化的过程。通过逐步实现各个功能模块,并不断进行测试和改进,可以打造出一个功能强大、用户体验良好的电商平台。
超级会员免费看

被折叠的 条评论
为什么被折叠?



