$data * @param array $settings * @return array{success:bool,message?:string} */ private function save_pay_meta(int $post_id, array $data, int $user_id, array $settings): array { if (!$this->user_can_cap('new_post_pay', $user_id)) { return ['success' => true]; } // Only touch pay meta when the form sent pay fields. $has_pay_payload = isset($data['aksrt_pay']) || isset($data['pay_enabled']); if (!$has_pay_payload) { return ['success' => true]; } $raw = is_array($data['aksrt_pay'] ?? null) ? $data['aksrt_pay'] : []; $enabled = !empty($data['pay_enabled']); $existing = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : DomainModel::default_pay_meta(); $meta = is_array($existing) ? $existing : DomainModel::default_pay_meta(); if (!$enabled) { $meta['pay_type'] = DomainModel::PAY_TYPE_NO; $this->write_pay_meta($post_id, $meta); return ['success' => true]; } $pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? '0')); $pay_modo = $pay_modo === 'points' ? 'points' : '0'; $pay_type = DomainModel::normalize_pay_type((string) ($raw['pay_type'] ?? DomainModel::PAY_TYPE_READING)); if ($pay_type === DomainModel::PAY_TYPE_NO) { $pay_type = DomainModel::PAY_TYPE_READING; } $downloads = DomainModel::sanitize_downloads($raw['downloads'] ?? []); if (!empty($downloads) && $this->user_can_cap('new_post_pay_download', $user_id)) { $pay_type = DomainModel::PAY_TYPE_DOWNLOAD; $meta['downloads'] = $downloads; } elseif ($pay_type === DomainModel::PAY_TYPE_DOWNLOAD && !$this->user_can_cap('new_post_pay_download', $user_id)) { $pay_type = DomainModel::PAY_TYPE_READING; $meta['downloads'] = []; } $meta['pay_type'] = $pay_type; $meta['pay_modo'] = $pay_modo; $allow_free = !empty($settings['pay_free_s']); $vip_manual = !empty($settings['pay_vip_price_s']); $vip1_enabled = !array_key_exists('pay_vip_1_enabled', $settings) || !empty($settings['pay_vip_1_enabled']); $vip2_enabled = !array_key_exists('pay_vip_2_enabled', $settings) || !empty($settings['pay_vip_2_enabled']); $vip1_disc = (int) ($settings['pay_vip_1_discount'] ?? 100); $vip2_disc = (int) ($settings['pay_vip_2_discount'] ?? 100); if ($pay_modo === 'points') { $points = $this->parse_front_points($raw['points_price'] ?? $data['points_price'] ?? null); $min = $this->parse_front_points_limit($settings['pay_price_min'] ?? '0.00'); $max = $this->parse_front_points_limit($settings['pay_price_max'] ?? '0.00'); if ($points === null) { return ['success' => false, 'message' => __('请设置正确的积分价格', 'aksrt')]; } if (!$allow_free && $points <= 0) { return ['success' => false, 'message' => __('请设置付费内容的积分价格', 'aksrt')]; } if ($min > 0 && $points < $min) { return ['success' => false, 'message' => sprintf(__('积分价格不能低于:%s', 'aksrt'), (string) $min)]; } if ($max > 0 && $points > $max) { return ['success' => false, 'message' => sprintf(__('积分价格不能高于:%s', 'aksrt'), (string) $max)]; } if ($vip_manual) { $vip1 = $vip1_enabled ? $this->parse_front_points($raw['vip1_points'] ?? null) : 0; $vip2 = $vip2_enabled ? $this->parse_front_points($raw['vip2_points'] ?? null) : 0; if (($vip1_enabled && $vip1 === null) || ($vip2_enabled && $vip2 === null)) { return ['success' => false, 'message' => __('请设置正确的会员积分价格', 'aksrt')]; } if ($vip1_enabled && $vip1 > $points) { return ['success' => false, 'message' => __('会员积分价格不能高于普通积分价格', 'aksrt')]; } if ($vip2_enabled && $vip2 > ($vip1_enabled ? $vip1 : $points)) { return ['success' => false, 'message' => __('更高等级会员积分价格不能高于较低等级会员积分价格', 'aksrt')]; } } else { $vip1 = (intdiv($points, 100) * $vip1_disc) + intdiv(($points % 100) * $vip1_disc, 100); $vip2 = (intdiv($points, 100) * $vip2_disc) + intdiv(($points % 100) * $vip2_disc, 100); } $vip1 = max(0, min($points, (int) $vip1)); $vip2 = max(0, min($vip1_enabled ? $vip1 : $points, (int) $vip2)); $meta['points_price'] = $points; $meta['price'] = '0.00'; $meta['vip1_points'] = $vip1; $meta['vip2_points'] = $vip2; $meta['vip1_price'] = '0.00'; $meta['vip2_price'] = '0.00'; } else { $price_minor = $this->parse_front_cash_minor($raw['price'] ?? null); $min_minor = $this->parse_front_cash_minor($settings['pay_price_min'] ?? '0') ?? 0; $max_minor = $this->parse_front_cash_minor($settings['pay_price_max'] ?? '0') ?? 0; if ($price_minor === null) { return ['success' => false, 'message' => __('请设置正确的价格', 'aksrt')]; } if (!$allow_free && $price_minor <= 0) { return ['success' => false, 'message' => __('请设置付费内容的价格', 'aksrt')]; } if ($min_minor > 0 && $price_minor < $min_minor) { return ['success' => false, 'message' => sprintf(__('价格不能低于:%s', 'aksrt'), \AKSRT\Ledger\AssetAmount::decimal_from_minor($min_minor))]; } if ($max_minor > 0 && $price_minor > $max_minor) { return ['success' => false, 'message' => sprintf(__('价格不能高于:%s', 'aksrt'), \AKSRT\Ledger\AssetAmount::decimal_from_minor($max_minor))]; } if ($vip_manual) { $vip1_minor = $vip1_enabled ? $this->parse_front_cash_minor($raw['vip1_price'] ?? null) : 0; $vip2_minor = $vip2_enabled ? $this->parse_front_cash_minor($raw['vip2_price'] ?? null) : 0; if (($vip1_enabled && $vip1_minor === null) || ($vip2_enabled && $vip2_minor === null)) { return ['success' => false, 'message' => __('请设置正确的会员价格', 'aksrt')]; } if ($vip1_enabled && $vip1_minor > $price_minor) { return ['success' => false, 'message' => __('会员价不能高于普通价', 'aksrt')]; } if ($vip2_enabled && $vip2_minor > ($vip1_enabled ? $vip1_minor : $price_minor)) { return ['success' => false, 'message' => __('更高等级会员价不能高于较低等级会员价', 'aksrt')]; } } else { $vip1_minor = \AKSRT\Ledger\AssetAmount::percentage($price_minor, max(0, min(100, $vip1_disc))); $vip2_minor = \AKSRT\Ledger\AssetAmount::percentage($price_minor, max(0, min(100, $vip2_disc))); } $vip1_minor = max(0, min($price_minor, (int) $vip1_minor)); $vip2_minor = max(0, min($vip1_enabled ? $vip1_minor : $price_minor, (int) $vip2_minor)); $meta['price'] = \AKSRT\Ledger\AssetAmount::decimal_from_minor($price_minor); $meta['points_price'] = 0; $meta['vip1_price'] = \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip1_minor); $meta['vip2_price'] = \AKSRT\Ledger\AssetAmount::decimal_from_minor($vip2_minor); $meta['vip1_points'] = 0; $meta['vip2_points'] = 0; } $pay_limit = (int) ($raw['pay_limit'] ?? 0); $meta['pay_limit'] = in_array($pay_limit, [0, 1, 2], true) ? $pay_limit : 0; $this->write_pay_meta($post_id, $meta); return ['success' => true]; } private function parse_front_points($value): ?int { if (is_int($value)) { return $value >= 0 ? $value : null; } if (!is_string($value) || !preg_match('/^(0|[1-9]\d*)$/', trim($value))) { return null; } $value = trim($value); if (strlen($value) > strlen((string) PHP_INT_MAX) || (strlen($value) === strlen((string) PHP_INT_MAX) && strcmp($value, (string) PHP_INT_MAX) > 0) ) { return null; } return (int) $value; } private function parse_front_cash_minor($value): ?int { if (!is_string($value)) { return null; } try { return \AKSRT\Ledger\AssetAmount::balance_minor($value); } catch (\InvalidArgumentException $e) { return null; } } private function parse_front_points_limit($value): int { if (!is_string($value) || !preg_match('/^(0|[1-9]\d*)(?:\.00)?$/', trim($value), $matches)) { return 0; } return $this->parse_front_points((string) $matches[1]) ?? 0; } /** * @param array $meta */ private function write_pay_meta(int $post_id, array $meta): void { $meta = DomainModel::sanitize_pay_meta($meta); update_post_meta($post_id, DomainModel::PAY_META_KEY, $meta); update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) ($meta['pay_type'] ?? DomainModel::PAY_TYPE_NO)); update_post_meta( $post_id, DomainModel::PAY_PRICE_KEY, ($meta['pay_modo'] ?? '0') === 'points' ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00') ); update_post_meta( $post_id, DomainModel::RESOURCE_MARKER_KEY, DomainModel::is_resource_pay_type((string) ($meta['pay_type'] ?? 'no')) ? 1 : 0 ); } }