Plugin Directory

Changeset 3720581


Ignore:
Timestamp:
09/30/2026 07:50:27 AM (3 weeks ago)
Author:
philiasolutions
Message:
  • Rebrand and change name to Legion
  • Added Refund order to Data Synchronization
  • Bugs fixes and improvements
Location:
philia-integration
Files:
19 added
2 deleted
12 edited

Legend:

Unmodified
Added
Removed
  • philia-integration/trunk/assets/philia-wallet.js

    r3683158 r3720581  
    66    }
    77
    8     var isActive = Boolean(philiaWalletParams.isActive);
     8    var walletActive = Boolean(philiaWalletParams.isActive);
     9    var cashbackActive = null;
     10    var currentBalance = 0;
    911    var isToggling = false;
    1012    var activeGateway = $('input[name="payment_method"]:checked').val() || '';
    … …  
    1416    }
    1517
    16     function fetchAndDisplayBalance() {
    17         $.post(philiaWalletParams.ajaxUrl, {
    18             action: 'philia_get_wallet_balance',
    19             nonce: philiaWalletParams.nonce
    20         }).done(function (response) {
    21             if (!response.success) {
    22                 return;
    23             }
    24 
    25             var balance = parseFloat(response.data.user_cashback) || 0;
    26             $('.philia-wallet-balance').text(formatPrice(balance) + ' ' + response.data.currency);
    27 
    28             var $toggle = $('.partial_pay_through_wallet');
    29             var $container = $('.philia-wallet-checkout-container');
    30 
    31             if (balance <= 0 && !isActive) {
    32                 $toggle.prop('checked', false).prop('disabled', true);
    33                 $container.addClass('philia-disabled');
    34             } else {
    35                 enforceGatewayRestriction(balance);
    36             }
    37         });
     18    function toBoolean(value) {
     19        return value === true || value === 1 || value === '1' || value === 'true';
    3820    }
    3921
    40     function enforceGatewayRestriction(balance) {
     22    function hasOwn(object, property) {
     23        return object && Object.prototype.hasOwnProperty.call(object, property);
     24    }
     25
     26    function updateToggleUI() {
     27        $('.partial_pay_through_wallet').prop('checked', walletActive);
     28    }
     29
     30    function applyWalletAvailability(suppressAutoToggle) {
    4131        var $toggle = $('.partial_pay_through_wallet');
    4232        var $container = $('.philia-wallet-checkout-container');
    4333        var selected = $('input[name="payment_method"]:checked').val() || '';
    4434
    45         if (balance <= 0 && !isActive) {
     35        updateToggleUI();
     36
     37        if (cashbackActive !== true) {
    4638            $toggle.prop('disabled', true);
    4739            $container.addClass('philia-disabled');
    … …  
    4941        }
    5042
    51         if (!selected && isActive) {
     43        if (currentBalance <= 0 && !walletActive) {
     44            $toggle.prop('disabled', true);
     45            $container.addClass('philia-disabled');
     46            return;
     47        }
     48
     49        if (!selected && walletActive) {
    5250            $toggle.prop('disabled', false);
    5351            $container.removeClass('philia-disabled');
    … …  
    5654
    5755        if (philiaWalletParams.enabledGateways.indexOf(selected) !== -1) {
    58             if (isActive) {
     56            if (walletActive) {
    5957                activeGateway = selected;
    6058            }
     59
    6160            $toggle.prop('disabled', false);
    6261            $container.removeClass('philia-disabled');
    63         } else {
    64             if ($toggle.is(':checked')) {
    65                 $toggle.prop('checked', false);
    66                 callToggle(false);
    67             }
    68             $toggle.prop('disabled', true);
    69             $container.addClass('philia-disabled');
     62            return;
     63        }
     64
     65        $toggle.prop('disabled', true);
     66        $container.addClass('philia-disabled');
     67
     68        if (walletActive && !isToggling && !suppressAutoToggle) {
     69            callToggle(false);
    7070        }
    7171    }
    7272
    73     function updateToggleUI() {
    74         $('.partial_pay_through_wallet').prop('checked', isActive);
     73    function fetchAndDisplayBalance() {
     74        $.post(philiaWalletParams.ajaxUrl, {
     75            action: 'philia_get_wallet_balance',
     76            nonce: philiaWalletParams.nonce
     77        }).done(function (response) {
     78            if (!response.success || !response.data) {
     79                cashbackActive = false;
     80                applyWalletAvailability(true);
     81                return;
     82            }
     83
     84            currentBalance = parseFloat(response.data.user_cashback) || 0;
     85            cashbackActive = toBoolean(response.data.is_active);
     86
     87            if (hasOwn(response.data, 'wallet_active')) {
     88                walletActive = toBoolean(response.data.wallet_active);
     89            }
     90
     91            $('.philia-wallet-balance').text(
     92                formatPrice(currentBalance) + ' ' + response.data.currency
     93            );
     94
     95            applyWalletAvailability(false);
     96        }).fail(function () {
     97            cashbackActive = false;
     98            applyWalletAvailability(true);
     99        });
    75100    }
    76101
    77102    function callToggle(enabled) {
    78103        if (isToggling) {
     104            updateToggleUI();
    79105            return;
    80106        }
    … …  
    82108        isToggling = true;
    83109
     110        var previousActive = walletActive;
     111        var stateChanged = false;
     112        var requestFailed = false;
    84113        var $orderReview = $('.woocommerce-checkout-review-order-table');
     114
     115        $('.partial_pay_through_wallet').prop('disabled', true);
     116
    85117        if ($.isFunction($orderReview.block)) {
    86118            $orderReview.block({
    … …  
    96128            payment_method: $('input[name="payment_method"]:checked').val() || activeGateway || ''
    97129        }).done(function (response) {
     130            var data = response && response.data ? response.data : {};
     131
     132            if (hasOwn(data, 'wallet_active')) {
     133                walletActive = toBoolean(data.wallet_active);
     134            } else if (response.success) {
     135                walletActive = enabled;
     136            } else {
     137                walletActive = previousActive;
     138            }
     139
     140            if (hasOwn(data, 'cashback_active')) {
     141                cashbackActive = toBoolean(data.cashback_active);
     142            }
     143
     144            stateChanged = walletActive !== previousActive;
     145            requestFailed = !response.success;
     146            updateToggleUI();
     147
    98148            if (response.success) {
    99                 isActive = enabled;
    100                 activeGateway = enabled
     149                activeGateway = walletActive
    101150                    ? ($('input[name="payment_method"]:checked').val() || activeGateway || '')
    102151                    : '';
     152            }
     153
     154            if (response.success || stateChanged) {
    103155                $(document.body).trigger('update_checkout');
    104                 fetchAndDisplayBalance();
    105156            } else {
    106                 isActive = false;
    107                 updateToggleUI();
    108                 fetchAndDisplayBalance();
     157                applyWalletAvailability(true);
    109158            }
     159        }).fail(function () {
     160            requestFailed = true;
     161            walletActive = previousActive;
     162            updateToggleUI();
     163            applyWalletAvailability(true);
    110164        }).always(function () {
    111165            isToggling = false;
     166
    112167            if ($.isFunction($orderReview.unblock)) {
    113168                $orderReview.unblock();
    114169            }
     170
     171            applyWalletAvailability(requestFailed);
    115172        });
    116173    }
    … …  
    126183
    127184    updateToggleUI();
     185    applyWalletAvailability(true);
    128186    fetchAndDisplayBalance();
    129187}(jQuery));
  • philia-integration/trunk/includes/class-api-log.php

    r3583389 r3720581  
    11<?php
    22/**
    3  * Philia API Log
    4  * The class responsible for handling logging in the Philia API plugin.
     3 * Legion API Log
     4 * The class responsible for handling logging in the Legion API plugin.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
  • philia-integration/trunk/includes/class-api-settings.php

    r3683431 r3720581  
    11<?php
    22/**
    3  * Philia API Settings
    4  * The class responsible for managing the Philia API settings in WordPress.
     3 * Legion API Settings
     4 * The class responsible for managing the Legion API settings in WordPress.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
    … …  
    8181        add_submenu_page(
    8282            'philia-api-settings',
    83             __('Philia API Settings', 'philia-integration'), // Page title
     83            __('Legion API Settings', 'philia-integration'), // Page title
    8484            __('Settings', 'philia-integration'), // Menu title
    8585            'manage_options', // Capability
    … …  
    167167        );
    168168
     169        // Wallet display title
     170        add_settings_field(
     171            'wallet_title',
     172            __('Wallet Title', 'philia-integration'),
     173            array($this, 'wallet_title_field_callback'),
     174            'philia-api-settings',
     175            'philia_api_settings_section'
     176        );
     177
    169178        // Wallet Enabled Payment Gateways
    170179        add_settings_field(
    … …  
    179188        add_settings_field(
    180189            'philia_product_id_fill_mode',
    181             __('Auto-fill Philia Product ID', 'philia-integration'),
     190            __('Auto-fill Legion Product ID', 'philia-integration'),
    182191            array($this, 'philia_product_id_fill_mode_field_callback'),
    183192            'philia-api-settings',
    … …  
    198207            <div class="notice">
    199208                <p>
    200                     <?php esc_attr_e('Click the "Test Connection" button to check the connection to Philia.', 'philia-integration'); ?>
     209                    <?php esc_attr_e('Click the "Test Connection" button to check the connection to Legion.', 'philia-integration'); ?>
    201210                    <button id="test-philia-connection" class="button"><?php echo esc_attr__('Test Connection','philia-integration'); ?></button>
    202211                </p>
    … …  
    210219                {
    211220                    $res = self::create_and_send_woocommerce_api_key( $settings['api_url'] , $settings['api_key'], $settings['phone_meta'], $settings['data_sync'], $settings['order_sync_status'], $settings['philia_product_id_fill_mode'] );
    212                     error_log("RESPONSE FROM SAVE SETTINGS : " . print_r($res, true ) );
    213221                    if( wp_remote_retrieve_response_code( $res ) == 200 )
    214222                    {
    215223                        ?>
    216224                        <div class="notice notice-success is-dismissible">
    217                             <p><?php esc_html_e('The Synchronization with Philia has been successfully configured.', 'philia-integration'); ?></p>
     225                            <p><?php esc_html_e('The Synchronization with Legion has been successfully configured.', 'philia-integration'); ?></p>
    218226                        </div>
    219227                        <?php
    … …  
    231239            <hr>
    232240            <h1><?php echo esc_attr__('Shortcodes', 'philia-integration'); ?></h1>
    233             <p><?php echo esc_html__('Use the following shortcodes to display Philia features on your pages.', 'philia-integration'); ?></p>
     241            <p><?php echo esc_html__('Use the following shortcodes to display Legion features on your pages.', 'philia-integration'); ?></p>
    234242            <table class="form-table">
    235243                <tbody>
    … …  
    291299        ?>
    292300        <input type="text" name="philia_api_settings[api_key]" value="<?php echo esc_attr( $settings['api_key'] ?? ''); ?>" size="50" />
    293         <p class="description"><?php echo esc_attr__('Please enter the access key for the Philia web service.', 'philia-integration'); ?></p>
     301        <p class="description"><?php echo esc_attr__('Please enter the access key for the Legion web service.', 'philia-integration'); ?></p>
    294302        <?php
    295303    }
    … …  
    317325            ?>
    318326        </select>
    319         <p class="description"><?php echo esc_attr__('The meta key of the phone is required for syncing users with Philia. If not selected, the synchronization process will not take place.', 'philia-integration'); ?></p>
     327        <p class="description"><?php echo esc_attr__('The meta key of the phone is required for syncing users with Legion. If not selected, the synchronization process will not take place.', 'philia-integration'); ?></p>
    320328        <?php
    321329    }
    322330
    323331    /**
    324      * Outputs the dropdown for order status synchronization.
     332     * Outputs the order statuses available for synchronization.
    325333     *
    326334     * @return void
    … …  
    329337    {
    330338        $settings = get_option('philia_api_settings');
    331        
    332         // Get the selected order status, default to 'completed'
    333         $selected_status = isset($settings['order_sync_status']) ? $settings['order_sync_status'] : 'completed';
     339
     340        $selected_status = self::normalize_order_sync_status(
     341            isset( $settings['order_sync_status'] ) ? $settings['order_sync_status'] : 'completed'
     342        );
    334343
    335344        if ( ! function_exists( 'wc_get_order_statuses' ) ) {
    … …  
    338347        }
    339348
    340         // Get all available WooCommerce order statuses
    341349        $order_statuses = wc_get_order_statuses();
    342        
    343         echo '<select name="philia_api_settings[order_sync_status]" id="philia-order-sync-status">';
    344         foreach ($order_statuses as $status_key => $status_label) {
    345             // Remove 'wc-' prefix from status key
    346             $status_value = str_replace('wc-', '', $status_key);
    347             $selected = selected($selected_status, $status_value, false);
    348             echo '<option value="' . esc_attr($status_value) . '" ' . $selected . '>' . esc_html($status_label) . '</option>';
     350
     351        echo '<select id="philia-order-sync-status" name="philia_api_settings[order_sync_status]">';
     352        foreach ( $order_statuses as $status_key => $status_label ) {
     353            $status_value = 0 === strpos( $status_key, 'wc-' ) ? substr( $status_key, 3 ) : $status_key;
     354            echo '<option value="' . esc_attr( $status_value ) . '" ' . selected( $selected_status, $status_value, false ) . '>' . esc_html( $status_label ) . '</option>';
    349355        }
    350356        echo '</select>';
    351357        ?>
    352         <p class="description"><?php echo esc_attr__('Select which order status should trigger synchronization with Philia. Only orders with this status will be sent.', 'philia-integration'); ?></p>
     358        <p class="description"><?php echo esc_html__('Orders are synchronized with Legion only when they reach the selected status.', 'philia-integration'); ?></p>
    353359        <?php
    354360    }
    … …  
    394400            __('Customer', 'philia-integration')            => '4',
    395401            __('Engagement Events', 'philia-integration')   => '5',
     402            __('Refunded Order', 'philia-integration')      => '6',
    396403        ];
    397404
    … …  
    423430        echo '</fieldset>';
    424431        ?>
    425         <p class="description"><?php echo esc_attr__('Select the data types you want to sync with Philia.', 'philia-integration'); ?></p>
     432        <p class="description"><?php echo esc_attr__('Select the data types you want to sync with Legion.', 'philia-integration'); ?></p>
     433        <?php
     434    }
     435
     436    /**
     437     * Outputs the wallet title used on WooCommerce orders and invoices.
     438     *
     439     * @return void
     440     */
     441    public function wallet_title_field_callback()
     442    {
     443        ?>
     444        <input type='text' id='philia-wallet-title' name='philia_api_settings[wallet_title]' value='<?php echo esc_attr( self::get_wallet_title() ); ?>' class='regular-text' />
     445        <p class='description'><?php echo esc_html__( 'This title is displayed for wallet payments on orders and invoices.', 'philia-integration' ); ?></p>
    426446        <?php
    427447    }
    … …  
    486506        }
    487507        echo '</select>';
    488         echo '<p class="description">' . esc_html__('Determines how the philia product id is auto-filled when a new product is saved and the field is empty.', 'philia-integration') . '</p>';
     508        echo '<p class="description">' . esc_html__('Determines how the legion product id is auto-filled when a new product is saved and the field is empty.', 'philia-integration') . '</p>';
    489509    }
    490510
    … …  
    495515     */
    496516    public function philia_api_settings_section_callback() {
    497         echo '<p>' . esc_attr__('Configure the Philia API settings below.', 'philia-integration') . '</p>';
     517        echo '<p>' . esc_attr__('Configure the Legion API settings below.', 'philia-integration') . '</p>';
    498518    }
    499519
    … …  
    548568        }
    549569
    550         // Sanitize order sync status
    551         if (isset($input['order_sync_status'])) {
    552             $sanitized_input['order_sync_status'] = sanitize_text_field($input['order_sync_status']);
     570        if ( isset( $input['wallet_title'] ) ) {
     571            $wallet_title = sanitize_text_field( $input['wallet_title'] );
     572            $sanitized_input['wallet_title'] = $wallet_title !== '' ? $wallet_title : self::get_default_wallet_title();
    553573        } else {
    554             // Default to 'completed' if not set
    555             $sanitized_input['order_sync_status'] = 'completed';
    556         }
     574            $sanitized_input['wallet_title'] = self::get_default_wallet_title();
     575        }
     576
     577        // Only one registered WooCommerce order status may trigger synchronization.
     578        $sanitized_input['order_sync_status'] = self::normalize_order_sync_status(
     579            isset( $input['order_sync_status'] ) ? $input['order_sync_status'] : 'completed'
     580        );
    557581
    558582        // Sanitize product ID fill mode
    … …  
    654678            'version'           => $version,
    655679            'plugin_version'    => PHILIA_PLUGIN_VERSION,
    656             'order_sync_status' => ( isset( $order_sync_status ) && ! is_null( $order_sync_status ) ) ? sanitize_text_field( $order_sync_status ) : 'completed',
     680            'order_sync_status' => self::normalize_order_sync_status( $order_sync_status ),
    657681            'rest_prefix'       => $rest_prefix, // Include the current REST API prefix,
    658682            'data_sync'         => $philia_data_sync,
    … …  
    760784     */
    761785    public function add_bulk_action_fill_philia_product_id($bulk_actions) {
    762         $bulk_actions['fill_philia_product_id'] = __('Fill Philia Product ID', 'philia-integration');
     786        $bulk_actions['fill_philia_product_id'] = __('Fill Legion Product ID', 'philia-integration');
    763787        return $bulk_actions;
    764788    }
    … …  
    845869        printf(
    846870            /* translators: %1$d: number of filled products, %2$d: number of skipped products */
    847             esc_html__('Philia Product ID filled for %1$d product(s). %2$d product(s) skipped (already filled, no SKU available, or mode is Custom).', 'philia-integration'),
     871            esc_html__('Legion Product ID filled for %1$d product(s). %2$d product(s) skipped (already filled, no SKU available, or mode is Custom).', 'philia-integration'),
    848872            $filled,
    849873            $skipped
    … …  
    10711095     * Checks if a specific data sync option is enabled.
    10721096     *
    1073      * @param string $key_to_check The value to check for (e.g., '1', '2', '3', '4','5').
     1097     * @param string $key_to_check The value to check for (e.g., '1', '2', '3', '4', '5', '6').
    10741098     * @return bool True if the key is in the data_sync array, false otherwise.
    10751099     */
    … …  
    11111135
    11121136    /**
     1137     * Get the default wallet display title.
     1138     *
     1139     * @return string
     1140     */
     1141    public static function get_default_wallet_title()
     1142    {
     1143        return __('Legion Wallet', 'philia-integration');
     1144    }
     1145
     1146    /**
     1147     * Get the configured wallet display title.
     1148     *
     1149     * @return string
     1150     */
     1151    public static function get_wallet_title()
     1152    {
     1153        $settings = get_option( 'philia_api_settings' );
     1154        $title = isset( $settings['wallet_title'] ) ? sanitize_text_field( $settings['wallet_title'] ) : '';
     1155
     1156        return $title !== '' ? $title : self::get_default_wallet_title();
     1157    }
     1158
     1159    /**
    11131160     * Get the payment gateways where Philia wallet partial payment is enabled.
    11141161     *
    … …  
    11831230
    11841231    /**
    1185      * Get the order status that should trigger synchronization.
    1186      *
    1187      * @return string The order status slug (e.g., 'completed', 'processing')
     1232     * Get the order statuses that should trigger synchronization.
     1233     *
     1234     * @return string The selected order status slug.
    11881235     */
    11891236    public static function get_order_sync_status()
    … …  
    11921239        $settings = get_option( 'philia_api_settings' );
    11931240
    1194         // Return the saved status or default to 'completed'
    1195         return isset( $settings['order_sync_status'] ) ? $settings['order_sync_status'] : 'completed';
     1241        return self::normalize_order_sync_status(
     1242            isset( $settings['order_sync_status'] ) ? $settings['order_sync_status'] : 'completed'
     1243        );
     1244    }
     1245
     1246    /**
     1247     * Normalizes the selected order status and limits it to registered WooCommerce values.
     1248     *
     1249     * Completed is used as the fallback when the value is missing or invalid.
     1250     *
     1251     * @param string|array|null $status Selected order status. Arrays are accepted for legacy settings.
     1252     * @return string
     1253     */
     1254    private static function normalize_order_sync_status( $status )
     1255    {
     1256        if ( is_array( $status ) ) {
     1257            $status = reset( $status );
     1258        }
     1259
     1260        $status = is_scalar( $status ) ? sanitize_key( (string) $status ) : '';
     1261        $status = 0 === strpos( $status, 'wc-' ) ? substr( $status, 3 ) : $status;
     1262
     1263        if ( function_exists( 'wc_get_order_statuses' ) ) {
     1264            foreach ( array_keys( wc_get_order_statuses() ) as $status_key ) {
     1265                $allowed_status = 0 === strpos( $status_key, 'wc-' ) ? substr( $status_key, 3 ) : $status_key;
     1266                if ( $status === $allowed_status ) {
     1267                    return $status;
     1268                }
     1269            }
     1270        }
     1271
     1272        return 'completed';
    11961273    }
    11971274
  • philia-integration/trunk/includes/class-core-api.php

    r3683431 r3720581  
    11<?php
    22/**
    3  * Philia Core API
    4  * This class handles API requests to the configured Philia API endpoint.
     3 * Legion Core API
     4 * This class handles API requests to the configured Legion API endpoint.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
    … …  
    2626
    2727    /**
    28      * Endpoint used to exchange a Philia API key for a JWT access token.
     28     * Endpoint used to exchange a Legion API key for a JWT access token.
    2929     *
    3030     * @var string
    … …  
    8585        if (!empty( $settings ) && empty($settings['api_url'])) {
    8686            //error_log("API URL is not configured in the settings.");
    87             // throw new \Exception(__('API URL is not configured. Please set it in the Philia API settings.', 'philia-integration'));
     87            // throw new \Exception(__('API URL is not configured. Please set it in the Legion API settings.', 'philia-integration'));
    8888        }
    8989    }
    … …  
    168168
    169169        if ($api_key === '') {
    170             return new \WP_Error('philia_api_key_missing', 'The Philia API key is not set.');
     170            return new \WP_Error('philia_api_key_missing', 'The Legion API key is not set.');
    171171        }
    172172
    … …  
    201201            return new \WP_Error(
    202202                'philia_jwt_request_failed',
    203                 'Failed to retrieve a Philia access token.',
     203                'Failed to retrieve a Legion access token.',
    204204                array('status' => $status_code, 'response' => $response_body)
    205205            );
    … …  
    213213            return new \WP_Error(
    214214                'philia_jwt_invalid_response',
    215                 'The Philia token response did not contain an access token.',
     215                'The Legion token response did not contain an access token.',
    216216                array('status' => $status_code, 'response' => $response_body)
    217217            );
    … …  
    241241
    242242        if (empty($settings['api_key'])) {
    243             error_log("Error occurred: The API key is not set. Please configure the API key in the Philia API settings.");
     243            error_log("Error occurred: The API key is not set. Please configure the API key in the Legion API settings.");
    244244            return;
    245245        }
  • philia-integration/trunk/includes/class-event-sender.php

    r3683158 r3720581  
    11<?php
    22/**
    3  * Philia Event Sender
    4  * Sends engagement events to Philia independently from data-sync webhooks.
     3 * Legion Event Sender
     4 * Sends engagement events to Legion independently from data-sync webhooks.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
    … …  
    1818    private const EVENT_ID = 44;
    1919    private const FORM_EVENT_ID = 3;
     20    private const ADD_TO_CART_EVENT_ID = 43;
     21    private const REFUNDED_ORDER_EVENT_ID = 45;
    2022
    2123    /** @var Philia_Event_Sender|null */
    … …  
    8789
    8890    /**
    89      * Registers all engagement hooks controlled by data-sync setting 5.
     91     * Registers event hooks controlled by their respective data-sync settings.
    9092     *
    9193     * @return void
    … …  
    102104
    103105        try {
    104             $enabled = (bool) Philia_API_Settings::is_philia_data_sync_enabled( '5' );
     106            $engagement_enabled = (bool) Philia_API_Settings::is_philia_data_sync_enabled( '5' );
     107            $refunds_enabled    = (bool) Philia_API_Settings::is_philia_data_sync_enabled( '6' );
    105108        } catch ( \Throwable $exception ) {
    106             error_log( 'Philia_Event_Sender: Failed checking engagement sync setting: ' . $exception->getMessage() );
     109            error_log( 'Philia_Event_Sender: Failed checking event sync settings: ' . $exception->getMessage() );
    107110            return;
    108111        }
    109112
    110113        $this->event_actions_registered = true;
    111         if ( ! $enabled ) {
    112             return;
    113         }
    114 
    115         $actions = array(
    116             'philia_viewed',
    117             'comment_post',
    118             'transition_comment_status',
    119             'spammed_comment',
    120             'deleted_comment',
    121             'woocommerce_add_to_cart',
    122         );
    123 
    124         foreach ( $actions as $action ) {
    125             add_action( $action, array( $this, 'queue_event_callback' ), 10, 99 );
    126         }
    127 
    128         // Optional integrations; the hooks are harmless when these plugins are inactive.
    129         add_action( 'gform_after_submission', array( $this, 'handle_gravity_forms_submission' ), 10, 2 );
    130         add_action( 'wpcf7_mail_sent', array( $this, 'handle_contact_form_7_submission' ), 10, 1 );
    131         add_action( 'wpforms_process_complete', array( $this, 'handle_wpforms_submission' ), 10, 4 );
     114        if ( $engagement_enabled ) {
     115            $actions = array(
     116                'philia_viewed',
     117                'comment_post',
     118                'transition_comment_status',
     119                'spammed_comment',
     120                'deleted_comment',
     121                'woocommerce_add_to_cart',
     122            );
     123
     124            foreach ( $actions as $action ) {
     125                add_action( $action, array( $this, 'queue_event_callback' ), 10, 99 );
     126            }
     127
     128            // Optional integrations; the hooks are harmless when these plugins are inactive.
     129            add_action( 'gform_after_submission', array( $this, 'handle_gravity_forms_submission' ), 10, 2 );
     130            add_action( 'wpcf7_mail_sent', array( $this, 'handle_contact_form_7_submission' ), 10, 1 );
     131            add_action( 'wpforms_process_complete', array( $this, 'handle_wpforms_submission' ), 10, 4 );
     132        }
     133
     134        if ( $refunds_enabled ) {
     135            add_action( 'woocommerce_order_refunded', array( $this, 'queue_event_callback' ), 10, 2 );
     136        }
    132137    }
    133138
    … …  
    144149        }
    145150
    146         // Capture request-bound moderator, timestamp and deleted-comment data now.
    147         if ( in_array( $current_action, array( 'transition_comment_status', 'spammed_comment', 'deleted_comment' ), true ) ) {
     151        // Cart events are tracked only for authenticated customers.
     152        if ( 'woocommerce_add_to_cart' === $current_action && ! is_user_logged_in() ) {
     153            return;
     154        }
     155
     156        // Capture request-bound moderation and cart data before the event enters the queue.
     157        if ( in_array( $current_action, array( 'transition_comment_status', 'spammed_comment', 'deleted_comment', 'woocommerce_add_to_cart', 'woocommerce_order_refunded' ), true ) ) {
    148158            $args = $this->normalize_hook_arguments( $current_action, $args );
    149159        }
    … …  
    300310        }
    301311
    302         $timestamp = current_time( 'mysql' );
     312        $timestamp = date('Y-m-d H:i:s');
    303313        if ( is_callable( array( $submission, 'get_meta' ) ) ) {
    304314            $submitted_at = $submission->get_meta( 'timestamp' );
    … …  
    611621                    $already_normalized && isset( $args[3] )
    612622                        ? sanitize_text_field( (string) $args[3] )
    613                         : gmdate( 'Y-m-d\TH:i:s\Z' ),
     623                        : current_time( 'mysql' ),
    614624                );
     625
     626            case 'woocommerce_add_to_cart':
     627                if ( isset( $args[0] ) && is_array( $args[0] ) && isset( $args[0]['products'] ) ) {
     628                    return array( $this->normalize_queue_value( $args[0] ) );
     629                }
     630
     631                $payload = $this->build_add_to_cart_payload( $args );
     632                return empty( $payload ) ? array() : array( $payload );
     633
     634            case 'woocommerce_order_refunded':
     635                if ( isset( $args[0] ) && is_array( $args[0] ) && isset( $args[0]['refund_id'] ) ) {
     636                    return array( $this->normalize_queue_value( $args[0] ) );
     637                }
     638
     639                $payload = $this->build_refunded_order_payload( $args );
     640                return empty( $payload ) ? array() : array( $payload );
    615641
    616642            case 'deleted_comment':
    … …  
    661687                        'comment_text'   => $content,
    662688                        'comment_length' => $length,
    663                         'timestamp'      => gmdate( 'Y-m-d\TH:i:s\Z' ),
     689                        'timestamp'      => current_time( 'mysql' ),
    664690                    ),
    665691                );
    … …  
    667693
    668694        return $this->normalize_queue_value( $args );
     695    }
     696
     697    /**
     698     * Captures the complete cart state while the WooCommerce request is active.
     699     *
     700     * @param array $args Raw woocommerce_add_to_cart hook arguments.
     701     * @return array
     702     */
     703    private function build_add_to_cart_payload( array $args ) {
     704        $woocommerce = function_exists( 'WC' ) ? WC() : null;
     705        if ( ! $woocommerce || ! $woocommerce->cart ) {
     706            return array();
     707        }
     708
     709        $cart_items          = $woocommerce->cart->get_cart();
     710        $products            = array();
     711        $included_categories = array();
     712        $cart_total_price    = 0.0;
     713
     714        foreach ( $cart_items as $cart_item ) {
     715            if ( ! is_array( $cart_item ) ) {
     716                continue;
     717            }
     718
     719            $product_id = isset( $cart_item['product_id'] ) ? absint( $cart_item['product_id'] ) : 0;
     720            $quantity   = isset( $cart_item['quantity'] ) ? max( 0, (float) $cart_item['quantity'] ) : 0.0;
     721            if ( ! $product_id ) {
     722                continue;
     723            }
     724
     725            $products[] = $product_id;
     726
     727            $term_ids = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'ids' ) );
     728            if ( ! is_wp_error( $term_ids ) ) {
     729                $included_categories = array_merge( $included_categories, array_map( 'absint', $term_ids ) );
     730            }
     731
     732            $product = isset( $cart_item['data'] ) && is_object( $cart_item['data'] )
     733                ? $cart_item['data']
     734                : wc_get_product( $product_id );
     735
     736            if ( $product && is_callable( array( $product, 'get_price' ) ) ) {
     737                $cart_total_price += (float) $product->get_price() * $quantity;
     738            }
     739        }
     740
     741        $products            = array_values( array_unique( array_filter( array_map( 'absint', $products ) ) ) );
     742        $included_categories = array_values( array_unique( array_filter( array_map( 'absint', $included_categories ) ) ) );
     743
     744        if ( empty( $products ) ) {
     745            $added_product_id = isset( $args[1] ) ? absint( $args[1] ) : 0;
     746            if ( ! $added_product_id || ! wc_get_product( $added_product_id ) ) {
     747                return array();
     748            }
     749            $products[] = $added_product_id;
     750        }
     751
     752        $user_id    = get_current_user_id();
     753        $session_id = '';
     754        if ( $woocommerce->session && is_callable( array( $woocommerce->session, 'get_customer_id' ) ) ) {
     755            $session_id = sanitize_text_field( (string) $woocommerce->session->get_customer_id() );
     756        }
     757
     758        $cart_item_key = isset( $args[0] ) ? sanitize_text_field( (string) $args[0] ) : '';
     759        $cart_id       = is_callable( array( $woocommerce->cart, 'get_cart_hash' ) )
     760            ? sanitize_text_field( (string) $woocommerce->cart->get_cart_hash() )
     761            : $cart_item_key;
     762
     763        $payload = array(
     764            'username'            => $user_id ? (string) Philia_API_Settings::get_user_mobile( $user_id ) : '',
     765            'timestamp'           => date('Y-m-d H:i:s'),
     766            'anonymous_id'        => '',
     767            'uid'                 => $user_id ? (string) $user_id : '',
     768            'cart_id'             => $cart_id ?: $cart_item_key,
     769            'device_id'           => $session_id,
     770            'session_id'          => $session_id,
     771            'cart_total_price'    => (float) $cart_total_price,
     772            'currency'            => function_exists( 'get_woocommerce_currency' ) ? (string) get_woocommerce_currency() : '',
     773            'products'            => $products,
     774            'included_categories' => $included_categories,
     775            'purchase_channel'    => 'web',
     776            'branch'              => '',
     777        );
     778
     779        return $payload;
     780    }
     781
     782    /**
     783     * Captures a WooCommerce refund as a serializable event payload.
     784     *
     785     * @param array $args Raw woocommerce_order_refunded hook arguments.
     786     * @return array
     787     */
     788    private function build_refunded_order_payload( array $args ) {
     789        $order_id  = isset( $args[0] ) ? absint( $args[0] ) : 0;
     790        $refund_id = isset( $args[1] ) ? absint( $args[1] ) : 0;
     791
     792        if ( ! $order_id || ! $refund_id || ! function_exists( 'wc_get_order' ) ) {
     793            return array();
     794        }
     795
     796        $order  = wc_get_order( $order_id );
     797        $refund = wc_get_order( $refund_id );
     798        if ( ! $order || ! $refund || ! is_a( $refund, 'WC_Order_Refund' ) ) {
     799            return array();
     800        }
     801
     802        $user_id = absint( $order->get_customer_id() );
     803        $mobile  = sanitize_text_field( (string) $order->get_billing_phone() );
     804        $mobile  = (string) preg_replace( '/^(?:\+98|0098|98|0)/', '', $mobile );
     805        if ( '' === $mobile && $user_id ) {
     806            $mobile = (string) Philia_API_Settings::get_user_mobile( $user_id );
     807        }
     808
     809        $products = array();
     810        foreach ( $refund->get_items( 'line_item' ) as $item ) {
     811            if ( ! is_object( $item ) ) {
     812                continue;
     813            }
     814
     815            $variation_id = is_callable( array( $item, 'get_variation_id' ) ) ? absint( $item->get_variation_id() ) : 0;
     816            $product_id   = $variation_id ?: ( is_callable( array( $item, 'get_product_id' ) ) ? absint( $item->get_product_id() ) : 0 );
     817            $quantity     = is_callable( array( $item, 'get_quantity' ) ) ? abs( (float) $item->get_quantity() ) : 0;
     818            if ( ! $product_id || 0 >= $quantity ) {
     819                continue;
     820            }
     821
     822            $products[] = array(
     823                'id'       => (string) $product_id,
     824                'quantity' => floor( $quantity ) === $quantity ? (int) $quantity : $quantity,
     825            );
     826        }
     827
     828        $created_at = $refund->get_date_created();
     829        $timestamp  = $created_at && is_callable( array( $created_at, 'date' ) )
     830            ? $created_at->date( 'Y-m-d H:i:s' )
     831            : current_time( 'mysql' );
     832
     833        $anonymous_id   = (string) apply_filters( 'philia_refunded_order_anonymous_id', '', $order, $refund );
     834        $refund_channel = (string) apply_filters( 'philia_refunded_order_channel', '', $order, $refund );
     835
     836        $payload = array(
     837            'username'           => $mobile,
     838            'first_name'         => sanitize_text_field( (string) $order->get_billing_first_name() ),
     839            'last_name'          => sanitize_text_field( (string) $order->get_billing_last_name() ),
     840            'mobile'             => $mobile,
     841            'email'              => sanitize_email( (string) $order->get_billing_email() ),
     842            'timestamp'          => $timestamp,
     843            'order_id'           => $order_id,
     844            'refund_id'          => $refund_id,
     845            'anonymous_id'       => sanitize_text_field( $anonymous_id ),
     846            'uid'                => $user_id,
     847            'currency'           => sanitize_text_field( (string) $order->get_currency() ),
     848            'refund_total_price' => abs( (float) $refund->get_amount() ),
     849            'refund_reason'      => sanitize_textarea_field( (string) $refund->get_reason() ),
     850            'refund_channel'     => sanitize_text_field( $refund_channel ),
     851            'products'           => $products,
     852        );
     853
     854        return (array) apply_filters( 'philia_refunded_order_payload', $payload, $order, $refund );
    669855    }
    670856
    … …  
    712898    private function send_event( $current_action, array $args ) {
    713899        if ( empty( $this->app_secret ) ) {
     900            return;
     901        }
     902
     903        // Re-check at dispatch time so disabling refund sync also cancels queued refund events.
     904        if (
     905            'woocommerce_order_refunded' === $current_action
     906            && ! Philia_API_Settings::is_philia_data_sync_enabled( '6' )
     907        ) {
    714908            return;
    715909        }
    … …  
    8471041
    8481042                $view_payload = array(
    849                     'timestamp'   => $timestamp ?: current_time( 'mysql' ),
     1043                    'timestamp'   => $timestamp ?: date('Y-m-d H:i:s'),
    8501044                    'action'      => 'view',
    8511045                    'object_type' => 'view',
    … …  
    9591153
    9601154                $deleted_payload = array(
    961                     'timestamp'      => isset( $deleted['timestamp'] ) ? sanitize_text_field( (string) $deleted['timestamp'] ) : gmdate( 'Y-m-d\TH:i:s\Z' ),
     1155                    'timestamp'      => isset( $deleted['timestamp'] ) ? sanitize_text_field( (string) $deleted['timestamp'] ) : date('Y-m-d H:i:s'),
    9621156                    'action'         => 'delete',
    9631157                    'object_type'    => 'comment',
    … …  
    10071201                    'target_type' => $target_type,
    10081202                    'target_id'   => $target_type . '_' . absint( $comment->comment_post_ID ),
    1009                     'timestamp'   => $timestamp ?: current_time( 'mysql' ),
     1203                    'timestamp'   => $timestamp ?: date('Y-m-d H:i:s'),
    10101204                );
    10111205
    … …  
    10381232                    'object_type' => 'comment',
    10391233                    'object_id'   => 'comment_' . $comment_id,
    1040                     'timestamp'   => $timestamp ?: gmdate( 'Y-m-d\TH:i:s\Z' ),
     1234                    'timestamp'   => $timestamp ?: date('Y-m-d H:i:s'),
    10411235                );
    10421236                if ( $moderator_id ) {
    … …  
    10511245
    10521246            case 'woocommerce_add_to_cart':
    1053                 if ( ! is_user_logged_in() ) {
    1054                     return array();
    1055                 }
    1056 
    1057                 $product_id = isset( $args[1] ) ? absint( $args[1] ) : 0;
    1058                 if ( $product_id && class_exists( 'WC_Product' ) && wc_get_product( $product_id ) ) {
     1247                $payload = isset( $args[0] ) && is_array( $args[0] ) ? $args[0] : array();
     1248                if ( ! empty( $payload ) && isset( $payload['products'] ) ) {
    10591249                    $data = array(
    1060                         'product_id'   => $product_id,
    1061                         'quantity'     => isset( $args[2] ) ? absint( $args[2] ) : 0,
    1062                         'variation_id' => isset( $args[3] ) ? absint( $args[3] ) : 0,
     1250                        'event_id' => self::ADD_TO_CART_EVENT_ID,
     1251                        'payload'  => $payload,
     1252                    );
     1253                }
     1254                break;
     1255
     1256            case 'woocommerce_order_refunded':
     1257                $payload = isset( $args[0] ) && is_array( $args[0] ) ? $args[0] : array();
     1258                if ( ! empty( $payload ) && ! empty( $payload['order_id'] ) && ! empty( $payload['refund_id'] ) ) {
     1259                    $data = array(
     1260                        'event_id' => self::REFUNDED_ORDER_EVENT_ID,
     1261                        'payload'  => $payload,
    10631262                    );
    10641263                }
  • philia-integration/trunk/includes/class-login-redirect.php

    r3683158 r3720581  
    11<?php
    22/**
    3  * Philia Login Redirect
    4  * Manages user login to Philia using redirect from WooCommerce user account.
     3 * Legion Login Redirect
     4 * Manages user login to Legion using redirect from WooCommerce user account.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
  • philia-integration/trunk/includes/class-philia-referral.php

    r3693436 r3720581  
    11<?php
    22/**
    3  * Philia Referral System
     3 * Legion Referral System
    44 * Manages referral tracking and user registration with referral codes.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
  • philia-integration/trunk/includes/class-philia-wallet.php

    r3683431 r3720581  
    11<?php
    22/**
    3  * Philia Wallet
    4  * Manages Philia cashback wallet integration on the WooCommerce checkout page.
     3 * Legion Wallet
     4 * Manages Legion cashback wallet integration on the WooCommerce checkout page.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
    … …  
    4646   
    4747    /**
    48      * Translatable label used both as the order's payment-method title
    49      * and as the name of the wallet fee line (kept in sync so detection
    50      * logic in get_wallet_amount_for_order() always matches).
     48     * Display label used as the order's payment-method title and wallet fee name.
    5149     *
    5250     * @return string
    5351     */
    5452    public static function get_payment_method_title() {
    55         return __( 'Philia Wallet', 'philia-integration' );
     53        if ( class_exists( 'Philia_API\Philia_API_Settings' ) ) {
     54            return Philia_API_Settings::get_wallet_title();
     55        }
     56
     57        return __('Legion Wallet', 'philia-integration');
    5658    }
    5759
    … …  
    175177     */
    176178    public function fetch_balance( $phone ) {
    177         $result = [];
    178 
    179179        $response = Philia_Core_API::send_request('integration/cashback/user/balance/?phone_number=' . urlencode( $phone ), 'GET', []);
    180180
    181         if ( is_wp_error( $response ) ) {
     181        if ( is_wp_error( $response ) || ! is_array( $response ) ) {
    182182            error_log("Error in 'integration/cashback/user/balance/' for get user wallet balance");
    183             $result = [ 'user_cashback' => 0, 'currency' => $this->get_currency_symbol() ];
    184             return $result;
    185         }
    186 
    187         $result = [ 'user_cashback' => json_decode( wp_remote_retrieve_body( $response ), true )['data']['user_cashback'], 'currency' => $this->get_currency_symbol() ];
    188 
    189         return $result;
     183            return false;
     184        }
     185
     186        $status_code   = (int) wp_remote_retrieve_response_code( $response );
     187        $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
     188        if (
     189            $status_code < 200
     190            || $status_code >= 300
     191            || ! is_array( $response_body )
     192            || empty( $response_body['success'] )
     193            || ! isset( $response_body['data'] )
     194            || ! is_array( $response_body['data'] )
     195            || ! isset( $response_body['data']['user_cashback'] )
     196        ) {
     197            error_log( "Philia wallet balance request failed with HTTP status {$status_code}." );
     198            return false;
     199        }
     200
     201        return array(
     202            'user_cashback' => (float) $response_body['data']['user_cashback'],
     203            'is_active'     => isset( $response_body['data']['is_active'] )
     204                ? filter_var( $response_body['data']['is_active'], FILTER_VALIDATE_BOOLEAN )
     205                : false,
     206            'currency'      => $this->get_currency_symbol(),
     207        );
    190208    }
    191209
    … …  
    211229        $response = Philia_Core_API::send_request( 'integration/cashback/user/balance/adjust/', 'POST', $body );
    212230
    213         if ( is_wp_error( $response ) ) {
     231        if ( is_wp_error( $response ) || ! is_array( $response ) ) {
    214232            error_log("Error in 'integration/cashback/user/balance/adjust/' for adjust user wallet balance");
    215233            return false;
    216234        }
    217235
    218         return json_decode( wp_remote_retrieve_body( $response ), true );
     236        $status_code   = (int) wp_remote_retrieve_response_code( $response );
     237        $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
     238        if (
     239            $status_code < 200
     240            || $status_code >= 300
     241            || ! is_array( $response_body )
     242            || empty( $response_body['success'] )
     243        ) {
     244            error_log( "Philia wallet balance adjustment failed with HTTP status {$status_code}." );
     245            return false;
     246        }
     247
     248        return $response_body;
    219249    }
    220250
    … …  
    244274        }
    245275
     276        $woocommerce = self::get_woocommerce_instance();
     277        $data['wallet_active'] = (bool) (
     278            $woocommerce
     279            && $woocommerce->session
     280            && $woocommerce->session->get( self::SESSION_ACTIVE, false )
     281        );
     282
    246283        wp_send_json_success( $data );
    247284    }
    … …  
    250287     * AJAX: Toggle wallet usage on / off.
    251288     *
    252      * ON  (checked=true)  → action='add'      + add negative fee → update_checkout
    253      * OFF (checked=false) → action='decrease' + remove fee       → update_checkout
     289     * ON  (checked=true)  → action='decrease' + add negative fee → update_checkout
     290     * OFF (checked=false) → action='add'      + remove fee       → update_checkout
    254291     */
    255292    public function ajax_toggle_wallet() {
    … …  
    265302            );
    266303        }
    267        
    268         $user_id = get_current_user_id();
    269         $enabled = filter_var( $_POST['checked'] ?? 'false', FILTER_VALIDATE_BOOLEAN );
    270         $phone   = $this->get_user_phone( $user_id );
    271 
    272         $prev_active = $woocommerce->session->get( self::SESSION_ACTIVE, false );
     304
     305        $user_id    = get_current_user_id();
     306        $enabled    = filter_var( $_POST['checked'] ?? 'false', FILTER_VALIDATE_BOOLEAN );
     307        $phone      = $this->get_user_phone( $user_id );
     308        $prev_active = (bool) $woocommerce->session->get( self::SESSION_ACTIVE, false );
    273309        $prev_amount = floatval( $woocommerce->session->get( self::SESSION_AMOUNT, 0 ) );
     310
     311        if ( ! $user_id || empty( $phone ) ) {
     312            wp_send_json_error(
     313                array(
     314                    'code'          => 'no_phone',
     315                    'message'       => __( 'A valid phone number is required to use the wallet.', 'philia-integration' ),
     316                    'wallet_active' => $prev_active,
     317                )
     318            );
     319        }
    274320
    275321        if ( $enabled ) {
    … …  
    281327            if ( class_exists( 'Philia_API\Philia_API_Settings' ) && ! Philia_API_Settings::is_wallet_enabled_for_gateway( $chosen_gateway ) ) {
    282328                if ( $prev_active && $prev_amount > 0 ) {
    283                     $this->adjust_balance( $phone, $prev_amount, 'add', $user_id, 'Checkout Gateway Restriction' );
     329                    $adjusted = $this->adjust_balance( $phone, $prev_amount, 'add', $user_id, 'Checkout Gateway Restriction' );
     330                    if ( false === $adjusted ) {
     331                        wp_send_json_error(
     332                            array(
     333                                'code'          => 'wallet_refund_failed',
     334                                'message'       => __( 'The wallet credit could not be restored. Wallet usage remains active.', 'philia-integration' ),
     335                                'wallet_active' => true,
     336                            )
     337                        );
     338                    }
    284339                }
    285340
    286341                $woocommerce->session->set( self::SESSION_ACTIVE, false );
    287342                $woocommerce->session->set( self::SESSION_AMOUNT, 0 );
     343                $woocommerce->cart->calculate_fees();
     344
    288345                wp_send_json_error(
    289346                    array(
    290                         'message' => __( 'Philia wallet is not enabled for the selected payment method.', 'philia-integration' ),
     347                        'code'          => 'gateway_not_allowed',
     348                        'message'       => __( 'Legion wallet is not enabled for the selected payment method.', 'philia-integration' ),
     349                        'wallet_active' => false,
    291350                    )
    292351                );
    293352            }
    294353
    295             // Activate: deduct the amount from the wallet and store it in the cart session.
     354            // Activate only after the cashback service and balance adjustment both succeed.
    296355            $balance_data = $this->fetch_balance( $phone );
    297             $balance      = isset( $balance_data['user_cashback'] ) ? floatval( $balance_data['user_cashback'] ) : 0;
    298            
     356            if ( false === $balance_data ) {
     357                wp_send_json_error(
     358                    array(
     359                        'code'          => 'balance_unavailable',
     360                        'message'       => __( 'The wallet balance could not be retrieved.', 'philia-integration' ),
     361                        'wallet_active' => $prev_active,
     362                    )
     363                );
     364            }
     365
     366            if ( empty( $balance_data['is_active'] ) ) {
     367                wp_send_json_error(
     368                    array(
     369                        'code'            => 'cashback_inactive',
     370                        'message'         => __( 'Cashback is currently inactive.', 'philia-integration' ),
     371                        'wallet_active'   => $prev_active,
     372                        'cashback_active' => false,
     373                    )
     374                );
     375            }
     376
     377            $balance = isset( $balance_data['user_cashback'] ) ? floatval( $balance_data['user_cashback'] ) : 0;
     378
    299379            // Calculate the cart total without the current wallet discount.
    300380            $current_total = floatval( $woocommerce->cart->get_total('edit') );
    301381            $wallet_to_use = min( $balance, $current_total + $prev_amount );
    302382
    303             if ( $wallet_to_use > 0 ) {
    304                 $this->adjust_balance( $phone, $wallet_to_use, 'decrease', $user_id, 'Checkout Activation' );
    305                 $woocommerce->session->set( self::SESSION_ACTIVE, true );
    306                 $woocommerce->session->set( self::SESSION_AMOUNT, $wallet_to_use );
    307             }
     383            if ( $wallet_to_use <= 0 ) {
     384                wp_send_json_error(
     385                    array(
     386                        'code'            => 'insufficient_wallet_balance',
     387                        'message'         => __( 'There is no wallet balance available to use.', 'philia-integration' ),
     388                        'wallet_active'   => $prev_active,
     389                        'cashback_active' => true,
     390                    )
     391                );
     392            }
     393
     394            $adjusted = $this->adjust_balance( $phone, $wallet_to_use, 'decrease', $user_id, 'Checkout Activation' );
     395            if ( false === $adjusted ) {
     396                wp_send_json_error(
     397                    array(
     398                        'code'            => 'wallet_decrease_failed',
     399                        'message'         => __( 'The wallet balance could not be deducted.', 'philia-integration' ),
     400                        'wallet_active'   => $prev_active,
     401                        'cashback_active' => true,
     402                    )
     403                );
     404            }
     405
     406            $woocommerce->session->set( self::SESSION_ACTIVE, true );
     407            $woocommerce->session->set( self::SESSION_AMOUNT, $wallet_to_use );
    308408        } else {
    309             // Deactivate: return the amount to the wallet and clear the session.
     409            // Deactivate only after the reserved amount is restored successfully.
    310410            if ( $prev_active && $prev_amount > 0 ) {
    311                 $this->adjust_balance( $phone, $prev_amount, 'add', $user_id, 'Checkout Deactivation' );
    312             }
     411                $adjusted = $this->adjust_balance( $phone, $prev_amount, 'add', $user_id, 'Checkout Deactivation' );
     412                if ( false === $adjusted ) {
     413                    wp_send_json_error(
     414                        array(
     415                            'code'          => 'wallet_refund_failed',
     416                            'message'       => __( 'The wallet credit could not be restored. Wallet usage remains active.', 'philia-integration' ),
     417                            'wallet_active' => true,
     418                        )
     419                    );
     420                }
     421            }
     422
    313423            $woocommerce->session->set( self::SESSION_ACTIVE, false );
    314424            $woocommerce->session->set( self::SESSION_AMOUNT, 0 );
    … …  
    316426
    317427        $woocommerce->cart->calculate_fees();
    318         wp_send_json_success();
     428        wp_send_json_success(
     429            array(
     430                'wallet_active' => (bool) $woocommerce->session->get( self::SESSION_ACTIVE, false ),
     431            )
     432        );
    319433    }
    320434
    … …  
    341455        if ( $active && $amount > 0 ) {
    342456            $cart->add_fee(
    343                 __( 'Philia Wallet', 'philia-integration' ),
     457                self::get_payment_method_title(),
    344458                -$amount,
    345459                false   // not taxable
    … …  
    425539            $order->payment_complete();
    426540            $order->add_order_note(
    427                 __( 'The order was fully paid using the Philia wallet.', 'philia-integration' )
     541                __( 'The order was fully paid using the Legion wallet.', 'philia-integration' )
    428542            );
    429543        }
    … …  
    457571        }
    458572
    459         $this->adjust_balance(
     573        $adjusted = $this->adjust_balance(
    460574            $phone,
    461575            $wallet_amount,
    … …  
    468582            )
    469583        );
     584
     585        if ( false === $adjusted ) {
     586            $order->add_order_note(
     587                __( 'Legion wallet credit could not be restored because the cashback service rejected the request.', 'philia-integration' )
     588            );
     589            error_log( "Philia wallet refund failed for cancelled order ID {$order_id}." );
     590        }
    470591    }
    471592
    … …  
    523644        // ── 2. Philia wallet fee line fallback ───────────────────────────────
    524645        $philia_fee = 0.0;
     646        $recognized_fee_titles = array(
     647            self::get_payment_method_title(),
     648            __( 'Legion Wallet', 'philia-integration' ),
     649        );
    525650        foreach ( $order->get_items( 'fee' ) as $fee_item ) {
    526651            /** @var \WC_Order_Item_Fee $fee_item */
    527             if ( $fee_item->get_name() === self::get_payment_method_title() ) {
     652            if ( in_array( $fee_item->get_name(), $recognized_fee_titles, true ) ) {
    528653                $philia_fee += abs( (float) $fee_item->get_total() );
    529654            }
    … …  
    571696        $this->enqueue_wallet_script();
    572697
    573         $atts  = shortcode_atts( [ 'title' => '' ], $atts, 'philia-wallet' );
    574         $title = esc_html( $atts['title'] );
     698        $atts = shortcode_atts( [ 'title' => '' ], $atts, 'philia-wallet' );
     699        $shortcode_title = trim( sanitize_text_field( $atts['title'] ) );
     700        $title = esc_html( $shortcode_title !== '' ? $shortcode_title : self::get_payment_method_title() );
    575701
    576702        ob_start();
  • philia-integration/trunk/includes/class-webhook-sender.php

    r3683158 r3720581  
    11<?php
    22/**
    3  * Philia Webhook Sender
     3 * Legion Webhook Sender
    44 * The class responsible for sending webhook notifications for various WordPress/WooCommerce events.
    55 *
    6  * Package: Philia
    7  * Author: Philia
     6 * Package: Legion
     7 * Author: Legion
    88 */
    99
    … …  
    936936                    $order = wc_get_order($order_id);
    937937                    if ($order) {
    938                         // Get the configured order status for synchronization (default: 'completed')
     938                        // Get the configured order status for synchronization (default: 'completed').
    939939                        $settings_class = __NAMESPACE__ . '\\Philia_API_Settings';
    940940                        $sync_status = ( class_exists( $settings_class ) && is_callable( array( $settings_class, 'get_order_sync_status' ) ) )
    941941                            ? call_user_func( array( $settings_class, 'get_order_sync_status' ) )
    942942                            : 'completed';
    943                        
    944                         // ONLY proceed if the order status matches the configured sync status
    945                         if ($order->get_status() !== $sync_status) {
    946                             error_log("Philia_Webhook_Sender: Order ID {$order_id} status is '{$order->get_status()}', not '{$sync_status}'. Skipping webhook send.");
     943                        $order_status = $order->get_status();
     944
     945                        // ONLY proceed if the order has the configured synchronization status.
     946                        if ( $order_status !== $sync_status ) {
     947                            error_log( "Philia_Webhook_Sender: Order ID {$order_id} status is '{$order_status}', not the configured status '{$sync_status}'. Skipping webhook send." );
    947948                            return []; // Return empty data to prevent sending webhook
    948949                        }
    … …  
    12011202            array(
    12021203                'id'          => '_philia_product_id',
    1203                 'label'       => __( 'Philia Product ID', 'philia-integration' ),
     1204                'label'       => __( 'Legion Product ID', 'philia-integration' ),
    12041205                'desc_tip'    => true,
    1205                 'description' => __( 'This ID is used for syncing with the philia club.', 'philia-integration' ),
     1206                'description' => __( 'This ID is used for syncing with the legion club.', 'philia-integration' ),
    12061207                'value'       => get_post_meta( $post->ID, '_philia_product_id', true ),
    12071208            )
    … …  
    12191220            'name'          => '_philia_product_id[' . $variation->ID . ']',
    12201221            'value'         => get_post_meta( $variation->ID, '_philia_product_id', true ),
    1221             'label'         => __( 'Philia Product ID', 'philia-integration' ),
     1222            'label'         => __( 'Legion Product ID', 'philia-integration' ),
    12221223            'desc_tip'      => true,
    1223             'description'   => __( 'Enter the Philia Product ID for this variation.', 'philia-integration' ),
     1224            'description'   => __( 'Enter the Legion Product ID for this variation.', 'philia-integration' ),
    12241225            'wrapper_class' => 'form-row form-row-full',
    12251226        ) );
  • philia-integration/trunk/includes/templates/philia-wallet-payment.php

    r3683431 r3720581  
    1515
    1616$philia_wallet_allowed = true;
     17$philia_wallet_title   = __( 'Legion Wallet', 'philia-integration' );
    1718if ( class_exists( 'Philia_API\Philia_API_Settings' ) ) {
     19    $philia_wallet_title   = \Philia_API\Philia_API_Settings::get_wallet_title();
    1820    $philia_chosen_gateway = $philia_session ? $philia_session->get( 'chosen_payment_method' ) : '';
    1921    if ( ! empty( $philia_chosen_gateway ) ) {
    … …  
    3133            </span>
    3234            <div class="philia-wallet-text">
    33                 <span class="philia-wallet-label"><?php esc_html_e( 'Pay with Philia Wallet', 'philia-integration' ); ?></span>
     35                <span class="philia-wallet-label">
     36                    <?php
     37                    /* translators: %s: configured wallet title. */
     38                    printf( esc_html__( 'Pay with %s', 'philia-integration' ), esc_html( $philia_wallet_title ) );
     39                    ?>
     40                </span>
    3441                <span class="philia-wallet-balance-wrap">(<?php esc_html_e( 'Balance:', 'philia-integration' ); ?> <span class="philia-wallet-balance"><?php esc_html_e( 'Loading...', 'philia-integration' ); ?></span>)</span>
    3542            </div>
  • philia-integration/trunk/philia-integration.php

    r3693436 r3720581  
    11<?php
    22/**
    3  * Plugin Name: Philia Integration with WooCommerce
    4  * Description: Sending Users, Products, Categories and invoices from WooCommerce to Philia and Receive Coupons and Cashback from Philia
    5  * Version: 1.18.2
    6  * Author: Philia
    7  * Author URI: https://fa.philia.vip
     3 * Plugin Name: Legion Integration with WooCommerce
     4 * Description: Sending Users, Products, Categories and invoices from WooCommerce to Legion and Receive Coupons and Cashback from Legion
     5 * Version: 1.19
     6 * Author: Legion
     7 * Author URI: https://legion.tools
    88 * License: GPL-2.0+
    99 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
    … …  
    1717
    1818// Define constants for plugin directory, URL, and table prefix.
    19 define('PHILIA_PLUGIN_VERSION', '1.18.2');
     19define('PHILIA_PLUGIN_VERSION', '1.19');
    2020define('PHILIA_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2121define('PHILIA_PLUGIN_URL', plugin_dir_url(__FILE__));
    … …  
    4444function philia_api_add_menu_page() {
    4545    add_menu_page(
    46         __('Philia API Settings', 'philia-integration'), // Page title
    47         __('Philia', 'philia-integration'), // Menu title
     46        __('Legion API Settings', 'philia-integration'), // Page title
     47        __('Legion', 'philia-integration'), // Menu title
    4848        'manage_options', // Capability
    4949        'philia-api-settings', // Menu slug
  • philia-integration/trunk/readme.txt

    r3693436 r3720581  
    1 === Philia Integration with WooCommerce ===
     1=== Legion Integration with WooCommerce ===
    22Contributors: philiasolutions
    33Tags: philia, loyalty program, events & attributes
    44Requires at least: 6.0
    55Tested up to: 7.1
    6 Stable tag: 1.18.2
     6Stable tag: 1.19
    77License: GPL-2.0+
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
    99
    10 Send Users, Products, Categories and invoices from WooCommerce to Philia and Receive Coupons and Cashback from Philia
     10Send Users, Products, Categories and invoices from WooCommerce to Legion and Receive Coupons and Cashback from Legion
    1111
    1212== Description ==
    1313
    14 Philia Sync Plugin For WooCommerce is a product developed by the <a href="https://fa.philia.vip">Philia</a> company. Philia is a Customer Loyalty Program platform that offers services such as Defining Loyalty Roadmaps and programs for each business, in any industry and more. This plugin is a sync and integration solution for sending trackable data to philia for progressing loyalty missions, sending required events and etc within the WooCommerce platform. Using this plugin, businesses can integrate their WooCommerce store with Philia.
    15 This plugin automatically transfers users, products, categories, orders and other required events. It also uses WooWallet plugin to integrate philia's cashback to WooCommerce.
    16 It's important to note that the plugin connects to philia via a web service, and we have designed this web service to be secure. We attach great importance to user rights and data, and we recommend that you read the <a href="https://philia.vip/terms-of-service/" target="_blank">Terms of Service</a> of philia and our plugin.
     14Legion Sync Plugin For WooCommerce is a product developed by the <a href="https://legion.tools">Legion</a> company. Legion is a Customer Loyalty Program platform that offers services such as Defining Loyalty Roadmaps and programs for each business, in any industry and more. This plugin is a sync and integration solution for sending trackable data to legion for progressing loyalty missions, sending required events and etc within the WooCommerce platform. Using this plugin, businesses can integrate their WooCommerce store with Legion.
     15This plugin automatically transfers users, products, categories, orders and other required events. It also uses WooWallet plugin to integrate legion's cashback to WooCommerce.
     16It's important to note that the plugin connects to legion via a web service, and we have designed this web service to be secure. We attach great importance to user rights and data, and we recommend that you read the <a href="https://legion.tools/terms-of-service/" target="_blank">Terms of Service</a> of legion and our plugin.
    1717
    1818Key features of the plugin:
    1919
    20 - Integrate WooCommerce users and Philia
    21 - Integrate WooCommerce products and Philia
    22 - Integrate WooCommerce categories and Philia
    23 - Integrate WooCommerce orders and Philia
    24 - Integrate Philia Coupon Codes and WooCommerce
    25 - Integrate Philia Wallet (Cashback)
     20- Integrate WooCommerce users and Legion
     21- Integrate WooCommerce products and Legion
     22- Integrate WooCommerce categories and Legion
     23- Integrate WooCommerce orders and Legion
     24- Integrate Legion Coupon Codes and WooCommerce
     25- Integrate Legion Wallet (Cashback)
    2626
    2727== Installation ==
    … …  
    3434= This plugin is free =
    3535
    36 Yes, the Philia Integration is a free plugin.
     36Yes, the Legion Integration is a free plugin.
    3737
    3838= Is this plugin compatible with WooCommerce? =
    … …  
    4242= How does this plugin work? =
    4343
    44 You first need to purchase the one of Philia Subscription plans. Then, you need to obtain the web service key and enter it in the plugin settings section of the WooCommerce configuration. Once this is done, the plugin will be connected to the your philia panel.
     44You first need to purchase the one of Legion Subscription plans. Then, you need to obtain the web service key and enter it in the plugin settings section of the WooCommerce configuration. Once this is done, the plugin will be connected to the your philia panel.
    4545
    4646== Changelog ==
     47
     48= 1.19 =
     49* Rebrand and change name to Legion
     50* Added Refund order to Data Synchronization
     51* Bugs fixes and improvements
    4752
    4853= 1.18.2 =
    … …  
    6873= 1.16 =
    6974* Compatible with WP 7
    70 * Added Philia Wallet & Cashback
     75* Added Legion Wallet & Cashback
    7176* Bugs fixes and improvements
    7277
    … …  
    7681
    7782= 1.14 =
    78 * Added Fill mode field for Philia Product ID
     83* Added Fill mode field for Legion Product ID
    7984* Bugs fixes and improvements
    8085
    … …  
    8691
    8792= 1.12 =
    88 * Added custom field for better syncing with philia
     93* Added custom field for better syncing with legion
    8994* Fixed login to club bug and optimize the speed of login
    9095* Bugs fixes and improvements
    … …  
    101106
    102107= 1.10 =
    103 * Added Selective data for syncing with philia
     108* Added Selective data for syncing with legion
    104109* Bugs fixes and improvements
    105110
Note: See TracChangeset for help on using the changeset viewer.