feat(shop): wire storefront buy path to server cart

Connect product options/add-cart, server cart checkout payloads,
favorite/rebuy/receipt REST, and order-center actions without
localStorage dual-read or hardcoded cart/orders URL hacks.
This commit is contained in:
kate522 committed 2026-07-12 09:15:45 +08:00
1 parent 2ed3ffe317
commit 50e3db85bb
6 files changed
+621 -304

No files matched your search

@@ -130,16 +130,37 @@ trait AKSRT_Admin_Shop_Controller_Frontend_Flow_Operations {
$items = [];
foreach ($cart as $pid => $opts_map) {
$product = $shop->build_product_card_view((int) $pid);
$product_id = (int) $pid;
$product = $shop->build_product_card_view($product_id);
if (empty($product) || !is_array($opts_map)) {
continue;
}
foreach ($opts_map as $opt_str => $qty) {
$options_string = $opt_str === '_default' ? '' : (string) $opt_str;
$unit_price = method_exists($shop, 'resolve_option_price')
? (float) $shop->resolve_option_price($product_id, $options_string !== '' ? $options_string : '_default')
: (float) ($product['price'] ?? 0);
$options_label = method_exists($shop, 'resolve_option_labels')
? (string) $shop->resolve_option_labels($product_id, $options_string !== '' ? $options_string : '_default')
: '';
$option_image = method_exists($shop, 'get_option_image')
? (string) $shop->get_option_image($product_id, $options_string !== '' ? $options_string : '_default')
: '';
$stock = method_exists($shop, 'resolve_option_stock')
? (int) $shop->resolve_option_stock($product_id, $options_string !== '' ? $options_string : '_default')
: (int) ($product['stock'] ?? 0);
$items[] = [
'product_id' => (int) $pid,
'options_string' => $opt_str === '_default' ? '' : $opt_str,
'quantity' => (int) $qty,
'product' => $product,
'product_id' => $product_id,
'options_string' => $options_string,
'options_label' => $options_label,
'quantity' => (int) $qty,
'unit_price' => $unit_price,
'line_total' => round($unit_price * (int) $qty, 2),
'stock' => $stock,
'image' => $option_image !== '' ? $option_image : (string) ($product['image'] ?? ''),
'title' => (string) ($product['title'] ?? ''),
'product' => $product,
];
}
}
@@ -219,6 +240,40 @@ trait AKSRT_Admin_Shop_Controller_Frontend_Flow_Operations {
return $this->success(['data' => $result]);
}
public function toggle_frontend_product_favorite(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shop')) {
return $this->error('商城服务不可用', 500);
}
$product_id = (int) $request->get_param('id');
$result = AKSRT_Shop::get_instance()->toggle_favorite(get_current_user_id(), $product_id);
if (empty($result['success'])) {
return $this->error(__('收藏操作失败', 'aksrt'));
}
return $this->success([
'message' => !empty($result['is_favorite']) ? __('已收藏', 'aksrt') : __('已取消收藏', 'aksrt'),
'data' => $result,
]);
}
public function rebuy_frontend_order(WP_REST_Request $request): WP_REST_Response {
if (!class_exists('AKSRT_Shop')) {
return $this->error('商城服务不可用', 500);
}
$order_number = sanitize_text_field((string) $request->get_param('order_number'));
$result = AKSRT_Shop::get_instance()->re_purchase(get_current_user_id(), $order_number);
if (empty($result['success'])) {
return $this->error($result['message'] ?? __('再次购买失败', 'aksrt'));
}
return $this->success([
'message' => __('商品已加入购物车', 'aksrt'),
'data' => $result,
]);
}
public function get_frontend_addresses(): WP_REST_Response {
if (!class_exists('AKSRT_Shop')) {
return $this->success(['data' => ['addresses' => []]]);
@@ -346,6 +346,18 @@ class AKSRT_Admin_Shop_Controller extends AKSRT_Admin_Controller_Base {
'permission_callback' => [$this, 'check_logged_in_permission'],
]);
register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P<order_number>[A-Za-z0-9]+)/rebuy', [
'methods' => 'POST',
'callback' => [$this, 'rebuy_frontend_order'],
'permission_callback' => [$this, 'check_logged_in_permission'],
]);
register_rest_route(self::API_NAMESPACE, '/shop/frontend/products/(?P<id>\d+)/favorite', [
'methods' => 'POST',
'callback' => [$this, 'toggle_frontend_product_favorite'],
'permission_callback' => [$this, 'check_logged_in_permission'],
]);
register_rest_route(self::API_NAMESPACE, '/shop/after-sale/reasons', [
'methods' => 'GET',
'callback' => [$this, 'get_after_sale_reasons'],