\\d+)', [ [ 'methods' => 'GET', 'callback' => [$this, 'rest_preview'], 'permission_callback' => [$this, 'check_rest_nonce'], ], [ 'methods' => 'POST', 'callback' => [$this, 'rest_purchase'], 'permission_callback' => [$this, 'check_rest_nonce'], ], ]); } /** * @return true|WP_Error */ public function check_rest_nonce(WP_REST_Request $request) { $nonce = $request->get_header('x-wp-nonce'); if (!$nonce) { return new WP_Error('aksrt_rest_no_nonce', __('缺少安全令牌', 'aksrt'), ['status' => 403]); } if (!wp_verify_nonce($nonce, 'wp_rest')) { return new WP_Error('aksrt_rest_bad_nonce', __('安全令牌已过期,请刷新页面', 'aksrt'), ['status' => 403]); } return true; } public function rest_preview(WP_REST_Request $request) { $plate_id = (int) $request['id']; $uid = get_current_user_id(); $coupon_code = sanitize_text_field((string) ($request->get_param('coupon_code') ?? $request->get_param('coupon') ?? '')); $preview = $this->preview($plate_id, $uid, $coupon_code); if (is_wp_error($preview)) { return $preview; } return new WP_REST_Response($preview, 200); } public function rest_purchase(WP_REST_Request $request) { $plate_id = (int) $request['id']; $uid = get_current_user_id(); $post = get_post($plate_id); $allow_guest = function_exists('aksrt_allow_guest_forum_pay') && aksrt_allow_guest_forum_pay($post); if ($uid <= 0 && !$allow_guest) { return new WP_Error('aksrt_need_login', __('请先登录后再关注。', 'aksrt'), [ 'status' => 401, 'login_url' => wp_login_url($post ? get_permalink($post) : home_url('/')), ]); } $params = $request->get_json_params(); if (!is_array($params)) { $params = $request->get_params(); } $payment_method = sanitize_key((string) ($params['payment_method'] ?? 'balance')); if ($payment_method === '' || $payment_method === 'cash') { $payment_method = 'balance'; } if ($uid <= 0 && in_array($payment_method, ['balance', 'points'], true)) { $payment_method = 'alipay'; } $coupon_code = sanitize_text_field((string) ($params['coupon_code'] ?? $params['coupon'] ?? '')); return $this->rest_wrap_result( $this->create_cash_order($plate_id, $uid, $payment_method, $coupon_code) ); } /** * @param mixed $result * @return WP_REST_Response|WP_Error */ private function rest_wrap_result($result) { if (is_wp_error($result)) { return $result; } if (empty($result['success'])) { $code = sanitize_key((string) ($result['code'] ?? 'aksrt_pay_failed')); $status = (int) ($result['status'] ?? 400); return new WP_Error( $code !== '' ? $code : 'aksrt_pay_failed', (string) ($result['message'] ?? __('支付失败', 'aksrt')), array_merge( ['status' => $status > 0 ? $status : 400], is_array($result['data'] ?? null) ? $result['data'] : [] ) ); } return new WP_REST_Response($result, 200); } /** * @return array|WP_Error */ public function preview(int $plate_id, int $user_id = 0, string $coupon_code = '') { $assert = $this->assert_plate($plate_id); if (is_wp_error($assert)) { return $assert; } /** @var WP_Post $post */ $post = $assert['post']; $uid = $user_id > 0 ? $user_id : get_current_user_id(); $coupon_code = sanitize_text_field($coupon_code); $allow_guest = function_exists('aksrt_allow_guest_forum_pay') && aksrt_allow_guest_forum_pay($post); if ($uid <= 0 && !$allow_guest) { return [ 'post_id' => $plate_id, 'title' => get_the_title($post), 'permalink' => get_permalink($post), 'pay_modo' => '0', 'pay_type' => 'plate_follow', 'pay_type_label' => __('付费关注', 'aksrt'), 'amount' => 0, 'base' => 0, 'list_amount' => 0, 'payable' => 0, 'currency' => '¥', 'amount_label' => '', 'base_label' => '', 'list_amount_label' => '', 'is_free' => false, 'has_paid_access' => false, 'need_login' => true, 'allow_guest' => false, 'need_vip' => false, 'need_purchase' => true, 'logged_in' => false, 'balance' => 0, 'balance_enough' => false, 'payment_methods' => [], 'login_url' => wp_login_url(get_permalink($post)), 'vip_url' => home_url('/'), 'credits_url' => home_url('/'), 'coupon_allowed' => false, 'coupon_desc' => '', 'coupon_code' => '', 'coupon' => null, 'coupon_error' => '', 'coupon_discount' => 0, 'coupon_discount_label' => '', 'rebate_discount' => 0, 'rebate_discount_label' => '', 'points_deduction_allowed' => false, 'card_pass_allowed' => false, 'price_type' => '', ]; } $priced = $this->resolve_price($plate_id, $uid); if (($priced['modo'] ?? 'money') === 'points') { return new WP_Error('aksrt_wrong_modo', __('该版块仅支持积分关注。', 'aksrt'), ['status' => 400]); } $list_amount = round((float) ($priced['amount'] ?? 0), 2); $base = round((float) ($priced['base'] ?? $list_amount), 2); $vip_free = !empty($priced['is_free']) || ($list_amount <= 0 && !empty($priced['is_vip_price'])); if ($uid <= 0) { $vip_free = false; } $has_followed = $this->user_has_followed($uid, $plate_id); $balance = 0; if ($uid > 0 && class_exists('AKSRT_Credits')) { $balance = (int) AKSRT_Credits::balance($uid); } $methods = function_exists('aksrt_list_cash_payment_methods') ? aksrt_list_cash_payment_methods($uid > 0) : ($uid > 0 ? [['id' => 'balance', 'name' => __('站内余额', 'aksrt')]] : [['id' => 'alipay', 'name' => __('支付宝', 'aksrt')]]); $coupon_allowed = $this->is_coupon_allowed($plate_id); $coupon_error = ''; $coupon_discount = 0.0; $coupon_meta = null; $applied_code = ''; $payable = $list_amount; if (!$vip_free && $list_amount > 0) { $discounted = $this->apply_plate_cash_discounts($priced, $plate_id, $uid, $coupon_code, true); if (is_wp_error($discounted)) { $coupon_error = $discounted->get_error_message(); $discounted = $this->apply_plate_cash_discounts($priced, $plate_id, $uid, '', true); } if (!is_wp_error($discounted)) { $list_amount = round((float) ($discounted['list_amount'] ?? $list_amount), 2); $payable = round((float) ($discounted['payable'] ?? $list_amount), 2); $coupon_discount = round((float) ($discounted['coupon_discount'] ?? 0), 2); $coupon_meta = !empty($discounted['coupon']) ? $discounted['coupon'] : null; $applied_code = (string) ($discounted['coupon_code'] ?? ($coupon_meta['code'] ?? '')); if ($coupon_error === '' && !empty($discounted['coupon_error'])) { $coupon_error = (string) $discounted['coupon_error']; } } } elseif ($vip_free) { $payable = 0.0; $list_amount = 0.0; } $is_free = $vip_free || $payable <= 0; $amount_label = $is_free ? __('免费', 'aksrt') : sprintf('¥%s', number_format($payable, 2, '.', '')); $list_label = sprintf('¥%s', number_format($list_amount > 0 ? $list_amount : $base, 2, '.', '')); $base_label = sprintf('¥%s', number_format($base, 2, '.', '')); return [ 'post_id' => $plate_id, 'title' => get_the_title($post), 'permalink' => get_permalink($post), 'pay_modo' => '0', 'pay_type' => 'plate_follow', 'pay_type_label' => __('付费关注', 'aksrt'), 'amount' => $payable, 'base' => $base, 'list_amount' => $list_amount > 0 ? $list_amount : $base, 'list_amount_label' => $list_label, 'payable' => $payable, 'currency' => '¥', 'amount_label' => $amount_label, 'base_label' => $base_label, 'is_free' => $is_free, 'has_paid_access' => $has_followed, 'paid_order' => null, 'pending_order' => null, 'need_login' => $uid <= 0 && !$allow_guest, 'allow_guest' => $allow_guest, 'need_vip' => false, 'need_purchase' => !$has_followed && !$is_free, 'logged_in' => $uid > 0, 'balance' => $balance, 'balance_enough' => true, 'payment_methods' => $methods, 'login_url' => wp_login_url(get_permalink($post)), 'vip_url' => home_url('/'), 'credits_url' => home_url('/'), 'coupon_allowed' => $coupon_allowed, 'coupon_desc' => $coupon_allowed ? __('支持优惠码', 'aksrt') : '', 'coupon_code' => $applied_code, 'coupon' => $coupon_meta, 'coupon_error' => $coupon_error, 'coupon_discount' => $coupon_discount, 'coupon_discount_label' => $coupon_discount > 0 ? ('-' . sprintf('¥%s', number_format($coupon_discount, 2, '.', ''))) : '', 'rebate_discount' => 0, 'rebate_discount_label' => '', 'points_deduction_allowed' => false, 'card_pass_allowed' => false, 'price_type' => '', 'vip_level' => (int) ($priced['vip_level'] ?? 0), 'is_vip_price' => !empty($priced['is_vip_price']), ]; } /** * @return array|WP_Error */ public function create_cash_order(int $plate_id, int $user_id, string $payment_method = 'balance', string $coupon_code = '') { $assert = $this->assert_plate($plate_id); if (is_wp_error($assert)) { return $assert; } /** @var WP_Post $post */ $post = $assert['post']; $allow_guest = function_exists('aksrt_allow_guest_forum_pay') && aksrt_allow_guest_forum_pay($post); if ($user_id <= 0 && !$allow_guest) { return new WP_Error('aksrt_need_login', __('请先登录后再关注。', 'aksrt'), [ 'status' => 401, 'login_url' => wp_login_url(get_permalink($post)), ]); } if ($this->user_has_followed($user_id, $plate_id)) { return new WP_Error('aksrt_already_followed', __('已关注该版块。', 'aksrt'), [ 'status' => 400, 'reload' => true, ]); } $priced = $this->resolve_price($plate_id, $user_id); if (($priced['modo'] ?? 'money') === 'points') { return new WP_Error('aksrt_wrong_modo', __('该版块仅支持积分关注。', 'aksrt'), ['status' => 400]); } $list_amount = round((float) ($priced['amount'] ?? 0), 2); $base = round((float) ($priced['base'] ?? $list_amount), 2); $vip_free = !empty($priced['is_free']) || ($list_amount <= 0 && !empty($priced['is_vip_price'])); if ($user_id <= 0) { $vip_free = false; } $coupon_code = sanitize_text_field($coupon_code); // VIP free — no coupon stack. Guests never hit this. if ($vip_free) { return $this->create_free_paid_order($plate_id, $user_id, $post, $priced, [ 'list_amount' => 0.0, 'payable' => 0.0, 'coupon_code' => '', 'coupon_discount' => 0.0, 'coupon' => [], 'message' => __('VIP 免费关注成功', 'aksrt'), 'paid_note' => __('VIP 免费关注版块。', 'aksrt'), ]); } if ($list_amount <= 0) { return new WP_Error('aksrt_price_unset', __('价格未设置', 'aksrt'), ['status' => 400]); } $discounted = $this->apply_plate_cash_discounts($priced, $plate_id, $user_id, $coupon_code, false); if (is_wp_error($discounted)) { return $discounted; } $list_amount = round((float) ($discounted['list_amount'] ?? $list_amount), 2); $amount = round((float) ($discounted['payable'] ?? $list_amount), 2); $coupon_discount = round((float) ($discounted['coupon_discount'] ?? 0), 2); $coupon_meta = is_array($discounted['coupon'] ?? null) ? $discounted['coupon'] : []; $coupon_code_norm = (string) ($coupon_meta['code'] ?? $discounted['coupon_code'] ?? $coupon_code); // Free after coupon. if ($amount <= 0) { return $this->create_free_paid_order($plate_id, $user_id, $post, $priced, [ 'list_amount' => $list_amount, 'payable' => 0.0, 'coupon_code' => $coupon_code_norm, 'coupon_discount' => $coupon_discount, 'coupon' => $coupon_meta, 'message' => __('无需支付,已关注。', 'aksrt'), 'paid_note' => __('优惠后无需支付。', 'aksrt'), ]); } if (!class_exists('AKSRT_Order') || !class_exists('AKSRT_Order_Repository')) { return new WP_Error('aksrt_pay_unavailable', __('支付系统不可用。', 'aksrt'), ['status' => 500]); } $payment_method = sanitize_key($payment_method); if ($payment_method === '') { $payment_method = $user_id > 0 ? 'balance' : 'alipay'; } if ($user_id <= 0 && in_array($payment_method, ['balance', 'points'], true)) { $payment_method = 'alipay'; } $pending = $this->find_pending_order($user_id, $plate_id); $can_resume = false; if ($pending && abs((float) ($pending['amount'] ?? 0) - $amount) < 0.001) { $pending_meta = $this->decode_order_note_meta($pending); $pending_coupon = (string) ($pending_meta['coupon_code'] ?? ''); if (is_array($pending_meta['coupon'] ?? null)) { $pending_coupon = (string) ($pending_meta['coupon']['code'] ?? $pending_coupon); } if ($pending_coupon === $coupon_code_norm) { $can_resume = true; } } if ($can_resume) { $order_number = (string) ($pending['order_number'] ?? ''); $order = $pending; } else { $note = wp_json_encode([ 'plate_follow_pay' => 1, 'plate_id' => $plate_id, 'pay_modo' => '0', 'vip_level' => (int) ($priced['vip_level'] ?? 0), 'base' => $base, 'list_amount' => $list_amount, 'amount' => $amount, 'payable' => $amount, 'coupon_code' => $coupon_code_norm, 'coupon_discount' => $coupon_discount, 'coupon' => $coupon_meta, 'guest' => $user_id <= 0 ? 1 : 0, 'ip_address' => function_exists('aksrt_pay_client_ip') ? aksrt_pay_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? ''), ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $order_number = AKSRT_Order_Repository::get_instance()->create([ 'user_id' => max(0, $user_id), 'product_type' => AKSRT_Domain_Model::ORDER_PRODUCT_PLATE_FOLLOW, 'product_id' => $plate_id, 'amount' => $amount, 'currency' => 'CNY', 'status' => AKSRT_Order::STATUS_PENDING, 'payment_method' => $payment_method, 'note' => is_string($note) ? $note : '', 'expires_at' => gmdate('Y-m-d H:i:s', time() + 30 * MINUTE_IN_SECONDS), ]); if ($order_number === '') { return new WP_Error('aksrt_order_create_failed', __('创建订单失败。', 'aksrt'), ['status' => 500]); } $order = AKSRT_Order_Repository::get_instance()->get($order_number); if (!$order) { return new WP_Error('aksrt_order_create_failed', __('创建订单失败。', 'aksrt'), ['status' => 500]); } } $order['subject'] = sprintf( /* translators: %s: plate title */ __('付费关注版块「%s」', 'aksrt'), get_the_title($post) ); $order['payment_method'] = $payment_method; $order['amount'] = $amount; if (!class_exists('AKSRT_Payment_Gateway')) { return new WP_Error('aksrt_pay_unavailable', __('支付网关不可用。', 'aksrt'), ['status' => 500]); } $gateway_manager = AKSRT_Payment_Gateway::get_instance(); $gateway = $gateway_manager->get_gateway($payment_method); if (!$gateway) { return new WP_Error('aksrt_gateway_unavailable', __('当前支付方式不可用。', 'aksrt'), [ 'status' => 400, 'order_number' => $order_number, ]); } if (method_exists($gateway, 'is_configured') && !$gateway->is_configured()) { return new WP_Error('aksrt_gateway_unconfigured', __('该支付方式尚未配置,请联系站长。', 'aksrt'), [ 'status' => 400, 'order_number' => $order_number, ]); } $payment_result = $gateway->create_payment($order); if (empty($payment_result['success'])) { $msg = (string) ($payment_result['message'] ?? __('支付失败。', 'aksrt')); $code = ($payment_method === 'balance' && (str_contains($msg, '余额') || str_contains($msg, '不足'))) ? 'aksrt_balance_insufficient' : 'aksrt_pay_failed'; return new WP_Error($code, $msg, [ 'status' => 402, 'order_number' => $order_number, ]); } $code_url = (string) ($payment_result['payment_data']['code_url'] ?? ''); $payment_url = (string) ($payment_result['payment_url'] ?? ''); if ($payment_url !== '' || $code_url !== '') { return [ 'success' => true, 'data' => [ 'redirect' => true, 'payment_url' => $payment_url, 'qr_content' => $code_url, 'order_number' => $order_number, 'amount' => $amount, 'pay_modo' => '0', 'message' => $payment_result['message'] ?? __('请在新页面完成支付。', 'aksrt'), 'reload' => false, 'poll' => true, ], ]; } $paid = AKSRT_Order::get_instance()->update_status($order_number, AKSRT_Order::STATUS_PAID, [ 'payment_method' => $payment_method, 'payment_transaction_id' => sanitize_text_field((string) ($payment_result['transaction_id'] ?? '')), 'note' => __('用户完成版块付费关注支付。', 'aksrt'), ]); if (!$paid) { return new WP_Error('aksrt_order_confirm_failed', __('订单确认失败。', 'aksrt'), ['status' => 500]); } if ($user_id > 0) { $this->mark_followed($user_id, $plate_id); } return [ 'success' => true, 'data' => [ 'order_number' => $order_number, 'amount' => $amount, 'pay_modo' => '0', 'message' => $payment_result['message'] ?? __('关注成功', 'aksrt'), 'reload' => true, ], ]; } public function on_order_paid(string $order_number, array $paid_order): void { $product_type = sanitize_key((string) ($paid_order['product_type'] ?? '')); if ($product_type !== AKSRT_Domain_Model::ORDER_PRODUCT_PLATE_FOLLOW) { return; } $plate_id = (int) ($paid_order['product_id'] ?? 0); $user_id = (int) ($paid_order['user_id'] ?? 0); if ($plate_id <= 0) { return; } $meta = $this->decode_order_note_meta($paid_order); if (empty($meta['plate_follow_pay'])) { $post = get_post($plate_id); if (!($post instanceof WP_Post) || $post->post_type !== 'aksrt_forum_plate') { return; } } if ($user_id > 0) { $this->mark_followed($user_id, $plate_id); } elseif (function_exists('aksrt_set_guest_pay_cookies')) { if (class_exists('AKSRT_Order_Repository')) { $full = AKSRT_Order_Repository::get_instance()->get($order_number); if (is_array($full)) { $paid_order = array_merge($full, $paid_order); } } aksrt_set_guest_pay_cookies($paid_order); } $this->finalize_plate_order_side_effects($order_number, $paid_order); } /** * @return array{post:WP_Post}|WP_Error */ private function assert_plate(int $plate_id) { $post = get_post($plate_id); if (!($post instanceof WP_Post) || $post->post_type !== 'aksrt_forum_plate') { return new WP_Error('aksrt_invalid', __('版块不存在', 'aksrt'), ['status' => 404]); } if ($post->post_status !== 'publish') { return new WP_Error('aksrt_invalid', __('版块不可关注', 'aksrt'), ['status' => 404]); } if (!$this->is_follow_pay_enabled($plate_id)) { return new WP_Error('aksrt_not_paid', __('该版块未开启付费关注', 'aksrt'), ['status' => 400]); } $modo = sanitize_key((string) (get_post_meta($plate_id, '_aksrt_follow_pay_modo', true) ?: 'money')); if ($modo === 'points') { return new WP_Error('aksrt_wrong_modo', __('该版块仅支持积分关注。', 'aksrt'), ['status' => 400]); } return ['post' => $post]; } private function is_follow_pay_enabled(int $plate_id): bool { if (class_exists('AKSRT_Forum') && method_exists('AKSRT_Forum', 'is_plate_follow_pay_enabled')) { return (bool) AKSRT_Forum::get_instance()->is_plate_follow_pay_enabled($plate_id); } return (int) get_post_meta($plate_id, '_aksrt_follow_pay', true) === 1; } /** * @return array{amount:float,modo:string,is_vip_price:bool,is_free:bool,label:string,base:float,vip_level:int} */ private function resolve_price(int $plate_id, int $user_id): array { if (class_exists('AKSRT_Forum') && method_exists('AKSRT_Forum', 'resolve_plate_follow_pay_price')) { return AKSRT_Forum::get_instance()->resolve_plate_follow_pay_price($plate_id, $user_id); } return [ 'amount' => max(0.0, (float) get_post_meta($plate_id, '_aksrt_follow_pay_price', true)), 'modo' => 'money', 'is_vip_price' => false, 'is_free' => false, 'label' => '', 'base' => max(0.0, (float) get_post_meta($plate_id, '_aksrt_follow_pay_price', true)), 'vip_level' => 0, ]; } private function user_has_followed(int $user_id, int $plate_id): bool { if ($plate_id <= 0) { return false; } if ($user_id <= 0) { return function_exists('aksrt_guest_has_plate_follow_access') && aksrt_guest_has_plate_follow_access($plate_id); } if (class_exists('AKSRT_Forum') && method_exists('AKSRT_Forum', 'is_user_following_plate')) { return (bool) AKSRT_Forum::get_instance()->is_user_following_plate($user_id, $plate_id); } $follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true); if (!is_array($follow_list)) { return false; } return in_array($plate_id, array_map('intval', $follow_list), true); } private function mark_followed(int $user_id, int $plate_id): void { if (class_exists('AKSRT_Forum') && method_exists('AKSRT_Forum', 'mark_plate_followed')) { AKSRT_Forum::get_instance()->mark_plate_followed($user_id, $plate_id); return; } if ($user_id <= 0 || $plate_id <= 0) { return; } $follow_list = get_user_meta($user_id, 'aksrt_follow_plate', true); if (!is_array($follow_list)) { $follow_list = []; } $follow_list[] = $plate_id; update_user_meta($user_id, 'aksrt_follow_plate', array_values(array_unique(array_map('intval', $follow_list)))); } /** * Free VIP or free-after-discount paid order. * * @param array{ * list_amount?:float, * payable?:float, * coupon_code?:string, * coupon_discount?:float, * coupon?:array, * message?:string, * paid_note?:string * } $opts * @return array|WP_Error */ private function create_free_paid_order(int $plate_id, int $user_id, WP_Post $post, array $priced, array $opts = []) { $list_amount = round((float) ($opts['list_amount'] ?? 0), 2); $payable = round((float) ($opts['payable'] ?? 0), 2); $coupon_code = sanitize_text_field((string) ($opts['coupon_code'] ?? '')); $coupon_discount = round((float) ($opts['coupon_discount'] ?? 0), 2); $coupon_meta = is_array($opts['coupon'] ?? null) ? $opts['coupon'] : []; $message = (string) ($opts['message'] ?? __('VIP 免费关注成功', 'aksrt')); $paid_note = (string) ($opts['paid_note'] ?? __('VIP 免费关注版块。', 'aksrt')); if (!class_exists('AKSRT_Order') || !class_exists('AKSRT_Order_Repository')) { $this->mark_followed($user_id, $plate_id); return [ 'success' => true, 'data' => [ 'order_number' => '', 'amount' => 0, 'pay_modo' => '0', 'free' => true, 'message' => $message, 'reload' => true, ], ]; } $note = wp_json_encode([ 'plate_follow_pay' => 1, 'plate_id' => $plate_id, 'pay_modo' => '0', 'vip_level' => (int) ($priced['vip_level'] ?? 0), 'base' => (float) ($priced['base'] ?? 0), 'list_amount' => $list_amount, 'amount' => $payable, 'payable' => $payable, 'coupon_code' => $coupon_code, 'coupon_discount' => $coupon_discount, 'coupon' => $coupon_meta, 'free' => 1, 'guest' => $user_id <= 0 ? 1 : 0, 'ip_address' => function_exists('aksrt_pay_client_ip') ? aksrt_pay_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? ''), ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $order_number = AKSRT_Order_Repository::get_instance()->create([ 'user_id' => max(0, $user_id), 'product_type' => AKSRT_Domain_Model::ORDER_PRODUCT_PLATE_FOLLOW, 'product_id' => $plate_id, 'amount' => 0.0, 'currency' => 'CNY', 'status' => AKSRT_Order::STATUS_PENDING, 'payment_method' => 'free', 'note' => is_string($note) ? $note : '', 'expires_at' => gmdate('Y-m-d H:i:s', time() + 30 * MINUTE_IN_SECONDS), ]); if ($order_number === '') { $this->mark_followed($user_id, $plate_id); return [ 'success' => true, 'data' => [ 'order_number' => '', 'amount' => 0, 'free' => true, 'message' => $message, 'reload' => true, ], ]; } $paid = AKSRT_Order::get_instance()->update_status($order_number, AKSRT_Order::STATUS_PAID, [ 'payment_method' => 'free', 'payment_transaction_id' => 'free_' . $order_number, 'note' => $paid_note, ]); if (!$paid) { return new WP_Error('aksrt_order_confirm_failed', __('订单确认失败。', 'aksrt'), ['status' => 500]); } $this->mark_followed($user_id, $plate_id); return [ 'success' => true, 'data' => [ 'order_number' => $order_number, 'amount' => 0, 'pay_modo' => '0', 'free' => true, 'message' => $message, 'reload' => true, ], ]; } private function is_coupon_allowed(int $plate_id): bool { return (int) get_post_meta($plate_id, '_aksrt_follow_pay_coupon_s', true) === 1; } /** * VIP list price → coupon → payable. * * @param array $priced * @return array|WP_Error */ private function apply_plate_cash_discounts(array $priced, int $plate_id, int $user_id, string $coupon_code = '', bool $soft_coupon = false) { $list_amount = round(max(0.0, (float) ($priced['amount'] ?? 0)), 2); $pay = $list_amount; $coupon_discount = 0.0; $coupon_meta = []; $coupon_error = ''; $coupon_code = sanitize_text_field($coupon_code); if ($coupon_code !== '') { if (!$this->is_coupon_allowed($plate_id)) { $err = new WP_Error('aksrt_coupon_not_allowed', __('当前版块不支持优惠码。', 'aksrt'), ['status' => 400]); if (!$soft_coupon) { return $err; } $coupon_error = $err->get_error_message(); $coupon_code = ''; } elseif (!class_exists('AKSRT_Coupon')) { $err = new WP_Error('aksrt_coupon_unavailable', __('优惠券系统不可用。', 'aksrt'), ['status' => 500]); if (!$soft_coupon) { return $err; } $coupon_error = $err->get_error_message(); $coupon_code = ''; } else { $result = AKSRT_Coupon::get_instance()->validate($coupon_code, $pay, [ 'user_id' => max(0, $user_id), 'product_ids' => [$plate_id], 'context' => 'plate_follow', ]); if (empty($result['valid'])) { $err = new WP_Error( 'aksrt_coupon_error', (string) ($result['message'] ?? __('优惠码不可用。', 'aksrt')), ['status' => 400] ); if (!$soft_coupon) { return $err; } $coupon_error = $err->get_error_message(); $coupon_code = ''; } else { $coupon_discount = round(max(0.0, (float) ($result['discount'] ?? 0)), 2); if ($coupon_discount > $pay) { $coupon_discount = $pay; } $pay = round(max(0.0, $pay - $coupon_discount), 2); $c = is_array($result['coupon'] ?? null) ? $result['coupon'] : []; $coupon_meta = [ 'id' => (int) ($c['id'] ?? 0), 'code' => (string) ($c['code'] ?? $coupon_code), 'type' => (string) ($c['type'] ?? ''), 'value' => (float) ($c['value'] ?? 0), 'discount' => $coupon_discount, ]; $coupon_code = (string) $coupon_meta['code']; } } } return [ 'list_amount' => $list_amount, 'coupon_discount' => $coupon_discount, 'coupon' => $coupon_meta, 'coupon_code' => $coupon_code, 'coupon_error' => $coupon_error, 'payable' => round(max(0.0, $pay), 2), ]; } private function finalize_plate_order_side_effects(string $order_number, array $order): void { $meta = $this->decode_order_note_meta($order); if (empty($meta['plate_follow_pay'])) { return; } if (($meta['pay_modo'] ?? '') === 'points') { return; } $buyer_id = (int) ($order['user_id'] ?? 0); if (function_exists('aksrt_burn_order_coupon')) { aksrt_burn_order_coupon($order_number, $meta, $buyer_id); } } private function find_pending_order(int $user_id, int $plate_id): ?array { if ($user_id <= 0 || $plate_id <= 0 || !class_exists('AKSRT_Order_Repository')) { return null; } $orders = AKSRT_Order_Repository::get_instance()->get_user_orders($user_id, [ 'product_type' => AKSRT_Domain_Model::ORDER_PRODUCT_PLATE_FOLLOW, 'status' => AKSRT_Domain_Model::ORDER_STATUS_PENDING, 'limit' => 20, 'offset' => 0, ]); foreach ($orders as $order) { if ((int) ($order['product_id'] ?? 0) !== $plate_id) { continue; } $meta = $this->decode_order_note_meta($order); if (!empty($meta['plate_follow_pay'])) { return $order; } return $order; } return null; } private function decode_order_note_meta(array $order): array { if (isset($order['meta_payload']) && is_array($order['meta_payload']) && $order['meta_payload'] !== []) { return $order['meta_payload']; } $note = (string) ($order['note'] ?? ''); if ($note === '') { return []; } $decoded = json_decode($note, true); return is_array($decoded) ? $decoded : []; } }