get_charset_collate(); $p = $wpdb->prefix . ADSD_RCC_TABLE_PREFIX; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // Table names built from $wpdb->prefix + plugin constant — never user input. // Table: referral_links dbDelta( "CREATE TABLE {$p}referral_links ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL, token VARCHAR(64) NOT NULL, device_hash VARCHAR(128) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at DATETIME DEFAULT NULL, click_count INT UNSIGNED NOT NULL DEFAULT 0, status ENUM('active','expired','disabled') NOT NULL DEFAULT 'active', PRIMARY KEY (id), UNIQUE KEY token (token), KEY user_id (user_id), KEY status (status) ) {$charset};" ); // Table: referrals dbDelta( "CREATE TABLE {$p}referrals ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, link_id BIGINT UNSIGNED NOT NULL, referrer_id BIGINT UNSIGNED NOT NULL, referee_id BIGINT UNSIGNED DEFAULT NULL, referee_email VARCHAR(191) NOT NULL, status ENUM('pending','verified','rewarded','failed','expired') NOT NULL DEFAULT 'pending', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, verified_at DATETIME DEFAULT NULL, rewarded_at DATETIME DEFAULT NULL, ip_address VARCHAR(45) DEFAULT NULL, device_hash VARCHAR(128) DEFAULT NULL, PRIMARY KEY (id), KEY link_id (link_id), KEY referrer_id (referrer_id), KEY referee_id (referee_id), KEY status (status) ) {$charset};" ); // Table: rewards dbDelta( "CREATE TABLE {$p}rewards ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, referral_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, reward_code VARCHAR(64) NOT NULL, reward_type ENUM('fixed','percent') NOT NULL DEFAULT 'fixed', reward_value DECIMAL(12,4) NOT NULL DEFAULT 0, status ENUM('active','claimed','used','expired') NOT NULL DEFAULT 'active', woo_coupon_code VARCHAR(191) DEFAULT NULL, woo_order_id BIGINT UNSIGNED DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at DATETIME DEFAULT NULL, claimed_at DATETIME DEFAULT NULL, used_at DATETIME DEFAULT NULL, used_by ENUM('woocommerce','local_shop') DEFAULT NULL, shopkeeper_used TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY reward_code (reward_code), KEY referral_id (referral_id), KEY user_id (user_id), KEY status (status) ) {$charset};" ); // Table: pending_verifications dbDelta( "CREATE TABLE {$p}pending_verifications ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, email VARCHAR(191) NOT NULL, token VARCHAR(128) NOT NULL, referral_id BIGINT UNSIGNED DEFAULT NULL, temp_password VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at DATETIME NOT NULL, verified TINYINT(1) NOT NULL DEFAULT 0, ip_address VARCHAR(45) DEFAULT NULL, device_hash VARCHAR(128) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY email (email), UNIQUE KEY token (token), KEY referral_id (referral_id) ) {$charset};" ); // Table: settings dbDelta( "CREATE TABLE {$p}settings ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, setting_key VARCHAR(191) NOT NULL, setting_value LONGTEXT DEFAULT NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY setting_key (setting_key) ) {$charset};" ); // Table: email_templates dbDelta( "CREATE TABLE {$p}email_templates ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, template_key VARCHAR(64) NOT NULL, subject VARCHAR(255) NOT NULL, heading VARCHAR(255) NOT NULL, body LONGTEXT NOT NULL, button_text VARCHAR(128) DEFAULT NULL, footer TEXT DEFAULT NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY template_key (template_key) ) {$charset};" ); // Table: logs dbDelta( "CREATE TABLE {$p}logs ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, event_type VARCHAR(64) NOT NULL, user_id BIGINT UNSIGNED DEFAULT NULL, object_id BIGINT UNSIGNED DEFAULT NULL, message TEXT DEFAULT NULL, context LONGTEXT DEFAULT NULL, ip_address VARCHAR(45) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY event_type (event_type), KEY user_id (user_id), KEY created_at (created_at) ) {$charset};" ); // Table: passkeys dbDelta( "CREATE TABLE {$p}passkeys ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, passkey_hash VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by BIGINT UNSIGNED NOT NULL, status ENUM('active','revoked') NOT NULL DEFAULT 'active', PRIMARY KEY (id) ) {$charset};" ); } /** * Insert default plugin settings into the database. * Uses overwrite=false so existing values are preserved on upgrade. */ private static function seed_default_settings(): void { $defaults = array( 'reward_recipient' => 'both', 'offer_type' => 'automatic', 'offer_min' => 20, 'offer_max' => 100, 'offer_fixed' => 50, 'offer_percent_min' => 10, 'offer_percent_max' => 30, 'offer_percent_fixed' => 20, 'sender_offer_type' => 'automatic', 'sender_offer_min' => 20, 'sender_offer_max' => 100, 'receiver_offer_type' => 'automatic', 'receiver_offer_min' => 20, 'receiver_offer_max' => 100, 'code_expiry_days' => 30, 'daily_link_limit' => 5, 'code_format' => 'mixed', 'code_prefix' => '', 'woo_enabled' => '0', 'page_dashboard' => 0, 'page_reward_list' => 0, 'page_register' => 0, 'page_login' => 0, 'page_checker' => 0, 'page_set_password' => 0, 'verification_expiry_hours' => 24, 'mail_from_name' => '', 'mail_from_email' => '', 'smtp_enabled' => '0', 'smtp_host' => '', 'smtp_port' => 587, 'smtp_user' => '', 'smtp_pass' => '', 'smtp_encryption' => 'tls', ); $db = ADSD_RCC_DB::instance(); foreach ( $defaults as $key => $value ) { $db->set_setting( $key, $value, false ); } } /** * Insert default email templates into the database. */ private static function seed_default_email_templates(): void { global $wpdb; $adsd_rcc_tpl_table = $wpdb->prefix . ADSD_RCC_TABLE_PREFIX . 'email_templates'; $templates = array( array( 'template_key' => 'verification', 'subject' => __( 'Verify your email — {site_name}', 'adsd-referral-couponcore' ), 'heading' => __( 'Almost there! Verify your email', 'adsd-referral-couponcore' ), 'body' => __( "Hi,\n\nThank you for registering! Please click the button below to verify your email and activate your account.\n\nAfter verifying, you will be prompted to set your own password.", 'adsd-referral-couponcore' ), 'button_text' => __( 'Verify My Email', 'adsd-referral-couponcore' ), 'footer' => __( 'If you did not create an account, please ignore this email.', 'adsd-referral-couponcore' ), ), array( 'template_key' => 'reward_generated', 'subject' => __( 'Your reward is ready — {reward_code}', 'adsd-referral-couponcore' ), 'heading' => __( 'You earned a reward!', 'adsd-referral-couponcore' ), 'body' => __( "Hi,\n\nA referral reward has been generated for you.\n\nReward Code: {reward_code}\nReward Value: {reward_value}\nExpiry Date: {expiry_date}\n\nYou can use this reward at our store or at a participating local shop.", 'adsd-referral-couponcore' ), 'button_text' => __( 'View My Rewards', 'adsd-referral-couponcore' ), 'footer' => __( 'Thank you for participating in our referral programme.', 'adsd-referral-couponcore' ), ), array( 'template_key' => 'reward_used', 'subject' => __( 'Your reward {reward_code} has been used', 'adsd-referral-couponcore' ), 'heading' => __( 'Reward used', 'adsd-referral-couponcore' ), 'body' => __( "Hi,\n\nYour reward code {reward_code} has been marked as used. We hope you enjoyed the benefit!", 'adsd-referral-couponcore' ), 'button_text' => '', 'footer' => __( 'Thank you for shopping with us.', 'adsd-referral-couponcore' ), ), array( 'template_key' => 'reward_expired', 'subject' => __( 'Your reward {reward_code} has expired', 'adsd-referral-couponcore' ), 'heading' => __( 'Reward expired', 'adsd-referral-couponcore' ), 'body' => __( "Hi,\n\nUnfortunately your reward code {reward_code} has expired on {expiry_date}. Refer more friends to earn new rewards!", 'adsd-referral-couponcore' ), 'button_text' => __( 'Refer a Friend', 'adsd-referral-couponcore' ), 'footer' => __( 'Thank you for being part of our community.', 'adsd-referral-couponcore' ), ), ); // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $adsd_rcc_safe_tpl = esc_sql( $adsd_rcc_tpl_table ); foreach ( $templates as $tpl ) { $existing = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM `{$adsd_rcc_safe_tpl}` WHERE template_key = %s", $tpl['template_key'] ) ); if ( ! $existing ) { $wpdb->insert( $adsd_rcc_tpl_table, $tpl ); // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter } } } }