Probo Configurator Price API

User
after configurator complete

jQuery('#launchConfigurator').attr('disabled', true).css('opacity', 0.5);
function page() {
return {
updateModal() {
const result = window.proboConfigurator.getResult();
const code = window.proboConfigurator.getConfiguration();

console.log("🧾 Probo Configurator Result:", result);
console.log("🧾 Probo Configurator Configuration:", code);
}
}
}

// Listen for the Probo Configurator's "finished" event
window.addEventListener("proboConfigurator:finished", async function (event) {
console.log("βœ… Configuration finished");
jQuery('#launchConfigurator').attr('disabled', false).css('opacity', 1);
// Optionally, you can call updateModal() here if you want to log automatically
const result = window.proboConfigurator.getResult();
const code = window.proboConfigurator.getConfiguration();

console.log("🎯 Final Result:", result);
console.log("πŸ“¦ Final Configuration:", code);

});
how call price api from probo

Assistant
To call the **Probo Price API** after the Probo Configurator finishes, you can trigger the API inside the `proboConfigurator:finished` event listener by sending a `POST` request with the final configuration data.

### βœ… Step-by-step Integration

Assuming the API endpoint is something like `https://api.proboprints.com/v2/price`, here's how you can do it using `fetch` or jQuery's `$.ajax`.

### βœ… 1. Using `fetch` (Recommended Modern Way)

window.addEventListener("proboConfigurator:finished", async function (event) {
    console.log("βœ… Configuration finished");
    jQuery('#launchConfigurator').attr('disabled', false).css('opacity', 1);

    const result = window.proboConfigurator.getResult();
    const config = window.proboConfigurator.getConfiguration();

    console.log("🎯 Final Result:", result);
    console.log("πŸ“¦ Final Configuration:", config);

    try {
        const response = await fetch("https://api.proboprints.com/v2/price", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer YOUR_API_TOKEN" // replace with your token if needed
            },
            body: JSON.stringify({
                configuration: config
            })
        });

        const priceData = await response.json();
        console.log("πŸ’° Price API Response:", priceData);

        // You can now use `priceData` to update your UI, modal, etc.
    } catch (error) {
        console.error("❌ Price API Error:", error);
    }
});

### βœ… 2. Using jQuery AJAX (Alternative)

window.addEventListener("proboConfigurator:finished", function (event) {
    console.log("βœ… Configuration finished");
    jQuery('#launchConfigurator').attr('disabled', false).css('opacity', 1);

    const result = window.proboConfigurator.getResult();
    const config = window.proboConfigurator.getConfiguration();

    console.log("🎯 Final Result:", result);
    console.log("πŸ“¦ Final Configuration:", config);

    $.ajax({
        url: "https://api.proboprints.com/v2/price",
        method: "POST",
        contentType: "application/json",
        headers: {
            "Authorization": "Bearer YOUR_API_TOKEN" // if needed
        },
        data: JSON.stringify({
            configuration: config
        }),
        success: function(priceData) {
            console.log("πŸ’° Price API Response:", priceData);
            // Update UI with priceData
        },
        error: function(err) {
            console.error("❌ Price API Error:", err);
        }
    });
});

### 🧠 Notes:
– Replace `YOUR_API_TOKEN` with your actual Probo API key/token, if required.
– The payload (`configuration`) should match the expected format by Probo's pricing API.
– You can move the API call to a dedicated method inside your Alpine.js/Vue component if you want better structure.

Let me know if you want to also **display the price inside a modal or DOM element** after getting the response.

User
{status: 'ok', code: 200, message: 'Options calculated', products: Array(1), calculation_id: 80760555}
calculation_id
:
80760555
code
:
200
message
:
"Options calculated"
products
:
Array(1)
0
:
amount
:
2
available_options
:
[]
can_order
:
true
code
:
"banner"
height
:
12
id
:
1
length
:
0
linked_products
:
[{…}]
prices_per_product
:
24
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 2, purchase_price: 5.865, purchase_price_incl_vat: 7.0967, sales_price: 0, …}
30
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 2, purchase_price: 5.865, purchase_price_incl_vat: 7.0967, sales_price: 0, …}
48
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 1.5, purchase_price: 5.365, purchase_price_incl_vat: 6.4917, sales_price: 0, …}
72
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0.75, purchase_price: 4.615, purchase_price_incl_vat: 5.5842, sales_price: 0, …}
96
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
120
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
144
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
168
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
192
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
216
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
240
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
264
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
288
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
312
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
336
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
360
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
384
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
408
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
432
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
456
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
480
:
{purchase_base_price: 3.865, purchase_rush_surcharge: 0, purchase_price: 3.865, purchase_price_incl_vat: 4.6767, sales_price: 0, …}
[[Prototype]]
:
Object
prices_total
:
24
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 4, purchase_price: 11.73, purchase_price_incl_vat: 14.19, sales_price: 0, …}
30
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 4, purchase_price: 11.73, purchase_price_incl_vat: 14.19, sales_price: 0, …}
48
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 3, purchase_price: 10.73, purchase_price_incl_vat: 12.98, sales_price: 0, …}
72
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 1.5, purchase_price: 9.23, purchase_price_incl_vat: 11.17, sales_price: 0, …}
96
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
120
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
144
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
168
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
192
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
216
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
240
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
264
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
288
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
312
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
336
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
360
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
384
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
408
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
432
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
456
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
480
:
{purchase_base_price: 7.73, purchase_rush_surcharge: 0, purchase_price: 7.73, purchase_price_incl_vat: 9.35, sales_price: 0, …}
[[Prototype]]
:
Object
selected_options
:
Array(7)
0
:
{type_code: 'width', name: 'Width', label: null, description: null, value: '11', …}
1
:
{type_code: 'height', name: 'Height', label: null, description: null, value: '12', …}
2
:
{type_code: 'amount', name: 'Amount', label: null, description: null, value: '2', …}
3
:
{type_code: 'radio', name: 'Single-sided', label: null, description: null, value: null, …}
4
:
{type_code: 'radio', name: 'Banner 510', label: 'Most popular', description: 'PVC banner suited for both indoor and outdoor applications.', value: null, …}
5
:
{type_code: 'radio', name: 'Use same finish for all edges', label: null, description: 'All edges of the banner receive the same finish.', value: null, …}
6
:
{type_code: 'radio', name: 'Cut', label: null, description: 'The material is cut to the desired size.', value: null, …}
length
:
7
[[Prototype]]
:
Array(0)
we above out after call configurator how to retieve price from it
Assistant
To retrieve and display the **price** from the Probo API response after the configurator completes, you should extract the relevant fields from the `products` array returned by the Price API.

