get_price());
$sub_price = $cfg['price'] > 0 ? $cfg['price'] : $regular_price;
$discount = ($regular_price > 0 && $sub_price < $regular_price) ? (1 - $sub_price / $regular_price) * 100.0 : 0.0;
// 简单模板输出(不使用专门模板文件,保持轻量)
echo '
';
echo '
' . esc_html__('订阅选项', 'yoone-subscriptions') . '
';
// 一次性 vs 订阅
if ($cfg['allow_onetime']) {
echo '
';
echo '
';
} else {
echo '
';
}
// 周期选择
echo '
'
. ' '
. ''
. '
';
// 数量输入
echo '
'
. ' '
. ''
. '
';
// 价格与折扣展示(每周期价格)
echo '
'
. esc_html__('订阅价格(每周期):', 'yoone-subscriptions')
. wc_price($sub_price)
. ($discount > 0 ? ' ' . sprintf(esc_html__('折扣约 %.1f%%', 'yoone-subscriptions'), $discount) . '' : '')
. '
';
echo '
';
}
/**
* 校验加入购物车的订阅参数。
*/
public function validate_add_to_cart($passed, $product_id, $quantity) {
$product = wc_get_product($product_id);
if (! $product) return $passed;
$cfg = Yoone_Subscriptions::get_config($product);
if (! $cfg['enabled']) return $passed;
$mode = isset($_POST['yoone_sub_purchase_mode']) ? sanitize_text_field($_POST['yoone_sub_purchase_mode']) : '';
if ($cfg['allow_onetime'] && $mode === 'onetime') return $passed; // 一次性购买,无需校验订阅参数
$period = isset($_POST['yoone_sub_period']) ? sanitize_text_field($_POST['yoone_sub_period']) : $cfg['period'];
$qty = isset($_POST['yoone_sub_quantity']) ? absint($_POST['yoone_sub_quantity']) : $cfg['qty_default'];
$period = in_array($period, array('month','year'), true) ? $period : $cfg['period'];
$qty = max(1, $qty);
if ($mode !== 'subscribe' && ! $cfg['allow_onetime']) {
wc_add_notice(__('该产品仅支持订阅购买。', 'yoone-subscriptions'), 'error');
return false;
}
// 可根据需要扩展更多校验(库存、最大值等)
return $passed;
}
/**
* 将订阅参数写入购物车项。
*/
public function add_cart_item_data($cart_item_data, $product_id, $variation_id) {
$product = wc_get_product($product_id);
if (! $product) return $cart_item_data;
$cfg = Yoone_Subscriptions::get_config($product);
if (! $cfg['enabled']) return $cart_item_data;
// 若是混装子项目(存在父容器ID),不在子项目上应用订阅,避免重复计价
if (isset($cart_item_data['yoone_bundle_parent_id'])) {
return $cart_item_data;
}
$mode = isset($_POST['yoone_sub_purchase_mode']) ? sanitize_text_field($_POST['yoone_sub_purchase_mode']) : '';
$period = isset($_POST['yoone_sub_period']) ? sanitize_text_field($_POST['yoone_sub_period']) : $cfg['period'];
$qty = isset($_POST['yoone_sub_quantity']) ? absint($_POST['yoone_sub_quantity']) : $cfg['qty_default'];
$period = in_array($period, array('month','year'), true) ? $period : $cfg['period'];
$qty = max(1, $qty);
// 如果选择一次性购买,则不写入订阅元数据,购物车中不显示任何订阅信息
if ($cfg['allow_onetime'] && $mode === 'onetime') {
return $cart_item_data;
}
$cart_item_data['yoone_subscriptions'] = array(
'mode' => 'subscribe',
'period' => $period,
'qty' => $qty,
'price' => ($cfg['price'] > 0 ? $cfg['price'] : floatval($product->get_price())), // 每周期价格;对于混装容器,将在后续根据子项目求和覆盖
);
return $cart_item_data;
}
/**
* 购物车/订单行项目中展示订阅摘要。
*/
public function display_item_data($item_data, $cart_item) {
if (empty($cart_item['yoone_subscriptions'])) return $item_data;
$data = $cart_item['yoone_subscriptions'];
// 仅在选择订阅购买时展示订阅信息;一次性购买不展示任何订阅相关信息
if (! isset($data['mode']) || $data['mode'] !== 'subscribe') {
return $item_data;
}
$period_label = $data['period'] === 'year' ? __('年', 'yoone-subscriptions') : __('月', 'yoone-subscriptions');
$item_data[] = array('key' => __('购买方式', 'yoone-subscriptions'), 'value' => __('订阅', 'yoone-subscriptions'));
$item_data[] = array('key' => __('周期', 'yoone-subscriptions'), 'value' => $period_label);
$item_data[] = array('key' => __('订阅数量', 'yoone-subscriptions'), 'value' => intval($data['qty']));
$item_data[] = array('key' => __('订阅价格(每周期)', 'yoone-subscriptions'), 'value' => wc_price(floatval($data['price'])));
return $item_data;
}
/**
* 根据订阅规则设置行项目价格:价格 = 每周期价格 × 周期系数 × 订阅数量 × 购物车数量。
*/
public function adjust_subscription_price($cart) {
if (is_admin() && ! defined('DOING_AJAX')) return;
if (empty($cart) || ! method_exists($cart, 'get_cart')) return;
foreach ($cart->get_cart() as $key => $item) {
if (empty($item['yoone_subscriptions'])) continue;
$data = $item['yoone_subscriptions'];
if ($data['mode'] !== 'subscribe') continue;
$product = isset($item['data']) ? $item['data'] : null;
if (! $product || ! is_a($product, 'WC_Product')) continue;
$price_per_cycle = floatval($data['price']);
$factor = Yoone_Subscriptions::period_factor($data['period']);
$sub_qty = absint($data['qty']);
$cart_qty = absint(isset($item['quantity']) ? $item['quantity'] : 1);
// 若为混装容器,则以其子项目的价格汇总作为每周期基价(若有配置价则优先使用配置价)
if (isset($item['yoone_bundle_container_id'])) {
$container_id = $item['yoone_bundle_container_id'];
// 使用配置价优先,否则求和子项目价
$cfg = Yoone_Subscriptions::get_config($product);
if (!($cfg['price'] > 0)) {
$sum = 0.0;
foreach ($cart->get_cart() as $k2 => $child) {
if (isset($child['yoone_bundle_parent_id']) && $child['yoone_bundle_parent_id'] === $container_id) {
$child_product = isset($child['data']) ? $child['data'] : null;
if ($child_product && is_a($child_product, 'WC_Product')) {
$child_qty = absint(isset($child['quantity']) ? $child['quantity'] : 1);
$sum += floatval($child_product->get_price()) * $child_qty;
}
}
}
$price_per_cycle = $sum; // 以子项目合计为每周期基价
} else {
$price_per_cycle = floatval($cfg['price']);
}
// 将子项目的价格置为 0,避免重复计费
foreach ($cart->get_cart() as $k2 => $child) {
if (isset($child['yoone_bundle_parent_id']) && $child['yoone_bundle_parent_id'] === $container_id) {
$child_product = isset($child['data']) ? $child['data'] : null;
if ($child_product && is_a($child_product, 'WC_Product')) {
$child_product->set_price(0);
}
}
}
}
$line_price = $price_per_cycle * $factor * $sub_qty * $cart_qty;
$product->set_price($line_price);
}
}
}