530 lines
27 KiB
PHP
530 lines
27 KiB
PHP
<?php
|
|
/**
|
|
* @package AKSRT
|
|
*/
|
|
|
|
namespace AKSRT\Settings;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class PaymentRuntime {
|
|
|
|
/**
|
|
* Paid content / download policy.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public static function get_paid_content_settings(): array {
|
|
$settings = Settings::get_all();
|
|
|
|
$box_position = sanitize_key((string) ($settings['pay_box_position'] ?? 'top'));
|
|
// Zibll: 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']),
|
|
'down_alone_page' => false,
|
|
'display' => [
|
|
'mark' => sanitize_text_field((string) ($settings['pay_mark'] ?? '付费')),
|
|
'currency_symbol' => '¥',
|
|
'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' => true,
|
|
'verify_nonce' => true,
|
|
'user_log' => 50,
|
|
'post_log' => 50,
|
|
'remote_size' => 2048,
|
|
'remote_ext' => 'zip,rar,7z',
|
|
'alone_page' => false,
|
|
],
|
|
// 内容收银台优惠码:不再提供全局开关,默认关闭。
|
|
'coupon_post_s' => false,
|
|
'coupon_desc' => '',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Content coupon global switches (zibll 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 (Zibll pay_balance_* contract).
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public static function get_payment_recharge_settings(): array {
|
|
$settings = Settings::get_all();
|
|
|
|
$packages = (string) ($settings['pay_balance_product'] ?? "30|30|入门包\n98|108|推荐包");
|
|
if (is_array($settings['pay_balance_product'] ?? null)) {
|
|
$packages = self::format_product_lines((array) $settings['pay_balance_product']);
|
|
}
|
|
$desc = sanitize_textarea_field((string) ($settings['pay_balance_desc'] ?? ''));
|
|
|
|
return [
|
|
'enabled' => !empty($settings['pay_balance_s']),
|
|
'desc' => $desc,
|
|
'auto_to_balance' => !isset($settings['pay_auto_to_balance']) || !empty($settings['pay_auto_to_balance']),
|
|
'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' => max(0, (float) ($settings['pay_balance_price'] ?? 0)),
|
|
'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' => [
|
|
'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']),
|
|
],
|
|
'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'] ?? '')),
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 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 (Zibll pay_user_vip_1 / pay_user_vip_2).
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
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,
|
|
'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'] : '<li>免费下载部分资源</li><li>会员专属身份标识</li><li>付费内容会员价</li>'), $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'] : '<li>更高免费下载额度</li><li>更高等级身份标识</li><li>更多付费内容会员价</li>'), $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))),
|
|
'pass_charge' => !empty($settings['pay_vip_pass_charge_s']),
|
|
'pass_charge_desc' => sanitize_textarea_field((string) ($settings['pay_vip_pass_charge_desc'] ?? '')),
|
|
'pass_charge_only_password' => !isset($settings['pay_vip_pass_charge_only_password']) || !empty($settings['pay_vip_pass_charge_only_password']),
|
|
],
|
|
'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' => max(0, min(9999, (int) ($settings['vip_renew_reduce'] ?? 20))),
|
|
],
|
|
'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<string,array<string,int>>,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) : __('<p>订单完成并经过结算周期后,作者分成会自动转入对应的钱包。</p>', 'aksrt'),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<string,mixed>
|
|
*/
|
|
public static function get_payment_affiliate_settings(): array {
|
|
$settings = Settings::get_all();
|
|
$ratio = max(0, min(100, (float) ($settings['affiliate_ratio'] ?? 5)));
|
|
$fee = max(0, min(100, (float) ($settings['affiliate_withdrawal_fee'] ?? 5)));
|
|
$types = (array) ($settings['affiliate_order_types'] ?? ['membership', 'content']);
|
|
$types = array_values(array_intersect(['membership', 'content'], array_map('sanitize_key', $types)));
|
|
if (!$types) { $types = ['membership', 'content']; }
|
|
$channels = array_values(array_intersect(['alipay', 'wechat'], array_map('sanitize_key', (array) ($settings['affiliate_payout_channels'] ?? ['alipay', 'wechat']))));
|
|
if (!$channels) { $channels = ['alipay', 'wechat']; }
|
|
return [
|
|
'enabled' => !empty($settings['affiliate_enabled']),
|
|
'ratio' => $ratio,
|
|
'order_types' => $types,
|
|
'window_days' => max(1, min(365, (int) ($settings['affiliate_touch_window_days'] ?? 30))),
|
|
'settlement_days' => max(0, min(90, (int) ($settings['affiliate_settlement_days'] ?? 7))),
|
|
'min_withdrawal' => max(0, min(999999, (float) ($settings['affiliate_min_withdrawal'] ?? 50))),
|
|
'withdrawal_fee' => $fee,
|
|
'payout_channels' => $channels,
|
|
'allow_self_purchase' => !empty($settings['affiliate_allow_self_purchase']),
|
|
'show_stats' => !isset($settings['affiliate_show_stats']) || !empty($settings['affiliate_show_stats']),
|
|
'promotion_text' => sanitize_textarea_field((string) ($settings['affiliate_promotion_text'] ?? '')),
|
|
'earnings_text' => sanitize_textarea_field((string) ($settings['affiliate_earnings_text'] ?? '')),
|
|
'withdrawal_agreement' => sanitize_textarea_field((string) ($settings['affiliate_withdrawal_agreement'] ?? '')),
|
|
];
|
|
}
|
|
|
|
public static function affiliate_enabled(): bool { return !empty(self::get_payment_affiliate_settings()['enabled']); }
|
|
public static function affiliate_ratio(): float { return (float) self::get_payment_affiliate_settings()['ratio']; }
|
|
public static function get_affiliate_settings(): array { return self::get_payment_affiliate_settings(); }
|
|
public static function get_settlement_settings(): array {
|
|
$s = self::get_payment_affiliate_settings();
|
|
return ['observation_days' => (int) $s['settlement_days'], 'retry_limit' => 100];
|
|
}
|
|
|
|
/**
|
|
* @return array<string,mixed>
|
|
*/
|
|
public static function get_payment_gateway_settings(): array {
|
|
$settings = Settings::get_all();
|
|
$gateway_keys = ['wechat', 'alipay', 'paypal', 'stripe', 'balance', 'card_pass', 'epay'];
|
|
$gateways = [];
|
|
foreach ($gateway_keys as $gw) {
|
|
$gateways[$gw] = !empty($settings["payment_{$gw}_enabled"]);
|
|
}
|
|
|
|
return [
|
|
'help_text' => __('以渠道开关与凭据是否齐全为准;异步回调由各网关验签。卡密为兑换入口,不进入收银台。', 'aksrt'),
|
|
'gateways' => $gateways,
|
|
'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'] ?? '')),
|
|
'channel_alipay' => !array_key_exists('epay_channel_alipay', $settings) || !empty($settings['epay_channel_alipay']),
|
|
'channel_wxpay' => !array_key_exists('epay_channel_wxpay', $settings) || !empty($settings['epay_channel_wxpay']),
|
|
'device' => sanitize_key((string) ($settings['epay_device'] ?? 'auto')),
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Order display / checkout policy (Zibll pay_* + AKSRT order ops).
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
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)));
|
|
$max_minutes = max(5, min(1440, (int) ($s['payment_order_max_minutes'] ?? 30)));
|
|
|
|
$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', 'completed', 'cancelled', 'refunded', 'refunding', 'processing'];
|
|
$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 [
|
|
'prefix' => sanitize_text_field((string) ($s['payment_order_prefix'] ?? 'AKSRT')),
|
|
'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']),
|
|
'order_max_minutes' => $max_minutes,
|
|
'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' => [
|
|
'text' => sanitize_textarea_field((string) ($s['pay_service'] ?? '')),
|
|
'icon' => sanitize_text_field((string) ($s['pay_service_icon'] ?? '')),
|
|
'value' => sanitize_text_field((string) ($s['pay_service_value'] ?? '')),
|
|
'qq' => sanitize_text_field((string) ($s['pay_service_qq'] ?? '')),
|
|
'wechat' => esc_url_raw((string) ($s['pay_service_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<string,mixed>
|
|
*/
|
|
public static function get_card_pass_settings(): array {
|
|
$s = Settings::get_all();
|
|
|
|
$type = sanitize_key((string) ($s['card_pass_type'] ?? 'balance'));
|
|
if (!in_array($type, ['balance', 'vip', 'credits'], true)) {
|
|
$type = 'balance';
|
|
}
|
|
|
|
$success_msg = trim((string) ($s['card_pass_success_msg'] ?? ''));
|
|
$fail_msg = trim((string) ($s['card_pass_fail_msg'] ?? ''));
|
|
|
|
return [
|
|
'enabled' => !empty($s['card_pass_enabled']),
|
|
'desc' => sanitize_textarea_field((string) ($s['card_pass_desc'] ?? '')),
|
|
'type' => $type,
|
|
'prefix' => sanitize_text_field((string) ($s['card_pass_prefix'] ?? 'AK')),
|
|
'length' => max(8, min(32, (int) ($s['card_pass_length'] ?? 16))),
|
|
'batch_size' => max(1, min(500, (int) ($s['card_pass_batch_size'] ?? 50))),
|
|
'max_redeem' => max(1, min(100, (int) ($s['card_pass_max_redeem'] ?? 1))),
|
|
'expiry_days' => max(0, (int) ($s['card_pass_expiry'] ?? 0)),
|
|
'amount' => max(1, min(9999, (int) ($s['card_pass_amount'] ?? 10))),
|
|
'vip_days' => max(1, min(3650, (int) ($s['card_pass_vip_days'] ?? 30))),
|
|
'credits' => max(0, min(99999, (int) ($s['card_pass_credits'] ?? 0))),
|
|
'redeem_page' => esc_url_raw((string) ($s['card_pass_redeem_page'] ?? '')),
|
|
'success_msg' => $success_msg !== '' ? $success_msg : __('兑换成功!', 'aksrt'),
|
|
'fail_msg' => $fail_msg !== '' ? $fail_msg : __('卡号无效或已过期', 'aksrt'),
|
|
];
|
|
}
|
|
|
|
/** @return array{min_amount:float,service_charge_rate:int,daily_count:int,daily_limit:float,reset_time:string} */
|
|
public static function get_withdrawal_settings(): array {
|
|
$s = Settings::get_all();
|
|
$affiliate = self::get_payment_affiliate_settings();
|
|
return [
|
|
'min_amount' => (float) $affiliate['min_withdrawal'],
|
|
'service_charge_rate' => (int) round((float) $affiliate['withdrawal_fee']),
|
|
'daily_count' => max(0, min(99999, (int) ($s['affiliate_daily_withdrawal_count'] ?? 3))),
|
|
'daily_limit' => max(0, min(999999, (float) ($s['affiliate_daily_withdrawal_limit'] ?? 500))),
|
|
'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',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Whether a registered adapter is admin-enabled.
|
|
* Maps gateway ids (wechatpay) to schema toggles (payment_wechat_enabled).
|
|
*/
|
|
public static function is_gateway_enabled(string $gateway_id): bool {
|
|
$settings = Settings::get_all();
|
|
$gateway_id = sanitize_key($gateway_id);
|
|
$map = [
|
|
'alipay' => 'payment_alipay_enabled',
|
|
'wechatpay' => 'payment_wechat_enabled',
|
|
'paypal' => 'payment_paypal_enabled',
|
|
'stripe' => 'payment_stripe_enabled',
|
|
'balance' => 'payment_balance_enabled',
|
|
'card_pass' => 'payment_card_pass_enabled',
|
|
'epay' => 'payment_epay_enabled',
|
|
'epay_alipay' => 'payment_epay_enabled',
|
|
'epay_wxpay' => 'payment_epay_enabled',
|
|
];
|
|
$key = $map[$gateway_id] ?? '';
|
|
if ($key === '') {
|
|
return false;
|
|
}
|
|
if ($gateway_id === 'balance') {
|
|
// Master: 余额充值 → 启用余额功能. Optional kill-switch: payment_balance_enabled=false.
|
|
// Missing key (legacy sites) follows pay_balance_s so "open wallet" alone enables pay.
|
|
if (empty($settings['pay_balance_s'])) {
|
|
return false;
|
|
}
|
|
if (array_key_exists('payment_balance_enabled', $settings) && empty($settings['payment_balance_enabled'])) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
if (empty($settings[$key])) {
|
|
return false;
|
|
}
|
|
// Epay sub-channels require the matching channel switch (default on when key missing).
|
|
if ($gateway_id === 'epay_alipay') {
|
|
return !array_key_exists('epay_channel_alipay', $settings) || !empty($settings['epay_channel_alipay']);
|
|
}
|
|
if ($gateway_id === 'epay_wxpay') {
|
|
return !array_key_exists('epay_channel_wxpay', $settings) || !empty($settings['epay_channel_wxpay']);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @param array<int,mixed> $products
|
|
*/
|
|
private static function format_product_lines(array $products): string {
|
|
$lines = [];
|
|
foreach ($products as $row) {
|
|
if (!is_array($row)) {
|
|
continue;
|
|
}
|
|
$price = $row['pay_price'] ?? $row['price'] ?? '';
|
|
$value = $row['value'] ?? $row['balance'] ?? $price;
|
|
$title = $row['title'] ?? $row['name'] ?? '';
|
|
$lines[] = $price . '|' . $value . '|' . $title;
|
|
}
|
|
return implode("\n", $lines);
|
|
}
|
|
}
|