<?php defined('ABSPATH') || exit;
/**
* 健壮的部分结算系统 - 带持久化购物车快照
* - 结算前创建完整购物车快照
* - 订单创建成功后才移除商品
* - 感谢页面重建购物车(快照 - 已购买商品)
* - 取消/返回时恢复完整快照
* - 访客支持:localStorage + AJAX 重新水合
*/
/* -------------------------------------------------
* 辅助函数
* ------------------------------------------------- */
/**
* 购物车数量AJAX端点
*/
add_action('wp_ajax_get_cart_count', 'pc_get_cart_count');
add_action('wp_ajax_nopriv_get_cart_count', 'pc_get_cart_count');
function pc_get_cart_count() {
check_ajax_referer('woocommerce-cart', 'security');
$count = WC()->cart->get_cart_contents_count();
wp_send_json_success(array('count' => $count));
}
function pc_get_cart_uid() {
if (is_user_logged_in()) {
return 'user_' . get_current_user_id();
}
if (empty($_COOKIE['pc_cart_uid'])) {
$token = wp_generate_uuid4();
setcookie('pc_cart_uid', $token, time() + YEAR_IN_SECONDS, COOKIEPATH ?: '/', '', is_ssl(), false);
$_COOKIE['pc_cart_uid'] = $token;
}
return 'guest_' . sanitize_text_field(wp_unslash($_COOKIE['pc_cart_uid']));
}
function pc_build_item_key($product_id, $variation_id = 0) {
return (int)$product_id . '|' . (int)$variation_id;
}
function pc_snapshot_current_cart() {
if (!isset(WC()->cart)) {
wc_load_cart();
}
$items = array();
foreach (WC()->cart->get_cart() as $ci_key => $ci) {
$pid = isset($ci['product_id']) ? (int)$ci['product_id'] : 0;
$vid = isset($ci['variation_id']) ? (int)$ci['variation_id'] : 0;
$qty = isset($ci['quantity']) ? wc_stock_amount($ci['quantity']) : 0;
$var = isset($ci['variation']) && is_array($ci['variation']) ? $ci['variation'] : array();
if ($pid && $qty > 0) {
$items[] = array(
'product_id' => $pid,
'variation_id' => $vid,
'variation' => array_map('wc_clean', $var),
'quantity' => $qty,
);
}
}
return $items;
}
function pc_restore_cart_from_items($items) {
if (!isset(WC()->cart)) {
wc_load_cart();
}
WC()->cart->empty_cart();
foreach ((array)$items as $it) {
$pid = isset($it['product_id']) ? (int)$it['product_id'] : 0;
$vid = isset($it['variation_id']) ? (int)$it['variation_id'] : 0;
$qty = isset($it['quantity']) ? wc_stock_amount($it['quantity']) : 0;
$var = isset($it['variation']) && is_array($it['variation']) ? array_map('wc_clean', $it['variation']) : array();
if ($pid && $qty > 0) {
WC()->cart->add_to_cart($pid, $qty, $vid, $var);
}
}
WC()->cart->calculate_totals();
}
// 完全重构的购物车恢复逻辑 - 确保WC会话存在
add_action('wp_loaded', function() {
// 严格检查WC会话可用性
$wc = function_exists('WC') ? WC() : null;
if (!$wc) return;
$session = property_exists($wc, 'session') ? $wc->session : null;
if (!$session) return;
$token = method_exists($session, 'get')
? $session->get('pc_partial_token')
: null;
if (!$token) return;
// 检查购物车是否为空
$cart = property_exists($wc, 'cart') ? $wc->cart : null;
if (!$cart) return;
if ($cart->is_empty()) {
$payload = get_transient(pc_transient_key($token));
if (!empty($payload['snapshot'])) {
pc_restore_cart_from_items($payload['snapshot']);
$cart->calculate_totals();
}
}
}, 20);
function pc_transient_key($token) {
return 'pc_partial_payload_' . sanitize_key($token);
}
/* -------------------------------------------------
* AJAX: 当Woo购物车为空时本地重新水合
* ------------------------------------------------- */
add_action('wp_ajax_pc_rehydrate_cart', 'pc_rehydrate_cart');
add_action('wp_ajax_nopriv_pc_rehydrate_cart', 'pc_rehydrate_cart');
function pc_rehydrate_cart() {
check_ajax_referer('woocommerce-cart', 'security');
$raw = isset($_POST['items']) ? wp_unslash($_POST['items']) : '';
$items = is_string($raw) ? json_decode($raw, true) : (array)$raw;
if (!is_array($items)) {
wp_send_json_error(array('message' => '无效的商品数据'), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
if (!WC()->cart->is_empty()) {
wp_send_json_success(array('message' => '购物车非空'));
}
foreach ($items as $it) {
$pid = isset($it['product_id']) ? (int)$it['product_id'] : 0;
$vid = isset($it['variation_id']) ? (int)$it['variation_id'] : 0;
$qty = isset($it['quantity']) ? wc_stock_amount($it['quantity']) : 0;
$var = isset($it['variation']) && is_array($it['variation']) ? array_map('wc_clean', $it['variation']) : array();
if ($pid && $qty > 0) {
WC()->cart->add_to_cart($pid, $qty, $vid, $var);
}
}
WC()->cart->calculate_totals();
wp_send_json_success(array('rehydrated' => true));
}
/* -------------------------------------------------
* AJAX: 更新购物车商品数量(无需刷新页面)
* ------------------------------------------------- */
add_action('wp_ajax_update_cart_item_qty', 'pc_update_cart_item_qty');
add_action('wp_ajax_nopriv_update_cart_item_qty', 'pc_update_cart_item_qty');
function pc_update_cart_item_qty() {
check_ajax_referer('woocommerce-cart', 'security');
$key = isset($_POST['cart_item_key']) ? wc_clean(wp_unslash($_POST['cart_item_key'])) : '';
$qty = isset($_POST['qty']) ? wc_stock_amount($_POST['qty']) : null;
if (!$key || $qty === null) {
wp_send_json_error(array('message' => '参数缺失'), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
if ($qty <= 0) {
$removed = WC()->cart->remove_cart_item($key);
WC()->cart->calculate_totals();
wp_send_json_success(array('removed' => (bool)$removed));
} else {
$set = WC()->cart->set_quantity($key, $qty, true);
WC()->cart->calculate_totals();
$cart_item = WC()->cart->get_cart_item($key);
if (!$cart_item) {
wp_send_json_error(array('message' => '更新后未找到购物车商品'), 404);
}
$_product = $cart_item['data'];
$subtotal_html = apply_filters(
'woocommerce_cart_item_subtotal',
WC()->cart->get_product_subtotal($_product, $cart_item['quantity']),
$cart_item,
$key
);
// 小计计算(含税)
$line_total_incl_tax = (float)($cart_item['line_total'] + $cart_item['line_tax']);
wp_send_json_success(array(
'subtotal_html' => $subtotal_html,
'line_total_incl_tax' => $line_total_incl_tax,
'removed' => false,
));
}
}
/* -------------------------------------------------
* AJAX: 删除选中商品
* ------------------------------------------------- */
add_action('wp_ajax_remove_selected_cart_items', 'pc_remove_selected_cart_items');
add_action('wp_ajax_nopriv_remove_selected_cart_items', 'pc_remove_selected_cart_items');
function pc_remove_selected_cart_items() {
check_ajax_referer('woocommerce-cart', 'security');
$keys = isset($_POST['selected_items']) ? (array) $_POST['selected_items'] : array();
if (!isset(WC()->cart)) {
wc_load_cart();
}
foreach ($keys as $k) {
$k = wc_clean(wp_unslash($k));
WC()->cart->remove_cart_item($k);
}
WC()->cart->calculate_totals();
wp_send_json_success(true);
}
/* -------------------------------------------------
* AJAX: 清空购物车
* ------------------------------------------------- */
add_action('wp_ajax_empty_cart', 'pc_empty_cart');
add_action('wp_ajax_nopriv_empty_cart', 'pc_empty_cart');
function pc_empty_cart() {
check_ajax_referer('woocommerce-cart', 'security');
if (!isset(WC()->cart)) {
wc_load_cart();
}
WC()->cart->empty_cart();
wp_send_json_success(true);
}
/* -------------------------------------------------
* AJAX: 应用优惠券
* ------------------------------------------------- */
add_action('wp_ajax_apply_coupon', 'pc_apply_coupon');
add_action('wp_ajax_nopriv_apply_coupon', 'pc_apply_coupon');
function pc_apply_coupon() {
check_ajax_referer('woocommerce-cart', 'security');
$code = isset($_POST['coupon_code']) ? wc_format_coupon_code(wp_unslash($_POST['coupon_code'])) : '';
if (!$code) {
wp_send_json_error(array('message' => __('请输入优惠券代码', 'woocommerce')), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
$applied = WC()->cart->apply_coupon($code);
WC()->cart->calculate_totals();
if (is_wp_error($applied)) {
wp_send_json_error(array('message' => $applied->get_error_message()), 400);
}
if (!$applied) {
wp_send_json_error(array('message' => __('优惠券应用失败', 'woocommerce')), 400);
}
wp_send_json_success(true);
}
/* -------------------------------------------------
* AJAX: 启动部分结算到常规结算页面
* ------------------------------------------------- */
add_action('wp_ajax_create_direct_order', 'pc_create_direct_order');
add_action('wp_ajax_nopriv_create_direct_order', 'pc_create_direct_order');
function pc_create_direct_order() {
check_ajax_referer('woocommerce-cart', 'security');
$selected_keys = isset($_POST['selected_items']) ? (array) $_POST['selected_items'] : array();
if (empty($selected_keys)) {
wp_send_json_error(array('message' => __('请选择要结算的商品', 'woocommerce')), 400);
}
if (!isset(WC()->cart)) {
wc_load_cart();
}
// 创建完整购物车快照
$snapshot = pc_snapshot_current_cart();
// 从当前购物车构建选中商品
$selected = array();
foreach (WC()->cart->get_cart() as $ci_key => $ci) {
if (!in_array($ci_key, $selected_keys, true)) {
continue;
}
$pid = (int)$ci['product_id'];
$vid = (int)$ci['variation_id'];
$qty = wc_stock_amount($ci['quantity']);
$var = isset($ci['variation']) && is_array($ci['variation']) ? array_map('wc_clean', $ci['variation']) : array();
if ($pid && $qty > 0) {
$selected[] = array(
'product_id' => $pid,
'variation_id' => $vid,
'variation' => $var,
'quantity' => $qty,
);
}
}
if (empty($selected)) {
wp_send_json_error(array('message' => __('没有可结算的商品', 'woocommerce')), 400);
}
$token = wp_generate_uuid4();
$payload = array(
'uid' => pc_get_cart_uid(),
'snapshot' => $snapshot,
'selected' => $selected,
'created' => time(),
);
set_transient(pc_transient_key($token), $payload, 2 * DAY_IN_SECONDS);
// 将会话令牌存入WooCommerce会话
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', $token);
}
$checkout_url = add_query_arg('pc_token', rawurlencode($token), wc_get_checkout_url());
wp_send_json_success(array('checkout_url' => $checkout_url));
}
/* -------------------------------------------------
* 结账时虚拟化购物车并在购买后重建
* ------------------------------------------------- */
// 加载结账页面时虚拟化购物车
add_action('woocommerce_before_checkout_form', function() {
if (!isset($_GET['pc_token'])) return;
$token = sanitize_text_field(wp_unslash($_GET['pc_token']));
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['selected'])) return;
if (!isset(WC()->cart)) {
wc_load_cart();
}
// 仅加载选中的商品
pc_restore_cart_from_items($payload['selected']);
// 持久化令牌到会话
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', $token);
}
}, 1);
// 订单处理前确保虚拟化
add_action('woocommerce_before_checkout_process', function() {
if (!isset(WC()->session) || !method_exists(WC()->session, 'get')) return;
$token = WC()->session->get('pc_partial_token');
if (!$token) return;
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['selected'])) return;
// 确保购物车仅包含选中商品
pc_restore_cart_from_items($payload['selected']);
}, 1);
// 为订单添加令牌标记
add_action('woocommerce_checkout_create_order', function($order) {
$token = null;
if (isset($_GET['pc_token'])) {
$token = sanitize_text_field(wp_unslash($_GET['pc_token']));
} elseif (isset(WC()->session) && method_exists(WC()->session, 'get')) {
$token = WC()->session->get('pc_partial_token');
}
if ($token) {
$order->update_meta_data('_pc_partial_token', $token);
$order->update_meta_data('_pc_cart_snapshot', $token);
}
}, 10, 1);
// 成功结账后仅移除已购买商品
add_action('woocommerce_thankyou', function($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
$token = $order->get_meta('_pc_partial_token');
if (!$token) return;
$payload = get_transient(pc_transient_key($token));
if (empty($payload) || empty($payload['snapshot'])) {
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', null);
}
delete_transient(pc_transient_key($token));
return;
}
// 1. 恢复完整快照
pc_restore_cart_from_items($payload['snapshot']);
WC()->cart->calculate_totals();
// 2. 只移除已购买商品
foreach ($payload['selected'] as $selected_item) {
$cart_item_key = pc_find_cart_item($selected_item['product_id'], $selected_item['variation_id']);
if ($cart_item_key) {
WC()->cart->remove_cart_item($cart_item_key);
}
}
// 3. 重新计算总计
WC()->cart->calculate_totals();
// 清理令牌
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', null);
You stopped at this part, please continue from
// 清理令牌
if (isset(WC()->session) && method_exists(WC()->session, 'set')) {
WC()->session->set('pc_partial_token', null);
最新发布