' => [ 'subscription_auto_renew' => bool, ... ], ... ] * * Defaults are explicit per gateway ID so the merchant sees a meaningful * matrix out of the box. The defaults reflect the regulatory reality * discussed in SUBSCRIPTION_MODULE_AUDIT.md §9.5: * - Indonesian VA/QRIS/e-wallet gateways: false (no recurring) * - Indonesian credit-card gateways: false (BI/PCI-DSS re-auth) * - PayPal/Stripe/Dodo: true ONLY when the merchant has a working * adapter that implements process_subscription_renewal_payment; * we still default to true because the integration is the common * case in WooNooW's target market. * * The default for any *unknown* gateway is `false` — the safe side. * * @package WooNooW\Modules\Subscription */ namespace WooNooW\Modules\Subscription; if (!defined('ABSPATH')) exit; class GatewayCapabilities { const OPTION_KEY = 'woonoow_gateway_subscription_capabilities'; /** * Built-in safe defaults. Keyed by WooCommerce payment-gateway ID. * * Filter 'woonoow_gateway_subscription_capabilities' lets adapters * and third-party code extend this list at boot time. */ public static function default_capabilities(): array { return [ // Global auto-debit-capable gateways 'paypal' => ['subscription_auto_renew' => true], 'stripe' => ['subscription_auto_renew' => true], 'stripe_cc' => ['subscription_auto_renew' => true], 'stripe_sepa' => ['subscription_auto_renew' => true], 'dodo' => ['subscription_auto_renew' => true], // Indonesian manual-only gateways (VA/QRIS/e-wallet/CC re-auth) 'tripay' => ['subscription_auto_renew' => false], 'midtrans' => ['subscription_auto_renew' => false], 'xendit' => ['subscription_auto_renew' => false], 'doku' => ['subscription_auto_renew' => false], 'duitku' => ['subscription_auto_renew' => false], // Cheques / offline / no auto-debit 'cheque' => ['subscription_auto_renew' => false], 'bacs' => ['subscription_auto_renew' => false], 'cod' => ['subscription_auto_renew' => false], ]; } /** * Read the merged capability map: defaults < stored < filter. * Always returns a fully-populated array (missing keys default to false). */ public static function all(): array { $stored = get_option(self::OPTION_KEY, []); if (!is_array($stored)) { $stored = []; } $merged = array_merge(self::default_capabilities(), $stored); $merged = (array) apply_filters('woonoow_gateway_subscription_capabilities', $merged); return $merged; } /** * Single-gateway capability lookup. * Returns true ONLY if explicitly declared true. Anything else is false. */ public static function supports_auto_renew(string $gateway_id): bool { $gateway_id = sanitize_key($gateway_id); if ($gateway_id === '') { return false; } $caps = self::all(); if (!isset($caps[$gateway_id])) { return false; // unknown gateway: safe default } return !empty($caps[$gateway_id]['subscription_auto_renew']); } /** * Site-level kill switch. When true, EVERY gateway is treated as * manual regardless of per-gateway capability. */ public static function force_manual(): bool { $settings = \WooNooW\Core\ModuleRegistry::get_settings('subscription'); return !empty($settings['force_manual_renewal']); } /** * The single decision function the renewal flow should call. * Combines: kill switch > gateway capability. */ public static function should_attempt_auto_renew(string $gateway_id): bool { if (self::force_manual()) { return false; } return self::supports_auto_renew($gateway_id); } }