DNN · Cart bridge
A diferencia de WooCommerce (que tiene eventos jQuery estandarizados), los módulos de e-commerce de DNN no comparten una API común. El cart bridge en DNN es manual: tu código emite window.dispatchEvent(new CustomEvent('neuroon:cart-update')) en los puntos donde tu módulo modifica el carrito.
Contrato del puente
El widget escucha neuroon:cart-update y, al recibirlo, llama a config.cart.onGetCart() para releer el carrito desde tu host. Ignora event.detail — sólo dispara sin payload (verificado en widget/src/context/CartContext.tsx:165-191).
Esto significa que tu trabajo en DNN es:
- Implementar
cart.onGetCart()en JS para que devuelva elCartStatedesde tu módulo (Razor lo renderiza inline, o un endpoint AJAX lo expone). - Emitir
neuroon:cart-updatecada vez que tu módulo muta el carrito (add, remove, update, clear).
Nada de
event.detail: aunque dispares con{ detail: cartState }, el listener actual lo descarta. El estado siempre llega víaonGetCart().
Schema del CartState
Lo que tu cart.onGetCart() debe devolver:
type CartItem = {
key: string; // identificador único de la línea (ID en tu tabla o GUID)
id: string; // tu externalId / productId (lo que mandas en sync)
name: string;
price: string; // formateado por el host: "12,50 €"
quantity: number;
image?: string;
url?: string;
variant?: string; // "Talla: 42, Color: Negro"
externalId?: string; // host-platform id si difiere del id Neuroon
maxQuantity?: number;
};
type CartState = {
items: CartItem[];
totalItems: number;
subtotal: string; // formateado por el host
total: string; // formateado por el host
currency: string; // ISO 4217: "EUR", "USD"
shipping?: CartShippingInfo; // ver Widget → Cart integration para el schema completo
};
Importante:
price,subtotal,totalson strings ya formateados por el host (con currency, separadores locales). El widget no recalcula. Esto evita disputas de redondeo entre regiones.
Razor partial reutilizable
Crea un partial que cualquier vista de carrito pueda renderizar:
@model CartPayload
<script>
(function() {
var detail = @Html.Raw(JsonConvert.SerializeObject(Model));
window.dispatchEvent(new CustomEvent('neuroon:cart-update', { detail: detail }));
})();
</script>
Y desde tu vista de "Add to cart":
@{
var payload = new CartPayload {
Items = currentCart.Lines.Select(l => new CartItem {
ProductId = l.Sku,
Name = l.ProductName,
Price = l.UnitPrice,
Quantity = l.Quantity,
ImageUrl = l.ImageUrl
}).ToList(),
Total = currentCart.Total,
Currency = currentCart.Currency
};
}
@Html.Partial("_NeuroonCartUpdate", payload)
Hook por módulo (DNN Commerce, NB_Store, Hotcakes)
Cada módulo expone su propio mecanismo. Estas son las superficies habituales; verifica con la versión exacta de tu módulo (los nombres pueden variar entre majors).
NB_Store / NBrightBuy
NB_Store usa eventos AJAX en el frontend: nbsAddItem, nbsRemoveItem, nbsUpdateQty. Engánchalos desde el Skin (Skin.ascx) o desde un módulo propio cargado en todas las páginas:
<script>
(function() {
function debounce(fn, ms) {
var t; return function() { clearTimeout(t); t = setTimeout(fn, ms); };
}
var ping = debounce(function() {
window.dispatchEvent(new CustomEvent('neuroon:cart-update'));
}, 250);
document.addEventListener('nbsAddItem', ping);
document.addEventListener('nbsRemoveItem', ping);
document.addEventListener('nbsUpdateQty', ping);
})();
</script>
Server-side (al renderizar la página de carrito o cualquier vista que muestre líneas):
// En NBrightBuy.NBrightBuyUtils.AddCartItem o tu wrapper, tras persistir:
var snapshot = MapCartToNeuroonState(NBrightBuyUtils.GetCurrentCart(portalId));
ViewBag.NeuroonCart = snapshot; // CartState canónico
return PartialView("_NeuroonCartUpdate", snapshot);
DNN Commerce / Catalook
DNN Commerce usa el BasketController (MVC) con acciones Add, Remove, Update. Patrón:
public class BasketController : Controller
{
public ActionResult Add(int productId, int qty)
{
// ... lógica original ...
var payload = MapBasketToNeuroonState(this.CurrentBasket);
return PartialView("_NeuroonCartUpdate", payload);
}
}
_NeuroonCartUpdate.cshtml (modo push):
@model NeuroonCartState
<script>
window.dispatchEvent(new CustomEvent('neuroon:cart-update', {
detail: @Html.Raw(JsonConvert.SerializeObject(Model))
}));
</script>
Hotcakes Commerce
Hotcakes expone CartController::AddToCart y dispara el evento global Hotcakes.Modules.Cart.OrderAddedToCart. Patrón modo ping (recomendado):
public override void OnOrderItemAdded(OrderItemEventArgs e)
{
base.OnOrderItemAdded(e);
// Render Razor partial that emits ping:
// <script>window.dispatchEvent(new CustomEvent('neuroon:cart-update'));</script>
}
Si tu versión de Hotcakes no expone
OnOrderItemAdded, suscríbete víaEventLogo haz polling client-side encart.ascx.
Módulo propio o módulo no listado
Si tu módulo dispara un evento jQuery (por ejemplo cart:updated), engánchalo de forma global desde el Skin:
<script>
(function() {
var debounceTimer = null;
document.addEventListener('cart:updated', function(e) {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(function() {
// Modo ping (recommended): el widget llama a tu API de carrito
window.dispatchEvent(new CustomEvent('neuroon:cart-update'));
}, 250);
});
})();
</script>
El debounce de 250 ms replica lo que hace el plugin WordPress (ver plugins/wordpress/cart-bridge).
Si tu módulo no está aquí: expón cualquier hook server-side (
OnAdd/OnRemove/OnComplete) o undispatchEventjQuery, y disparaneuroon:cart-updatedesde el handler. El widget no necesita saber qué módulo usas.
SSR en Razor (.cshtml)
Si tu carrito se renderiza server-side y el carrito ya viene poblado en el modelo, emite el CustomEvent directamente desde el HTML:
<script>
window.dispatchEvent(new CustomEvent('neuroon:cart-update', {
detail: {
items: [
@foreach (var line in Model.Cart.Lines)
{
<text>{ productId: '@line.Sku', name: '@line.Name.Replace("'", "\\'")', price: @line.Price.ToString(System.Globalization.CultureInfo.InvariantCulture), quantity: @line.Quantity },</text>
}
],
total: @Model.Cart.Total.ToString(System.Globalization.CultureInfo.InvariantCulture),
currency: '@Model.Cart.Currency'
}
}));
</script>
Atención al escaping de comillas en name. Para casos seguros, usa JsonConvert.SerializeObject en lugar de inline string interpolation.
Validar que el bridge funciona
- Abre la consola del navegador en una página con producto.
- Suscríbete:
window.addEventListener('neuroon:cart-update', e => console.log(e.detail));. - Añade un producto al carrito desde tu módulo. Deberías ver el log con el payload completo.
Cuándo NO emitir el payload completo
El widget también acepta el evento sin detail. En ese caso, el widget pide al servidor el estado actualizado del carrito. Esto es útil cuando el carrito es complejo (ofertas, descuentos, tax) y prefieres no exponer ese cálculo en JavaScript:
window.dispatchEvent(new CustomEvent('neuroon:cart-update'));
Próximos pasos
- Tracking de conversiones — server-side al confirmar pedido.
- Recipe · Custom cart bridge — schema y ejemplos genéricos.