*/ public static function get_paid_content_settings(): array { $settings = Settings::get_all(); $box_position = sanitize_key((string) ($settings['pay_box_position'] ?? 'top')); // legacy: box_top | top | bottom | box_bottom if (!in_array($box_position, ['box_top', 'top', 'bottom', 'box_bottom'], true)) { $box_position = 'top'; } return [ // 下载门槛永远支持积分购买模式(不再由页面开关控制)。 'download_credit' => Settings::credits_enabled(), 'default_cost' => Settings::download_default_cost(), // 付费内容仅文章。 'paid_post_types' => ['post'], 'vip_free_download' => false, 'vip_exempt_roles' => [], 'catalog_note' => '', 'manual_delivery_note' => __('付费或人工确认内容请在订单备注中记录交付方式。', 'aksrt'), 'no_logged_in' => !isset($settings['pay_no_logged_in']) || !empty($settings['pay_no_logged_in']), 'free_logged_show' => !isset($settings['pay_free_logged_show']) || !empty($settings['pay_free_logged_show']), 'down_alone_page' => !empty($settings['down_alone_page']), 'display' => [ 'mark' => sanitize_text_field((string) ($settings['pay_mark'] ?? '付费')), 'currency_symbol' => sanitize_text_field((string) ($settings['pay_currency_mark'] ?? '¥')), 'currency_unit' => sanitize_text_field((string) ($settings['pay_currency_unit'] ?? '元')), 'show_user' => !isset($settings['pay_show_user']) || !empty($settings['pay_show_user']), 'show_paycount' => !isset($settings['pay_show_paycount']) || !empty($settings['pay_show_paycount']), 'box_position' => $box_position, ], 'download' => [ 'enabled' => true, 'remote' => !isset($settings['down_remote_to_local_s']) || !empty($settings['down_remote_to_local_s']), 'verify_nonce' => !isset($settings['down_verify_nonce']) || !empty($settings['down_verify_nonce']), 'remote_size' => max(0, (int) ($settings['down_remote_to_local_size'] ?? 2048)), 'remote_ext' => sanitize_text_field((string) ($settings['down_remote_to_local_ext'] ?? 'zip,rar,7z')), 'alone_page' => !empty($settings['down_alone_page']), ], // 内容收银台优惠码:全局开关 + 默认说明。 'coupon_post_s' => !empty($settings['coupon_post_s']), 'coupon_desc' => sanitize_textarea_field((string) ($settings['coupon_desc'] ?? '')), ]; } /** * Content coupon global switches (legacy coupon_post_s). * * @return array{enabled:bool,desc:string} */ public static function get_content_coupon_settings(): array { $settings = self::get_paid_content_settings(); return [ 'enabled' => !empty($settings['coupon_post_s']), 'desc' => (string) ($settings['coupon_desc'] ?? ''), ]; } /** * Balance recharge settings (legacy pay_balance_* contract). * * @return array */ public static function get_payment_recharge_settings(): array { $settings = Settings::get_all(); // 该选项可能存字符串(旧格式)或数组(结构化保存):先判类型再转换, // 避免对数组做强转触发 "Array to string conversion" 警告。 $raw_packages = $settings['pay_balance_product'] ?? null; $packages = is_array($raw_packages) ? self::format_product_lines($raw_packages) : (string) ($raw_packages ?? "30|30|入门包\n98|108|推荐包"); $desc = sanitize_textarea_field((string) ($settings['pay_balance_desc'] ?? '')); return [ 'enabled' => !empty($settings['pay_balance_s']), 'withdraw_enabled' => !empty($settings['pay_balance_withdraw_s']), 'desc' => $desc, 'packages_raw' => $packages, 'package_count' => self::count_structured_lines($packages), 'help_text' => $desc !== '' ? $desc : __('当前支持人工确认与测试联调;接入真实网关时复用同一组字段。', 'aksrt'), 'product' => [ 'name' => sanitize_text_field((string) ($settings['pay_balance_product_name'] ?? '')), 'price' => self::money_decimal($settings['pay_balance_price'] ?? '0.00', '0.00'), 'tag' => sanitize_text_field((string) ($settings['pay_balance_tag'] ?? '元')), 'tag_class' => sanitize_text_field((string) ($settings['pay_balance_tag_class'] ?? '')), 'custom_s' => !isset($settings['pay_balance_product_custom_s']) || !empty($settings['pay_balance_product_custom_s']), 'custom_limit' => max(1, min(99999, (int) ($settings['pay_balance_product_custom_limit'] ?? 9999))), ], 'transfer' => [ 'enabled' => !empty($settings['pay_balance_transfer_s']), 'service_charge' => max(0, min(50, (int) ($settings['pay_balance_transfer_service_charge'] ?? 0))), 'desc' => sanitize_textarea_field((string) ($settings['pay_balance_transfer_desc'] ?? '')), ], 'pass_charge' => [ // 三个独立开关:各类型卡密只进入各自的购买弹窗(收款接口同款拆分)。 'balance' => [ 'enabled' => !empty($settings['pay_balance_pass_charge_s']), 'desc' => sanitize_textarea_field((string) ($settings['pay_balance_pass_charge_desc'] ?? '')), 'only_password' => !empty($settings['pay_balance_pass_charge_only_password']), ], 'credits' => [ 'enabled' => !empty($settings['points_pass_exchange_s']), 'desc' => sanitize_textarea_field((string) ($settings['points_pass_exchange_desc'] ?? '')), 'only_password' => !empty($settings['points_pass_exchange_only_password']), ], 'vip' => [ 'enabled' => !empty($settings['pay_vip_pass_charge_s']), 'desc' => sanitize_textarea_field((string) ($settings['pay_vip_pass_charge_desc'] ?? '')), // 默认非单密码(卡号+密码),对齐参考主题 pay_vip_pass_charge_only_password 默认 false。 'only_password' => !empty($settings['pay_vip_pass_charge_only_password']), ], ], 'points_exchange' => [ 'enabled' => !empty($settings['pay_balance_points_exchange_s']), 'rate' => max(1, min(10000, (int) ($settings['pay_balance_points_exchange_rate'] ?? 100))), 'min_points' => max(1, min(10000000, (int) ($settings['pay_balance_points_exchange_min_points'] ?? 100))), 'daily_max_points' => max(1, min(10000000, (int) ($settings['pay_balance_points_exchange_daily_max_points'] ?? 100000))), 'desc' => sanitize_textarea_field((string) ($settings['pay_balance_points_exchange_desc'] ?? '')), 'products_raw' => is_array($settings['pay_points_product'] ?? null) ? self::format_points_product_lines($settings['pay_points_product']) : (string) ($settings['pay_points_product'] ?? ''), ], ]; } /** * Resolve a configured VIP icon, falling back to the bundled level asset. */ private static function get_vip_icon_url(int $level, string $configured): string { $configured = esc_url_raw($configured); if ($configured !== '') { return $configured; } $filename = 'vip-' . $level . '.svg'; if (defined('AKSRT_DIR') && is_file(AKSRT_DIR . '/assets/img/user/vip/' . $filename)) { return defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/user/vip/' . $filename : ''; } return ''; } /** * Dual-level VIP settings (legacy pay_user_vip_1 / pay_user_vip_2). * * @return array */ public static function get_payment_vip_settings(): array { $settings = Settings::get_all(); $vip1_enabled = !empty($settings['pay_user_vip_1_s']); $vip2_enabled = !empty($settings['pay_user_vip_2_s']); $enabled = $vip1_enabled || $vip2_enabled; $name1 = trim((string) ($settings['pay_user_vip_1_name'] ?? '')); $name2 = trim((string) ($settings['pay_user_vip_2_name'] ?? '')); if ($name1 === '') { $name1 = __('一级会员', 'aksrt'); } if ($name2 === '') { $name2 = __('二级会员', 'aksrt'); } $badge = $vip2_enabled ? $name2 : ($vip1_enabled ? $name1 : __('VIP', 'aksrt')); $renew_price_type = sanitize_key((string) ($settings['vip_renew_price_type'] ?? 'discount')); if (!in_array($renew_price_type, ['same', 'discount', 'reduce'], true)) { $renew_price_type = 'discount'; } $default_l1 = "30|59|1|month|限时|月卡\n98|199|3|month|特惠|季卡\n365|599|12|month|超值|年卡\n999|1299|0|day|永久|永久卡"; $default_l2 = "58|99|1|month|限时|月卡\n168|299|3|month|特惠|季卡\n598|999|12|month|超值|年卡\n1299|1999|0|day|永久|永久卡"; $products_1 = sanitize_textarea_field((string) ($settings['payment_vip_1_products'] ?? $default_l1)); $products_2 = sanitize_textarea_field((string) ($settings['payment_vip_2_products'] ?? $default_l2)); // Restore newlines lost only if empty after sanitize — sanitize_textarea_field keeps \n. if (trim($products_1) === '') { $products_1 = $default_l1; } if (trim($products_2) === '') { $products_2 = $default_l2; } $desc = sanitize_textarea_field((string) ($settings['pay_user_vip_desc'] ?? '')); $more = (string) ($settings['pay_user_vip_more'] ?? ''); $allowed_equity = [ 'li' => [], 'ul' => [], 'ol' => [], 'strong' => [], 'b' => [], 'em' => [], 'i' => ['class' => true], 'span' => ['class' => true], 'br' => [], 'a' => ['href' => true, 'target' => true, 'rel' => true, 'class' => true], ]; return [ 'enabled' => $enabled, 'nav_pay_vip' => !isset($settings['nav_pay_vip']) || !empty($settings['nav_pay_vip']), 'nav_user_pay_vip' => !isset($settings['nav_user_pay_vip']) || !empty($settings['nav_user_pay_vip']), 'name' => $vip1_enabled ? $name1 : $badge, 'badge_text' => $badge, 'desc' => $desc, 'more' => wp_kses($more, $allowed_equity + ['div' => ['class' => true], 'p' => ['class' => true]]), 'help_text' => $desc !== '' ? $desc : __('会员权益以免扣下载和身份展示为主,开通后立即生效。', 'aksrt'), 'levels' => [ 1 => [ 'enabled' => $vip1_enabled, 'name' => $name1, 'desc' => sanitize_text_field((string) ($settings['pay_user_vip_1_desc'] ?? __('尊享一级会员特权', 'aksrt'))), 'equity' => wp_kses((string) (($settings['pay_user_vip_1_equity'] ?? '') !== '' ? $settings['pay_user_vip_1_equity'] : '
  • 免费下载部分资源
  • 会员专属身份标识
  • 付费内容会员价
  • '), $allowed_equity), 'img_icon' => self::get_vip_icon_url(1, (string) ($settings['vip_1_img_icon'] ?? '')), 'product_s' => !isset($settings['vip_1_product_s']) || !empty($settings['vip_1_product_s']), 'products_raw' => $products_1, 'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_1'] ?? 0))), ], 2 => [ 'enabled' => $vip2_enabled, 'name' => $name2, 'desc' => sanitize_text_field((string) ($settings['pay_user_vip_2_desc'] ?? __('尊享二级会员特权', 'aksrt'))), 'equity' => wp_kses((string) (($settings['pay_user_vip_2_equity'] ?? '') !== '' ? $settings['pay_user_vip_2_equity'] : '
  • 更高免费下载额度
  • 更高等级身份标识
  • 更多付费内容会员价
  • '), $allowed_equity), 'img_icon' => self::get_vip_icon_url(2, (string) ($settings['vip_2_img_icon'] ?? '')), 'product_s' => !isset($settings['vip_2_product_s']) || !empty($settings['vip_2_product_s']), 'products_raw' => $products_2, 'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit_vip_2'] ?? 0))), ], ], 'benefit' => [ 'download_limit' => max(0, min(9999, (int) ($settings['pay_download_limit'] ?? 0))), ], 'renew' => [ 'enabled' => !isset($settings['vip_renew']) || !empty($settings['vip_renew']), 'desc' => sanitize_textarea_field((string) ($settings['vip_renew_desc'] ?? '')), 'price_type' => $renew_price_type, 'discount' => max(1, min(100, (int) ($settings['vip_renew_discount'] ?? 8))), 'reduce' => self::money_decimal($settings['vip_renew_reduce'] ?? '20.00', '20.00', 999900), ], 'upgrade' => [ 'enabled' => !isset($settings['vip_upgrade']) || !empty($settings['vip_upgrade']), 'desc' => sanitize_textarea_field((string) ($settings['vip_upgrade_desc'] ?? '')), // Multi-line upgrade product rules — must not collapse newlines. 'product' => sanitize_textarea_field((string) ($settings['vip_upgrade_product'] ?? '')), ], 'points_exchange' => [ 'enabled' => !empty($settings['pay_vip_points_exchange_s']), 'desc' => sanitize_textarea_field((string) ($settings['pay_vip_points_exchange_desc'] ?? '')), 'packages_raw' => sanitize_textarea_field((string) ($settings['payment_vip_points_packages'] ?? "1|500|30|积分月卡\n1|5000|0|积分永久")), ], ]; } /** * Creator-income policy. Cash and points intentionally use separate ratio sets. * * @return array{enabled:bool,cycle_days:int,ratios:array>,note:string} */ public static function get_payment_revenue_settings(): array { $settings = Settings::get_all(); $note = trim((string) ($settings['payment_revenue_note'] ?? '')); $clamp_ratio = static fn ($value): int => max(0, min(100, (int) $value)); return [ 'enabled' => !empty($settings['pay_income_s']), 'cycle_days' => max(1, min(365, (int) ($settings['payment_revenue_cycle_days'] ?? 30))), 'ratios' => [ 'cash' => [ 'ordinary' => $clamp_ratio($settings['payment_revenue_cash_ratio_user'] ?? 60), 'vip1' => $clamp_ratio($settings['payment_revenue_cash_ratio_vip_1'] ?? 70), 'vip2' => $clamp_ratio($settings['payment_revenue_cash_ratio_vip_2'] ?? 80), ], 'points' => [ 'ordinary' => $clamp_ratio($settings['payment_revenue_points_ratio_user'] ?? 0), 'vip1' => $clamp_ratio($settings['payment_revenue_points_ratio_vip_1'] ?? 0), 'vip2' => $clamp_ratio($settings['payment_revenue_points_ratio_vip_2'] ?? 0), ], ], 'note' => $note !== '' ? wp_kses_post($note) : __('

    订单完成并经过结算周期后,作者分成会自动转入对应的钱包。

    ', 'aksrt'), ]; } /** * Affiliate policy. Layout follows the reference theme: one rebate rule * set per user tier (pay_rebate_user_s / _1 / _2 + pay_rebate_ratio*). * * @return array */ public static function get_payment_affiliate_settings(): array { $settings = Settings::get_all(); $clamp_ratio = static fn($value): int => max(0, min(100, (int) $value)); $resolve_types = static function (array $types): array { $types = array_values(array_intersect(['all', 'membership', 'content', 'credits'], array_map('sanitize_key', $types))); // 「全部订单」展开为全部可返佣订单类型。 if (in_array('all', $types, true)) { return ['membership', 'content', 'credits']; } return $types; }; $types_by_level = [ 0 => $resolve_types((array) ($settings['pay_rebate_user_s'] ?? [])), 1 => $resolve_types((array) ($settings['pay_rebate_user_s_1'] ?? ['all'])), 2 => $resolve_types((array) ($settings['pay_rebate_user_s_2'] ?? ['all'])), ]; $judgment = sanitize_key((string) ($settings['pay_rebate_judgment'] ?? 'all')); if (!in_array($judgment, ['all', 'link'], true)) { $judgment = 'all'; } return [ 'enabled' => !empty($settings['pay_rebate_s']), 'judgment' => $judgment, 'show_users' => !isset($settings['pay_rebate_show_users']) || !empty($settings['pay_rebate_show_users']), 'self_rebate' => !isset($settings['pay_rebate_self_s']) || !empty($settings['pay_rebate_self_s']), 'ratios' => [ 'user' => $clamp_ratio($settings['pay_rebate_ratio'] ?? 5), 'vip_1' => $clamp_ratio($settings['pay_rebate_ratio_vip_1'] ?? 10), 'vip_2' => $clamp_ratio($settings['pay_rebate_ratio_vip_2'] ?? 20), ], 'order_types' => $types_by_level[0], 'order_types_by_level' => $types_by_level, 'disabled_code_stops_commission' => !empty($settings['affiliate_disabled_code_stops_commission']), 'window_days' => (int) CreditsRuntime::get_referral_settings()['window_days'], 'promotion_text' => sanitize_textarea_field((string) ($settings['pay_rebate_text_details'] ?? '')), 'details_title' => sanitize_text_field((string) ($settings['pay_rebate_text_details_title'] ?? '返佣详解')), 'poster' => [ 'enabled' => !isset($settings['pay_rebate_poster_s']) || !empty($settings['pay_rebate_poster_s']), 'image' => esc_url_raw((string) ($settings['pay_rebate_poster_img'] ?? '')), 'title' => sanitize_text_field((string) ($settings['pay_rebate_poster_title'] ?? '')), 'description' => sanitize_text_field((string) ($settings['pay_rebate_poster_desc'] ?? '')), ], ]; } /** * 某等级推广者可获返佣的订单类型;空数组代表该等级用户不参与推广返佣。 * * @return array */ public static function affiliate_order_types_for_level(int $level): array { $settings = self::get_payment_affiliate_settings(); $key = $level >= 2 ? 2 : ($level >= 1 ? 1 : 0); return (array) ($settings['order_types_by_level'][$key] ?? []); } public static function affiliate_enabled(): bool { return !empty(self::get_payment_affiliate_settings()['enabled']); } public static function affiliate_ratio_for_user(int $user_id): int { $level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0; return self::affiliate_ratio_for_level($level); } public static function affiliate_ratio_for_level(int $level): int { $settings = self::get_payment_affiliate_settings(); $key = $level >= 2 ? 'vip_2' : ($level >= 1 ? 'vip_1' : 'user'); return (int) ($settings['ratios'][$key] ?? 0); } public static function get_affiliate_settings(): array { return self::get_payment_affiliate_settings(); } /** * Gateway channel selection. WeChat / Alipay each pick between the * official SDK and 易支付; PayPal / Stripe are standalone toggles. * * @return array */ public static function get_payment_gateway_settings(): array { $settings = Settings::get_all(); $wechat = sanitize_key((string) ($settings['pay_wechat_sdk_options'] ?? 'null')); if (!in_array($wechat, ['official_wechat', 'epay', 'null'], true)) { $wechat = 'null'; } $alipay = sanitize_key((string) ($settings['pay_alipay_sdk_options'] ?? 'null')); if (!in_array($alipay, ['official_alipay', 'epay', 'null'], true)) { $alipay = 'null'; } $paypal_enabled = !empty($settings['pay_paypal_sdk_s']); $stripe_enabled = !empty($settings['pay_stripe_sdk_s']); $balance_enabled = !empty($settings['pay_balance_s']); // 卡密收款接口随三个兑换开关任一生效(余额 / 积分 / 会员)。 $card_pass_enabled = !empty($settings['pay_balance_pass_charge_s']) || !empty($settings['points_pass_exchange_s']) || !empty($settings['pay_vip_pass_charge_s']); return [ 'help_text' => __('微信 / 支付宝可在官方与易支付之间选择;PayPal、Stripe 独立开关。异步回调由各网关验签。', 'aksrt'), 'wechat_sdk_options' => $wechat, 'alipay_sdk_options' => $alipay, 'paypal_enabled' => $paypal_enabled, 'stripe_enabled' => $stripe_enabled, 'balance_enabled' => $balance_enabled, 'card_pass_enabled' => $card_pass_enabled, 'gateways' => [ 'wechat' => $wechat === 'official_wechat', 'alipay' => $alipay === 'official_alipay', 'paypal' => $paypal_enabled, 'stripe' => $stripe_enabled, 'balance' => $balance_enabled, 'card_pass' => $card_pass_enabled, 'epay' => $wechat === 'epay' || $alipay === 'epay', ], 'currency' => [ 'mark' => sanitize_text_field((string) ($settings['pay_currency_mark'] ?? '¥')), 'unit' => sanitize_text_field((string) ($settings['pay_currency_unit'] ?? '元')), ], 'custom_title_s' => !empty($settings['pay_sdk_custom_title_s']), 'custom_title' => sanitize_text_field((string) ($settings['pay_sdk_custom_title'] ?? '')), 'epay' => [ 'apiurl' => esc_url_raw((string) ($settings['epay_apiurl'] ?? '')), 'pid' => sanitize_text_field((string) ($settings['epay_pid'] ?? '')), 'key' => sanitize_text_field((string) ($settings['epay_key'] ?? '')), 'device' => sanitize_key((string) ($settings['epay_device'] ?? 'auto')), ], ]; } /** * Order display / checkout policy (legacy pay_* + AKSRT order ops). * * @return array */ public static function get_order_settings(): array { $s = Settings::get_all(); $expiry = max(5, min(1440, (int) ($s['payment_order_expiry'] ?? 30))); $cookie_day = max(1, min(31, (int) ($s['pay_cookie_day'] ?? 15))); $box_position = sanitize_key((string) ($s['pay_box_position'] ?? 'top')); if (!in_array($box_position, ['box_top', 'top', 'bottom', 'box_bottom'], true)) { $box_position = 'top'; } $status_keys = ['pending', 'paid', 'refunded', 'closed']; $status_defaults = ['待支付', '已支付', '已退款', '已关闭']; $status_texts = []; foreach ($status_keys as $i => $key) { $val = trim((string) ($s["payment_order_status_{$key}"] ?? '')); $status_texts[$key] = $val !== '' ? $val : $status_defaults[$i]; } return [ 'expiry' => $expiry, 'show_user' => !isset($s['pay_show_user']) || !empty($s['pay_show_user']), 'show_paycount' => !isset($s['pay_show_paycount']) || !empty($s['pay_show_paycount']), 'no_logged_in' => !isset($s['pay_no_logged_in']) || !empty($s['pay_no_logged_in']), 'cookie_day' => $cookie_day, 'no_logged_remind' => sanitize_textarea_field((string) ($s['pay_no_logged_remind'] ?? '')), 'wait_float' => !isset($s['pay_wait_float_s']) || !empty($s['pay_wait_float_s']), 'mark' => sanitize_text_field((string) ($s['pay_mark'] ?? '付费')), 'box_position' => $box_position, 'show_ip' => !empty($s['payment_show_ip']), 'show_device' => !empty($s['payment_show_device']), 'show_order_no' => !isset($s['payment_show_order_no']) || !empty($s['payment_show_order_no']), 'show_create_time' => !isset($s['payment_show_create_time']) || !empty($s['payment_show_create_time']), 'show_pay_time' => !isset($s['payment_show_pay_time']) || !empty($s['payment_show_pay_time']), 'status_texts' => $status_texts, 'service' => self::format_service_items((array) ($s['pay_service'] ?? [])), ]; } /** * 客户服务:参考主题为 {value, icon} 数组;首项同时回填旧单值键,兼容既有消费方。 * * @param array $items * @return array */ private static function format_service_items(array $items): array { $clean = []; foreach ($items as $row) { if (!is_array($row)) { continue; } $value = sanitize_textarea_field((string) ($row['value'] ?? '')); if ($value === '') { continue; } $clean[] = [ 'value' => $value, 'icon' => sanitize_text_field((string) ($row['icon'] ?? '')), ]; } $first = $clean[0] ?? []; return [ 'items' => $clean, 'text' => (string) ($first['value'] ?? ''), 'icon' => (string) ($first['icon'] ?? ''), 'value' => (string) ($first['value'] ?? ''), 'qq' => '', 'wechat' => '', ]; } /** * Return the UTC expiry time for a newly created payable order. * * Keeping this in the payment runtime prevents individual checkout * flows from drifting away from the admin "payment_order_expiry" setting. */ public static function get_order_expiry_at(?int $now = null): string { $settings = self::get_order_settings(); $timestamp = $now ?? time(); $minutes = max(5, min(1440, (int) ($settings['expiry'] ?? 30))); return gmdate('Y-m-d H:i:s', $timestamp + $minutes * MINUTE_IN_SECONDS); } /** * @return array{min_amount:string,service_charge_rate:int,daily_count:int,daily_limit:string,reset_time:string,payout_channels:string[]} */ public static function get_withdrawal_settings(): array { $s = Settings::get_all(); $channels = array_values(array_intersect(['alipay', 'wechat'], array_map('sanitize_key', (array) ($s['affiliate_payout_channels'] ?? ['alipay', 'wechat'])))); if (!$channels) { $channels = ['alipay', 'wechat']; } return [ 'min_amount' => self::money_decimal($s['affiliate_min_withdrawal'] ?? '50.00', '50.00', 99999900), 'service_charge_rate' => max(0, min(100, (int) ($s['affiliate_withdrawal_fee'] ?? 5))), 'daily_count' => max(0, min(99999, (int) ($s['affiliate_daily_withdrawal_count'] ?? 3))), 'daily_limit' => self::money_decimal($s['affiliate_daily_withdrawal_limit'] ?? '500.00', '500.00', 99999900), 'reset_time' => preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', (string) ($s['affiliate_withdrawal_reset_time'] ?? '00:00')) ? (string) $s['affiliate_withdrawal_reset_time'] : '00:00', 'payout_channels' => $channels, ]; } /** * Whether a registered adapter is admin-enabled. * Gateway ids (wechatpay) map to the reference-theme channel selectors: * pay_wechat_sdk_options / pay_alipay_sdk_options / pay_paypal_sdk_s / pay_stripe_sdk_s. */ public static function is_gateway_enabled(string $gateway_id): bool { $settings = Settings::get_all(); $gateway_id = sanitize_key($gateway_id); $wechat = sanitize_key((string) ($settings['pay_wechat_sdk_options'] ?? 'null')); if (!in_array($wechat, ['official_wechat', 'epay', 'null'], true)) { $wechat = 'null'; } $alipay = sanitize_key((string) ($settings['pay_alipay_sdk_options'] ?? 'null')); if (!in_array($alipay, ['official_alipay', 'epay', 'null'], true)) { $alipay = 'null'; } switch ($gateway_id) { case 'alipay': return $alipay === 'official_alipay'; case 'wechatpay': return $wechat === 'official_wechat'; case 'paypal': return !empty($settings['pay_paypal_sdk_s']); case 'stripe': return !empty($settings['pay_stripe_sdk_s']); case 'balance': return !empty($settings['pay_balance_s']); case 'card_pass': // 收款接口随三个兑换开关任一生效(余额 / 积分 / 会员)。 return !empty($settings['pay_balance_pass_charge_s']) || !empty($settings['points_pass_exchange_s']) || !empty($settings['pay_vip_pass_charge_s']); case 'epay': return $wechat === 'epay' || $alipay === 'epay'; case 'epay_alipay': return $alipay === 'epay'; case 'epay_wxpay': return $wechat === 'epay'; } return false; } private static function count_structured_lines(string $raw): int { $lines = preg_split('/[\r\n]+/', $raw, -1, PREG_SPLIT_NO_EMPTY); $count = 0; foreach ((array) $lines as $line) { if (trim((string) $line) !== '') { $count++; } } return $count; } private static function money_decimal(mixed $value, string $default, int $max_minor = PHP_INT_MAX): string { try { if (!is_string($value)) { throw new \InvalidArgumentException('money_decimal_must_be_string'); } $amount = \AKSRT\Ledger\AssetAmount::balance(trim($value)); return $amount->minor() <= $max_minor ? $amount->decimal() : $default; } catch (\InvalidArgumentException $e) { return $default; } } /** * Format recharge product rows (reference-theme group shape: * price 充值金额 / pay_price 销售金额 / tag 小标签) into "支付|到账|标题" lines. * * @param array $products */ private static function format_product_lines(array $products): string { $lines = []; foreach ($products as $row) { if (!is_array($row)) { continue; } $price = max(0, (float) ($row['price'] ?? 0)); $pay_price = max(0, (float) ($row['pay_price'] ?? 0)); $pay = $pay_price > 0 ? $pay_price : $price; $title = trim((string) ($row['tag'] ?? '')); $lines[] = $pay . '|' . $price . '|' . $title; } return implode("\n", $lines); } /** * Format points product rows (reference-theme group shape: * points 积分 / pay_price 销售金额 / tag 小标签) into "积分|支付|标签" lines. * * @param array $products */ private static function format_points_product_lines(array $products): string { $lines = []; foreach ($products as $row) { if (!is_array($row)) { continue; } $points = max(0, (int) ($row['points'] ?? 0)); $pay_price = max(0, (float) ($row['pay_price'] ?? 0)); $title = trim((string) ($row['tag'] ?? '')); $lines[] = $points . '|' . $pay_price . '|' . $title; } return implode("\n", $lines); } }