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 devnpm 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
| Comando | Qué 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:
| Eventos | Filtros |
|---|---|
pedido.creado, pedido.pagado | precio.mostrado |
pedido.enviado, pedido.cancelado | producto.ficha |
producto.creado, producto.actualizado | envios.disponibles |
cliente.registrado | pagos.disponibles |
inventario.agotado, carrito.abandonado | correo.contenido |
factura.emitida | impuestos.calculados |
Un filtro puede cambiar lo que se enseña, pero nunca lo que se cobra:
producto.fichacambia textos, pero no precios, existencias ni variantes.envios.disponiblesypagos.disponiblesquitan, 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.calculadossí cambia lo que se cobra, y por eso pide el permisoimpuestos: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.emitidallega con cada factura y rectificativa ya numerada: emisor, cliente, líneas, desglose por tasa y la huella de la cadena. Contienda.facturas.sellar(serie, numero, { provider, status, reference, url, qr })(permisofacturas: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:
kind | Dónde correría | Hoy |
|---|---|---|
plugin | En tu tienda, cargado por el backend desde entry (JavaScript). | Funciona. Es la única clase que carga el cargador. pcc-plugin nuevo crea esta. |
app | En 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. |
sandbox | En 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
- Vender el tema que has hecho — empaquetar, el mercado y las licencias; también vale para las extensiones
- El contrato de extensiones en npm (
@pcreative/plugin-contract) - Los comandos