Adicionar uma gorjeta na página de checkout

add_action('admin_menu', function () {
    add_submenu_page(
        'woocommerce',
        'Checkout Tip',
        'Checkout Tip',
        'manage_woocommerce',
        'wc-checkout-tip',
        function () {
            if (isset($_POST['wc_tip_nonce']) && wp_verify_nonce($_POST['wc_tip_nonce'], 'wc_tip_save')) {
                update_option('wc_tip_enabled', isset($_POST['wc_tip_enabled']) ? 'yes' : 'no');
                update_option('wc_tip_label', isset($_POST['wc_tip_label']) ? sanitize_text_field($_POST['wc_tip_label']) : 'Tip');
                update_option('wc_tip_fixed_1', isset($_POST['wc_tip_fixed_1']) ? floatval($_POST['wc_tip_fixed_1']) : 2);
                update_option('wc_tip_fixed_2', isset($_POST['wc_tip_fixed_2']) ? floatval($_POST['wc_tip_fixed_2']) : 5);
                update_option('wc_tip_fixed_3', isset($_POST['wc_tip_fixed_3']) ? floatval($_POST['wc_tip_fixed_3']) : 10);
                update_option('wc_tip_allow_custom', isset($_POST['wc_tip_allow_custom']) ? 'yes' : 'no');
                echo '<div class="updated"><p>Saved.</p></div>';
            }

            $enabled      = get_option('wc_tip_enabled', 'yes');
            $label        = get_option('wc_tip_label', 'Tip');
            $v1           = get_option('wc_tip_fixed_1', 2);
            $v2           = get_option('wc_tip_fixed_2', 5);
            $v3           = get_option('wc_tip_fixed_3', 10);
            $allow_custom = get_option('wc_tip_allow_custom', 'yes');

            echo '<div class="wrap"><h1>Checkout Tip</h1><form method="post">';
            wp_nonce_field('wc_tip_save', 'wc_tip_nonce');
            echo '<table class="form-table" role="presentation">';
            echo '<tr><th scope="row">Enable</th><td><label><input type="checkbox" name="wc_tip_enabled" ' . checked($enabled, 'yes', false) . ' /> Enable tip on checkout</label></td></tr>';
            echo '<tr><th scope="row">Fee label</th><td><input type="text" name="wc_tip_label" value="' . esc_attr($label) . '" /></td></tr>';
            echo '<tr><th scope="row">Fixed Amount 1</th><td><input type="number" step="0.01" min="0" name="wc_tip_fixed_1" value="' . esc_attr($v1) . '" /></td></tr>';
            echo '<tr><th scope="row">Fixed Amount 2</th><td><input type="number" step="0.01" min="0" name="wc_tip_fixed_2" value="' . esc_attr($v2) . '" /></td></tr>';
            echo '<tr><th scope="row">Fixed Amount 3</th><td><input type="number" step="0.01" min="0" name="wc_tip_fixed_3" value="' . esc_attr($v3) . '" /></td></tr>';
            echo '<tr><th scope="row">Allow custom</th><td><label><input type="checkbox" name="wc_tip_allow_custom" ' . checked($allow_custom, 'yes', false) . ' /> Allow custom tip</label></td></tr>';
            echo '</table>';
            submit_button('Save Changes');
            echo '</form></div>';
        }
    );
});

