false, 'payouts_enabled' => false, 'details_submitted' => false]; } $raw = get_user_meta($seller_user_id, self::USER_META_STRIPE_CONNECT_STATUS, true); if ($raw === '' || $raw === null) { $raw = get_user_meta($seller_user_id, 'ToDownload_stripe_connect_status', true); } $data = []; if (is_array($raw)) { $data = $raw; } elseif (is_string($raw) && $raw !== '') { $decoded = json_decode($raw, true); if (is_array($decoded)) { $data = $decoded; } } return [ 'charges_enabled' => !empty($data['charges_enabled']), 'payouts_enabled' => !empty($data['payouts_enabled']), 'details_submitted' => !empty($data['details_submitted']), ]; } public static function set_stripe_connect_status(int $seller_user_id, bool $charges_enabled, bool $payouts_enabled, bool $details_submitted): void { if ($seller_user_id <= 0) { return; } $val = [ 'charges_enabled' => (bool) $charges_enabled, 'payouts_enabled' => (bool) $payouts_enabled, 'details_submitted' => (bool) $details_submitted, ]; update_user_meta($seller_user_id, self::USER_META_STRIPE_CONNECT_STATUS, $val); // Keep legacy key in sync. update_user_meta($seller_user_id, 'ToDownload_stripe_connect_status', $val); } /** * Returns null when not configured. */ public static function seller_tax_enabled(int $seller_user_id): ?bool { if ($seller_user_id <= 0) { return null; } $raw = get_user_meta($seller_user_id, self::USER_META_TAX_ENABLED, true); if ($raw === '' || $raw === null) { return null; } return (string) $raw === '1' || (int) $raw === 1 || $raw === true; } /** * Returns null when not configured. */ public static function seller_tax_percent(int $seller_user_id): ?float { if ($seller_user_id <= 0) { return null; } $raw = get_user_meta($seller_user_id, self::USER_META_TAX_PERCENT, true); if ($raw === '' || $raw === null) { return null; } $val = (float) $raw; $val = max(0.0, min(100.0, $val)); return $val; } /** * Returns null when not configured. */ public static function seller_default_access_days(int $seller_user_id): ?int { if ($seller_user_id <= 0) { return null; } $raw = get_user_meta($seller_user_id, self::USER_META_DEFAULT_ACCESS_DAYS, true); if ($raw === '' || $raw === null) { return null; } $days = (int) $raw; return max(0, $days); } public static function seller_prefers_discount_preview(int $seller_user_id): bool { if ($seller_user_id <= 0) { return false; } $raw = get_user_meta($seller_user_id, self::USER_META_CHECKOUT_DISCOUNT_PREVIEW, true); return (string) $raw === '1' || (int) $raw === 1 || $raw === true; } /** * Per-seller hosted connection. * * For seller orders, the store_public_id + api_key must be stored on the seller user. * For platform orders (seller_user_id <= 0), this falls back to the global Connect settings. * * @return array{connected:bool,hosted_base:string,hosted_host:string,store_public_id:string,api_key:string} */ public static function hosted_credentials(int $seller_user_id): array { $settings = get_option('ToDownload_settings', []); $settings = is_array($settings) ? $settings : []; $default_base = trim((string) ($settings['hosted_api_base_url'] ?? 'https://2dl.app')); $default_base = $default_base !== '' ? rtrim($default_base, '/') : 'https://2dl.app'; $host = strtolower(sanitize_text_field((string) (wp_parse_url($default_base, PHP_URL_HOST) ?? ''))); $scheme = strtolower(sanitize_text_field((string) (wp_parse_url($default_base, PHP_URL_SCHEME) ?? ''))); $is_wp_host = ($host === 'wp.2dl.app' || ($host !== '' && str_ends_with($host, '.wp.2dl.app'))); $is_http = ($scheme === 'https' || $scheme === 'http'); if ($default_base === '' || $host === '' || !$is_http || $is_wp_host) { $default_base = ($host !== '' && str_contains($host, 'staging')) ? 'https://staging.2dl.app' : 'https://2dl.app'; $host = strtolower((string) (wp_parse_url($default_base, PHP_URL_HOST) ?? '')); } if ($host === 'www.2dl.app') { $default_base = 'https://2dl.app'; } if ($host === 'staging.2dl.app') { $default_base = 'https://staging.2dl.app'; } // Environment selection (production vs staging) is site-level. $base = $default_base; $store_public_id = ''; $api_key = ''; if ($seller_user_id > 0) { $store_raw = get_user_meta($seller_user_id, self::USER_META_HOSTED_STORE_PUBLIC_ID, true); $key_raw = get_user_meta($seller_user_id, self::USER_META_HOSTED_STORE_API_KEY, true); $store_public_id = trim((string) $store_raw); $api_key = trim((string) $key_raw); } else { $store_public_id = trim((string) ($settings['hosted_store_public_id'] ?? '')); $api_key = trim((string) ($settings['hosted_store_api_key'] ?? '')); } $host = (string) wp_parse_url($base, PHP_URL_HOST); if ($host === '') { $host = $base !== '' ? $base : '2dl.app'; } $connected = !($store_public_id === '' || $api_key === ''); return [ 'connected' => $connected, 'hosted_base' => $base, 'hosted_host' => $host, 'store_public_id' => $store_public_id, 'api_key' => $api_key, ]; } public static function register(): void { add_action('init', [self::class, 'ensure_roles_and_caps']); add_filter('login_redirect', [self::class, 'login_redirect'], 10, 3); add_action('admin_init', [self::class, 'maybe_downgrade_current_seller']); Memberships::register(); // Platform admins can assign seller tiers via the user profile screen. add_action('show_user_profile', [self::class, 'render_tier_profile_fields']); add_action('edit_user_profile', [self::class, 'render_tier_profile_fields']); add_action('show_user_profile', [self::class, 'render_connection_profile_fields']); add_action('edit_user_profile', [self::class, 'render_connection_profile_fields']); add_action('personal_options_update', [self::class, 'save_tier_profile_fields']); add_action('edit_user_profile_update', [self::class, 'save_tier_profile_fields']); add_action('admin_post_ToDownload_admin_disconnect_seller_store', [self::class, 'handle_admin_disconnect_seller_store']); add_action('admin_enqueue_scripts', [self::class, 'enqueue_admin_styles']); if (is_admin()) { SellerAdmin::register(); } } public static function render_connection_profile_fields($user): void { if (!current_user_can('manage_options')) { return; } if (!($user instanceof \WP_User)) { return; } $user_id = (int) $user->ID; if ($user_id <= 0) { return; } // Only show for seller-capable users. if (!user_can($user_id, 'edit_todownload_downloads')) { return; } $hosted = self::hosted_credentials($user_id); $hosted_base = (string) ($hosted['hosted_base'] ?? ''); $store_public_id = (string) ($hosted['store_public_id'] ?? ''); $api_key_set = !empty($hosted['api_key']); $connected = !empty($hosted['connected']); $disconnect_url = add_query_arg( [ 'action' => 'ToDownload_admin_disconnect_seller_store', 'user_id' => (string) $user_id, ], admin_url('admin-post.php') ); $disconnect_url = wp_nonce_url($disconnect_url, self::ADMIN_DISCONNECT_NONCE_ACTION, self::ADMIN_DISCONNECT_NONCE_PARAM); echo '
| ' . esc_html__('Hosted base', '2download-connector') . ' | ';
if ($hosted_base !== '') {
echo '' . esc_html($hosted_base) . '';
} else {
echo '' . esc_html__('Not set', '2download-connector') . '';
}
echo ' |
|---|---|
| ' . esc_html__('Store public ID', '2download-connector') . ' | ';
if ($store_public_id !== '') {
echo '' . esc_html($store_public_id) . '';
} else {
echo '' . esc_html__('Not set', '2download-connector') . '';
}
echo ' |
| ' . esc_html__('Store API key', '2download-connector') . ' | ';
if ($api_key_set) {
echo esc_html__('Set', '2download-connector');
} else {
echo esc_html__('Not set', '2download-connector');
}
echo ' ' . esc_html__('The API key is a secret and is never displayed in WordPress.', '2download-connector') . ' '; echo ' |
| ' . esc_html__('Status', '2download-connector') . ' | '; if ($connected) { echo '' . esc_html__('Connected', '2download-connector') . ''; } else { echo '' . esc_html__('Not connected', '2download-connector') . ''; } echo ' |
| ' . esc_html__('Actions', '2download-connector') . ' | '; if ($connected || $api_key_set || $store_public_id !== '' || $hosted_base !== '') { echo '' . esc_html__('Disconnect', '2download-connector') . ''; } else { echo '' . esc_html__('No connection to remove.', '2download-connector') . ''; } echo ' |
| ';
echo '';
echo ' ' . esc_html__('Admins can override seller tier here. Pro/Studio may also be granted via plan purchase.', '2download-connector') . ' '; echo ' | |
| ';
echo ''
. ''
. ''
. '';
echo ' ' . esc_html__('If set and the date passes, Pro/Studio auto-downgrades to Standard.', '2download-connector') . ' '; echo ' |