### βœ… From the Data You Shared:

You can access:

– `response.products[0].prices_total[delivery_time].purchase_price_incl_vat` β†’ total price **(incl. VAT)** for given delivery time
– `response.products[0].prices_per_product[delivery_time].purchase_price_incl_vat` β†’ **per unit price** (incl. VAT)

### 🧠 Example to Extract Final Price (Assuming 96-hour delivery):

window.addEventListener("proboConfigurator:finished", async function () {
    jQuery('#launchConfigurator').attr('disabled', false).css('opacity', 1);

    const config = window.proboConfigurator.getConfiguration();

    try {
        const res = await fetch("https://api.proboprints.com/v2/price", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer YOUR_API_TOKEN"
            },
            body: JSON.stringify({ configuration: config })
        });

        const data = await res.json();
        console.log("πŸ’° Price Response", data);

        // Access price data
        const product = data.products[0];
        const deliveryTime = 96; // in hours (can be 24, 48, 72, 96, etc.)

        const totalPrice = product.prices_total[deliveryTime].purchase_price_incl_vat;
        const perUnitPrice = product.prices_per_product[deliveryTime].purchase_price_incl_vat;
        const amount = product.amount;

        console.log(`🧾 Total Price (${amount}x): €${totalPrice.toFixed(2)}`);
        console.log(`πŸ’΅ Price per Unit: €${perUnitPrice.toFixed(2)}`);

        // Inject into HTML if needed
        document.querySelector('#totalPrice').innerText = `€${totalPrice.toFixed(2)}`;
        document.querySelector('#perUnitPrice').innerText = `€${perUnitPrice.toFixed(2)}`;

    } catch (err) {
        console.error("❌ Error fetching price:", err);
    }
});

### βœ… HTML Example:

<div>
  <p>Price per Unit: <span id="perUnitPrice">–</span></p>
  <p>Total Price: <span id="totalPrice">–</span></p>
</div>

### πŸ“ Optional: Auto-pick Cheapest Delivery Time

To automatically pick the delivery time with the **lowest total price**, you can do this:

let bestTime = null;
let lowest = Infinity;
for (const [time, price] of Object.entries(product.prices_total)) {
    if (price.purchase_price_incl_vat < lowest) {
        lowest = price.purchase_price_incl_vat;
        bestTime = time;
    }
}
console.log(`🚚 Best delivery time: ${bestTime}h, Price: €${lowest.toFixed(2)}`);

Would you like to format and show delivery options as a dropdown or select best price automatically?

User
actually we get above price from
const payload = window.proboConfigurator.getRaw();
now display all prices includeing radio
and let user click on any radio want to add to cart in woocommerce
Assistant
Great β€” since you're using `const payload = window.proboConfigurator.getRaw();`, and it returns the full structured result **including `selected_options`, `prices_total`, and `prices_per_product`**, here’s how to:

### βœ… 1. **Extract & Display All Delivery Prices as Radio Buttons**

Each delivery time (e.g., 24h, 48h, 72h, 96h…) is a **key** inside `prices_total`.

We'll generate a set of “ buttons for each delivery option.

### βœ… 2. **Let User Click to Select a Delivery Option & Add to Cart**

When the user clicks a radio, you can:
– Save the selected delivery time
– Enable a β€œAdd to Cart” button
– On click of "Add to Cart", use AJAX or redirect to send data to WooCommerce cart (via a custom endpoint or product add logic)

### βœ… Full Working Example:

<div id="probo-price-options">
  <h3>Select Delivery Option</h3>
  <div id="deliveryOptions"></div>
  <button id="addToCartBtn" disabled>Add to Cart</button>
</div>
window.addEventListener("proboConfigurator:finished", function () {
    const payload = window.proboConfigurator.getRaw();
    const product = payload.products[0];
    const prices = product.prices_total;
    const amount = product.amount;

    const deliveryOptionsContainer = document.getElementById("deliveryOptions");
    deliveryOptionsContainer.innerHTML = "";

    // Generate radio buttons for each delivery time
    Object.entries(prices).forEach(([hours, price]) => {
        const label = document.createElement("label");
        label.innerHTML = `
            <input type="radio" name="delivery_time" value="${hours}">
            ${hours}h – €${price.purchase_price_incl_vat.toFixed(2)} (for ${amount} units)
        `;
        deliveryOptionsContainer.appendChild(label);
        deliveryOptionsContainer.appendChild(document.createElement("br"));
    });

    // Listen for selection
    let selectedHours = null;
    deliveryOptionsContainer.addEventListener("change", (e) => {
        selectedHours = e.target.value;
        document.getElementById("addToCartBtn").disabled = false;
    });

    // Add to cart button logic
    document.getElementById("addToCartBtn").addEventListener("click", () => {
        if (!selectedHours) return alert("Please select a delivery time!");

        // Gather all needed data
        const config = window.proboConfigurator.getConfiguration(); // for saving config
        const selectedPrice = prices[selectedHours];
        const selectedOptions = product.selected_options;

        // Send to WooCommerce cart (Example: POST to custom PHP endpoint or redirect)
        console.log("πŸ›’ Add to cart with config:", config);
        console.log("πŸ•’ Selected delivery time:", selectedHours);
        console.log("πŸ’Ά Price to add:", selectedPrice);
        console.log("πŸ“¦ Options:", selectedOptions);

        // πŸ‘‰ Here you can send via AJAX to your custom endpoint to add to WooCommerce cart
        // OR redirect with query params if you're not using AJAX.

        // Example (redirect, custom implementation needed in backend):
        const productId = 1234; // your WooCommerce product ID
        const url = `/add-to-cart-handler/?product_id=${productId}&price=${selectedPrice.purchase_price_incl_vat}&delivery=${selectedHours}&config=${encodeURIComponent(JSON.stringify(config))}`;
        window.location.href = url;
    });
});

### βœ… Backend WooCommerce Integration

You need a custom handler on `/add-to-cart-handler/` that:

1. Creates a WooCommerce cart item for the given product ID
2. Adds metadata like:
– Config JSON
– Delivery time
– Price override
3. Redirects to cart or checkout

If needed, I can help you write this WordPress PHP handler too (`add_to_cart_handler.php` or via a `add_action('template_redirect')`).

Would you like the full PHP code for the WooCommerce cart handler that handles this JS request?

User
display those where actually extra price applied
Assistant
Perfect β€” if you only want to **display delivery options where an extra price (rush surcharge) is applied**, you can filter the entries in `prices_total` where `purchase_rush_surcharge > 0`.

### βœ… Updated JavaScript to Display Only Extra Price (Rush) Options

