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
- BSS_PO is undefined: The app hasn't loaded yet. Make sure OPTIS is installed and enabled in the theme app extension.
- Data seems outdated: The data updates in real-time. Make sure you're listening to the events.
- Validation not working: Check that you're using the latest validation methods and listening to validation events.
- Price calculations incorrect: Ensure you're using the appropriate price methods (
getPriceByLabel
vsgetVariantPriceByLabel
).
Updated on: 09/06/2025
Thank you!