_DB_VERSION constant). * Bump it whenever the addon's schema files change so the central manager * (Addons::sync_addon_schemas) reruns install_tables(). Return an empty * string when the addon has no tables of its own. * * @return string */ public function get_db_version(): string; /** * (Re)create the addon's tables via dbDelta. * * Called by the central schema manager when get_db_version() differs from * the stored value. Must be idempotent. * * @return void */ public function install_tables(): void; } } namespace StoreEngine\Classes { abstract class AbstractAddon implements \StoreEngine\Interfaces\AddonInterface { /** * Addon Name. * Unique name for the addon. This will be used for identifying & loading the addon, * and triggering its action & filter hooks. * * @var string */ protected string $addon_name; /** * Trigger Initialization hooks. */ protected function __construct() { } protected bool $running = false; final public function run() { } public function addon_activation_hook() { } public function addon_deactivation_hook() { } /** * Addons with their own tables override this to return a version string. * Empty string means "no tables" — the central manager skips this addon. */ public function get_db_version(): string { } /** * Addons with their own tables override this to run their dbDelta calls. */ public function install_tables(): void { } /** * Cloning is forbidden. */ public function __clone() { } /** * Unserializing instances of this class is forbidden. */ public function __wakeup() { } } } namespace StoreEngine\Traits { trait Singleton { protected static $instance; public static function init() { } public static function get_instance() { } protected function __construct() { } /** * Cloning is forbidden. */ public function __clone() { } /** * Unserializing instances of this class is forbidden. */ public function __wakeup() { } } } namespace StoreEngine\Addons\Affiliate { final class Affiliate extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'affiliate'; public function define_constants() { } public function init_addon() { } public function dispatch_hooks() { } public function get_db_version(): string { } public function install_tables(): void { } public function addon_activation_hook() { } public function addon_deactivation_hook() { } } class Ajax { public static function init() { } public function dispatch_hooks() { } } } namespace StoreEngine\Classes { /** * Base abstract request handler. * * Each extending class represents a logical request group * (e.g. products, licenses, subscriptions). * * This class is intentionally opinionated: * - Declarative input schema * - Centralized sanitization & validation * - WordPress-native error handling * * Extend this class instead of directly using wp_ajax_* callbacks. */ abstract class AbstractRequestHandler { public const ABSINT = 'absint'; public const ID = 'id'; public const INT = 'int'; public const INTEGER = 'integer'; public const ABS_INTEGER = 'absint'; public const ID_ARR = 'array-id'; public const IDS = 'ids'; public const DOUBLE = 'double'; public const FLOAT = 'float'; public const ABS_DOUBLE = 'abs-double'; public const ABS_FLOAT = 'abs-float'; public const URL = 'url'; public const BOOLEAN = 'bool'; public const POST = 'post'; public const HTML = 'post'; public const SLUG = 'slug'; public const EMAIL = 'email'; public const USER = 'user'; public const USERNAME = 'username'; public const TEXTAREA = 'textarea'; public const TEXT = 'text'; public const STRING = 'string'; public const SAFE_TEXT = 'safe_text'; public const SAFE_STR = 'safe_text'; public const SAFE_HTML = 'safe_text'; public const STR_ARR = 'array-string'; public const STRINGS = 'strings'; public const PASSWORD = 'password'; public const COLOR = 'color'; public const HEX_COLOR = 'hex_color'; public const HEX_COLOR_NO_HASH = 'hex_color_no_hash'; public const KEY = 'key'; /** * Default Nonce Action. * * @var string */ protected string $nonce_action = 'storeengine_nonce'; /** * Request namespace. * * @var string */ protected string $namespace = STOREENGINE_PLUGIN_SLUG; /** * Action registry. * * Maps action names to permission, schema, and callback definitions. * * Structure: * [ * 'action_name' => [ * 'capability' => 'manage_options', * 'allow_visitor_action' => false, * 'callback' => [ $this, 'method_name' ], * 'fields' => [ ...schema... ], * ], * ] * * @var array */ protected array $actions = array(); protected static string $current_wp_action; protected ?bool $is_ajax = null; protected ?bool $is_admin_post = null; protected ?bool $is_unauthenticated = null; protected ?bool $is_visitor_action = null; private array $safe_text_kses_rules = array('u' => true, 'i' => true, 'b' => true, 'br' => true, 'hr' => true, 'img' => ['alt' => true, 'class' => true, 'src' => true, 'title' => true], 'p' => ['class' => true], 'ul' => ['class' => true], 'li' => ['class' => true], 'span' => ['class' => true, 'title' => true], 'a' => ['class' => true, 'title' => true, 'href' => true, 'target' => true, 'rel' => true, 'download' => true]); abstract public function __construct(); /** * Register WordPress hooks for the defined actions. * * Implementations should bind `handle_request()` to the appropriate * WordPress action hooks (wp_ajax_*, admin_post_*). * * Example: * ``` * add_action( 'wp_ajax_storeengine/update_data', [ $this, 'handle_request' ] ); * ``` * * @return void */ abstract public function dispatch_actions(); protected function is_ajax_request(): bool { } protected function is_admin_post_request(): bool { } protected function is_unauthenticated_request(): bool { } /** * Main request entry point. * * This method: * - Detects current WordPress action * - Disables caching * - Prepares and validates payload * - Executes the mapped callback * - Sends success or error response * * All exceptions are normalized into WP_Error responses. * * @return void */ public function handle_request() { } /** * Prepare error response. * * @param \WP_Error $response * * @return void */ protected function respond_error(\WP_Error $response) { } /** * Prepare success response. * * @param $response * * @return void */ protected function respond_success($response) { } /** * Prepare response for the request. * * @return \WP_Error|array|\stdClass|string * @throws \StoreEngine\Classes\Exceptions\StoreEngineException */ protected function prepare_response() { } /** * Prepare and sanitize request payload using a declarative schema. * * This method: * - Iterates over defined fields * - Recursively sanitizes input * - Applies validation rules * - Supports nested and repeated fields * * Invalid values are silently discarded (returned as null). * * @param array|null $fields Schema definition from `$actions`. * * @return array Sanitized payload ready for callback consumption. * @throws \StoreEngine\Classes\Exceptions\StoreEngineException */ protected function prepare_payload(?array $fields = null): array { } /** * Recursively sanitize data based on schema definition. * * Supported schema shapes: * * 1. Scalar: * 'title' => 'string|min:3|max:100' * * 2. Nested object: * 'meta' => [ * 'color' => 'hex_color', * 'size' => 'int|min:1', * ] * * 3. Repeated fields: * 'features' => [ * [ * 'title' => 'string', * 'cost' => 'float', * ] * ] * * @param mixed $value Raw request value. * @param mixed $schema Field schema definition. * * @return mixed Sanitized value. * @throws \StoreEngine\Classes\Exceptions\StoreEngineException */ protected function sanitize_by_schema($value, $schema, string $path = '') { } /** * Sanitize and validate scalar values. * * Supports extended rule syntax using pipe separators. * * Supported behaviors: * - required → field must be present and non-empty * - nullable → allows null values * - default → applied when value is missing * - cast-only → sanitize & cast without validation * * Examples: * - string|min:3|max:50|required * - float|min:0 * - string|enum:draft,published * - slug|regex:/^[a-z0-9-]+$/ * * @param mixed $value * @param string $type * @param string $field_path * @param string|null $custom_label * @param string|null $custom_message * @param bool $cast_only * @param array $rule_messages * * @return mixed * @throws \StoreEngine\Classes\Exceptions\StoreEngineException */ protected function sanitize_scalar($value, string $type, string $field_path, ?string $custom_label = null, ?string $custom_message = null, bool $cast_only = false, array $rule_messages = []) { } /** * Execute action callback safely. * * Any thrown exception is converted into a StoreEngineException * and later returned as a WP_Error response. * * @param callable|string|array $callback Action callback. * @param array $payload Sanitized payload. * * @return \WP_Error|array|\stdClass|string * * @throws \StoreEngine\Classes\Exceptions\StoreEngineException */ final protected function respond($callback, array $payload) { } /** * Check user permission for the current action. * * Visitor actions may bypass login checks when explicitly allowed. * * @param string $capability Required capability. * @param bool $allow_visitors Whether unauthenticated users are allowed. * * @return true|\WP_Error True on success, WP_Error otherwise. */ protected function check_permission(string $capability, bool $allow_visitors = false) { } /** * Export action schema to OpenAPI-compatible structure. * Generate Schema for frontend devs to use. * * @param string $action * * @return array|null */ public function export_openapi_schema(?string $action = null): ?array { } /** * Build OpenAPI properties recursively. * * @param array $fields * * @return array */ protected function build_openapi_properties(array $fields): array { } /** * Convert internal schema to OpenAPI format. * * @param mixed $schema * * @return array */ protected function convert_schema_to_openapi($schema): array { } } abstract class AbstractAjaxHandler extends \StoreEngine\Classes\AbstractRequestHandler { final public function dispatch_actions() { } } } namespace StoreEngine\Addons\Affiliate\Ajax { class AffiliateAjax extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } public function update_an_affiliates_status($payload) { } public function add_an_affiliate($payload) { } public function add_an_affiliate_by_id($payload) { } public function get_an_affiliate($payload) { } public function get_all_affiliates($payload) { } public function get_all_users() { } } class AffiliateReportAjax extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } public function add_an_affiliate_report($payload) { } public function get_an_affiliate_report($payload) { } public function get_all_affiliate_reports() { } } class CommissionAjax extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } public function update_a_commissions_status($payload) { } public function add_a_commission($payload) { } public function get_a_commission($payload) { } public function get_all_commissions($payload) { } public function get_all_order_ids() { } protected function update_the_affiliate_commission($commission_id = 0) { } } class PayoutAjax extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } public function update_a_payouts_status($payload) { } public function add_a_payout($payload) { } public function get_a_payout($payload) { } public function get_all_payouts($payload) { } public function get_affiliates_current_balance($payload) { } } class ReferralAjax extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } public function add_an_referral($payload) { } public function get_an_referral($payload) { } public function get_all_referrals($payload) { } } class ReferralTrackAjax extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } public function get_a_click($payload) { } public function get_a_referral_clicks($payload) { } public function get_all_clicks($payload) { } } class Settings extends \StoreEngine\Classes\AbstractAjaxHandler { public function __construct() { } protected function update_affiliate_settings($payload) { } } } namespace StoreEngine\Addons\Affiliate { class CookieHandler { public static function init() { } public function set_affiliate_code_cookie() { } public function set_affiliate_cookie($cookie_data) { } public function handle_order(\StoreEngine\Classes\Order $order) { } /** * Whether the order's buyer is the referring affiliate themselves — matched * by WP user id (logged-in purchase) or by billing email (guest checkout * using the affiliate's own email). */ protected static function is_self_referral(\StoreEngine\Classes\Order $order, int $affiliate_id): bool { } } class Database { public static function init() { } public static function create_initial_custom_table() { } } } namespace StoreEngine\Addons\Affiliate\Database { class CreateAffiliateReport { public static function up($prefix, $charset_collate) { } } class CreateAffiliate { /** * Canonical order of the `status` ENUM. Keep in sync with * {@see \StoreEngine\Addons\Affiliate\models\Affiliate::statuses()}. */ const STATUS_ENUM = ['active', 'inactive', 'pending', 'rejected', 'suspended']; public static function up($prefix, $charset_collate) { } /** * Bring an existing `status` column up to the full ENUM. Runs on the * version-triggered schema re-sync; no-op once already widened. */ private static function ensure_status_enum(string $table_name): void { } } class CreateCommission { public static function up($prefix, $charset_collate) { } } class CreateReferralTrack { public static function up($prefix, $charset_collate) { } } class CreateReferral { public static function up($prefix, $charset_collate) { } } } namespace StoreEngine\Addons\Affiliate { class Helper { /** * @throws \StoreEngine\Classes\Exceptions\StoreEngineException */ public static function generate_random_code(string $code_type, int $code_length = 8) { } public static function get_affiliate_setting($setting_name) { } /** * The public URL query parameter that carries the referral code, e.g. * `?ref=CODE`. Kept short and generic; filterable so a store can rename it. * This is intentionally separate from the internal cookie name * (STOREENGINE_AFFILIATE_COOKIE_KEY) so the URL stays clean while the stored * cookie remains namespaced. */ public static function get_referral_param(): string { } /** * Resolve the commission for an order amount. * * When `$affiliate_id` is given, that affiliate's own commission_type / * commission_rate (stored on the affiliates row) takes precedence over the * global default — previously these per-affiliate columns were saved but * never applied. * * @param float $total_amount Base amount to commission on. * @param int|null $affiliate_id Optional affiliate for a per-affiliate rate. */ public static function get_commission_amount(float $total_amount = 0, ?int $affiliate_id = null) { } public static function format_payment_method($payment_method = null): string { } /** * Resolve a referral code to its referral row. * * The code is valid on *any* landing page — a referral link can point at a * product, a blog post, the home page, or the shop. Previously a click was * only credited when the landing page matched the stored referral_post_id, * so links to anything but the shop page silently failed to track. * * @param string|null $referral_code Referral code from the URL. * * @return array|false Referral row (with affiliate status) or false. */ public static function is_valid_referrer($referral_code = null) { } public static function update_affiliate_commission($affiliate_id, $commission_amount) { } public static function get_payment_card($payment_method = '', $minimum_withdraw = 0) { } } class Hooks { public static function init() { } /** * Read-only affiliate profile panel on the WP user-edit screen so admins can * see the details captured at signup (stored as user meta). * * @param \WP_User $user User being edited. */ public function render_admin_affiliate_profile($user) { } public function auto_approve_commission(\StoreEngine\Classes\Order $order, string $status) { } /** * Reverse a commission when its order is refunded / cancelled / failed. * * An approved commission is clawed back from the affiliate's totals and * available balance. A commission already paid out is flagged rejected but * the withdrawn funds are left untouched (they cannot be reclaimed here). */ protected function reverse_commission_for_order(\StoreEngine\Classes\Order $order) { } public function dashboard_menu_items(array $items): array { } public function admin_menu_items(array $items): array { } public function dashboard_affiliate_partner_content() { } public function integrate_affiliate_settings($settings) { } public function add_pages_to_tools($pages) { } public function add_display_post_states($post_states, $post) { } } } namespace StoreEngine\Addons\Affiliate\Models { class AffiliateReport { public static function get_affiliate_reports($report_id = null, $affiliate_id = null) { } public static function save($args = []) { } public static function update(int $id, array $args, string $by = 'report_id') { } public static function get_current_balance(int $affiliate_id) { } /** * Atomically hold `$amount` from the affiliate's available balance. * * The guard (`current_balance >= amount`) and the decrement happen in a * single UPDATE so two concurrent withdrawal requests cannot both pass a * read-then-write check and overdraw. Returns true only when exactly one * row was reserved (sufficient funds); false means insufficient balance. */ public static function reserve_balance(int $affiliate_id, float $amount): bool { } /** * Return a previously-reserved `$amount` to the affiliate's available * balance (e.g. a payout was rejected or cancelled). */ public static function release_balance(int $affiliate_id, float $amount): bool { } /** * Claw back a reversed commission from lifetime totals and available * balance. Balance is floored at zero so a reversal that lands after the * funds were already withdrawn cannot push the balance negative. */ public static function reverse_commission(int $affiliate_id, float $amount): bool { } } } namespace StoreEngine\Addons\Affiliate\models { class Affiliate { const STATUS_PENDING = 'pending'; const STATUS_ACTIVE = 'active'; const STATUS_INACTIVE = 'inactive'; const STATUS_REJECTED = 'rejected'; const STATUS_SUSPENDED = 'suspended'; /** * Every valid affiliate status. Order matches the DB ENUM * ({@see \StoreEngine\Addons\Affiliate\Database\CreateAffiliate::STATUS_ENUM}). * * @return string[] */ public static function statuses(): array { } /** * Whether the given string is a known affiliate status. */ public static function is_valid_status(string $status): bool { } /** * Human-readable, translated label for a status value. */ public static function status_label(string $status): string { } public static function get_affiliates($args = []) { } /** * Attach the affiliate's signup profile (stored as user meta) to a result * row so the admin UI / API can show who submitted the request and what * they entered — website, promotional methods, payout email, terms date. * * @param array $row A single affiliate row (must contain user_id). * * @return array */ protected static function attach_profile_meta(array $row): array { } public static function update(int $id, array $args) { } public static function add_an_affiliate_report(int $affiliate_id) { } public static function save($args = []) { } /** * Ensure the given user has an affiliate record, creating one if missing. * * Needed because a user can hold the `storeengine_affiliate` role without a * matching affiliates row (e.g. admins auto-granted the role by * Role::add_affiliate_role). `save()` refuses to create a record for anyone * who already has the role, so this inserts directly (bypassing that guard) * and reuses any existing record. * * @param int $user_id User to provision. * @param string $status Status for a newly-created record. * * @return array|\WP_Error The affiliate row, or WP_Error on failure. */ public static function ensure_affiliate_for_user(int $user_id, string $status = 'pending') { } public static function create_new_user($args = []) { } public static function create_username($email): string { } } } namespace StoreEngine\Addons\Affiliate\Models { class Commission { public static function get_commission($args = []) { } public static function save($args = []) { } /** * Mark an affiliate's approved commissions as paid, oldest first, until the * cumulative commission amount covers the given payout amount. * * Payouts are recorded as an affiliate + amount (not linked to specific * commission rows), so this reconciles them FIFO when a payout completes. * * @param int $affiliate_id Affiliate whose commissions to settle. * @param float $amount Payout amount to cover. * * @return int Number of commissions marked paid. */ public static function mark_approved_as_paid(int $affiliate_id, float $amount): int { } public static function update(int $id, array $args) { } } } namespace StoreEngine\Addons\Affiliate\models { class Payout { public static function get_payouts($args = []) { } public static function save($args = []) { } public static function update(int $id, array $args) { } /** * Shapes a unified-ledger row back into the column names the affiliate * UI was built against, so the React table doesn't need to change. */ protected static function shape_for_legacy_ui(object $row): array { } } class ReferralTrack { public static function get_referral_tracks($args) { } public static function get_clicks_by_referral_id($referral_id = null, $offset = 0, $per_page = 10, $status = 'any', $search = '') { } public static function save($args = []) { } public static function update(int $id, array $args) { } } } namespace StoreEngine\Addons\Affiliate\Models { class Referral { public static function get_referrals($args = []) { } public static function modify_referral_url($query_result = []) { } public static function create_link($referral_code, $referral_post_id) { } public static function save($args = []) { } public static function update(int $id, array $args) { } /** * Whether a referral code is free to use — either unused, or already owned * by the given affiliate (so re-saving an unchanged slug is allowed). */ public static function is_code_available(string $code, int $for_affiliate_id = 0): bool { } /** * Set a custom (vanity) referral code on an affiliate's primary referral * row. Validates length + uniqueness. Returns the normalised code on * success or a WP_Error. * * Note: does not reuse self::update() because that method formats every * value as %d, which would coerce a string code to 0. * * @param int $affiliate_id Affiliate to update. * @param string $code Desired slug. * * @return string|\WP_Error */ public static function update_code(int $affiliate_id, string $code) { } } } namespace StoreEngine\Addons\Affiliate { class Post { public static function init() { } public function dispatch_hooks() { } } } namespace StoreEngine\Classes { abstract class AbstractPostHandler extends \StoreEngine\Classes\AbstractRequestHandler { public function dispatch_actions() { } } } namespace StoreEngine\Addons\Affiliate\Post { class Affiliate extends \StoreEngine\Classes\AbstractPostHandler { public function __construct() { } public function apply_for_affiliation($payload = []) { } public function register_for_affiliate($payload) { } /** * Persist the affiliate profile fields collected at signup to user meta. * * Stored as user meta (not a schema change) alongside the existing payout * details. The payout email also seeds the withdrawal PayPal email when the * affiliate hasn't set one yet, so approved affiliates can be paid without a * second data-entry step. * * @param int $user_id Target user. * @param array $payload Sanitized form payload. */ protected function save_affiliate_profile_meta(int $user_id, array $payload) { } } class Settings extends \StoreEngine\Classes\AbstractPostHandler { protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_affiliate'; public function __construct() { } public function save_frontend_dashboard_withdraw_settings($payload) { } public function affiliate_earning_withdrawal($payload) { } } } namespace StoreEngine\Addons\Affiliate { class Role { const ROLE = 'storeengine_affiliate'; const CAPS = [ 'manage_storeengine_affiliate', // affiliate post type 'edit_storeengine_affiliate', 'read_storeengine_affiliate', 'delete_storeengine_affiliate', 'delete_storeengine_affiliates', 'edit_storeengine_affiliates', 'edit_others_storeengine_affiliates', 'read_private_storeengine_affiliates', // common WP 'edit_post', 'edit_posts', 'read', 'edit_others_posts', ]; public static function add_affiliate_role() { } public static function remove_affiliate_role(): void { } } } namespace StoreEngine\Addons\Affiliate\Settings { class Affiliate { public static function get_settings_saved_data() { } public static function get_settings_default_data() { } public static function save_settings($form_data = false) { } } } namespace StoreEngine\Addons\Affiliate { class Shortcode { public static function init() { } public function dispatch_shortcode() { } } } namespace StoreEngine\Addons\Affiliate\Shortcode { class Registration { public function __construct() { } /** * Expose the affiliate registration form as a configurable block via the * StoreEngine shortcode → block bridge, so the Affiliate Registration page * (and any other page) can drop it in with editor controls. No-op when the * bridge isn't present; the shortcode still works everywhere. */ public function register_shortcode_block() { } public function registration_form($atts) { } } } namespace StoreEngine\Addons\Ai { class Abilities { use \StoreEngine\Traits\Singleton; const CATEGORY = 'storeengine'; protected function __construct() { } /** * Capability gate shared by every read ability. */ public static function can_read(): bool { } public function register(): void { } /** * Thin wrapper that fills in the shared category + read permission and maps * `callback` to whichever execute key the installed Abilities API expects. */ public function register_ability(string $id, array $args): void { } /* ----------------------------------------------------------- callbacks */ public static function ability_get_product(array $input) { } public static function ability_search_products(array $input): array { } public static function ability_get_order(array $input) { } public static function ability_get_inventory(array $input) { } } final class Ai extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'ai'; public function define_constants() { } public function init_addon() { } public function addon_activation_hook() { } } } namespace StoreEngine\Addons\Ai\Classes { class ProductGenerator { /** * Generate a single field for a product. * * @param int $product_id * @param string $field title | description | short_description | image_alt * @param array $opts { tone?:string, language?:string } * * @return string|\WP_Error */ public static function generate(int $product_id, string $field, array $opts = []) { } /** * Build the product context block the model reasons over. * * @param int $product_id * @param mixed $product StoreEngine product object. */ protected static function build_context(int $product_id, $product): string { } } class PromptLibrary { /** * Supported product fields. */ public static function fields(): array { } /** * Human phrase describing a tone, woven into the instruction. */ protected static function tone_phrase(string $tone): string { } /** * Instruction + soft length cap for a field. * * @return array{system:string, max_chars:int} */ public static function for_field(string $field, string $tone = 'professional', string $language = ''): array { } } class TermGenerator { /** * Generate a description for a taxonomy term. * * @param string $taxonomy The WP taxonomy slug (e.g. storeengine_product_category). * @param string $name The term name/title to write copy for. * @param array $opts { tone?:string, language?:string } * * @return string|\WP_Error */ public static function generate(string $taxonomy, string $name, array $opts = []) { } protected static function taxonomy_label(string $taxonomy): string { } protected static function tone_phrase(string $tone): string { } } } namespace StoreEngine\Addons\Ai { class Hooks { use \StoreEngine\Traits\Singleton; protected function __construct() { } /** * Only nudge on StoreEngine admin screens, and only while unavailable. */ public function maybe_render_unavailable_notice(): void { } } } namespace StoreEngine\Addons\Ai\Rest { final class RestProduct { const NS = 'storeengine/v1'; public static function init(): void { } public static function permission(): bool { } public static function register_routes(): void { } public static function generate(\WP_REST_Request $request) { } } class RestSettings { const NS = 'storeengine/v1'; public static function init(): void { } public static function permission(): bool { } public static function register_routes(): void { } /** Keys this tab manages, with their value type for sanitisation. */ protected static function field_types(): array { } protected static function current(): array { } public static function get_settings(): \WP_REST_Response { } public static function save_settings(\WP_REST_Request $request): \WP_REST_Response { } /** * @param string $type bool | int | float | text | list | enum:a,b,c * @param mixed $value * * @return mixed */ protected static function sanitize_field(string $type, $value) { } } final class RestStatus { const NS = 'storeengine/v1'; public static function init(): void { } public static function permission(): bool { } public static function register_routes(): void { } public static function get_status(): \WP_REST_Response { } } final class RestTerm { const NS = 'storeengine/v1'; public static function init(): void { } public static function permission(): bool { } public static function register_routes(): void { } public static function generate(\WP_REST_Request $request) { } } } namespace StoreEngine\Interfaces { interface AddonSettingsInterface { public function get_default_settings(): array; public function get_settings_fields(): array; public function save_default_settings(): void; } } namespace StoreEngine\Traits { trait AbstractSingleton { protected static array $instances = []; public static function init() { } public static function get_instance() { } protected function __construct() { } /** * Cloning is forbidden. */ public function __clone() { } /** * Unserializing instances of this class is forbidden. */ public function __wakeup() { } } } namespace StoreEngine\Classes { abstract class AbstractAddonSettings implements \StoreEngine\Interfaces\AddonSettingsInterface { use \StoreEngine\Traits\AbstractSingleton; /** * Addon Settings Name. * Unique name for the addon settings. * * @var string */ protected ?string $settings_name = null; protected ?array $settings = null; protected bool $validate_on_save = false; protected function __construct() { } public function get_settings_name(): string { } /** * Get settings. * * @param string $key * @param mixed $default * * @return mixed */ public function get_settings(string $key, $default = null) { } public function load_settings(bool $reload = false) { } public function dispatch_hooks() { } public function save_default_settings(): void { } public function update_settings_name(string $old_name): void { } public function validate_settings(\WP_Error $errors, array $payload): \WP_Error { } } } namespace StoreEngine\Addons\Ai { class Settings extends \StoreEngine\Classes\AbstractAddonSettings { protected ?string $settings_name = 'ai'; public function get_default_settings(): array { } public function get_settings_fields(): array { } } } namespace StoreEngine\Addons\Brand { final class Brand extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'brand'; /** * Flat product-brand taxonomy slug. Kept local to the addon so the brand * concept stays fully self-contained (no edit to the core Helper). */ const TAXONOMY = 'storeengine_product_brand'; /** * Term-meta keys for the brand images (both store WP attachment IDs). */ const META_IMAGE = 'storeengine_brand_image'; const META_BANNER = 'storeengine_brand_banner'; public function define_constants() { } /** * Registered only while the addon is active (AbstractAddon::run() gates this). * * The taxonomy must be registered on `init` (priority 5, matching core * Database) — NOT inline here, because Addons::init() runs at plugins_loaded, * before `init` and before $wp_rewrite is ready. */ public function init_addon() { } /** * Flush rewrite rules once when the merchant enables the addon so the * /product-brand// archive permalink works immediately. */ public function addon_activation_hook() { } public function addon_deactivation_hook() { } /** * Register the flat product-brand taxonomy. Mirrors the core Category/Tag * registration (see includes/database.php) but non-hierarchical. */ public function register_taxonomy() { } /** * Register the brand logo + banner term meta. Both are attachment IDs and * are surfaced inside the WP_REST_Terms_Controller `meta` object, so the * React form can read/write them with no custom REST controller. */ public function register_term_metas() { } /** * Append a "Brands" sub-item to the existing Products menu. Because this * filter is only added while the addon is active, the menu entry (and its * SPA route) disappears when the addon is disabled. * * @param array $menu Admin menu list (see Admin\Menu::get_menu_lists()). * @return array */ public function inject_menu_item(array $menu): array { } } final class Frontend { use \StoreEngine\Traits\Singleton; public static function init() { } /** * Output the brand(s) assigned to the current product on the single-product * page. Mirrors templates/single-product/categories.php. */ public function render_single_product_brands() { } /** * Register the "brand" archive filter widget so it shows in the shop * sidebar alongside the Category / Tags filters. * * @param array $config Widget config keyed by widget slug. * @return array */ public function register_archive_filter_widget($config) { } /** * Render the brand filter widget markup. Flat list of brands (no hierarchy). * The checkboxes use name="brand" — handled client-side by ssr.js, which * pushes selected slugs into the archive AJAX payload. */ public function render_archive_filter_widget() { } /** * Narrow the archive query by the selected brand slugs. The brand selection * arrives in the `storeengine/archive_product_filter` AJAX GET payload, so we * read it from $_REQUEST and append a tax_query clause. * * @param array $args WP_Query args built by Helper::prepare_product_search_query_args(). * @return array */ public function apply_brand_filter_args($args) { } } } namespace StoreEngine\Addons\CatalogMode { final class CatalogMode extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'catalog-mode'; protected static ?array $settings = null; public function define_constants() { } public function init_addon() { } public function addon_activation_hook() { } } class Hooks { use \StoreEngine\Traits\Singleton; protected function __construct() { } /** * Dispatch catalog mode if enabled. * Using anonymous function so actions can't be removed. * * @return void */ public function dispatch_catalog_mode() { } public function product_loop_add_to_cart_replacement() { } public function single_product_add_to_cart_replacement() { } protected function get_replacement_args(): array { } public static function maybe_exclude_user(): bool { } public static function should_replace_add_to_cart(): bool { } public static function maybe_hide_add_to_cart(): bool { } /** * Reject REST hits to the storefront cart + checkout routes when * `disable_cart_checkout` is on. Replaces the legacy admin-ajax block. */ public function block_cart_rest_routes($result, $server, $request) { } public function add_script_data(array $data): array { } } class Settings extends \StoreEngine\Classes\AbstractAddonSettings { protected ?string $settings_name = 'catalog_mode'; public function isEnabled(): bool { } public function get_default_settings(): array { } public function get_settings_fields(): array { } } } namespace StoreEngine\Addons\Couriers { final class Api { public static function init(): void { } } } namespace StoreEngine\Addons\Couriers\Api { final class OAuthController { const NS = 'storeengine/v1'; public static function init(): void { } public static function register_routes(): void { } public static function permission(): bool { } public static function authorize_url(\WP_REST_Request $request) { } /** * Uniform disconnect for any courier: revoke OAuth tokens for interactive * couriers; for the rest (2-legged OAuth / API-key), "disconnect" clears the * saved credentials so the card returns to a not-connected state. */ public static function disconnect(\WP_REST_Request $request) { } /** * Test a 2-legged (client_credentials / password) courier by minting a token * from the saved client id/secret — the "auth check" for carriers that have * no interactive connect step. */ public static function test(\WP_REST_Request $request) { } /** * Provider redirect target. Validates `state`, exchanges `code`, then bounces * the browser back to the couriers settings screen with a status flag. */ public static function callback(\WP_REST_Request $request) { } /** * 302 back to Settings → Couriers with the outcome. Empty $error = success. */ private static function bounce(string $provider, string $error) { } } final class ProvidersController { const NS = 'storeengine/v1'; public static function init(): void { } public static function register_routes(): void { } public static function permission(): bool { } public static function list_all() { } private static function is_connected(string $id, $provider): bool { } /** * OAuth connection state for a provider, so the settings UI can render a * Connect / Disconnect control for the 3-legged (authorization_code) flow. * `supported` is false for API-key providers and 2-legged carriers (which * need no interactive connect step). * * @return array{supported:bool,grant?:string,interactive?:bool,configured?:bool,connected?:bool,expires_at?:int|null} */ private static function oauth_state(string $id): array { } public static function pathao_cities() { } public static function pathao_zones(\WP_REST_Request $request) { } } final class SettingsController { const NS = 'storeengine/v1'; const OPT = 'storeengine_courier_settings'; public static function init(): void { } public static function register_routes(): void { } public static function permission(): bool { } public static function fetch() { } public static function save(\WP_REST_Request $request) { } protected static function default_auto_push(array $cfg): array { } } final class ShipmentsController { const NS = 'storeengine/v1'; public static function init(): void { } public static function register_routes(): void { } public static function permission(): bool { } public static function list_all(\WP_REST_Request $request) { } public static function create(\WP_REST_Request $request) { } public static function track(\WP_REST_Request $request) { } public static function cancel(\WP_REST_Request $request) { } public static function shipment_defaults(\WP_REST_Request $request) { } } } namespace StoreEngine\Addons\Couriers\Classes { /** * Courier provider contract. * * Implementations live under addons/couriers/providers/. Each implementation * is a thin wrapper around the courier's HTTP API: sandbox by default, * configurable via the storeengine_courier_settings option. */ interface ProviderInterface { public function id(): string; public function label(): string; /** * Per-provider config schema for the React settings page. * * @return array */ public function settings_schema(): array; /** * Push a shipment to the courier API. Returns the provider's * tracking_id, label_url, etc. * * @param array $payload order, customer, items, weight_kg, cod_amount * @return array{ok:bool,tracking_id?:string,consignment_id?:string,label_url?:string,tracking_url?:string,raw?:array,errors?:array} */ public function create_shipment(array $payload): array; /** * Poll the courier for current status of a shipment. * * @return array{ok:bool,status?:string,internal_status?:string,delivered?:bool,raw?:array,errors?:array} */ public function check_status(string $tracking_id): array; /** * Cancel an outstanding shipment. * * @return array{ok:bool,errors?:array} */ public function cancel(string $tracking_id): array; } abstract class AbstractProvider implements \StoreEngine\Addons\Couriers\Classes\ProviderInterface { /** * @return array */ protected function settings(): array { } protected function get_setting(string $key, $default = '') { } protected function http_post(string $url, array $body, array $headers = []): array { } protected function http_get(string $url, array $headers = []): array { } /** * Default raw → internal status mapper. Each provider should override * to handle its own status vocabulary; this fallback covers the common * lowercase aliases. */ protected function map_status(string $raw): string { } protected function parse_response($response): array { } } /** * Optional: auto-push paid orders to a configured courier provider. * * Listens for `storeengine/payment_complete` and, if `auto_push.enabled` * is true in the courier-settings option, calls the chosen provider's * `create_shipment()` with the order's billing address. POS sales are * skipped (cashier already handed the goods over). Online order numbers * are tagged with `_storeengine_courier_auto_pushed` to prevent duplicates * if the action fires twice (e.g. due to gateway retry). */ final class AutoPush { const META_KEY = '_storeengine_courier_auto_pushed'; const OPT_KEY = 'storeengine_courier_settings'; const ASYNC_HOOK = 'storeengine/couriers/auto_push'; public static function init(): void { } /** * Runs inside the user-facing payment-complete request. Does only cheap local * checks, then hands the courier API calls (which are external HTTP with a 30s * timeout, one per vendor) to Action Scheduler so they never block checkout or * a gateway callback. Falls back to inline execution only if Action Scheduler * is unavailable. */ public static function maybe_push(int $order_id): void { } public static function get_auto_push_config(): array { } public static function set_auto_push_config(array $cfg): void { } /** * The actual courier push (external HTTP). Runs via Action Scheduler, off the * user-facing request. Re-checks config and the idempotency guard because the * order/settings may have changed between enqueue and execution. */ public static function run_push(int $order_id): void { } /** * Group an order's line items by vendor (product post_author). * @return array> vendor_id => list of line items */ protected static function group_items_by_vendor($order): array { } } final class OAuthTokenStore { const OPTION = 'storeengine_courier_oauth_tokens'; /** * Token record for a provider. * * @return array{access_token?:string,refresh_token?:string,expires_at?:int,token_type?:string,scope?:string,obtained_at?:int} */ public static function get(string $provider_id): array { } /** * Merge-save a token record for a provider. Only the keys passed are * updated, so a refresh that returns no new refresh_token keeps the old one. * * @param array $data */ public static function save(string $provider_id, array $data): void { } public static function clear(string $provider_id): void { } public static function has_access_token(string $provider_id): bool { } } final class OAuth { const STATE_PREFIX = 'se_courier_oauth_state_'; const CC_CACHE_PREFIX = 'se_courier_oauth_cc_'; const STATE_TTL = 900; // 15 min to complete the consent round-trip. const EXPIRY_SKEW = 30; // Treat a token as expired this many secs early. private string $provider_id; /** @var array */ private array $cfg; /** * @param array $cfg Normalised oauth_config(). */ private function __construct(string $provider_id, array $cfg) { } /** * Build an engine for a registered provider, or null if the provider does * not support OAuth / declares no token endpoint. */ public static function for_provider(string $provider_id): ?\StoreEngine\Addons\Couriers\Classes\OAuth { } public function grant(): string { } public function is_authorization_code(): bool { } /** * Are the credentials needed to even attempt a connection present? * (client id/secret for code & client_credentials; username/password too for * the password grant.) */ public function is_configured(): bool { } /** * Connected = we hold a usable access token. For 3-legged that means the * store has one (refreshable); for 2-legged it means credentials that can * mint one on demand. */ public function is_connected(): bool { } /** * When the current authorization_code access token expires (unix ts), or null. */ public function expires_at(): ?int { } /** * The fixed redirect/callback URL the provider's OAuth app must whitelist. * Provider-agnostic — the provider is recovered from the signed `state`. */ public static function redirect_uri(): string { } // ── 3-legged: authorization_code ────────────────────────────────────────── /** * Build the provider's consent URL and persist a one-time `state` bound to * this provider + the current user (CSRF protection for the callback). */ public function authorize_url(): string { } /** * Resolve the provider id a callback `state` belongs to (and consume it). * Returns null if unknown/expired. Static because the callback endpoint does * not yet know which provider it is for. * * @return array{provider:string,user:int}|null */ public static function consume_state(string $state): ?array { } /** * Exchange an authorization code for tokens and persist them. * * @return true|\WP_Error */ public function exchange_code(string $code) { } /** * Refresh the authorization_code access token from the stored refresh token. * Clears the store on invalid_grant so the UI prompts a reconnect. */ public function refresh(): ?string { } // ── 2-legged: client_credentials / password ─────────────────────────────── private function fetch_two_legged(): ?string { } // ── Public entry point ──────────────────────────────────────────────────── /** * A valid bearer access token for the current grant, or null if the provider * is not connected / credentials are missing / the token cannot be minted. */ public function access_token(): ?string { } public function disconnect(): void { } // ── Internals ───────────────────────────────────────────────────────────── /** * @param array{expires_at?:int} $row */ private function token_fresh(array $row): bool { } /** * @param array $token OAuth token response body. */ private function persist_tokens(array $token): void { } /** * POST the token endpoint (form-encoded, per the OAuth2 spec) and return the * decoded token body, or a WP_Error whose code is the OAuth `error` field * (e.g. invalid_grant) when the server reports one. * * @param array $body Grant-specific params (client creds added here). * @return array|\WP_Error */ private function request_token(array $body) { } } /** * Maps a StoreEngine order to the unified shipment payload shape used by * every courier provider. Single source of truth for both AutoPush and the * manual create-shipment REST endpoint, so the two flows stay in lockstep. */ final class OrderPayloadMapper { /** * Build the standard payload (recipient, address, COD, weight, item summary). * * @param object $order StoreEngine order (AbstractOrder). * @param array $cfg Auto-push config (default_weight_kg, cod_methods, use_cod_when). */ public static function build_payload($order, array $cfg = []): array { } /** * Shiprocket-shaped line items: [{name, sku, units, selling_price}, ...]. */ public static function build_line_items($order): array { } /** * Provider-specific extras the admin shouldn't have to type. Currently * only Shiprocket needs anything beyond the standard payload. * * @return array */ public static function build_provider_extras($order, string $provider_id): array { } } /** * Bridges shipment-status events back into the parent order: writes * notes for every status transition, and on terminal statuses optionally * advances the order to `completed` or `on_hold` based on admin settings. * * Order stays in `processing` for the entire fulfilment journey unless * ALL shipments for the order reach `delivered`, which is when we flip to * `completed` (opt-out via auto_complete_on_delivered). */ final class OrderStatusSync { const OPT_KEY = 'storeengine_courier_settings'; public static function init(): void { } public static function get_sync_config(): array { } public static function set_sync_config(array $cfg): void { } public static function on_created(int $shipment_id, int $order_id, string $provider): void { } public static function on_updated(int $shipment_id, string $raw_status, string $internal_status, bool $delivered): void { } protected static function all_shipments_delivered(int $order_id): bool { } } final class Registry { /** @var array|null */ protected static ?array $cache = null; /** * @return array */ public static function all(): array { } /** * @return object|null A courier provider (framework or satellite-supplied). * Duck-typed against ProviderInterface — the concrete * class may live in the storeengine-courier satellite, * which implements its own copy of the contract. */ public static function get(string $id): ?object { } } /** * Polls in-flight shipments via Action Scheduler. * * Frequency is intentionally conservative (15 minutes) since most BD/IN * couriers don't push webhooks reliably and we cap to 50 shipments per run. */ final class Scheduler { public const ACTION = 'storeengine/couriers/poll_in_flight'; public static function init(): void { } public static function maybe_schedule(): void { } public static function clear(): void { } public static function run(): void { } } /** * Internal shipment status enum. * * created → picked_up → in_transit → out_for_delivery → delivered * ↘ cancelled * ↘ returned * * Each provider's check_status() normalizes its raw string into one of * these values via AbstractProvider::map_status(). */ final class ShipmentStatus { const CREATED = 'created'; const PICKED_UP = 'picked_up'; const IN_TRANSIT = 'in_transit'; const OUT_FOR_DELIVERY = 'out_for_delivery'; const DELIVERED = 'delivered'; const CANCELLED = 'cancelled'; const RETURNED = 'returned'; const TERMINAL = [self::DELIVERED, self::CANCELLED, self::RETURNED]; public static function all(): array { } public static function is_valid(string $status): bool { } public static function is_terminal(string $status): bool { } } final class ShipmentsService { public static function all(array $filters = []): array { } public static function get(int $id): ?object { } public static function create_for_order(int $order_id, string $provider_id, array $payload): array { } public static function refresh_status(int $id): array { } public static function cancel(int $id): array { } public static function in_flight_ids(int $limit = 50): array { } } } namespace StoreEngine\Addons\Couriers { final class Couriers extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'couriers'; public function define_constants(): void { } public function init_addon(): void { } /** * Inject the Couriers top-level menu into the React admin shell. * (Provider settings live in the main Settings → Couriers tab.) * * @param array $menu Existing menu list. * @return array */ public static function register_menu_items(array $menu): array { } public function get_db_version(): string { } public function install_tables(): void { } public function addon_activation_hook(): void { } public function addon_deactivation_hook(): void { } } final class Database { const DB_VERSION = '1.2.0'; public static function shipments_table(): string { } /** * Create/upgrade the shipments table. * * No per-addon version bookkeeping: the core addon manager * (\StoreEngine\Addons::sync_schema_for) compares this addon's * get_db_version() against the shared `storeengine_addons_db_version` map and * calls install_tables() (→ here) only on a mismatch, then records the * version itself. dbDelta is idempotent, so activation can call this directly. */ public static function install(): void { } } } namespace StoreEngine\Addons\Csv\Ajax { class Export extends \StoreEngine\Classes\AbstractAjaxHandler { protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_csv'; protected static \StoreEngine\Classes\EventStreamServer $sse; public function __construct() { } /** * Returns the user_id to scope export queries by, or 0 for full visibility. * Vendors get scoped to their own data; admin/shop_manager get everything. */ protected function vendor_scope_user_id(): int { } public function file_download(array $payload) { } public function export(array $payload) { } private function export_products() { } private function get_product_categories(\StoreEngine\Classes\AbstractProduct $product): string { } private function get_product_tags(\StoreEngine\Classes\AbstractProduct $product): string { } private function get_product_images(\StoreEngine\Classes\AbstractProduct $product): string { } private function get_pricing_index(\StoreEngine\Classes\AbstractProduct $product, int $pricing_id): ?int { } private function export_orders() { } private function export_customers() { } private function export_access_groups() { } private function get_slug_from_item(string $title): ?string { } private function export_subscriptions() { } public static function run_clean_hook(string $filepath) { } private static function open_file(string $filename, string $mode, bool $use_include_path = false, $context = null) { } private static function close_file($stream): bool { } } class Import extends \StoreEngine\Classes\AbstractAjaxHandler { protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_csv'; protected static \StoreEngine\Classes\EventStreamServer $sse; protected array $orders_key_mapping = ['Order Key' => 'order_key', 'Order Status' => 'order_status', 'Customer Email' => 'customer_email', 'Order Currency' => 'order_currency', 'Order Subtotal' => 'order_subtotal', 'Order Total' => 'order_total', 'Order Tax' => 'order_tax', 'Date Created (GMT)' => 'date_created_gmt', 'Date Updated (GMT)' => 'date_updated_gmt', 'Date Placed (GMT)' => 'date_placed_gmt', 'Date Paid (GMT)' => 'order_paid_date_gmt', 'Payment Method (name)' => 'payment_method', 'Payment Method (title)' => 'payment_method_title', 'Transaction ID' => 'transaction_id', 'IP Address' => 'ip_address', 'User Agent' => 'user_agent', 'Customer Note' => 'customer_note', 'Cart Hash' => 'cart_hash', 'Meta Data' => 'meta_data']; protected array $customers_key_mapping = ['Username' => 'username', 'First Name' => 'first_name', 'Last Name' => 'last_name', 'Nick Name' => 'nickname', 'Display Name' => 'display_name', 'Email' => 'email', 'Website' => 'url', 'Biographical Info' => 'description', 'Registered Date (GMT)' => 'user_registered_date', 'Roles' => 'roles', 'Billing First Name' => 'billing_first_name', 'Billing Last Name' => 'billing_last_name', 'Billing Company' => 'billing_company', 'Billing Address 1' => 'billing_address_1', 'Billing Address 2' => 'billing_address_2', 'Billing City' => 'billing_city', 'Billing State' => 'billing_state', 'Billing Postcode' => 'billing_postcode', 'Billing Country' => 'billing_country', 'Billing Email' => 'billing_email', 'Billing Phone' => 'billing_phone', 'Shipping First Name' => 'shipping_first_name', 'Shipping Last Name' => 'shipping_last_name', 'Shipping Company' => 'shipping_company', 'Shipping Address 1' => 'shipping_address_1', 'Shipping Address 2' => 'shipping_address_2', 'Shipping City' => 'shipping_city', 'Shipping State' => 'shipping_state', 'Shipping Postcode' => 'shipping_postcode', 'Shipping Country' => 'shipping_country', 'Shipping Email' => 'shipping_email', 'Shipping Phone' => 'shipping_phone', 'Email Subscribe' => 'subscribe_to_email']; protected array $products_key_mapping = ['ID' => 'id', 'Type' => 'type', 'Name' => 'name', 'Slug' => 'slug', 'Published' => 'published_date_gmt', 'Description' => 'content', 'Is Hide' => 'hide', 'Shipping Type' => 'shipping_type', 'Weight' => 'weight', 'Weight Unit' => 'weight_unit', 'Product Categories' => 'product_categories', 'Product Tags' => 'product_tags', 'Images' => 'product_gallery']; protected array $access_groups_key_mapping = ['Access Group Name' => 'name', 'Unauthorized Access Message' => 'message', 'Protected Content Type' => 'content_type', 'Specific Content Items' => 'specific_content_items', 'Excluded Content Items' => 'excluded_content_items', 'Downloadable files' => 'downloadable_files', 'Enable Redirect URL' => 'enable_redirect_url', 'Redirect URL' => 'redirect_url', 'Enable Access Group Expiration' => 'enable_expiration', 'Expiration Date type' => 'expiration_date_type', 'Expiration Relative Date' => 'expiration_relative_date', 'Expiration Specific Date' => 'expiration_specific_date', 'User Role Sync' => 'user_role_sync', 'Priority' => 'priority']; protected array $subscriptions_key_mapping = ['Subscription Key' => 'subscription_key', 'Subscription Status' => 'subscription_status', 'Customer Email' => 'customer_email', 'Subscription Currency' => 'subscription_currency', 'Subscription Subtotal' => 'subscription_subtotal', 'Subscription Total' => 'subscription_total', 'Subscription Tax' => 'subscription_tax', 'Date Created (GMT)' => 'date_created_gmt', 'Date Updated (GMT)' => 'date_updated_gmt', 'Date Placed (GMT)' => 'date_placed_gmt', 'Date Paid (GMT)' => 'subscription_paid_date_gmt', 'Payment Method (name)' => 'payment_method', 'Payment Method (title)' => 'payment_method_title', 'IP Address' => 'ip_address', 'User Agent' => 'user_agent', 'Customer Note' => 'customer_note', 'Cart Hash' => 'cart_hash', 'Parent Order Key' => 'parent_order_key', 'Child Order Keys' => 'child_order_keys', 'Start Date (GMT)' => 'start_date', 'Has Trial' => 'has_trial', 'Trial Days' => 'trial_days', 'Trial End Date (GMT)' => 'trial_end_date', 'Next Payment Date (GMT)' => 'next_payment_date', 'Meta Data' => 'meta_data']; public function __construct() { } public function import(array $payload) { } public function import_process(array $payload) { } private function import_products(string $filename, ?int $start_index = null) { } private function chunk_products(string $filename) { } private function import_chunk_products(string $filename, $start_index) { } private function remote_to_local_image(string $url) { } private function import_orders(string $filename, ?int $start_index = null) { } private function chunk_orders(string $filename) { } private function get_filename_without_extension(string $filename) { } private function import_customers(string $filename, ?int $start_index = null) { } private function chunk_customers(string $filename) { } private function import_access_groups(string $filename, ?int $start_index = null) { } private function chunk_access_groups(string $filename) { } private function import_chunk_access_groups(string $filename, int $start_index) { } private function import_subscriptions(string $filename, ?int $start_index = null) { } private function chunk_subscriptions(string $filename) { } private function get_label_value(string $slug): ?array { } public function download_sample_file(array $payload) { } } } namespace StoreEngine\Addons\Csv { final class Csv extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'csv'; public function define_constants() { } public function init_addon() { } } class Hooks { public static function init() { } public function clean_tmp_csv(string $filepath) { } public function add_scripts_data(array $data): array { } } } namespace StoreEngine\Addons\Csv\ThirdParty { class Rcp extends \StoreEngine\Classes\AbstractAjaxHandler { protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_csv'; protected static \StoreEngine\Classes\EventStreamServer $sse; public function __construct() { } /** * @param array $payload * * @return void */ public function rcp_export(array $payload) { } private function export_membership_levels() { } private function export_payments() { } private function export_memberships() { } private function export_customers() { } /** * @param \RCP\Membership_Level $membership_level * * @return float|int|null */ private function get_days(\RCP\Membership_Level $membership_level) { } private function get_order_key(int $payment_id, string $subscription_key) { } } } namespace StoreEngine\Addons\Csv\Utils { class Import { public static function validate_csv_structure(string $file, array $expected_header) { } public static function import_chunk_customers(string $filename, int $start_index, \StoreEngine\Classes\EventStreamServer $stream) { } public static function import_chunk_orders(string $filename, int $start_index, \StoreEngine\Classes\EventStreamServer $stream) { } public static function import_chunk_subscriptions(string $filename, int $start_index, \StoreEngine\Classes\EventStreamServer $stream) { } public static function store_chunk_data(string $type, string $filename, array $data): array { } public static function generate_unique_username($first_name, $last_name): string { } public static function validate_data_type(string $type = null) { } } } namespace StoreEngine\Addons\Email\Traits { trait Email { protected string $email_name; private $settings; public function __construct(string $name) { } /** * @param string|string[] $to Array or comma-separated list of email addresses to send message. * @param string $subject Email subject. * @param string $body Message contents. * @param string|string[] $headers Optional. Additional headers. * @param string|string[] $args Optional. Additional headers. * * @return bool|mixed */ public function mail_send($to, string $subject, string $body, $headers, $args = []) { } public function get_from_name($name) { } public function get_from_address($email_address) { } public function get_order_item_template_old(): string { } public function get_order_item_template(): string { } private function get_settings($action_name) { } /** * Append a Reply-To header pointing to the customer for admin-targeted * order emails. Without this, when multiple customers place orders the * site admin gets a stream of notifications all From the same site * address — hitting Reply goes to the site itself, not the customer who * actually placed the order, which is rarely useful. * * Filterable: `storeengine/email/admin_reply_to` receives the formed * header string, the order, and the email_name slug so site owners can * swap in their own routing logic (e.g. a shared support inbox). * * @param array $headers Existing wp_mail headers. * @param \StoreEngine\Classes\Order $order * @return array Headers with Reply-To appended (or unchanged if the * billing email is missing/invalid). */ protected function with_customer_reply_to(array $headers, \StoreEngine\Classes\Order $order): array { } private function get_the_email_body($settings, $template_path): array { } protected function prepare_text_body($email_heading, $email_content, $email_footer): string { } protected function prepare_body_without_layout($body): string { } /** * Apply inline styles to dynamic content. * * We only inline CSS for html emails, and to do so we use Emogrifier library (if supported). * * @param string|null $content Content that will receive inline styles. * * @return string */ protected function style_inline($content) { } /** * Returns CSS styles that should be included with all HTML e-mails, regardless of theme specific customizations. * * @return string * @since 0.0.4 */ protected function get_must_use_css_styles(): string { } private function get_order_items_data(\StoreEngine\Classes\Order $order): string { } private function get_order_totals_data(\StoreEngine\Classes\Order $order): string { } private function get_order_email_body(\StoreEngine\Classes\Order $order, string $body): string { } private function get_email_subject(\StoreEngine\Classes\Order $order, $email_subject = '') { } protected function get_hook_name(string $suffix = null): string { } } } namespace StoreEngine\Addons\Email\account { class PasswordReset { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'password_reset'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } /** * @param array $defaults WP's default [subject, message, headers, attachments]. * @param string $key Reset key (one-time token). * @param string $user_login * @param \WP_User|null $user_data */ public function filter_email($defaults, $key, $user_login, $user_data): array { } public function remove_from_filters(): void { } } class RegistrationWelcome { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'registration_welcome'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } /** * @param array $email WP's default [to, subject, message, headers]. * @param \WP_User $user The newly-registered user. * @param string $blogname */ public function filter_email($email, $user, $blogname): array { } public function remove_from_filters(): void { } } } namespace StoreEngine\Addons\Email\Admin { class Settings { public function __construct() { } public function include_settings($settings) { } public static function get_settings_saved_data() { } public static function get_settings_default_data() { } /** * Seed an empty `email_content_tree` default wherever an `email_content` * default exists. Keeps the block-editor tree key present through the * deep-merge in {@see self::save_settings()} and returned by * {@see HelperAddon::get_setting()}. Mirrors the fields whitelist injection * in the AJAX handler. * * @param array $data Default settings data. * @return array */ protected static function inject_content_tree_defaults(array $data): array { } public static function save_settings($form_data = false) { } /** * Recursive defaults-aware merge. Each key in $override replaces the * value in $base; when both sides are associative arrays we recurse so * nested defaults survive a partial save (e.g. saved `email_subject` * overrides default while a missing nested `is_enable` inherits the * default `true`). */ protected static function deep_merge_defaults(array $base, array $override): array { } /** * One-shot upgrade for sites that activated the email addon before * subjects included {order_id}. Without a unique token, Gmail/Outlook * thread every "New order" notification into one conversation in the * admin's inbox. * * We only overwrite if the saved subject *exactly* matches the historic * default — that's the strong signal the admin never customized it. * Anything else is left alone. Idempotent: once flipped to the new * value, future runs no longer match the old default and bail out. * * @param array $settings_data * @return array */ protected static function migrate_threading_safe_subjects(array $settings_data): array { } } } namespace StoreEngine\Addons\Email\affiliate { abstract class AbstractAffiliateMail { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; \StoreEngine\Addons\Email\Traits\Email::get_settings as protected; } public function __construct() { } /** * Wire the addon action(s) that fire this email. */ abstract protected function register_hooks(): void; /** * Default settings tree for this email (subject/heading/content under a * 'customer' sub-key, matching every other StoreEngine email). */ abstract public static function default_template(): array; public static function register_defaults(array $defaults): array { } /** * Tokens shared by every affiliate email. * * @param string $name Affiliate display name. * @param string $email Affiliate email. */ protected function base_replacements(string $name, string $email): array { } /** * Render (HTML or plain text) and send a non-order email. * * @param array $settings The 'customer' settings row (subject/heading/content). * @param string $to Recipient email. * @param array $replacements Token => value map applied to subject + body. */ protected function dispatch(array $settings, string $to, array $replacements): void { } } class Approved extends \StoreEngine\Addons\Email\affiliate\AbstractAffiliateMail { const SETTINGS_KEY = 'affiliate_approved'; protected function register_hooks(): void { } public static function default_template(): array { } public function send_mail($affiliate_id, $status): void { } } class CommissionApproved extends \StoreEngine\Addons\Email\affiliate\AbstractAffiliateMail { const SETTINGS_KEY = 'affiliate_commission_approved'; protected function register_hooks(): void { } public static function default_template(): array { } public function send_mail($commission_id): void { } } class PayoutCompleted extends \StoreEngine\Addons\Email\affiliate\AbstractAffiliateMail { const SETTINGS_KEY = 'affiliate_payout_completed'; protected function register_hooks(): void { } public static function default_template(): array { } public function send_mail($payout_id, $status): void { } } class Registered extends \StoreEngine\Addons\Email\affiliate\AbstractAffiliateMail { const SETTINGS_KEY = 'affiliate_registered'; protected function register_hooks(): void { } public static function default_template(): array { } public function send_mail($affiliate_id): void { } } class Rejected extends \StoreEngine\Addons\Email\affiliate\AbstractAffiliateMail { const SETTINGS_KEY = 'affiliate_rejected'; protected function register_hooks(): void { } public static function default_template(): array { } public function send_mail($affiliate_id, $status): void { } } class Suspended extends \StoreEngine\Addons\Email\affiliate\AbstractAffiliateMail { const SETTINGS_KEY = 'affiliate_suspended'; protected function register_hooks(): void { } public static function default_template(): array { } public function send_mail($affiliate_id, $status): void { } } } namespace StoreEngine\Addons\Email { class Ajax { public function __construct() { } private function dispatch_hooks() { } } } namespace StoreEngine\Addons\Email\Ajax { class Admin extends \StoreEngine\Classes\AbstractAjaxHandler { use \StoreEngine\Addons\Email\Traits\Email { __construct as private EmailInit; } /** * WP Mail error. * * @var \WP_Error|null */ protected ?\WP_Error $mail_error = null; protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_email'; public function __construct() { } public function preview_template($payload) { } /** * Resolve the email template file to render for preview / test mail. * * Only the original emails ship a dedicated `-.php` file. Every * email added later (password_reset, registration_welcome, subscription_*, * affiliate_*, order_item_shipped, order_delivered, order_cancelled, …) * renders through the shared generic shell — the same one their send path * uses ({@see Helper::get_template( 'email/order-status-customer.php', … )}). * * Without this fallback the preview/test built `email/-.php` * unconditionally; locate_template() returned that non-existent default path * and the include produced an empty document — the "preview not working" * report. * * @param string $template_name * @param string $template_sub_name * @return string Template file name relative to `templates/email/`. */ protected static function resolve_template_file(string $template_name, string $template_sub_name = ''): string { } private function get_dummy_content(): array { } public function test_email($payload) { } public function catch_wp_mail_error(\WP_Error $error) { } protected function get_template_name(string $templateName, string $templateSubName) { } } class Settings extends \StoreEngine\Classes\AbstractAjaxHandler { protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_email'; public function __construct() { } /** * Build the save whitelist at request time from the registered email * defaults, so every email that registers itself via the * `storeengine/email/settings_default_data` filter is automatically * savable — without a second, hand-maintained list. * * A stale static whitelist here is exactly what silently dropped every * email added after the original set (password_reset, registration_welcome, * subscription_*, affiliate_*, order_item_shipped, …): their toggles were * stripped from the payload on save, so save_settings() re-applied the * `is_enable => false` default and the switch appeared to "auto-disable". * * @param array|null $fields Schema declared on the action (unused for the * save action — we always rebuild it here). * @return array */ protected function prepare_payload(?array $fields = null): array { } /** * Derive the sanitizer schema from the default settings structure. * * Top-level scalar options keep their explicit types; every registered * email template (a nested channel → leaf array) is mapped uniformly. * * @return array */ protected static function build_fields_schema(): array { } /** * Map a single email template (channel => [leaf => default, …]) to its * sanitizer schema. Every email shares the same leaf shape, so the type is * decided purely by the leaf name. * * @param array $template * @return array */ protected static function map_template_schema(array $template): array { } /** * Sanitizer type for an email template leaf key. * * @param string $leaf * @return string */ protected static function leaf_type(string $leaf): string { } /** * Whitelist a `email_content_tree` sibling wherever `email_content` is * allowed, so the block editor's tree (base64url JSON) round-trips on save. * * The generated schema already includes the tree key (it exists in the * defaults via {@see EmailSettings::inject_content_tree_defaults()}); this * still runs so any email injected by a third-party `settings_fields` * filter that only declares `email_content` also gets the tree key. * * @param array $fields Sanitizer schema. * @return array */ private static function inject_content_tree(array $fields): array { } public function get_email_settings() { } public function save_email_settings($payload) { } public function migrate_settings() { } } } namespace StoreEngine\Addons\Email { class EmailLogger { use \StoreEngine\Traits\Singleton; /** * The EmailLog row created by the most recent chokepoint filter call. * Consumed by wp_mail_succeeded / wp_mail_failed in the same request. * * wp_mail() is synchronous and there are no nested filter calls between * our chokepoint filter and wp_mail() itself, so a single-slot pending * tracker is sufficient (no need for a stack keyed by recipient + subject). * * @var \StoreEngine\Classes\EmailLog|null */ protected ?\StoreEngine\Classes\EmailLog $pending = null; protected function __construct() { } /** * Chokepoint filter handler. * * @param array $arguments mail arguments: to, subject, body, headers, attachments. * @param string $email_name Email type slug (e.g. 'order_confirmation'). * @param array|mixed $context Extra args passed to mail_send() — typically carries order_id, abandoned_cart, subscription_id, etc. * * @return array Unmodified arguments. */ public function capture_send(array $arguments, string $email_name, $context = []): array { } /** * Flip the pending row to 'sent' once wp_mail returns true. */ public function mark_sent($mail_data) { } /** * Flip the pending row to 'failed' and record the WP_Error message. */ public function mark_failed($wp_error) { } /** * Password reset filter handler. WP core dispatches the email after this * filter, then fires wp_mail_succeeded / wp_mail_failed — so we just stage * the row and let the same terminal-state actions update it. */ public function capture_password_reset($defaults, $key = '', $user_login = '', $user_data = null): array { } /** * Map the chokepoint's $context array into entity links. * * @return array [ related_entity_type, related_entity_id, order_id, customer_id ] */ protected function resolve_context(array $context): array { } /** * Reduce array / comma-separated / single-address recipients to one canonical * string. The 'recipient' column is indexed for fast per-customer lookups; * storing the original (comma-separated) form keeps the audit truthful, but * we strip whitespace so the index doesn't drift. */ protected function normalize_recipient($to): string { } } final class Email extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'email'; public function define_constants() { } public function init_addon() { } public function addon_activation_hook() { } } class HelperAddon { public static function get_setting(string $name, $default = null) { } public static function sanitize_email_template_data($template_data) { } public static function sanitize_email_template_item($template_arr) { } } class Hooks { public function __construct() { } public function boot_deferred_emails(): void { } } } namespace StoreEngine\Addons\Email\order { class AbstractAbandonedCartMail { use \StoreEngine\Addons\Email\Traits\Email; private function get_abc_subject(\StoreEnginePro\Addons\AbandonedCart\Classes\AbandonedCart $abc, $email_subject = '') { } private function get_abc_email_body(\StoreEnginePro\Addons\AbandonedCart\Classes\AbandonedCart $abc, string $body, string $email_type): string { } public function btn_styles(): string { } function generate_random_code($size = 8): string { } public function send_email(\StoreEnginePro\Addons\AbandonedCart\Classes\AbandonedCart $abc, $email_type) { } private function get_the_email_body($settings, $template_path): array { } public function mail_send($to, string $subject, string $body, $headers, $args = []) { } public function get_cart_item_template(): string { } private function get_cart_items_data(\StoreEngine\Classes\Cart $cart): string { } private function get_cart_totals_data(\StoreEngine\Classes\Cart $cart): string { } } class AbandonedCartFirst extends \StoreEngine\Addons\Email\order\AbstractAbandonedCartMail { public function __construct() { } } class AbandonedCartSecond extends \StoreEngine\Addons\Email\order\AbstractAbandonedCartMail { public function __construct() { } } class AbandonedCartThird extends \StoreEngine\Addons\Email\order\AbstractAbandonedCartMail { public function __construct() { } } class Cancelled { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'order_cancelled'; /** * Both spellings exist in Constants (ORDER_STATUS_CANCELED = 'canceled' and * ORDER_STATUS_CANCELLED = 'cancelled'); match either so the email fires * regardless of which the transition used. */ const CANCELLED_STATUSES = ['cancelled', 'canceled']; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } public function send_mail($order_id, $old_status, $new_status, \StoreEngine\Classes\Order $order) { } } class Confirm { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } public function __construct() { } /** * Admin + customer emails can involve a slow SMTP handshake. Sending them * inline here added that latency to the synchronous checkout/place-order * request the customer's browser is waiting on — a contributor to gateways * (e.g. Razorpay) occasionally surfacing an upstream-timeout error even * though the order/payment already succeeded. Defer via Action Scheduler * (already bundled/used elsewhere, e.g. the webhooks addon) so the * checkout response returns immediately. */ public function schedule_order_email(\StoreEngine\Classes\Order $order): void { } public function send_order_email_by_id(int $order_id): void { } public function send_order_email(\StoreEngine\Classes\Order $order) { } private function send_admin_mail(\StoreEngine\Classes\Order $order, array $settings) { } private function send_customer_mail(\StoreEngine\Classes\Order $order, array $settings) { } } class Delivered { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'order_delivered'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } /** * @param int $order_id * @param int $order_item_id * @param int $product_id * @param array $shipment courier/tracking_number/tracking_url/… * @param string $new_status */ public function on_item_shipped($order_id, $order_item_id, $product_id, $shipment, $new_status): void { } } class Invoice { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } public function __construct() { } public function send_email(\StoreEngine\Classes\Order $order) { } } class ItemShipped { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'order_item_shipped'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } /** * @param int $order_id * @param int $order_item_id * @param int $product_id * @param array $shipment courier/tracking_number/tracking_url/… * @param string $new_status */ public function on_item_shipped($order_id, $order_item_id, $product_id, $shipment, $new_status): void { } } class NewUserNotification { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } public function __construct() { } public function send_mail(int $user_id, array $userdata) { } private function get_email_subject(int $user_id) { } private function get_email_body(array $userdata, int $user_id, string $body) { } } class Note { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } public function __construct() { } public function send_order_email(string $note, \StoreEngine\Classes\Order $order) { } private function send_customer_mail(\StoreEngine\Classes\Order $order, string $note, array $settings) { } } class PaymentFailed { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'order_payment_failed'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } public function on_payment_status_changed($order, $new_status, $old_status): void { } protected function send_customer(\StoreEngine\Classes\Order $order): void { } protected function send_admin(\StoreEngine\Classes\Order $order): void { } protected function send(array $settings, string $to, \StoreEngine\Classes\Order $order, bool $is_admin): void { } } class Refund { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } public function __construct() { } public function send_mail(\StoreEngine\Classes\Refund $refund) { } } class Status { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } public function __construct() { } public function send_mail($order_id, $old_status, $new_status, \StoreEngine\Classes\Order $order) { } } } namespace StoreEngine\Addons\Email { class ResendRegistry { use \StoreEngine\Traits\Singleton; /** @var array */ protected array $handlers = []; protected function __construct() { } protected function register_core_handlers() { } public function get_handler(string $email_type): ?callable { } public function get_resendable_types(): array { } public function is_resendable(string $email_type): bool { } } } namespace StoreEngine\Addons\Email\subscription { class Cancelled { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'subscription_cancelled'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } public function on_status_updated($subscription, $new_status, $old_status): void { } } class RenewalFailed { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'subscription_renewal_failed'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } public function on_renewal_failed($subscription, $renewal_order): void { } } class Renewed { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'subscription_renewed'; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } public function on_renewal_complete($subscription, $renewal_order): void { } } class TrialEndingSoon { use \StoreEngine\Addons\Email\Traits\Email { \StoreEngine\Addons\Email\Traits\Email::__construct as private __EmailConstruct; } const SETTINGS_KEY = 'trial_ending_soon'; const AS_HOOK = 'storeengine/subscription/trial_ending_soon'; const AS_GROUP = 'storeengine-subscription'; /** Days before trial_end_date to fire the email. Filterable. */ const LEAD_DAYS = 3; public function __construct() { } public static function register_defaults(array $defaults): array { } public static function default_template(): array { } // ── Scheduling ─────────────────────────────────────────────────────────── /** * Wrapper so add_action's default 1-arg accept_args still binds the * subscription cleanly. `checkout_subscription_created` fires with * ($subscription, $order, $recurring_cart) — we only need the first. */ public static function on_subscription_created($subscription): void { } public static function schedule_for_subscription($subscription): void { } public static function unschedule_for_subscription($subscription): void { } public static function on_status_updated($subscription, $new_status, $old_status): void { } // ── Send ───────────────────────────────────────────────────────────────── public function send($subscription_id): void { } } } namespace StoreEngine\Addons\EmbeddableCheckout\Api { /** * Hooks the cross-origin auth path into both filter chains: * * - The Instant Checkout addon's session-creation filters (storeengine/instant_checkout/session/*) * - Core's checkout controller filters (storeengine/checkout/*) used by /state, /update, /place, * /payment-intent, /coupon/*, /states. Without these, core returns 401 with * "Publishable-key authentication is not active" whenever an embed key is sent. * * Requires the Instant Checkout addon to be active — otherwise the session-side * filters have nothing to fire on. The parent addon class enforces that * requirement in `init_addon()` and surfaces an admin notice when missing. */ class EmbedAuth { public static function init(): void { } /** * Bridge for core's `/checkout/*` permission_callback. Returns true|WP_Error * (null is treated by core as "no handler installed" → 401). */ public function core_pk_auth($existing, \WP_REST_Request $request) { } public function core_cors_origin($existing, \WP_REST_Request $request) { } /** * @param array|\WP_Error|null $existing * @return array|\WP_Error|null Returns array on cross-origin success, WP_Error * on failure, null to fall through to same-origin. */ public function authorize($existing, \WP_REST_Request $request) { } public function restrict_product($allowed, int $product_id, \WP_REST_Request $request) { } public function cors_origin($existing, \WP_REST_Request $request) { } /** * Per-key rate limit. Falls back to per-IP buckets for same-origin callers * so a noisy storefront button can't run away with sessions either. */ public function rate_limit($existing, \WP_REST_Request $request) { } /** * @return array|\WP_Error|null */ protected function resolve_key(\WP_REST_Request $request) { } } } namespace StoreEngine\API { /** * Abstract Rest Controller Class * * @package StoreEngine\Api * @extends WP_REST_Controller */ abstract class AbstractRestApiController extends \WP_REST_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = STOREENGINE_PLUGIN_SLUG . '/v1'; /** * Route base. * * @var string */ protected $rest_base = ''; /** * Used to cache computed return fields. * * @var null|array */ private ?array $_fields = null; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore /** * Used to verify if cached fields are for correct request object. * * @var null|\WP_REST_Request */ private ?\WP_REST_Request $_request = null; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore public function __construct() { } /** * Add the schema from additional fields to an schema array. * * The type of object is inferred from the passed schema. * * @param array $schema Schema array. * * @return array */ protected function add_additional_fields_schema($schema): array { } /** * Get normalized rest base. * * @return string */ protected function get_normalized_rest_base(): string { } /** * Check batch limit. * * @param array $items Request items. * * @return bool|\WP_Error */ protected function check_batch_limit(array $items) { } /** * Bulk create, update and delete items. * * @param \WP_REST_Request $request Full details about the request. * * @return \WP_Error|\WP_Error[]|array Of WP_Error or WP_REST_Response. */ public function batch_items(\WP_REST_Request $request) { } /** * Validate a text value for a text based setting. * * @param ?string $value Value. * @param array $setting Setting. * * @return string */ public function validate_setting_text_field(?string $value, array $setting): string { } /** * Validate select based settings. * * @param string $value Value. * @param array $setting Setting. * * @return string|\WP_Error */ public function validate_setting_select_field(string $value, array $setting) { } /** * Validate multiselect based settings. * * @param array $values Values. * @param array $setting Setting. * * @return array|\WP_Error */ public function validate_setting_multiselect_field(array $values, array $setting) { } /** * Validate image_width based settings. * * @param array $values Values. * @param array $setting Setting. * * @return string|\WP_Error */ public function validate_setting_image_width_field(array $values, array $setting) { } /** * Validate radio based settings. * * @param string $value Value. * @param array $setting Setting. * * @return string|\WP_Error */ public function validate_setting_radio_field(string $value, array $setting) { } /** * Validate checkbox based settings. * * @param string $value Value. * @param array $setting Setting. * * @return string|\WP_Error */ public function validate_setting_checkbox_field(string $value, array $setting) { } /** * Validate textarea based settings. * * @param string $value Value. * @param array $setting Setting. * * @return string */ public function validate_setting_textarea_field(string $value, array $setting) { } /** * Add meta query. * * @param array $args Query args. * @param array $meta_query Meta query. * * @return array */ protected function add_meta_query(array $args, array $meta_query): array { } /** * Get the batch schema, conforming to JSON Schema. * * @return array */ public function get_public_batch_schema(): array { } /** * Limit the contents of the meta_data property based on certain request parameters. * * Note that if both `include_meta` and `exclude_meta` are present in the request, * `include_meta` will take precedence. * * @param \WP_REST_Request $request The request. * @param array $meta_data All the meta data for an object. * * @return array */ protected function get_meta_data_for_response(\WP_REST_Request $request, array $meta_data): array { } /** * @param \StoreEngine\Classes\AbstractEntity|\WP_Post|array|\stdClass $item * @param \WP_REST_Request $request * * @return array */ protected function prepare_links($item, \WP_REST_Request $request): array { } protected function prepare_pagination_headers(\WP_REST_Response $response, \WP_REST_Request $request, int $current_page = 1, int $total = 0, int $total_pages = 1) { } protected function prepare_query_response(array $data, $query, \WP_REST_Request $request) { } protected function date_as_string(?\StoreEngine\Classes\StoreengineDatetime $date_time_object): ?string { } } } namespace StoreEngine\Addons\EmbeddableCheckout\Api { /** * Authenticated, manager-only CRUD for Quick Checkout embed keys. */ class EmbedKeys extends \StoreEngine\API\AbstractRestApiController { protected $rest_base = 'embed-keys'; public static function init() { } public function register_routes() { } public function permission_callback() { } public function list_keys() { } public function create_key(\WP_REST_Request $request) { } public function update_key(\WP_REST_Request $request) { } public function delete_key(\WP_REST_Request $request) { } public function revoke_key(\WP_REST_Request $request) { } protected function present(?array $row, bool $reveal_key = false): array { } } } namespace StoreEngine\Addons\EmbeddableCheckout { final class EmbeddableCheckout extends \StoreEngine\Classes\AbstractAddon { use \StoreEngine\Traits\Singleton; protected string $addon_name = 'embeddable-checkout'; public function define_constants() { } public function init_addon() { } public function render_missing_dependency_notice(): void { } public function get_db_version(): string { } public function install_tables(): void { } public function addon_activation_hook() { } public function addon_deactivation_hook() { } } } namespace StoreEngine\Addons\EmbeddableCheckout\Models { /** * Embed Key model. * * Manages publishable keys used by the Quick Checkout embed SDK to authorise * cross-origin checkout sessions. Keys are publishable (safe to ship in * client-side JS); the trust boundary is the per-key allowed-origin list. */ class EmbedKey { const SCOPE_ALL = 'all'; const SCOPE_IDS = 'ids'; const TABLE = 'storeengine_embed_keys'; public static function table_name(): string { } public static function create_table(): void { } public static function generate_key(): string { } public static function hash_key(string $key): string { } public static function find_by_key(string $key): ?array { } public static function find(int $id): ?array { } public static function all(): array { } public static function find_or_create_system_key(): array { } public static function same_site_origin_variants(): array { } public static function create(array $data): int { } public static function update(int $id, array $data): bool { } public static function revoke(int $id): bool { } public static function delete(int $id): bool { } protected static function is_system_key(int $id): bool { } public static function touch_last_used(int $id): void { } public static function is_same_site_origin(string $origin): bool { } public static function origin_is_allowed(array $row, string $origin): bool { } public static function append_allowed_origin(int $key_id, string $origin): void { } public static function product_is_in_scope(array $row, int $product_id): bool { } private static function hydrate(array $row): array { } private static function decode_origins(string $json): array { } public static function sanitize_origins($value): array { } /** * Normalise an origin string for comparison against the allow-list. * * Accepts http(s) origins (browsers + Next.js / Nuxt storefronts) AND * native-app schemes like `app://com.example.myapp` or * `capacitor://localhost` (React Native, Capacitor, Cordova, Tauri, * Electron). For non-http schemes the embed key itself is the auth * boundary; the "origin" string is just a stable identifier the admin * adds to the allow-list. * * Bare strings (no scheme) are coerced to https:// for back-compat. */ public static function normalize_origin(string $origin): string { } } } namespace StoreEngine\Addons\EmbeddableCheckout { /** * Streams the slim Quick Checkout SDK to external sites at /se-embed/v1/sdk.js * so partners can drop a single