Saltar al contenido
pcreative Commerce

Escribir un plugin

Un plugin añade cosas a la tienda sin tocar su código: escuchar lo que pasa, cambiar lo que se calcula, meter una pantalla en el panel.

Un plugin añade cosas a la tienda sin tocar su código: escuchar lo que pasa, cambiar lo que se calcula, meter una pantalla en el panel.

Empezar

npx pcc-plugin nuevo mi-plugin
cd mi-plugin && npm install && npm run dev

npm run dev carga el plugin y lo vuelve a cargar cada vez que guardas un fichero. Lo carga fuera de la tienda, en un bus propio: sirve para ver que arranca, que sus enganches existen y que declara los permisos que piden. Los eventos de verdad le llegan cuando lo instalas.

Los tres comandos

ComandoQué hace
pcc-plugin nuevo <nombre>Crea la carpeta con plugin.json, src/index.js, package.json, jsconfig.json, README.md y .gitignore. El nombre va en minúsculas, números y guiones, y la carpeta no puede existir.
pcc-plugin comprobar [dir]Valida plugin.json, mira que entry exista y dice todos los errores a la vez. Si está bien, enseña lo que el plugin podrá hacer y marca los permisos delicados.
pcc-plugin dev [dir]Lo carga en modo desarrollo y lo recarga al guardar. Ctrl+C para salir.

Sin [dir] se usa la carpeta actual. El plugin que crea nuevo ya trae npm run dev y npm run comprobar apuntando a estos dos.

El plugin entero

import { definirPlugin } from "@pcreative/plugin-contract"

export default definirPlugin({
  arrancar(api, { registro }) {
    api.on("pedido.creado", (pedido) => {
      registro.info(`Pedido ${pedido.number}: ${pedido.total.amount} €`)
    })
  },
})

Escribe api.on(" y el editor te enseña los eventos disponibles, ya tipados. Es lo único que hay que saber para empezar.

Eventos y filtros

Un evento avisa de algo que ya ha pasado. No puedes cambiar el resultado:

api.on("cliente.registrado", (cliente) => { /* dale la bienvenida */ })

Un filtro te deja modificar algo antes de que se use:

api.filtro("envios.disponibles", (metodos, { carrito }) => {
  return metodos.filter((m) => m.id !== "urgente")
})

Los puntos de enganche que hay hoy:

EventosFiltros
pedido.creado, pedido.pagadoprecio.mostrado
pedido.enviado, pedido.canceladoproducto.ficha
producto.creado, producto.actualizadoenvios.disponibles
cliente.registradopagos.disponibles
inventario.agotado, carrito.abandonadocorreo.contenido
factura.emitidaimpuestos.calculados

Un filtro puede cambiar lo que se enseña, pero nunca lo que se cobra:

  • producto.ficha cambia textos, pero no precios, existencias ni variantes.
  • envios.disponibles y pagos.disponibles quitan, ordenan o renombran opciones, pero no cambian su precio ni su recargo.

Para cambiar un precio está precio.mostrado, que se aplica igual en la ficha, en el carrito y en el cobro.

carrito.totales sigue aceptándose, pero ya no se aplica: cambiaba los totales que se enseñan sin cambiar lo que se cobra ni lo que se factura. Si tu plugin lo declara, al comprobarlo te sale un aviso.

Impuestos y factura electrónica

Estos dos enganches son los que usa un conector fiscal (Stripe Tax, Avalara, la factura electrónica de cada país):

  • impuestos.calculados sí cambia lo que se cobra, y por eso pide el permiso impuestos:escribir, que es delicado. Recibe los impuestos de cada línea y de cada envío ({ lines: { [id]: [{ name, code, rate, compound }] }, shipping: {…} }) y el destino del carrito, y devuelve lo mismo con las tasas buenas. Si devuelve algo mal formado, falla o tarda más de 5 segundos, se quedan las tasas de la tienda: la caja nunca se bloquea por un servicio de fuera.
  • factura.emitida llega con cada factura y rectificativa ya numerada: emisor, cliente, líneas, desglose por tasa y la huella de la cadena. Con tienda.facturas.sellar(serie, numero, { provider, status, reference, url, qr }) (permiso facturas:escribir) el conector deja su justificante, y el sello sale impreso en la factura, con su QR.

Además de eventos y filtros, un plugin puede aportar interfaz al panel en menu.principal, pedido.lateral, producto.lateral, ajustes.pestaña e inicio.tarjeta.

Tres clases de extensión, funciona una

plugin.json dice qué clase de extensión es, en kind. El contrato acepta tres, y hoy solo corre una:

kindDónde correríaHoy
pluginEn tu tienda, cargado por el backend desde entry (JavaScript).Funciona. Es la única clase que carga el cargador. pcc-plugin nuevo crea esta.
appEn el servidor de quien la hizo: no tiene entry, declara un webhookUrl, y lo que aporta al panel necesita una url.Sin implementar. comprobar la valida, pero la tienda no la carga ni le manda nada.
sandboxEn tu tienda, en un espacio aislado con permisos concedidos uno a uno. Su entry tiene que ser un fichero .wasm.Sin implementar. Igual: se valida y no se carga.

Si pones una app o un sandbox en la carpeta de plugins, el backend la rechaza al arrancar y dice por qué: «este cargador solo carga plugins».

Instalarlo

Copia la carpeta del plugin en plugins/ en la raíz del repositorio (al lado de apps/, no dentro de apps/backend). En Docker la carpeta es /plugins dentro del contenedor del backend. Aparece en Panel → Extensiones.

Si tus plugins viven en otro sitio, dilo con PLUGINS_DIR.

Para apagar uno sin desinstalarlo:

PLUGINS_DISABLED=uno,otro

Útil cuando un plugin da problemas y quieres seguir vendiendo mientras lo miras.

Ajustes

Si tu plugin declara ajustes, salen solos en Panel → Extensiones: no tienes que escribir la pantalla. Los secretos se guardan aparte y no se devuelven al navegador — el panel enseña «hay una clave guardada», nunca la clave.

Plugins de pago

Un plugin de pago declara su bloque license en plugin.json y la tienda lo comprueba antes de cargarlo. Se verifica contra una clave pública (LICENSE_PUBLIC_KEY), y se verifica sin conexión: una tienda que se queda sin internet no puede quedarse sin sus plugins. Si la licencia no vale, el plugin no se carga; no hay modo a medias.

Mientras lo desarrollas no necesitas licencia: pcc-plugin dev carga en modo desarrollo, que no la comprueba y lo avisa en cada carga. Ese modo lo elige quien ejecuta, nunca el plugin: nada de lo que pongas en plugin.json hace que una tienda se salte la comprobación.

La clave es pública a propósito: solo sirve para comprobar firmas, nunca para crearlas.

El contrato no depende de lo de dentro

A propósito. Un plugin escrito hoy debería seguir valiendo si mañana la tienda cambia por dentro. Por eso los eventos hablan de pedido y carrito, no de las tablas del motor.

Ver también