From 9277674da0d24fa054e63094a8c91ea202f8dec5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 25 Jul 2026 14:12:40 +0800 Subject: [PATCH] fix(orders): restore frontend pay REST and harden order-center client Add AKSRT\Payment\OrderCenterRest so /shop/frontend/orders/{n}/pay|cancel always register on rest_api_init; widen order_number route charset; make order-center.js use credentials, encodeURIComponent, and clearer 404 errors. --- assets/js/order-center.js | 54 +++++++--- functions.php | 3 + src/AKSRT/Admin/ShopController.php | 18 ++-- src/AKSRT/Payment/OrderCenterRest.php | 148 ++++++++++++++++++++++++++ 4 files changed, 200 insertions(+), 23 deletions(-) create mode 100644 src/AKSRT/Payment/OrderCenterRest.php diff --git a/assets/js/order-center.js b/assets/js/order-center.js index 1cf97a73..1d5ca379 100644 --- a/assets/js/order-center.js +++ b/assets/js/order-center.js @@ -51,18 +51,44 @@ } function request(path, body, method) { - return fetch(restRoot() + path, { + var opts = { method: method || "POST", + credentials: "same-origin", headers: { "Content-Type": "application/json", - "X-WP-Nonce": aksrtData.restNonce - }, - body: body === undefined ? undefined : JSON.stringify(body || {}) - }).then(function (response) { - return response.json(); + "X-WP-Nonce": (aksrtData && aksrtData.restNonce) || "" + } + }; + if (body !== undefined) { + opts.body = JSON.stringify(body || {}); + } + return fetch(restRoot() + path, opts).then(function (response) { + return response.text().then(function (text) { + var data = {}; + try { + data = text ? JSON.parse(text) : {}; + } catch (e) { + data = { + success: false, + message: response.status === 404 + ? "支付接口未找到(404),请刷新页面或联系管理员。" + : ("请求失败(HTTP " + response.status + ")") + }; + } + if (typeof data.success === "undefined" && response.ok === false) { + data.success = false; + data.message = data.message || data.code || ("HTTP " + response.status); + } + return data; + }); }); } + function orderPath(orderNumber, suffix) { + var base = "/shop/frontend/orders/" + encodeURIComponent(orderNumber || ""); + return suffix ? base + suffix : base; + } + function cartUrl() { return aksrtData.cartUrl || (aksrtData.routes && aksrtData.routes.cart) || "/"; } @@ -217,7 +243,7 @@ return; } if (triggerBtn) triggerBtn.disabled = true; - var url = restRoot() + "/shop/frontend/orders/" + encodeURIComponent(orderNumber) + "/review-modal"; + var url = restRoot() + orderPath(orderNumber, "/review-modal"); window.AksrtRemoteModal.openRemote({ url: url, className: "modal-mini full-sm", @@ -274,7 +300,7 @@ return item.file ? fileToDataUrl(item.file) : Promise.resolve(item.url || ""); })).then(function (images) { images = images.filter(Boolean); - return request("/shop/frontend/orders/" + orderNumber + "/review", { + return request(orderPath(orderNumber, "/review"), { product_id: productId, content: comment, rating_product: scoreProduct, @@ -349,7 +375,7 @@ close(); // 关闭后查询订单是否已到账 if (orderNumber) { - request("/shop/frontend/orders/" + orderNumber, {}).then(function () { + request(orderPath(orderNumber), {}, "GET").then(function () { window.location.reload(); }).catch(function () {}); } @@ -424,7 +450,7 @@ submitBtn.textContent = "提交中..."; } - request("/shop/frontend/orders/" + orderNumber + "/after-sale", { + request(orderPath(orderNumber, "/after-sale"), { type: formData.get("type") || "refund", reason: reason, note: (formData.get("note") || "").trim() @@ -486,7 +512,7 @@ if (!receiptOrder) return; if (!window.confirm("确认已收到货物?")) return; receiptBtn.disabled = true; - request("/shop/frontend/orders/" + receiptOrder + "/confirm-receipt", {}) + request(orderPath(receiptOrder, "/confirm-receipt"), {}) .then(function (result) { receiptBtn.disabled = false; if (!result || !result.success) { @@ -509,7 +535,7 @@ var rebuyOrder = rebuyBtn.getAttribute("data-aksrt-order-rebuy"); if (!rebuyOrder) return; rebuyBtn.disabled = true; - request("/shop/frontend/orders/" + rebuyOrder + "/rebuy", {}) + request(orderPath(rebuyOrder, "/rebuy"), {}) .then(function (result) { rebuyBtn.disabled = false; if (!result || !result.success) { @@ -544,7 +570,7 @@ var payMethod = payButton.getAttribute("data-aksrt-order-method") || ""; payButton.disabled = true; payButton.textContent = "处理中..."; - request("/shop/frontend/orders/" + payOrderNumber + "/pay", payMethod ? { payment_method: payMethod } : {}) + request(orderPath(payOrderNumber, "/pay"), payMethod ? { payment_method: payMethod } : {}) .then(function (result) { if (!result.success) { payButton.disabled = false; @@ -589,7 +615,7 @@ var cancelOrderNumber = cancelButton.getAttribute("data-aksrt-order-cancel"); cancelButton.disabled = true; cancelButton.textContent = "取消中..."; - request("/shop/frontend/orders/" + cancelOrderNumber + "/cancel") + request(orderPath(cancelOrderNumber, "/cancel")) .then(function (result) { if (!result.success) { cancelButton.disabled = false; diff --git a/functions.php b/functions.php index fa1f21c9..a0feab3e 100644 --- a/functions.php +++ b/functions.php @@ -223,6 +223,9 @@ if (class_exists('\AKSRT\Content\VipService')) { if (class_exists('\AKSRT\Payment\ChargeCard')) { \AKSRT\Payment\ChargeCard::get_instance()->init(); } +if (class_exists('\AKSRT\Payment\OrderCenterRest')) { + \AKSRT\Payment\OrderCenterRest::get_instance()->init(); +} if (class_exists('\AKSRT\Credits\BalanceService')) { \AKSRT\Credits\BalanceService::get_instance()->init(); } diff --git a/src/AKSRT/Admin/ShopController.php b/src/AKSRT/Admin/ShopController.php index 9fd48104..4d9ca529 100644 --- a/src/AKSRT/Admin/ShopController.php +++ b/src/AKSRT/Admin/ShopController.php @@ -47,25 +47,25 @@ class ShopController extends ControllerBase { 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/pay', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/pay', [ 'methods' => 'POST', 'callback' => [$this, 'pay_frontend_order'], 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/cancel', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/cancel', [ 'methods' => 'POST', 'callback' => [$this, 'cancel_frontend_order'], 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)', [ 'methods' => 'GET', 'callback' => [$this, 'get_frontend_order'], 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/after-sale', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/after-sale', [ 'methods' => 'POST', 'callback' => [$this, 'create_frontend_after_sale'], 'permission_callback' => [$this, 'check_logged_in_permission'], @@ -239,7 +239,7 @@ class ShopController extends ControllerBase { ]); // Order single read - register_rest_route(self::API_NAMESPACE, '/shop/orders/(?P[A-Za-z0-9]+)', [ + register_rest_route(self::API_NAMESPACE, '/shop/orders/(?P[A-Za-z0-9_-]+)', [ 'methods' => 'GET', 'callback' => [$this, 'get_order'], 'permission_callback' => [$this, 'check_permission'], @@ -361,13 +361,13 @@ class ShopController extends ControllerBase { 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/confirm-receipt', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/confirm-receipt', [ 'methods' => 'POST', 'callback' => [$this, 'confirm_receipt'], 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/rebuy', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/rebuy', [ 'methods' => 'POST', 'callback' => [$this, 'rebuy_frontend_order'], 'permission_callback' => [$this, 'check_logged_in_permission'], @@ -398,13 +398,13 @@ class ShopController extends ControllerBase { 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/review', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/review', [ 'methods' => 'POST', 'callback' => [$this, 'submit_frontend_product_review'], 'permission_callback' => [$this, 'check_logged_in_permission'], ]); - register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9]+)/review-modal', [ + register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P[A-Za-z0-9_-]+)/review-modal', [ 'methods' => 'GET', 'callback' => [$this, 'get_frontend_product_review_modal'], 'permission_callback' => [$this, 'check_logged_in_permission'], diff --git a/src/AKSRT/Payment/OrderCenterRest.php b/src/AKSRT/Payment/OrderCenterRest.php new file mode 100644 index 00000000..1fbac6a1 --- /dev/null +++ b/src/AKSRT/Payment/OrderCenterRest.php @@ -0,0 +1,148 @@ +[A-Za-z0-9_-]+)'; + + register_rest_route($ns, '/shop/frontend/orders/' . $on . '/pay', [ + 'methods' => 'POST', + 'callback' => [$this, 'pay'], + 'permission_callback' => [$this, 'require_logged_in'], + ]); + + register_rest_route($ns, '/shop/frontend/orders/' . $on . '/cancel', [ + 'methods' => 'POST', + 'callback' => [$this, 'cancel'], + 'permission_callback' => [$this, 'require_logged_in'], + ]); + + register_rest_route($ns, '/shop/frontend/orders/' . $on, [ + [ + 'methods' => 'GET', + 'callback' => [$this, 'get_order'], + 'permission_callback' => [$this, 'require_logged_in'], + ], + [ + // order-center.js historically POSTed for refresh — accept both. + 'methods' => 'POST', + 'callback' => [$this, 'get_order'], + 'permission_callback' => [$this, 'require_logged_in'], + ], + ]); + } + + /** + * @return true|\WP_Error + */ + public function require_logged_in() { + if (!is_user_logged_in()) { + return new \WP_Error( + 'aksrt_rest_forbidden', + __('请先登录', 'aksrt'), + ['status' => 401] + ); + } + return true; + } + + public function pay(\WP_REST_Request $request): \WP_REST_Response { + $order_number = sanitize_text_field((string) $request->get_param('order_number')); + $params = $request->get_json_params(); + if (!is_array($params)) { + $params = []; + } + $payment_method = sanitize_key((string) ($params['payment_method'] ?? '')); + + if (class_exists(UserOrders::class)) { + $result = UserOrders::get_instance()->pay_order(get_current_user_id(), $order_number, $payment_method); + } elseif (class_exists('\AKSRT\Shop\Shop')) { + $result = \AKSRT\Shop\Shop::get_instance()->pay_order( + get_current_user_id(), + $order_number, + $payment_method + ); + } else { + return $this->error(__('订单服务不可用', 'aksrt'), 500); + } + + if (empty($result['success'])) { + return $this->error((string) ($result['message'] ?? __('支付失败', 'aksrt')), 400); + } + + return $this->success(['data' => $result['data'] ?? []]); + } + + public function cancel(\WP_REST_Request $request): \WP_REST_Response { + $order_number = sanitize_text_field((string) $request->get_param('order_number')); + + if (class_exists(UserOrders::class)) { + $result = UserOrders::get_instance()->cancel_order(get_current_user_id(), $order_number); + } elseif (class_exists('\AKSRT\Shop\Shop')) { + $result = \AKSRT\Shop\Shop::get_instance()->cancel_order(get_current_user_id(), $order_number); + } else { + return $this->error(__('订单服务不可用', 'aksrt'), 500); + } + + if (empty($result['success'])) { + return $this->error((string) ($result['message'] ?? __('取消订单失败', 'aksrt')), 400); + } + + return $this->success(['data' => $result['data'] ?? []]); + } + + public function get_order(\WP_REST_Request $request): \WP_REST_Response { + $order_number = sanitize_text_field((string) $request->get_param('order_number')); + $order = null; + + if (class_exists(UserOrders::class)) { + $order = UserOrders::get_instance()->get_order_detail_view(get_current_user_id(), $order_number); + } elseif (class_exists('\AKSRT\Shop\Shop')) { + $order = \AKSRT\Shop\Shop::get_instance()->get_order_detail_view(get_current_user_id(), $order_number); + } + + if (!$order) { + return $this->error(__('订单不存在', 'aksrt'), 404); + } + + return $this->success(['data' => $order]); + } + + private function success(array $data = [], int $status = 200): \WP_REST_Response { + return new \WP_REST_Response(array_merge(['success' => true], $data), $status); + } + + private function error(string $message, int $status = 400): \WP_REST_Response { + return new \WP_REST_Response([ + 'success' => false, + 'message' => $message, + ], $status); + } +}