39、电商系统结账页面搭建与订单存储实现

电商系统结账页面搭建与订单存储实现

在电商系统开发中,结账页面的创建和订单的有效存储是至关重要的环节。下面将详细介绍如何创建结账页面,以及如何对数据库进行修改以存储与客户账户相关联的订单。

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 />
&nbsp;{$obj->mCustomerData.address_1}<br />
{if $obj->mCustomerData.address_2}
&nbsp;{$obj->mCustomerData.address_2}<br />
{/if}
&nbsp;{$obj->mCustomerData.city}<br />
&nbsp;{$obj->mCustomerData.region}<br />
&nbsp;{$obj->mCustomerData.postal_code}<br />
&nbsp;{$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>
  1. 创建 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 .
'&currency_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'];
}
}
}
?>
  1. 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>
  1. 打开 presentation/link.php 文件,在 Link 类的末尾添加以下方法:
// Creates a link to the checkout page
public static function ToCheckout()
{
return self::Build('checkout/', 'https');
}
  1. 在同一文件中,修改 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']))
  1. 打开项目根文件夹中的 .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
  1. 打开 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';
}
  1. 在同一文件中,为 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;
  1. 继续修改 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();
}
  1. 修改 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>
  1. 修改 presentation/templates/cart_details.tpl 文件,将用户重定向到 checkout_info 页面,而不是 PayPal:
...
<table class="cart-subtotal">
<tr>
<td>
<p>
Total amount:&nbsp;
<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}
...
  1. 修改 presentation/cart_details.php 文件中的 CartDetails 类,添加两个新的公共成员:
...
public $mRecommendations;
public $mLinkToCheckout;
public $mShowCheckoutLink = false;
// Private attributes
private $_mItemId;
...
  1. 更新 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 .
'&currency_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;
}
  1. 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;
  1. 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`;
  1. 添加新字段 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`);
  1. 删除旧的 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$$
  1. 修改 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);
}
  1. 修改 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 .
'&currency_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

通过这个流程图,我们可以清晰地看到用户从登录到完成订单支付的整个流程,以及各个环节之间的逻辑关系。

综上所述,电商系统的开发是一个不断完善和优化的过程。通过逐步实现各个功能模块,并不断进行测试和改进,可以打造出一个功能强大、用户体验良好的电商平台。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值