window.addEventListener("proboConfigurator:finished", function () {
    const payload = window.proboConfigurator.getRaw();
    const product = payload.products[0];
    const prices = product.prices_total;
    const amount = product.amount;

    const deliveryOptionsContainer = document.getElementById("deliveryOptions");
    deliveryOptionsContainer.innerHTML = "";

    // Filter and display only those with rush surcharge > 0
    const filtered = Object.entries(prices).filter(
        ([, price]) => price.purchase_rush_surcharge > 0
    );

    if (filtered.length === 0) {
        deliveryOptionsContainer.innerHTML = "<p>No rush delivery options with extra charges found.</p>";
        return;
    }

    // Create radio buttons
    filtered.forEach(([hours, price]) => {
        const label = document.createElement("label");
        label.innerHTML = `
            <input type="radio" name="delivery_time" value="${hours}">
            ${hours}h – €${price.purchase_price_incl_vat.toFixed(2)} (Rush Fee: €${price.purchase_rush_surcharge.toFixed(2)})
        `;
        deliveryOptionsContainer.appendChild(label);
        deliveryOptionsContainer.appendChild(document.createElement("br"));
    });

    // Listen for user selection
    let selectedHours = null;
    deliveryOptionsContainer.addEventListener("change", (e) => {
        selectedHours = e.target.value;
        document.getElementById("addToCartBtn").disabled = false;
    });

    // Handle Add to Cart
    document.getElementById("addToCartBtn").addEventListener("click", () => {
        if (!selectedHours) return alert("Please select a delivery option!");

        const selectedPrice = prices[selectedHours];
        const config = window.proboConfigurator.getConfiguration();

        console.log("πŸ›’ Add to Cart:");
        console.log("Delivery Time:", selectedHours);
        console.log("Rush Fee:", selectedPrice.purchase_rush_surcharge);
        console.log("Price incl. VAT:", selectedPrice.purchase_price_incl_vat);
        console.log("Config:", config);

        // TODO: AJAX or redirect to WooCommerce Add-to-Cart with this data
    });
});

### βœ… HTML:

<div id="probo-price-options">
  <h3>Select Rush Delivery Option (Extra Charge)</h3>
  <div id="deliveryOptions"></div>
  <button id="addToCartBtn" disabled>Add to Cart</button>
</div>

### βœ… Example Output:

[o] 24h – €14.19 (Rush Fee: €4.00)
[o] 48h – €12.98 (Rush Fee: €3.00)
[o] 72h – €11.17 (Rush Fee: €1.50)

Let me know if you want to also show **regular free options (surcharge = 0)** in a **separate list** for comparison β€” or handle differently in WooCommerce cart.

User
also so one more ption where no rush fee applied like NO rush hours more than then price
Assistant
Great β€” let’s now display **two sections**:

1. βœ… **Rush Delivery Options** β€” where `purchase_rush_surcharge > 0`
2. πŸ•’ **No Rush Options** β€” where `purchase_rush_surcharge === 0`

This gives the user a clear choice between **paying more for faster delivery** vs **waiting longer for cheaper price**.

### βœ… Full Code to Display Both Sets

<div id="probo-price-options">
  <h3>Rush Delivery Options (Extra Fee)</h3>
  <div id="rushOptions"></div>

  <h3 style="margin-top:20px;">No Rush (Standard Delivery)</h3>
  <div id="noRushOptions"></div>

  <button id="addToCartBtn" disabled>Add to Cart</button>
</div>