add_action('woocommerce_review_order_before_order_total', function () {
    if (get_option('wc_tip_enabled', 'yes') !== 'yes') {
        return;
    }

    $fixed_1      = floatval(get_option('wc_tip_fixed_1', 2));
    $fixed_2      = floatval(get_option('wc_tip_fixed_2', 5));
    $fixed_3      = floatval(get_option('wc_tip_fixed_3', 10));
    $allow_custom = get_option('wc_tip_allow_custom', 'yes');
    $session_tip  = (WC()->session ? floatval(WC()->session->get('wc_tip_amount')) : 0);

    $fixed_values = array($fixed_1, $fixed_2, $fixed_3);
    $custom_value = '';
    if ($session_tip > 0 && !in_array($session_tip, $fixed_values, true)) {
        $custom_value = number_format($session_tip, 2, '.', '');
    }

    $btn1_active = ($session_tip > 0 && abs($session_tip - $fixed_1) < 0.000001) ? ' is-active' : '';
    $btn2_active = ($session_tip > 0 && abs($session_tip - $fixed_2) < 0.000001) ? ' is-active' : '';
    $btn3_active = ($session_tip > 0 && abs($session_tip - $fixed_3) < 0.000001) ? ' is-active' : '';
    $none_active = ($session_tip <= 0) ? ' is-active' : '';

    $toggle_checked = ($session_tip > 0) ? ' checked="checked"' : '';

    echo '<tr class="wc-tip-row"><td colspan="2">';
    echo '<div class="wc-tip-wrap">';
    echo   '<div class="wc-tip-head"><input type="checkbox" class="wc-tip-toggle"' . $toggle_checked . '> <span>Add tip</span></div>';
    echo   '<div class="wc-tip-grid">';
    echo     '<div class="wc-tip-btn' . $btn1_active . '" data-amt="' . esc_attr($fixed_1) . '"><div>' . wc_price($fixed_1) . '</div></div>';
    echo     '<div class="wc-tip-btn' . $btn2_active . '" data-amt="' . esc_attr($fixed_2) . '"><div>' . wc_price($fixed_2) . '</div></div>';
    echo     '<div class="wc-tip-btn' . $btn3_active . '" data-amt="' . esc_attr($fixed_3) . '"><div>' . wc_price($fixed_3) . '</div></div>';
    echo     '<div class="wc-tip-btn wc-tip-none' . $none_active . '" data-amt="0"><div>None</div></div>';
    echo   '</div>';

    $show_custom = 'style="display:none"';
    if ($allow_custom === 'yes' && $session_tip > 0 && !in_array($session_tip, $fixed_values, true)) {
        $show_custom = 'style="display:block"';
    }
    if ($allow_custom === 'yes' && $session_tip <= 0) {
        $show_custom = 'style="display:block"';
    }

    if ($allow_custom === 'yes') {
        echo '<div class="wc-tip-custom-wrapper" ' . $show_custom . '>';
        echo   '<div class="wc-tip-custom">';
        echo     '<div class="wc-tip-number">';
        echo       '<button type="button" class="wc-tip-dec">-</button>';
        echo       '<input type="number" min="0" step="0.01" class="wc-tip-input" placeholder="Custom tip" value="' . esc_attr($custom_value) . '">';
        echo       '<button type="button" class="wc-tip-inc">+</button>';
        echo     '</div>';
        echo     '<button type="button" class="wc-tip-add">Set Tip</button>';
        echo   '</div>';
        echo '</div>';
    }

    echo   '<div class="wc-tip-note">Thank you, we appreciate it.</div>';
    echo '</div>';
    echo '<input type="hidden" name="wc_tip_amount" class="wc-tip-hidden" value="' . esc_attr($session_tip) . '">';
    echo '</td></tr>';
});

