*/ public static function order_product_type_labels(): array { return [ self::ORDER_PRODUCT_RESOURCE => __('付费内容', 'aksrt'), self::ORDER_PRODUCT_VIP => __('VIP 会员', 'aksrt'), self::ORDER_PRODUCT_RECHARGE => __('余额充值', 'aksrt'), ]; } public const PAY_TYPE_NO = 'no'; public const PAY_TYPE_READING = 'reading'; public const PAY_TYPE_DOWNLOAD = 'download'; public const PAY_TYPE_GALLERY = 'gallery'; public const PAY_TYPE_VIDEO = 'video'; public const PAY_TYPE_VIP = 'vip'; public const ORDER_STATUS_CREATED = 'created'; public const ORDER_STATUS_PENDING = 'pending'; public const ORDER_STATUS_PAID = 'paid'; public const ORDER_STATUS_DELIVERED = 'delivered'; public const ORDER_STATUS_COMPLETED = 'completed'; public const ORDER_STATUS_CANCELLED = 'cancelled'; public const ORDER_STATUS_EXPIRED = 'expired'; public const ORDER_STATUS_REFUNDING = 'refunding'; public const ORDER_STATUS_REFUNDED = 'refunded'; public const ORDER_STATUS_PENDING_REVIEW = 'pending_review'; public const SHIPPING_STATUS_PENDING = 0; public const SHIPPING_STATUS_SHIPPED = 1; public const SHIPPING_STATUS_COMPLETED = 2; /** * @return array */ public static function default_post_meta(): array { return [ 'cover_images' => '', 'custom_excerpt' => '', 'layout_override' => '', 'header_image_mode' => '', 'show_toc' => '', 'show_reading_progress' => '', 'enable_lightbox' => '', 'show_related' => '', 'related_count' => 0, 'enable_like' => '', 'enable_favorite' => '', 'enable_share' => '', 'enable_print' => '', 'enable_pdf' => '', 'copyright_text' => '', 'is_featured' => false, // §3.8 文章扩展字段 'thumbnail_url' => '', 'featured_slide' => '', 'featured_video' => '', // Episode list: [ ['url' => string, 'title' => string], ... ] 'featured_video_episode' => [], 'subtitle' => '', 'views' => 0, 'likes' => 0, 'maxheight' => 0, ]; } /** * @return array */ public static function default_term_meta(): array { return [ 'cover_url' => '', 'description' => '', 'layout_mode' => '', 'show_sidebar' => '', 'show_header' => true, 'show_featured' => false, ]; } /** * @return array */ public static function default_pay_meta(): array { return [ 'pay_type' => self::PAY_TYPE_NO, 'pay_modo' => '0', // '0' cash | 'points' credits (Zibll) 'price' => 0.0, 'points_price' => 0, 'original_price' => 0.0, 'vip1_price' => 0.0, 'vip2_price' => 0.0, 'vip1_points' => 0, 'vip2_points' => 0, 'pay_limit' => 0, // 0 all | 1 VIP1+ | 2 VIP2 only (Zibll) 'version' => '', 'size' => '', 'license' => '', 'compat' => '', 'demo_url' => '', 'video_url' => '', 'downloads' => [], 'attributes' => [], 'gallery' => [], 'require_login' => false, 'require_captcha' => false, // §3.8 电商增强字段 'virtual_sales' => 0, 'stock' => -1, 'card_keys' => [], 'purchase_limit' => 0, 'free_preview' => '', 'show_sales' => true, // Zibll: free daily quota overbuy price (cash or points by pay_modo). 'download_limit_over_price' => 0.0, // Content cashier: coupon + buyer-facing 推广折扣 (zibll posts_zibpay). 'coupon_s' => false, 'coupon_desc' => '', 'pay_rebate_discount' => 0.0, ]; } /** * @return array */ public static function pay_type_labels(): array { return [ self::PAY_TYPE_NO => '不付费', self::PAY_TYPE_READING => '付费阅读', self::PAY_TYPE_DOWNLOAD => '付费下载', self::PAY_TYPE_GALLERY => '付费图库', self::PAY_TYPE_VIDEO => '付费视频', self::PAY_TYPE_VIP => 'VIP 专属', ]; } public static function normalize_pay_type(string $pay_type): string { $pay_type = sanitize_key($pay_type); return array_key_exists($pay_type, self::pay_type_labels()) ? $pay_type : self::PAY_TYPE_NO; } public static function is_resource_pay_type(string $pay_type): bool { return self::normalize_pay_type($pay_type) === self::PAY_TYPE_DOWNLOAD; } /** * @param mixed $value * @return array> */ public static function sanitize_downloads($value): array { if (!is_array($value)) { return []; } $allowed_types = ['direct', 'net_disk', 'external']; $downloads = []; foreach ($value as $row) { if (!is_array($row)) { continue; } $url = isset($row['url']) ? esc_url_raw((string) $row['url']) : ''; if ($url === '') { continue; } $type = isset($row['type']) && in_array($row['type'], $allowed_types, true) ? (string) $row['type'] : 'direct'; $extract_code = ''; foreach ([ 'extract_code', 'extractCode', 'extract', 'extract_key', 'extraction_code', 'extractionCode', 'code', 'password', 'pwd', 'pan_code', 'panCode', 'pan_password', 'panPassword', 'access_code', 'accessCode', ] as $extract_key) { if (isset($row[$extract_key]) && !is_array($row[$extract_key])) { $candidate = trim((string) $row[$extract_key]); if ($candidate !== '') { $extract_code = sanitize_text_field($candidate); break; } } } $downloads[] = [ 'label' => isset($row['label']) ? sanitize_text_field((string) $row['label']) : '', 'url' => $url, 'type' => $type, 'extract_code' => $extract_code, ]; } return array_values($downloads); } /** * @param mixed $value * @return array> */ public static function sanitize_attributes($value): array { if (!is_array($value)) { return []; } $attributes = []; foreach ($value as $row) { if (!is_array($row)) { continue; } $key = sanitize_text_field((string) ($row['key'] ?? '')); $item_value = sanitize_text_field((string) ($row['value'] ?? '')); if ($key === '' && $item_value === '') { continue; } $attributes[] = [ 'key' => $key, 'value' => $item_value, ]; } return array_values($attributes); } /** * @param mixed $value * @return string[] */ public static function sanitize_gallery($value): array { if (!is_array($value)) { return []; } $gallery = []; foreach ($value as $url) { $url = esc_url_raw((string) $url); if ($url !== '') { $gallery[] = $url; } } return array_values(array_unique($gallery)); } /** * Sanitize card/key codes for auto-delivery. * Each entry is a non-empty sanitized string (one code per line). */ public static function sanitize_card_keys($value): array { if (!is_array($value)) { return []; } $keys = []; foreach ($value as $key) { $key = sanitize_text_field((string) $key); if ($key !== '') { $keys[] = $key; } } return $keys; } /** * @param array $raw * @return array */ public static function sanitize_pay_meta(array $raw): array { $meta = self::default_pay_meta(); $meta['pay_type'] = self::normalize_pay_type((string) ($raw['pay_type'] ?? $meta['pay_type'])); $pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? '0')); $meta['pay_modo'] = $pay_modo === 'points' ? 'points' : '0'; $meta['price'] = max(0, (float) ($raw['price'] ?? 0)); $meta['points_price'] = max(0, (int) ($raw['points_price'] ?? $raw['price'] ?? 0)); $meta['original_price'] = max(0, (float) ($raw['original_price'] ?? 0)); $meta['vip1_price'] = max(0, (float) ($raw['vip1_price'] ?? 0)); $meta['vip2_price'] = max(0, (float) ($raw['vip2_price'] ?? 0)); $meta['vip1_points'] = max(0, (int) ($raw['vip1_points'] ?? $raw['vip1_price'] ?? 0)); $meta['vip2_points'] = max(0, (int) ($raw['vip2_points'] ?? $raw['vip2_price'] ?? 0)); $pay_limit = (int) ($raw['pay_limit'] ?? 0); $meta['pay_limit'] = in_array($pay_limit, [0, 1, 2], true) ? $pay_limit : 0; $meta['version'] = sanitize_text_field((string) ($raw['version'] ?? '')); $meta['size'] = sanitize_text_field((string) ($raw['size'] ?? '')); $meta['license'] = sanitize_text_field((string) ($raw['license'] ?? '')); $meta['compat'] = sanitize_text_field((string) ($raw['compat'] ?? '')); $meta['demo_url'] = esc_url_raw((string) ($raw['demo_url'] ?? '')); $meta['video_url'] = esc_url_raw((string) ($raw['video_url'] ?? '')); $meta['downloads'] = self::sanitize_downloads($raw['downloads'] ?? []); $meta['attributes'] = self::sanitize_attributes($raw['attributes'] ?? []); $meta['gallery'] = self::sanitize_gallery($raw['gallery'] ?? []); $meta['require_login'] = !empty($raw['require_login']); $meta['require_captcha'] = !empty($raw['require_captcha']); // §3.8 电商增强字段 $meta['virtual_sales'] = max(0, (int) ($raw['virtual_sales'] ?? 0)); $meta['stock'] = (int) ($raw['stock'] ?? -1); $meta['card_keys'] = self::sanitize_card_keys($raw['card_keys'] ?? []); $meta['purchase_limit'] = max(0, (int) ($raw['purchase_limit'] ?? 0)); $meta['free_preview'] = wp_kses_post((string) ($raw['free_preview'] ?? '')); $meta['show_sales'] = !isset($raw['show_sales']) || !empty($raw['show_sales']); $meta['download_limit_over_price'] = max(0, (float) ($raw['download_limit_over_price'] ?? 0)); $meta['coupon_s'] = !empty($raw['coupon_s']); $meta['coupon_desc'] = sanitize_textarea_field((string) ($raw['coupon_desc'] ?? '')); $meta['pay_rebate_discount'] = max(0, (float) ($raw['pay_rebate_discount'] ?? 0)); return $meta; } /** * @return array */ public static function order_status_labels(): array { return [ self::ORDER_STATUS_CREATED => __('已创建', 'aksrt'), self::ORDER_STATUS_PENDING => __('待支付', 'aksrt'), self::ORDER_STATUS_PAID => __('已支付', 'aksrt'), self::ORDER_STATUS_DELIVERED => __('已发货', 'aksrt'), self::ORDER_STATUS_COMPLETED => __('已完成', 'aksrt'), self::ORDER_STATUS_CANCELLED => __('已取消', 'aksrt'), self::ORDER_STATUS_EXPIRED => __('已过期', 'aksrt'), self::ORDER_STATUS_REFUNDING => __('退款中', 'aksrt'), self::ORDER_STATUS_REFUNDED => __('已退款', 'aksrt'), self::ORDER_STATUS_PENDING_REVIEW => __('待评价', 'aksrt'), ]; } /** * @return array */ public static function order_transitions(): array { return [ self::ORDER_STATUS_CREATED => [ self::ORDER_STATUS_PENDING, self::ORDER_STATUS_CANCELLED, ], self::ORDER_STATUS_PENDING => [ self::ORDER_STATUS_PAID, self::ORDER_STATUS_CANCELLED, self::ORDER_STATUS_EXPIRED, ], self::ORDER_STATUS_PAID => [ self::ORDER_STATUS_DELIVERED, self::ORDER_STATUS_COMPLETED, self::ORDER_STATUS_REFUNDING, ], self::ORDER_STATUS_DELIVERED => [ self::ORDER_STATUS_COMPLETED, self::ORDER_STATUS_PENDING_REVIEW, self::ORDER_STATUS_REFUNDING, ], self::ORDER_STATUS_COMPLETED => [ self::ORDER_STATUS_PENDING_REVIEW, self::ORDER_STATUS_REFUNDING, ], self::ORDER_STATUS_CANCELLED => [], self::ORDER_STATUS_EXPIRED => [ self::ORDER_STATUS_PENDING, ], self::ORDER_STATUS_REFUNDING => [ self::ORDER_STATUS_REFUNDED, self::ORDER_STATUS_PAID, ], self::ORDER_STATUS_REFUNDED => [], self::ORDER_STATUS_PENDING_REVIEW => [ self::ORDER_STATUS_COMPLETED, self::ORDER_STATUS_REFUNDING, ], ]; } public static function normalize_order_status(string $status, string $default = self::ORDER_STATUS_PENDING): string { $status = sanitize_key($status); return array_key_exists($status, self::order_status_labels()) ? $status : $default; } public static function can_transition_order_status(string $from, string $to): bool { $from = self::normalize_order_status($from, self::ORDER_STATUS_CREATED); $to = self::normalize_order_status($to); $transitions = self::order_transitions(); return isset($transitions[$from]) && in_array($to, $transitions[$from], true); } /** * @return string[] */ public static function paid_order_statuses(): array { return [ self::ORDER_STATUS_PAID, self::ORDER_STATUS_DELIVERED, self::ORDER_STATUS_COMPLETED, self::ORDER_STATUS_PENDING_REVIEW, ]; } public static function is_paid_order_status(string $status): bool { return in_array(self::normalize_order_status($status), self::paid_order_statuses(), true); } /** * @return string[] */ public static function vip_active_order_statuses(): array { return self::paid_order_statuses(); } }