“`js
window.addEventListener("proboConfigurator:finished", function () {
const payload = window.proboConfigurator.getRaw();
const product = payload.products[0];
const prices = product.prices_total;
const amount = product.amount;

const rushContainer = document.getElementById("rushOptions");
const noRushContainer = document.getElementById("noRushOptions");
const addToCartBtn = document.getElementById("addToCartBtn");

rushContainer.innerHTML = "";
noRushContainer.innerHTML = "";
addToCartBtn.disabled = true;

let selectedHours = null;

User
In same deliveryoptions
Assistant
Perfect β€” if you want to **show both Rush and No Rush delivery options in the same section**, but **clearly indicate which one has a rush fee** and which one doesn’t, here’s a clean and intuitive solution:

### βœ… Updated HTML

<div id="probo-price-options">
  <h3>Select Delivery Option</h3>
  <div id="deliveryOptions"></div>
  <button id="addToCartBtn" disabled>Add to Cart</button>
</div>

### βœ… JavaScript: Display Rush & No-Rush Together (Marked Clearly)

window.addEventListener("proboConfigurator:finished", function () {
    const payload = window.proboConfigurator.getRaw();
    const product = payload.products[0];
    const prices = product.prices_total;
    const amount = product.amount;

    const deliveryOptionsContainer = document.getElementById("deliveryOptions");
    const addToCartBtn = document.getElementById("addToCartBtn");
    deliveryOptionsContainer.innerHTML = "";
    addToCartBtn.disabled = true;

    let selectedHours = null;

    Object.entries(prices).forEach(([hours, price]) => {
        const isRush = price.purchase_rush_surcharge > 0;

        const label = document.createElement("label");
        label.style.display = "block";
        label.style.marginBottom = "8px";

        label.innerHTML = `
            <input type="radio" name="delivery_time" value="${hours}">
            ${hours}h – €${price.purchase_price_incl_vat.toFixed(2)} 
            ${isRush ? `<span style="color: red;">(+ Rush Fee: €${price.purchase_rush_surcharge.toFixed(2)})</span>` : `<span style="color: green;">(No Extra Fee)</span>`}
        `;

        deliveryOptionsContainer.appendChild(label);
    });

    // Handle radio change
    deliveryOptionsContainer.addEventListener("change", (e) => {
        selectedHours = e.target.value;
        addToCartBtn.disabled = false;
    });

    // Handle Add to Cart
    addToCartBtn.addEventListener("click", () => {
        if (!selectedHours) return alert("Please select a delivery option");

        const selectedPrice = prices[selectedHours];
        const config = window.proboConfigurator.getConfiguration();

        console.log("πŸ›’ Adding to cart with:");
        console.log("Delivery Time:", selectedHours);
        console.log("Rush Fee:", selectedPrice.purchase_rush_surcharge);
        console.log("Total Price (incl. VAT):", selectedPrice.purchase_price_incl_vat);
        console.log("Config:", config);

        // TODO: Send this data to WooCommerce or backend
    });
});

### βœ… Example Output in UI

(o) 24h – €14.19 (+ Rush Fee: €4.00)
(o) 48h – €12.98 (+ Rush Fee: €3.00)
(o) 72h – €11.17 (+ Rush Fee: €1.50)
(o) 96h – €9.35 (No Extra Fee)
(o) 120h – €9.35 (No Extra Fee)

Let me know if you want:
– To **highlight the cheapest option**
– To show **estimated delivery date**
– Or handle **Add to Cart logic** with WooCommerce via AJAX or URL redirect.

User
like above 96 no rush
Assistant
Got it! You want to clearly **mark only the first option where `rush_surcharge === 0`** as **β€œNo Rush”**, and all earlier/faster ones (with surcharge) shown normally β€” even if more than one has no surcharge (like 96h, 120h, 144h… etc).

### βœ… Logic:
– Loop through delivery options sorted by hours.
– Find the **first** one where `purchase_rush_surcharge === 0` β€” mark that as **"No Rush"**.
– All earlier ones with surcharge are just regular rush options.

### βœ… Final Code (All in Same Section, Only One "No Rush")

window.addEventListener("proboConfigurator:finished", function () {
    const payload = window.proboConfigurator.getRaw();
    const product = payload.products[0];
    const prices = product.prices_total;
    const amount = product.amount;

    const deliveryOptionsContainer = document.getElementById("deliveryOptions");
    const addToCartBtn = document.getElementById("addToCartBtn");
    deliveryOptionsContainer.innerHTML = "";
    addToCartBtn.disabled = true;

    let selectedHours = null;

    // Sort delivery hours numerically
    const sortedPrices = Object.entries(prices).sort((a, b) => Number(a[0]) - Number(b[0]));

    let noRushMarked = false;

    sortedPrices.forEach(([hours, price]) => {
        const isNoRush = price.purchase_rush_surcharge === 0 && !noRushMarked;

        const label = document.createElement("label");
        label.style.display = "block";
        label.style.marginBottom = "8px";

        label.innerHTML = `
            <input type="radio" name="delivery_time" value="${hours}">
            ${hours}h – €${price.purchase_price_incl_vat.toFixed(2)}
            ${isNoRush
                ? `<span style="color: green;">(No Rush)</span>`
                : price.purchase_rush_surcharge > 0
                    ? `<span style="color: red;">(+ Rush Fee: €${price.purchase_rush_surcharge.toFixed(2)})</span>`
                    : ""}
        `;

        deliveryOptionsContainer.appendChild(label);

        if (isNoRush) noRushMarked = true;
    });

    // Handle selection
    deliveryOptionsContainer.addEventListener("change", (e) => {
        selectedHours = e.target.value;
        addToCartBtn.disabled = false;
    });

    // Add to cart logic
    addToCartBtn.addEventListener("click", () => {
        if (!selectedHours) return alert("Please select a delivery option");

        const selectedPrice = prices[selectedHours];
        const config = window.proboConfigurator.getConfiguration();

        console.log("πŸ›’ Adding to cart with:");
        console.log("Delivery Time:", selectedHours);
        console.log("Rush Fee:", selectedPrice.purchase_rush_surcharge);
        console.log("Price incl. VAT:", selectedPrice.purchase_price_incl_vat);
        console.log("Config:", config);

        // TODO: integrate into WooCommerce add-to-cart
    });
});

### βœ… Example Output:

(o) 24h – €14.19 (+ Rush Fee: €4.00)
(o) 48h – €12.98 (+ Rush Fee: €3.00)
(o) 72h – €11.17 (+ Rush Fee: €1.50)
(o) 96h – €9.35 (No Rush)
(o) 120h – €9.35
(o) 144h – €9.35

Only the **first no-surcharge option (96h)** is labeled β€œNo Rush”.

Would you like to style the No Rush option to **auto-select by default** or **highlight it visually** as the best value?

User
Okay now if click on add to cart attached selected price and save other
selected_options,calcution id and upload and uploaders to cart item meta Like add
common meta field probo_configure_meta and save in this when comletedd order after checkout
Assistant
Perfect. You're asking to:

βœ… On "Add to Cart" click:

1. Attach:
– `selected_options`
– `calculation_id`
– `upload` & `uploaders`
– `selected price (based on delivery time)`
2. Bundle these into a **single cart item meta field** called `probo_configure_meta`.

βœ… Then:

3. Save `probo_configure_meta` to the WooCommerce **order item meta** after checkout so it stays with the order.

### βœ… Frontend (JavaScript): Send to Add-to-Cart URL

Let’s assume:
– Your WooCommerce product has ID `1234`
– You pass `probo_configure_meta` via query param or via AJAX (recommended)

document.getElementById("addToCartBtn").addEventListener("click", () => {
    if (!selectedHours) return alert("Please select a delivery option");

    const payload = window.proboConfigurator.getRaw();
    const selectedPrice = payload.products[0].prices_total[selectedHours];
    const meta = {
        selected_options: payload.products[0].selected_options,
        calculation_id: payload.calculation_id,
        upload: payload.upload,
        uploaders: payload.uploaders,
        selected_delivery: selectedHours,
        price: selectedPrice,
    };

    const productId = 1234;

    // Convert meta to base64 string to avoid URL issues
    const metaEncoded = btoa(JSON.stringify(meta));

    // Send to cart via GET
    const url = `/add-to-cart-handler/?add-to-cart=${productId}&probo_configure_meta=${metaEncoded}`;
    window.location.href = url;
});

### βœ… Backend: Handle `probo_configure_meta` in Cart and Order

#### 1. **Add to Cart Item Meta**

add_filter('woocommerce_add_cart_item_data', function ($cart_item_data, $product_id) {
    if (isset($_GET['probo_configure_meta'])) {
        $decoded = json_decode(base64_decode(sanitize_text_field($_GET['probo_configure_meta'])), true);
        $cart_item_data['probo_configure_meta'] = $decoded;
    }
    return $cart_item_data;
}, 10, 2);

#### 2. **Display in Cart (Optional)**

add_filter('woocommerce_get_item_data', function ($item_data, $cart_item) {
    if (!empty($cart_item['probo_configure_meta'])) {
        $meta = $cart_item['probo_configure_meta'];
        $item_data[] = [
            'name' => 'Delivery Time',
            'value' => $meta['selected_delivery'] . 'h'
        ];
        $item_data[] = [
            'name' => 'Probo Config ID',
            'value' => $meta['calculation_id']
        ];
    }
    return $item_data;
}, 10, 2);

#### 3. **Save to Order Item Meta**

add_action('woocommerce_add_order_item_meta', function ($item_id, $values, $cart_item_key) {
    if (!empty($values['probo_configure_meta'])) {
        wc_add_order_item_meta($item_id, 'probo_configure_meta', $values['probo_configure_meta']);
    }
}, 10, 3);

### βœ… Result:

– Cart stores everything under one field: `probo_configure_meta`
– Saved in database with order
– You can access it in backend like:

$meta = get_post_meta($order_item_id, 'probo_configure_meta', true);

Would you like this meta also shown in:
– Order confirmation emails?
– Admin order view?
– My Account β†’ Orders?

Let me know and I’ll help you extend it.

User
we have to use default woocommerce add-to-cart
Assistant
Perfect β€” if you're using the **default WooCommerce `add-to-cart` mechanism via URL** (i.e., `?add-to-cart=PRODUCT_ID`), you can still pass your `probo_configure_meta` as a custom query parameter β€” and capture it using WooCommerce’s built-in hooks.

### βœ… 1. πŸ”— **Frontend URL: Use WooCommerce Add-to-Cart + Custom Meta**

document.getElementById("addToCartBtn").addEventListener("click", () => {
    if (!selectedHours) return alert("Please select a delivery option");

    const payload = window.proboConfigurator.getRaw();
    const selectedPrice = payload.products[0].prices_total[selectedHours];

    const meta = {
        selected_options: payload.products[0].selected_options,
        calculation_id: payload.calculation_id,
        upload: payload.upload,
        uploaders: payload.uploaders,
        selected_delivery: selectedHours,
        price: selectedPrice,
    };

    const metaEncoded = btoa(JSON.stringify(meta)); // safely encode

    const productId = 1234; // your WooCommerce product ID
    const url = `/cart/?add-to-cart=${productId}&probo_configure_meta=${metaEncoded}`;
    window.location.href = url;
});

### βœ… 2. πŸ›’ **Capture Meta in `woocommerce_add_cart_item_data`**

add_filter('woocommerce_add_cart_item_data', function ($cart_item_data, $product_id) {
    if (isset($_GET['probo_configure_meta'])) {
        $meta = json_decode(base64_decode(sanitize_text_field($_GET['probo_configure_meta'])), true);
        if ($meta && is_array($meta)) {
            $cart_item_data['probo_configure_meta'] = $meta;
        }
    }
    return $cart_item_data;
}, 10, 2);

### βœ… 3. πŸ“¦ **Show Meta in Cart (optional)**

add_filter('woocommerce_get_item_data', function ($item_data, $cart_item) {
    if (!empty($cart_item['probo_configure_meta'])) {
        $meta = $cart_item['probo_configure_meta'];

        $item_data[] = [
            'name'  => 'Probo Config ID',
            'value' => $meta['calculation_id'] ?? '–'
        ];
        $item_data[] = [
            'name'  => 'Delivery Time',
            'value' => ($meta['selected_delivery'] ?? '?') . 'h'
        ];
    }
    return $item_data;
}, 10, 2);

### βœ… 4. βœ… **Store Meta in Order Items**

add_action('woocommerce_add_order_item_meta', function ($item_id, $values, $cart_item_key) {
    if (!empty($values['probo_configure_meta'])) {
        wc_add_order_item_meta($item_id, 'probo_configure_meta', $values['probo_configure_meta']);
    }
}, 10, 3);

### βœ… 5. πŸ“– **Read in Admin / Custom Email / Dashboard**

// Get in order loop
$meta = wc_get_order_item_meta($item_id, 'probo_configure_meta', true);

### βœ… Result:

– βœ… User uses default WooCommerce `?add-to-cart=1234` flow.
– βœ… `probo_configure_meta` is captured, saved to cart, and stored in the order.
– βœ… No AJAX or REST API needed.

Would you like help **showing this config meta in the WooCommerce admin order page** or **showing in customer emails** too?

User
I have custom product type
probo_configurator
so its shows when click add to cart
Sorry, this product cannot be purchased.
Assistant
Ah, got it β€” you're using a **custom product type** called `probo_configurator`, and when users click the default WooCommerce β€œAdd to Cart” URL (e.g., `?add-to-cart=1234`), it shows:

> **"Sorry, this product cannot be purchased."**

This happens because WooCommerce by default doesn’t allow custom product types to be **purchasable** unless explicitly marked so.

### βœ… Step-by-step Fix for β€œThis product cannot be purchased” for custom product types

### βœ… 1. Mark `probo_configurator` as **purchasable**

Add this to your `functions.php` or plugin:

add_filter('woocommerce_is_purchasable', function($purchasable, $product) {
    if ($product->get_type() === 'probo_configurator') {
        return true;
    }
    return $purchasable;
}, 10, 2);

### βœ… 2. Enable Add to Cart for Custom Type

WooCommerce also restricts `add-to-cart` functionality for custom types unless you tell it how to handle them.

Tell WooCommerce that your custom type is "simple" in behavior:

add_filter('woocommerce_product_type_selector', function($types) {
    $types['probo_configurator'] = __('Probo Configurator', 'your-textdomain');
    return $types;
});

// Set your custom product type to behave like a simple product
add_action('init', function () {
    class WC_Product_Probo_Configurator extends WC_Product_Simple {
        public function get_type() {
            return 'probo_configurator';
        }
    }
});

### βœ… 3. Make Sure It’s Set as Sold Individually (if needed)

You may also need to set some product properties in the admin:

– βœ… Make sure β€œ**Sold Individually**” is **unchecked** unless you want 1 per order.
– βœ… Inventory: If managing stock, ensure the stock status is **In Stock**.
– βœ… Price: Even if you're replacing the price dynamically, add a dummy price like `1.00` in the product editor (to pass WooCommerce checks).

### βœ… 4. If Price is Missing, WooCommerce Will Block Add to Cart

If you're not using a fixed price, force the product to be purchasable without a price:

add_filter('woocommerce_is_sold_individually', function($sold_individually, $product) {
    if ($product->get_type() === 'probo_configurator') {
        return false;
    }
    return $sold_individually;
}, 10, 2);

add_filter('woocommerce_product_get_price', function($price, $product) {
    if ($product->get_type() === 'probo_configurator') {
        return 1.00; // minimum dummy price
    }
    return $price;
}, 10, 2);

### βœ… 5. Optional: Hide Add to Cart from Shop Loop (but allow direct add)

add_filter('woocommerce_is_visible', function($visible, $product_id) {
    $product = wc_get_product($product_id);
    if ($product->get_type() === 'probo_configurator') {
        return false; // hides from shop page
    }
    return $visible;
}, 10, 2);

### βœ… Final Result:

– βœ… `probo_configurator` products are **purchasable**
– βœ… Can be added to cart via default `?add-to-cart=ID` flow
– βœ… Your custom meta (`probo_configure_meta`) is saved as cart item data and order item meta

Would you like:
– To **hide price** on product page but show total after configurator?
– To customize the **single product page template** for this custom product type?

User
ok alos add price that selected in radio
Assistant
Perfect β€” you want to:

βœ… Dynamically **set the product price in cart and checkout** based on the **selected delivery option price** (from the configurator radio).

You're already storing this in your `probo_configure_meta` under the `price` key, like:

price: {
  purchase_price: 5.865,
  purchase_price_incl_vat: 7.0967,
  ...
}

### βœ… 1. Store `probo_configure_meta` in cart (you're already doing this):

You're passing it like this:

add_filter('woocommerce_add_cart_item_data', function ($cart_item_data, $product_id) {
    if (isset($_GET['probo_configure_meta'])) {
        $meta = json_decode(base64_decode(sanitize_text_field($_GET['probo_configure_meta'])), true);
        if ($meta && is_array($meta)) {
            $cart_item_data['probo_configure_meta'] = $meta;
        }
    }
    return $cart_item_data;
}, 10, 2);

### βœ… 2. Override Price in Cart Using Meta (Dynamic Price Per Cart Item)

Add this hook to modify the price dynamically from your meta:

add_filter('woocommerce_before_calculate_totals', function($cart) {
    if (is_admin() && !defined('DOING_AJAX')) return;

    foreach ($cart->get_cart() as $cart_item) {
        if (
            isset($cart_item['probo_configure_meta']['price']['purchase_price_incl_vat'])
            && is_numeric($cart_item['probo_configure_meta']['price']['purchase_price_incl_vat'])
        ) {
            $custom_price = (float) $cart_item['probo_configure_meta']['price']['purchase_price_incl_vat'];
            $cart_item['data']->set_price($custom_price);
        }
    }
}, 20, 1);

βœ… This will:
– Set the **cart item price** based on `purchase_price_incl_vat`
– Reflect in **cart total**, **checkout**, **emails**, and **orders**

### βœ… Optional: Show it in Cart Line Item

(Already shown from earlier step)

add_filter('woocommerce_get_item_data', function ($item_data, $cart_item) {
    if (!empty($cart_item['probo_configure_meta'])) {
        $meta = $cart_item['probo_configure_meta'];

        $item_data[] = [
            'name' => 'Delivery Time',
            'value' => ($meta['selected_delivery'] ?? '?') . 'h'
        ];
        $item_data[] = [
            'name' => 'Probo Price',
            'value' => '€' . number_format($meta['price']['purchase_price_incl_vat'], 2)
        ];
    }
    return $item_data;
}, 10, 2);

### βœ… Final Result:

– πŸ’Έ Customer sees correct delivery-based price
– 🧾 Price reflects in cart/checkout/order
– πŸ“¦ You have all Probo meta stored in order item

Let me know if you'd like to:
– Use `purchase_price` instead (excl. VAT)?
– Add a fallback price if missing?
– Format currency symbol dynamically?

I'm happy to help further.

User
we have amount parameter which have total quantity so set that amount parameter as a quantity
Assistant
Great β€” since the **Probo configurator includes an `amount` parameter** (e.g., number of items ordered), you want to use that as the **WooCommerce cart quantity** when the user clicks "Add to Cart".

### βœ… Goal:
– Use default `?add-to-cart=PRODUCT_ID` URL
– Set `quantity = amount` from configurator payload
– Still pass `probo_configure_meta` as usual

### βœ… 1. πŸ”— Frontend: Append `quantity` to URL based on `amount`

Update your existing redirect like this:

document.getElementById("addToCartBtn").addEventListener("click", () => {
    if (!selectedHours) return alert("Please select a delivery option");

    const payload = window.proboConfigurator.getRaw();
    const selectedPrice = payload.products[0].prices_total[selectedHours];
    const amount = payload.products[0].amount || 1;

    const meta = {
        selected_options: payload.products[0].selected_options,
        calculation_id: payload.calculation_id,
        upload: payload.upload,
        uploaders: payload.uploaders,
        selected_delivery: selectedHours,
        price: selectedPrice
    };

    const metaEncoded = btoa(JSON.stringify(meta));
    const productId = 1234;

    const url = `/cart/?add-to-cart=${productId}&quantity=${amount}&probo_configure_meta=${metaEncoded}`;
    window.location.href = url;
});

### βœ… 2. βœ… WooCommerce: Accept `quantity` Normally

You **don’t need to write extra PHP code** to handle `quantity` if it's passed via `?quantity=N` β€” WooCommerce handles it natively.

But just in case you need to force it from the `amount` inside meta (for example, if using AJAX or other workflows), you could add:

add_filter('woocommerce_add_cart_item_data', function ($cart_item_data, $product_id) {
    if (isset($_GET['probo_configure_meta'])) {
        $meta = json_decode(base64_decode(sanitize_text_field($_GET['probo_configure_meta'])), true);
        if ($meta && is_array($meta)) {
            $cart_item_data['probo_configure_meta'] = $meta;

            // Optional fallback: force quantity from meta
            if (!empty($meta['amount'])) {
                $cart_item_data['probo_quantity'] = (int) $meta['amount'];
            }
        }
    }
    return $cart_item_data;
}, 10, 2);

Then you could hook `woocommerce_add_to_cart_quantity` to override quantity β€” but **passing it via the URL is cleaner and native**.

### βœ… Summary

| What you want | Solution |
|——————————-|————————————|
| Use `amount` from configurator | Set `quantity=${amount}` in URL |
| Preserve custom meta | Pass via `probo_configure_meta` |
| Let WooCommerce calculate total price = price Γ— quantity | βœ… Already handled via `set_price()` and `quantity` |

Let me know if:
– You want to **override quantity validation** (e.g. max limits)
– You want to **disable editing quantity in cart** after this
– Or auto-hide the product from shop pages (since it’s added via config)

User
also include on item price and then show based on multiply by amount , and seperate surcharge additonal price in subtotal
Assistant
Perfect β€” you want the WooCommerce cart and order to reflect:

### 🧾 Desired Cart Breakdown:

1. **Unit Price** = `purchase_base_price`
2. **Quantity** = `amount` (from configurator)
3. **Line Subtotal (unit price Γ— quantity)** = shown as base product cost
4. **Surcharge (rush fee)** = shown as **separate cart fee**, not included in base subtotal

### βœ… Overview of Implementation:

| Data | Source | Handled In |
|————————–|—————————–|———————————-|
| Quantity (`amount`) | Probo Configurator payload | Set in `?quantity=` param |
| Unit price | `purchase_base_price` | Override in `set_price()` hook |
| Rush fee | `purchase_rush_surcharge` | Add via `woocommerce_cart_calculate_fees` |
| Custom meta | Whole config | Stored in `probo_configure_meta`|

### βœ… Step-by-Step

### 1. βœ… Update `set_price()` logic to use only `purchase_base_price`

add_action('woocommerce_before_calculate_totals', function ($cart) {
    if (is_admin() && !defined('DOING_AJAX')) return;

    foreach ($cart->get_cart() as $cart_item) {
        if (
            isset($cart_item['probo_configure_meta']['price']['purchase_base_price'])
            && is_numeric($cart_item['probo_configure_meta']['price']['purchase_base_price'])
        ) {
            $unit_price = (float) $cart_item['probo_configure_meta']['price']['purchase_base_price'];
            $cart_item['data']->set_price($unit_price);
        }
    }
}, 20, 1);

This sets **unit price** to the **base product price only**, not including surcharge.

### 2. βœ… Add Rush Fee as Cart-Level Fee

add_action('woocommerce_cart_calculate_fees', function($cart) {
    if (is_admin() && !defined('DOING_AJAX')) return;

    foreach ($cart->get_cart() as $item) {
        $meta = $item['probo_configure_meta'] ?? null;

        if ($meta && isset($meta['price']['purchase_rush_surcharge'])) {
            $surcharge = (float) $meta['price']['purchase_rush_surcharge'];
            $qty = isset($item['quantity']) ? (int) $item['quantity'] : 1;
            $total_surcharge = $surcharge * $qty;

            if ($total_surcharge > 0) {
                $label = 'Rush Fee for ' . ($meta['selected_delivery'] ?? 'selected') . 'h';
                $cart->add_fee($label, $total_surcharge, true); // true = taxable
            }
        }
    }
});

This adds the **rush surcharge** as a clearly labeled **fee**, and multiplies it by quantity.

### 3. βœ… Optional: Show this nicely in Cart & Checkout

You're already showing metadata. For even clearer output:

add_filter('woocommerce_get_item_data', function ($item_data, $cart_item) {
    if (!empty($cart_item['probo_configure_meta'])) {
        $meta = $cart_item['probo_configure_meta'];

        $item_data[] = [
            'name' => 'Quantity from Probo',
            'value' => $cart_item['quantity']
        ];

        $item_data[] = [
            'name' => 'Unit Price',
            'value' => '€' . number_format($meta['price']['purchase_base_price'], 2)
        ];

        $item_data[] = [
            'name' => 'Rush Surcharge (per unit)',
            'value' => '€' . number_format($meta['price']['purchase_rush_surcharge'], 2)
        ];

        $item_data[] = [
            'name' => 'Delivery Time',
            'value' => ($meta['selected_delivery'] ?? '?') . 'h'
        ];
    }
    return $item_data;
}, 10, 2);

### βœ… Final Result:

| Component | Value |
|—————|——————————————|
| Unit Price | From `purchase_base_price` |
| Qty | From `amount` in Probo Configurator |
| Subtotal | Unit Price Γ— Qty |
| Rush Fee | Surcharge Γ— Qty (as WooCommerce Fee) |
| Order Total | Subtotal + Surcharge |

Would you like:
– To **apply VAT separately** on rush fee?
– To **mark rush fee as non-taxable**?
– Or include rush fee as line item instead of cart fee?

Let me know how you'd like it structured.

User
also save hours of delivery like 24h etc
Assistant
Great! You want to **save the selected delivery time (e.g. "24h", "96h")** into the WooCommerce **order item meta**, so it's stored along with the item in the order.

You're already storing this in your `probo_configure_meta['selected_delivery']` field.

### βœ… Here's how to save the delivery hours cleanly into the order:

#### πŸ” Hook into `woocommerce_add_order_item_meta`

You’re already saving the full `probo_configure_meta` β€” now add a separate meta key like `probo_delivery_hours` for clarity:

add_action('woocommerce_add_order_item_meta', function ($item_id, $values, $cart_item_key) {
    if (!empty($values['probo_configure_meta'])) {
        $meta = $values['probo_configure_meta'];

        // Save full config
        wc_add_order_item_meta($item_id, 'probo_configure_meta', $meta);

        // Save delivery hours (like "24h")
        if (!empty($meta['selected_delivery'])) {
            wc_add_order_item_meta($item_id, 'probo_delivery_hours', $meta['selected_delivery'] . 'h');
        }
    }
}, 10, 3);

### βœ… Result in WooCommerce:

| Meta Key | Value |
|———————–|———|
| `probo_configure_meta` | full config array (JSON-encoded) |
| `probo_delivery_hours`| `24h`, `48h`, `96h`, etc. |

### πŸ“¦ Optional: Show on Order Admin Page

To display the delivery time in the **WooCommerce admin order page**, you can do this:

add_action('woocommerce_after_order_itemmeta', function($item_id, $item, $product){
    $delivery = wc_get_order_item_meta($item_id, 'probo_delivery_hours');
    if ($delivery) {
        echo '<p><strong>Probo Delivery:</strong> ' . esc_html($delivery) . '</p>';
    }
}, 10, 3);

Let me know if you’d also like to:
– Show it on **customer email**
– Show it in **My Account β†’ Orders**
– Or display it as a **tooltip or tag** in the admin interface for fast scanning.

User
How to save cart custom mets
Assistant
To **save custom meta from cart items into the WooCommerce order**, you need to:

### βœ… Summary Steps to Save Custom Cart Meta

| Step | What You Do | WooCommerce Hook |
|——|————————————-|——————————————-|
| 1 | Add meta to cart item | `woocommerce_add_cart_item_data` |
| 2 | Display in cart (optional) | `woocommerce_get_item_data` |
| 3 | Save meta to order item | `woocommerce_add_order_item_meta` (WC < 4.4) **OR**`woocommerce_checkout_create_order_line_item` (WC β‰₯ 4.4) |

## βœ… Full Working Example

Let’s say you're saving this custom meta in the cart:

add_filter('woocommerce_add_cart_item_data', function($cart_item_data, $product_id) {
    if (isset($_GET['probo_configure_meta'])) {
        $decoded = json_decode(base64_decode(sanitize_text_field($_GET['probo_configure_meta'])), true);
        if ($decoded && is_array($decoded)) {
            $cart_item_data['probo_configure_meta'] = $decoded;

            // Optional: add direct shortcut fields for convenience
            if (!empty($decoded['selected_delivery'])) {
                $cart_item_data['probo_delivery_hours'] = $decoded['selected_delivery'] . 'h';
            }
        }
    }
    return $cart_item_data;
}, 10, 2);

### βœ… Display Custom Meta in Cart (Optional)

add_filter('woocommerce_get_item_data', function($item_data, $cart_item) {
    if (!empty($cart_item['probo_delivery_hours'])) {
        $item_data[] = [
            'name'  => 'Delivery Time',
            'value' => $cart_item['probo_delivery_hours']
        ];
    }
    return $item_data;
}, 10, 2);

### βœ… Save to Order Item (WC 4.4+ Safe Way)

add_action('woocommerce_checkout_create_order_line_item', function($item, $cart_item_key, $values, $order) {
    if (!empty($values['probo_configure_meta'])) {
        $meta = $values['probo_configure_meta'];

        // Save full config
        $item->add_meta_data('probo_configure_meta', $meta);

        // Save delivery hours as separate meta
        if (!empty($meta['selected_delivery'])) {
            $item->add_meta_data('probo_delivery_hours', $meta['selected_delivery'] . 'h');
        }
    }
}, 10, 4);

βœ… Works safely in all WooCommerce versions.

### πŸ”Ž How to View Saved Meta

In order item line meta:

$meta = wc_get_order_item_meta($item_id, 'probo_configure_meta');
$delivery = wc_get_order_item_meta($item_id, 'probo_delivery_hours');

### 🎯 Bonus: Show in Admin Interface

add_action('woocommerce_after_order_itemmeta', function($item_id, $item, $product){
    $delivery = wc_get_order_item_meta($item_id, 'probo_delivery_hours');
    if ($delivery) {
        echo '<p><strong>Probo Delivery:</strong> ' . esc_html($delivery) . '</p>';
    }
}, 10, 3);

Let me know if you'd like to:
– Save JSON pretty format
– Access from REST API
– Show in customer emails or My Account > Orders

I’ll help with that next!

User
Probo Selected Options: Array ( [0] => Array ( [type_code] => width [name] => Width [label] => [description] => [value] => 25 [code] => width [default_value] => [min_value] => 0 [max_value] => 99999 [step_size] => [scale] => 1 [reversible] => 1 [parent_code] => size [parent_name] => Size [unit_code] => cm [images] => Array ( ) ) [1] => Array ( [type_code] => height [name] => Height [label] => [description] => [value] => 15 [code] => height [default_value] => [min_value] => 0 [max_value] => 99999 [step_size] => [scale] => 1 [reversible] => 1 [parent_code] => size [parent_name] => Size [unit_code] => cm [images] => Array ( ) ) [2] => Array ( [type_code] => amount [name] => Amount [label] => [description] => [value] => 3 [code] => amount [default_value] => [min_value] => [max_value] => [step_size] => [scale] => [reversible] => [parent_code] => amount [parent_name] => Amount [unit_code] => pc [images] => Array ( ) ) [3] => Array ( [type_code] => radio [name] => Single-sided [label] => [description] => [value] => [code] => single-sided [default_value] => [min_value] => [max_value] => [step_size] => [scale] => [reversible] => [parent_code] => print-side [parent_name] => Print [unit_code] => [images] => Array ( [0] => Array ( [language] => all [url] => https://cdn.print-uploader.com/201803/122/1157641fbfa8a2d9.svg ) ) ) [4] => Array ( [type_code] => radio [name] => Banner 510 [label] => Most popular [description] => PVC banner suited for both indoor and outdoor applications. [value] => [code] => banner-510 [default_value] => [min_value] => [max_value] => [step_size] => [scale] => [reversible] => [parent_code] => material [parent_name] => Material [unit_code] => [images] => Array ( [0] => Array ( [language] => all [url] => https://cdn.print-uploader.com/202408/8788/e865c5b7be52717d.jpg ) ) ) [5] => Array ( [type_code] => radio [name] => Use same finish for all edges [label] => [description] => All edges of the banner receive the same finish. [value] => [code] => finishing-all-sides [default_value] => [min_value] => [max_value] => [step_size] => [scale] => [reversible] => [parent_code] => finishing-per-side-all-sides [parent_name] => Finishing for all sides / per side [unit_code] => [images] => Array ( [0] => Array ( [language] => all [url] => https://cdn.print-uploader.com/201803/72/99e39932389f5591.svg ) ) ) [6] => Array ( [type_code] => radio [name] => Cut [label] => [description] => The material is cut to the desired size. [value] => [code] => cut [default_value] => [min_value] => [max_value] => [step_size] => [scale] => [reversible] => [parent_code] => finishing [parent_name] => Finish [unit_code] => [images] => Array ( [0] => Array ( [language] => all [url] => https://cdn.print-uploader.com/201803/74/6f7a271da0ab0f87.jpg ) ) )
show above array data in php line by line
Assistant
To display the `selected_options` array (from Probo) **line by line** in PHP, you can loop through it and display each property cleanly.

Here’s a full example that handles your structure:

### βœ… PHP Code to Display Each Selected Option:

$selected_options = get_post_meta($order_item_id, 'probo_configure_meta', true)['selected_options'] ?? [];

if (!empty($selected_options)) {
    echo '<h4>Probo Selected Options:</h4>';
    echo '<ul>';
    foreach ($selected_options as $option) {
        echo '<li>';
        echo '<strong>' . esc_html($option['name'] ?? '-') . '</strong>';

        if (!empty($option['value'])) {
            echo ': ' . esc_html($option['value']);
        } elseif (!empty($option['description'])) {
            echo ' – ' . esc_html($option['description']);
        }

        // If there's an image
        if (!empty($option['images'][0]['url'])) {
            echo '<br><img src="' . esc_url($option['images'][0]['url']) . '" alt="" width="60">';
        }

        // Extra detail like parent
        if (!empty($option['parent_name'])) {
            echo '<br><small>Group: ' . esc_html($option['parent_name']) . '</small>';
        }

        echo '</li>';
    }
    echo '</ul>';
}

### 🧾 Example Output (in admin/email/frontend):

βœ… Probo Selected Options:

β€’ Width: 25
β€’ Height: 15
β€’ Amount: 3
β€’ Single-sided – [image]
β€’ Banner 510 – PVC banner suited for both indoor and outdoor applications. [image]
β€’ Use same finish for all edges – All edges of the banner receive the same finish. [image]
β€’ Cut – The material is cut to the desired size. [image]

### βœ… Optional Styling Tips (for email or frontend):

Wrap inside a div for better control:

echo '<div class="probo-options" style="font-size:14px;line-height:1.6;">';
...
echo '</div>';

Let me know if you want to:
– Format this into a table instead of a list
– Output only certain option types (e.g., only `radio`)
– Show grouped by parent category (like "Size", "Material", etc.)

Leave a Comment

Your email address will not be published. Required fields are marked *