add_action('wp_enqueue_scripts', function () {
    if (!is_checkout()) {
        return;
    }

    wp_enqueue_script('jquery');

    $css = '
.wc-tip-wrap{margin:12px 0 0 0;border:1px solid #e5e5e5;border-radius:12px;overflow:hidden}
.wc-tip-head{display:flex;align-items:center;gap:10px;padding:14px 16px;font-weight:600}
.wc-tip-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border-top:1px solid #eee;border-bottom:1px solid #eee}
.wc-tip-btn{padding:16px;text-align:center;border-right:1px solid #eee;cursor:pointer}
.wc-tip-btn:last-child{border-right:none}
.wc-tip-btn.is-active{background:#b99a6b;color:#fff;outline:none;border-radius:0}
.wc-tip-custom-wrapper{border-top:1px solid #eee}
.wc-tip-custom{display:flex;align-items:center;gap:10px;padding:14px 16px}
.wc-tip-number{display:flex;align-items:center;border:1px solid #ddd;border-radius:10px;overflow:hidden}
.wc-tip-number button{width:40px;height:40px;background:#f7f7f7;border:none;font-size:18px;cursor:pointer}
.wc-tip-number input{width:110px;height:40px;border:none;text-align:center;outline:none}
.wc-tip-add{height:40px;padding:0 16px;border:1px solid #ddd;border-radius:10px;background:#f3f3f3;cursor:pointer}
.wc-tip-note{padding:10px 16px 14px 16px;font-size:14px;display:flex}
@media(max-width:600px){
  .wc-tip-grid{grid-template-columns:repeat(2,1fr)}
  .wc-tip-custom{display:flex;gap:10px;padding:14px 16px;flex-direction:column}
  .wc-tip-btn{
    padding:16px;
    text-align:center;
    border-right:1px solid #eee;
    cursor:pointer;
    border-left:1px solid #eee;
    border-bottom:1px solid #eee;
  }
  .wc-tip-btn.is-active{border-bottom:1px solid #eee}
}
';
    wp_register_style('wc_tip_css', false);
    wp_enqueue_style('wc_tip_css');
    wp_add_inline_style('wc_tip_css', $css);

    $enabled      = get_option('wc_tip_enabled', 'yes');
    $allow_custom = get_option('wc_tip_allow_custom', 'yes');

    $data = array(
        'enabled'     => $enabled,
        'allowCustom' => $allow_custom,
    );
    $setup = 'window.WCTIP = ' . wp_json_encode($data) . ';';
    wp_add_inline_script('jquery', $setup, 'after');

    $js = <<<'JS'
(function($){
    if (typeof window.WCTIP === 'undefined' || WCTIP.enabled !== 'yes') {
        return;
    }

    function toggleCustomTip(show) {
        if (WCTIP.allowCustom === 'yes') {
            $('.wc-tip-custom-wrapper').toggle(show);
        }
    }

    function syncFromHidden() {
        var v = parseFloat($('.wc-tip-hidden').val() || 0);
        if (isNaN(v)) { v = 0; }

        $('.wc-tip-btn').removeClass('is-active');

        if (v <= 0) {
            $('.wc-tip-none').addClass('is-active');
            toggleCustomTip(true);
            $('.wc-tip-toggle').prop('checked', false);
            return;
        }

        var matched = false;
        $('.wc-tip-btn').each(function () {
            if ($(this).hasClass('wc-tip-none')) return;
            var amt = parseFloat($(this).data('amt') || 0);
            if (!isNaN(amt) && amt.toFixed(2) === v.toFixed(2)) {
                $(this).addClass('is-active');
                matched = true;
            }
        });

        if (matched) {
            toggleCustomTip(false);
        } else {
            $('.wc-tip-none').addClass('is-active');
            toggleCustomTip(true);
            if (WCTIP.allowCustom === 'yes') {
                $('.wc-tip-input').val(v.toFixed(2));
            }
        }

        $('.wc-tip-toggle').prop('checked', v > 0);
    }

    function setTip(v) {
        v = Math.max(0, parseFloat(v || 0));
        if (isNaN(v)) { v = 0; }

        var $hidden = $('.wc-tip-hidden');
        if (!$hidden.length) return;

        var current = parseFloat($hidden.val() || 0);
        if (!isNaN(current) && current.toFixed(2) === v.toFixed(2)) {
            return;
        }

        $hidden.val(v);
        syncFromHidden();
        $(document.body).trigger('update_checkout');
    }

    $(document).on('click', '.wc-tip-btn', function () {
        var v = parseFloat($(this).data('amt') || 0);
        if (WCTIP.allowCustom === 'yes') {
            $('.wc-tip-input').val('');
        }
        setTip(v);
    });

    $(document).on('click', '.wc-tip-add', function () {
        var v = parseFloat($('.wc-tip-input').val() || 0);
        setTip(v);
    });

    $(document).on('click', '.wc-tip-dec, .wc-tip-inc', function () {
        var $i = $('.wc-tip-input');
        var v  = parseFloat($i.val() || 0);
        if (isNaN(v)) { v = 0; }
        var step = 1;
        if ($(this).hasClass('wc-tip-dec')) {
            v = Math.max(0, v - step);
        } else {
            v += step;
        }
        $i.val(v.toFixed(2));
    });

    $(document).on('change', '.wc-tip-toggle', function () {
        if (!$(this).is(':checked')) {
            setTip(0);
        } else {
            var currentTip = parseFloat($('.wc-tip-hidden').val() || 0);
            if (isNaN(currentTip) || currentTip <= 0) {
                var firstBtn = $('.wc-tip-btn').not('.wc-tip-none').first();
                var v = parseFloat(firstBtn.data('amt') || 0);
                if (isNaN(v)) { v = 0; }
                setTip(v);
            } else {
                setTip(currentTip);
            }
        }
    });

    $(function () {
        syncFromHidden();
    });

    $(document.body).on('updated_checkout', function () {
        syncFromHidden();
    });

})(jQuery);
JS;

    wp_add_inline_script('jquery', $js, 'after');
});

add_action('woocommerce_cart_calculate_fees', function ($cart) {
    if (is_admin() && !defined('DOING_AJAX')) {
        return;
    }
    if (get_option('wc_tip_enabled', 'yes') !== 'yes') {
        return;
    }

    $posted = array();
    if (isset($_POST['post_data'])) {
        parse_str(html_entity_decode(stripslashes($_POST['post_data'])), $posted);
    }
    if (isset($_POST['wc_tip_amount'])) {
        $posted['wc_tip_amount'] = $_POST['wc_tip_amount'];
    }

    $tip = 0;
    if (isset($posted['wc_tip_amount'])) {
        $tip = floatval($posted['wc_tip_amount']);
    } elseif (WC()->session) {
        $tip = floatval(WC()->session->get('wc_tip_amount'));
    }

    if ($tip < 0 || !is_numeric($tip)) {
        $tip = 0;
    }

    if (WC()->session) {
        WC()->session->set('wc_tip_amount', $tip);
    }

    if ($tip > 0) {
        $label = get_option('wc_tip_label', 'Tip');
        $cart->add_fee($label, $tip, false);
    }
}, 20);