prefix . 'abovault_subscriptions'; } /** * DDL for activation. HPOS-friendly (own table, integer FKs to WC customer/order ids). */ public static function schema(): string { global $wpdb; $table = self::table(); $collate = $wpdb->get_charset_collate(); return "CREATE TABLE {$table} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, customer_id BIGINT UNSIGNED NOT NULL, order_id BIGINT UNSIGNED NOT NULL DEFAULT 0, product_id BIGINT UNSIGNED NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', gateway VARCHAR(50) NOT NULL DEFAULT '', gateway_ref VARCHAR(191) NOT NULL DEFAULT '', amount DECIMAL(12,2) NOT NULL DEFAULT 0, currency CHAR(3) NOT NULL DEFAULT 'EUR', interval_unit VARCHAR(10) NOT NULL DEFAULT 'month', interval_count SMALLINT UNSIGNED NOT NULL DEFAULT 1, trial_end DATETIME NULL, current_period_start DATETIME NULL, current_period_end DATETIME NULL, cancel_at_period_end TINYINT(1) NOT NULL DEFAULT 0, retry_count SMALLINT UNSIGNED NOT NULL DEFAULT 0, pending_charge_ref VARCHAR(191) NOT NULL DEFAULT '', last_payment_failed_at DATETIME NULL, canceled_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status (status), KEY idx_customer (customer_id), KEY idx_order (order_id), KEY idx_period_end (current_period_end), KEY idx_gateway_ref (gateway_ref) ) {$collate};"; } /** * Transition a subscription's status and stamp the relevant timestamp. * Central choke-point so retention triggers can hook every transition. * * @return bool True if a row was updated. */ public static function transition(int $id, string $to, array $extra = []): bool { global $wpdb; $data = array_merge(['status' => $to], $extra); if ($to === self::STATUS_CANCELED && !isset($data['canceled_at'])) { $data['canceled_at'] = current_time('mysql', true); } $updated = $wpdb->update(self::table(), $data, ['id' => $id]); /** * Fires after every status transition. The retention engine listens here. * * @param int $id * @param string $to New status. * @param array $extra */ do_action('abovault_subscription_transitioned', $id, $to, $extra); return (bool) $updated; } }