%5$s%6$s', esc_attr($class), esc_attr($template_id), $user_id, $post_id, $icon, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fixed sprite markup. esc_html($label) ); } } if (!function_exists('aksrt_render_rewards_template_for_user')) { function aksrt_render_rewards_template_for_user(int $user_id, int $post_id = 0): void { static $done = []; $key = $user_id . ':' . $post_id; if ($user_id <= 0 || isset($done[$key]) || !aksrt_rewards_enabled()) { return; } $done[$key] = true; $settings = \AKSRT\Settings\Settings::get_post_page_settings(); $amounts = (array) ($settings['rewards_amounts'] ?? [5, 10, 20, 50, 100]); $render_amounts = []; foreach ($amounts as $amount) { try { $parsed = \AKSRT\Ledger\AssetAmount::balance(is_string($amount) ? $amount : ''); if ($parsed->minor() > 0) { $render_amounts[] = $parsed->decimal(); } } catch (\InvalidArgumentException $e) { continue; } } $recipient = get_userdata($user_id); $template_id = 'aksrt-reward-template-' . $user_id . '-' . $post_id; $title = (string) ($settings['rewards_text'] ?? __('赞赏', 'aksrt')); $money_icon = function_exists('aksrt_icon_markup') ? aksrt_icon_markup('zibsvg-money') : ''; $close_icon = function_exists('aksrt_icon_markup') ? aksrt_icon_markup('zibsvg-close', 'ic-close') : ''; ?> __('请先登录后赞赏', 'aksrt')], 401); } $recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : []; if (!aksrt_rewards_enabled() || empty($recharge['enabled'])) { wp_send_json_error(['message' => __('赞赏功能当前不可用', 'aksrt')], 400); } $sender_id = get_current_user_id(); $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0; $recipient_id = isset($_POST['recipient_id']) ? absint($_POST['recipient_id']) : 0; if ($post_id > 0) { $post = get_post($post_id); if (!$post instanceof WP_Post || $post->post_status !== 'publish') { wp_send_json_error(['message' => __('赞赏内容不存在', 'aksrt')], 404); } $recipient_id = (int) $post->post_author; } if ($recipient_id <= 0 || !get_userdata($recipient_id) || $recipient_id === $sender_id) { wp_send_json_error(['message' => __('不能赞赏该用户', 'aksrt')], 400); } $settings = \AKSRT\Settings\Settings::get_post_page_settings(); $raw_amount = isset($_POST['amount']) ? (string) wp_unslash($_POST['amount']) : ''; try { $amount = \AKSRT\Ledger\AssetAmount::balance($raw_amount); $minimum = \AKSRT\Ledger\AssetAmount::balance(is_string($settings['rewards_min'] ?? null) ? $settings['rewards_min'] : ''); $maximum = \AKSRT\Ledger\AssetAmount::balance(is_string($settings['rewards_max'] ?? null) ? $settings['rewards_max'] : ''); } catch (\InvalidArgumentException $e) { wp_send_json_error(['message' => __('赞赏金额格式无效', 'aksrt')], 400); } if ($amount->minor() < $minimum->minor() || $amount->minor() > $maximum->minor()) { wp_send_json_error(['message' => sprintf(__('赞赏金额需在 ¥%1$s 至 ¥%2$s 之间', 'aksrt'), $minimum->formatted(), $maximum->formatted())], 400); } if (empty($settings['rewards_custom'])) { $allowed_amounts = []; foreach ((array) ($settings['rewards_amounts'] ?? []) as $allowed) { try { $allowed_amounts[] = \AKSRT\Ledger\AssetAmount::balance(is_string($allowed) ? $allowed : '')->minor(); } catch (\InvalidArgumentException $e) { continue; } } if (!in_array($amount->minor(), $allowed_amounts, true)) { wp_send_json_error(['message' => __('请选择预设的赞赏金额', 'aksrt')], 400); } } $request_key = sanitize_text_field(wp_unslash((string) ($_SERVER['HTTP_IDEMPOTENCY_KEY'] ?? ''))); if (!wp_is_uuid($request_key)) { wp_send_json_error(['message' => __('缺少或无效的 Idempotency-Key 请求头', 'aksrt')], 400); } $operation_key = \AKSRT\Ledger\OperationKey::deterministic('client-tip', [$sender_id, strtolower($request_key)]); $rate_key = 'aksrt_tip_rate_' . $sender_id; $recent_operation = (string) get_transient($rate_key); if ($recent_operation !== '' && !hash_equals($recent_operation, $operation_key)) { wp_send_json_error(['message' => __('操作太频繁,请稍后再试', 'aksrt')], 429); } $captcha_result = aksrt_require_captcha('tip_user', wp_unslash($_POST)); if ($captcha_result !== true) { $captcha_error = is_wp_error($captcha_result) ? $captcha_result : null; $captcha_data = $captcha_error ? $captcha_error->get_error_data() : null; $captcha_status = is_array($captcha_data) ? (int) ($captcha_data['status'] ?? 400) : 400; wp_send_json_error([ 'code' => $captcha_error ? ($captcha_error->get_error_code() ?: 'aksrt_captcha_failed') : 'aksrt_captcha_failed', 'message' => $captcha_error ? ($captcha_error->get_error_message() ?: __('人机验证失败,请重试', 'aksrt')) : __('人机验证失败,请重试', 'aksrt'), ], $captcha_status > 0 ? $captcha_status : 400); } set_transient($rate_key, $operation_key, 3); $result = \AKSRT\Ledger\LedgerService::get_instance()->transfer( $sender_id, $recipient_id, \AKSRT\Ledger\AssetType::BALANCE, $amount->minor(), 0, $operation_key, 'tip', ['type' => 'tip', 'id' => $post_id], $post_id > 0 ? sprintf(__('赞赏文章 #%d', 'aksrt'), $post_id) : __('用户赞赏', 'aksrt'), __('收到用户赞赏', 'aksrt') ); if (is_wp_error($result)) { wp_send_json_error(['message' => $result->get_error_message()], (int) ($result->get_error_data()['status'] ?? 400)); } wp_send_json_success([ 'message' => __('赞赏成功,金额已进入对方余额', 'aksrt'), 'replayed' => $result->replayed, 'balance' => \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, $result->balance($sender_id, \AKSRT\Ledger\AssetType::BALANCE))->decimal(), ]); });