Articles on: Installation

OPTIS Product Options - Developer Integration Guide

Overview


The OPTIS Product Options app exposes a global JavaScript object BSS_PO.currentSelectedOptions that contains real-time information about customer-selected product options, including prices, validation status, and detailed breakdowns. This guide explains how third-party developers can integrate with and read data from our app.


Data Structure


BSS_PO.currentSelectedOptions

The main data object contains the following structure:


BSS_PO.currentSelectedOptions = {

byOptionId: {}, // Options indexed by option ID

byLabel: {}, // Options indexed by label name

byOptionSetId: {}, // Options grouped by option set ID

allValues: [], // Flat array of all selected options

totalPriceAddOn: 0, // Total price add-on from all options

totalVariantPriceAddOn: 0, // Total variant-based price add-on

variantPriceAddOn: [], // Array of variant price add-ons

priceBreakdown: {}, // Detailed price breakdown by option

validation: { // Validation status for required options

isValid: true,

requiredOptions: [],

missingOptions: [],

validOptions: [],

errors: []

},

timestamp: "2023-..." // Last update timestamp

}


API Methods

We provide a comprehensive API through BSS_PO.getSelectedOptions for easy data access:

Basic Data Access

Get All Selected Options


// Get all selected options with price information

const allOptions = BSS_PO.getSelectedOptions.all();

console.log(allOptions);

// Returns: [{ label: "Size", value: "Large", price: 5.00, variantPrice: 0, id: 123, index: 0 }, ...]


Get Options by Label


// Get all values for a specific option (useful for multi-select options)

const colorOptions = BSS_PO.getSelectedOptions.byLabel("Color");

console.log(colorOptions);

// Returns: [{ value: "Red", price: 2.00, variantPrice: 0, id: 124, index: 0 }]



// Get single value for radio/dropdown options

const selectedSize = BSS_PO.getSelectedOptions.getValue("Size");

console.log(selectedSize); // "Large"



// Get multiple values for checkbox options

const selectedFeatures = BSS_PO.getSelectedOptions.getValues("Features");

console.log(selectedFeatures); // ["Waterproof", "Extended Warranty"]


Check Option Selection


// Check if a specific option value is selected

const isRedSelected = BSS_PO.getSelectedOptions.isSelected("Color", "Red");

console.log(isRedSelected); // true


Price Information

Get Price Data


// Get total price add-on from all options
const totalPrice = BSS_PO.getSelectedOptions.getTotalPriceAddOn();
console.log(`Total extra: $${totalPrice.toFixed(2)}`);

// Get total variant price add-on
const variantPrice = BSS_PO.getSelectedOptions.getTotalVariantPriceAddOn();
console.log(`Variant price: $${variantPrice.toFixed(2)}`);

// Get price for specific option
const sizePrice = BSS_PO.getSelectedOptions.getPriceByLabel("Size");
console.log(`Size adds: $${sizePrice.toFixed(2)}`);

// Get price for specific option value
const redColorPrice = BSS_PO.getSelectedOptions.getPriceByValue("Color", "Red");
console.log(`Red color adds: $${redColorPrice.toFixed(2)}`);


Get Comprehensive Price Summary


const priceSummary = BSS_PO.getSelectedOptions.getPriceSummary();
console.log(priceSummary);
/* Returns:
{
total: 15.00,
totalVariant: 5.00,
grandTotal: 20.00,
formattedTotal: "15.00",
formattedVariantTotal: "5.00",
formattedGrandTotal: "20.00",
details: [
{
label: "Size",
price: 10.00,
variantPrice: 0,
formattedPrice: "10.00",
values: [{ value: "Large", price: 10.00, variantPrice: 0 }]
}
]
}
*/


Validation

Check Validation Status


// Check if all required options are selected
const isValid = BSS_PO.getSelectedOptions.isValid();
console.log(`Form valid: ${isValid}`);

// Get validation summary
const validation = BSS_PO.getSelectedOptions.getValidationSummary();
console.log(validation);
/* Returns:
{
isValid: false,
totalRequired: 3,
totalMissing: 1,
totalValid: 2,
message: "1 required option(s) need to be selected",
missingLabels: ["Color"],
errors: ["Color is required and must be selected."],
details: { required: [...], missing: [...], valid: [...] }
}
*/

// Check specific option requirements
const isColorRequired = BSS_PO.getSelectedOptions.isRequired("Color");
const isColorMissing = BSS_PO.getSelectedOptions.isMissing("Color");


Get Validation Errors


const errors = BSS_PO.getSelectedOptions.getValidationErrors();
errors.forEach(error => console.error(error));


Formatted Output


// Get formatted string of all selections with prices
const formatted = BSS_PO.getSelectedOptions.getFormattedSelectionsWithPrices();
console.log(formatted);
// "Size: Large (+10.00) | Color: Red (+2.00) | Features: Waterproof, Extended Warranty (+5.00)"


Event System


Listen for Option Changes


// Listen for any option selection changes
document.addEventListener('BSSOptionSelectionChanged', (event) => {
const { selectedOptions, totalPriceAddOn, priceBreakdown } = event.detail;

console.log('Options changed:', selectedOptions);
console.log('New total price:', totalPriceAddOn);
console.log('Price breakdown:', priceBreakdown);

// Update your UI here
updateCartSummary(totalPriceAddOn);
});

// Listen for validation failures
document.addEventListener('BSSOptionValidationFailed', (event) => {
const { validation, missingOptions, errors } = event.detail;

console.log('Validation failed:', validation);
console.log('Missing options:', missingOptions);

// Show validation errors to user
displayValidationErrors(errors);
});

// Listen for price changes (for price display integration)
document.addEventListener('BSSchangeProductPrice', (event) => {
console.log('Product price changed, recalculate total');

// Update your price display
const newTotal = BSS_PO.getSelectedOptions.getTotalPriceAddOn();
updatePriceDisplay(newTotal);
});


Cart Page Integration


Detecting Option Changes on Cart Page


On the cart page, customers can edit their product options. To detect these changes and update your integration accordingly, listen for the cart_changed event:


// Listen for cart changes (additions, removals, option edits)
window.addEventListener('cart_changed', (event) => {
const { newCart, oldCart, changedCartItems } = event.detail;

console.log('Cart changed:', {
newCart,
oldCart,
changedItems: changedCartItems
});

// Fetch updated cart data to get latest option information
fetchUpdatedCartData();
});

// Function to fetch and process updated cart data
async function fetchUpdatedCartData() {
try {
const response = await fetch('/cart.js');
const cartData = await response.json();

// Process cart items to extract option information
cartData.items.forEach(item => {
// Check for OPTIS option properties
const optionProperties = {};

// Extract option data from cart item properties
Object.keys(item.properties || {}).forEach(key => {
// OPTIS stores option data in properties with specific patterns
if (key.includes('_bssPrice') || key.includes('_bssVariants') ||
key.includes('_bssCustomAttributes')) {
try {
optionProperties[key] = JSON.parse(item.properties[key]);
} catch (e) {
optionProperties[key] = item.properties[key];
}
}
});

console.log(`Item ${item.key} options:`, optionProperties);

// Update your UI based on the new option data
updateCartItemDisplay(item.key, optionProperties);
});

} catch (error) {
console.error('Error fetching cart data:', error);
}
}

// Example: Update cart item display
function updateCartItemDisplay(itemKey, optionData) {
const cartItemElement = document.querySelector(`[data-cart-item="${itemKey}"]`);
if (cartItemElement && optionData._bssPrice) {
const extraPrice = optionData._bssPrice.extra || 0;

// Update price display
const priceElement = cartItemElement.querySelector('.item-price');
if (priceElement) {
// Add your price update logic here
console.log(`Item ${itemKey} has extra price: $${extraPrice}`);
}
}
}


Cart Data Structure


When customers edit options on the cart page, the updated information is stored in the cart item properties:


// Example cart item with OPTIS option data
{
"key": "39772975595588:abc123",
"id": 39772975595588,
"properties": {
"Size": "Large",
"Color": "Red (+$5.00)",
"_bssPrice": "{\"extra\":10.00}",
"_bssVariants": "{\"Size\":{\"id\":\"39772975595588\",\"quantity\":1}}",
"_bssCustomAttributes": "{\"Size\":\"Large\",\"Color\":\"Red\"}"
},
"quantity": 1,
"variant_id": 39772975595588
}


Real-time Cart Monitoring


For continuous monitoring of cart changes:


// Set up comprehensive cart monitoring
class CartMonitor {
constructor() {
this.previousCart = null;
this.init();
}

init() {
// Listen for cart changes
window.addEventListener('cart_changed', (event) => {
this.handleCartChange(event.detail);
});

// Initial cart load
this.loadInitialCart();
}

async loadInitialCart() {
const cart = await this.fetchCart();
this.previousCart = cart;
this.processCartOptions(cart);
}

async fetchCart() {
const response = await fetch('/cart.js');
return await response.json();
}

handleCartChange({ newCart, oldCart, changedCartItems }) {
console.log('Cart change detected:', {
itemsAdded: changedCartItems,
totalItems: newCart.items.length
});

// Process option changes
this.processCartOptions(newCart);

// Compare with previous state
this.compareCartStates(oldCart, newCart);

this.previousCart = newCart;
}

processCartOptions(cart) {
cart.items.forEach(item => {
const options = this.extractOptionsFromItem(item);
if (Object.keys(options).length > 0) {
console.log(`Item ${item.variant_id} options:`, options);

// Trigger your custom processing
this.onOptionsUpdated(item, options);
}
});
}

extractOptionsFromItem(item) {
const options = {};

Object.keys(item.properties || {}).forEach(key => {
// Skip internal OPTIS properties, focus on visible options
if (!key.startsWith('_bss') && item.properties[key]) {
options[key] = item.properties[key];
}
});

return options;
}

compareCartStates(oldCart, newCart) {
// Identify specific changes for detailed tracking
const changes = {
added: [],
removed: [],
modified: []
};

// Your comparison logic here
console.log('Cart state changes:', changes);
}

onOptionsUpdated(item, options) {
// Override this method for custom handling
console.log('Options updated for item:', item.key, options);
}
}

// Initialize cart monitoring
const cartMonitor = new CartMonitor();


Troubleshooting

Common Issues


  1. BSS_PO is undefined: The app hasn't loaded yet. Make sure OPTIS is installed and enabled in the theme app extension.
  2. Data seems outdated: The data updates in real-time. Make sure you're listening to the events.
  3. Validation not working: Check that you're using the latest validation methods and listening to validation events.
  4. Price calculations incorrect: Ensure you're using the appropriate price methods (getPriceByLabel vs getVariantPriceByLabel).

Updated on: 09/06/2025

Was this article helpful?

Share your feedback

Cancel

Thank you!