Ir al contenido

Crear una extensión

Esta página está pensada para desarrolladores. Una extensión se describe con un manifiesto JSON: lo que declara (permisos, columnas, bloques, reglas) lo valida Sweescape al guardarlo. Su interfaz es una página HTML alojada en Sweescape que se muestra en un iframe aislado de la página de venta.

Cualquier cuenta puede crear extensiones y usarlas en sus propios eventos. La publicación en el marketplace está reservada al equipo de Sweescape.

  1. Abre el menú de tu cuenta, haz clic en Extensiones y luego en Crear extensión.

  2. Rellena el formulario:

    • Nombre: el nombre que aparece en tus listas y en el editor de página.
    • Nombre del paquete: el identificador único de la extensión, en notación DNS inversa y en minúsculas, por ejemplo com.subsol.plano-sala. No se puede cambiar después de crearla y tiene que estar libre en todo Sweescape.
    • Descripción: opcional.
  3. Haz clic en Crear. La ventana pasa al paso Escribe el manifiesto, con un manifiesto de ejemplo ya rellenado.

Debajo del editor de manifiesto, el campo Sube los archivos de interfaz y luego copia su URL en el manifiesto envía tus archivos a Sweescape.

  • Formatos aceptados: html, js, mjs, css, json, svg, png, jpg, jpeg, gif, webp, ico, woff, woff2, ttf, map, txt.
  • 5 MB como máximo por archivo, 50 archivos como máximo por envío.
  • Cada archivo subido aparece con su URL pública, un botón para copiar la URL y una cruz para eliminarlo.
Editor del manifiesto de la extensión com.sweescape.subsol-seatmap (nombre «Plan de salle Subsol», versión 1.1.0, permisos ui:render, ticket:read y participant:write), la zona Upload the UI file(s), dos archivos seatmap.html y seatmap-1.1.0.html con su URL y un botón de copia, botones Cancel y Save

Un manifiesto mínimo que muestra un bloque en la página de venta:

{
"id": "com.subsol.plano-sala",
"apiVersion": 1,
"version": "1.0.0",
"name": "Plano de sala SUBSOL",
"description": "Elección del asiento antes de la compra.",
"scopes": ["ui:render", "ticket:read"],
"contributes": {
"uiExtensions": [
{
"surface": "page.block",
"entry": "https://…/URL-copiada-del-archivo",
"sandbox": { "allowedDomains": [] },
"descriptor": {
"name": "SeatMap",
"category": "media",
"description": "Plano de sala",
"fields": {
"title": { "type": "text", "description": "Título del bloque" },
"ticket": { "type": "event_ticket", "description": "Entrada vendida" }
},
"defaultProps": { "title": "Elige tu asiento", "ticket": "" }
}
}
]
}
}
Clave Función
id Igual al nombre del paquete.
apiVersion Versión del contrato de extensión. Solo se acepta el valor 1.
version Versión de tu extensión en formato semver (1.2.0).
name, description Obligatorias.
scopes Los permisos declarados.
contributes Lo que añade la extensión: resourceFields, blocks, uiExtensions, rules.
configSchema Opcional: ajustes descritos a nivel de la extensión.

El campo author se rellena automáticamente con el nombre de tu cuenta: no hace falta escribirlo.

Un permiso se escribe recurso:acción. Los recursos son event, ticket, participant, order, payment, ticket_code, audience, campaign, automation, contact, invitation, invoice, analytics, ticket_page, badge. Las acciones son read, write, create, act, subscribe. Se añaden dos permisos especiales: ui:render y net:fetch.

Cada contribución exige su permiso; si no, el guardado falla:

Contribución Permiso exigido
Un bloque (blocks) o una interfaz (uiExtensions) ui:render
Una interfaz con allowedDomains net:fetch
Una columna de participante (resourceFields) participant:write
Una regla activada por order.created (por ejemplo) order:subscribe
Una acción adjust_price, reject o assign_ticket order:act
Una acción webhook net:fetch
Una acción set_field sobre un recurso recurso:write

La surface de una interfaz indica dónde se muestra. Hoy:

  • checkout.confirmation se muestra en la página de confirmación, después de un pago correcto;
  • todas las demás superficies (page.block, checkout.step…) aparecen como un bloque, con el nombre de la extensión, en la última categoría del editor de página, que por ahora se llama OTHER.

Existen dos tipos de bloques:

  • blocks reutiliza un bloque existente del editor de página, indicado en baseComponent (por ejemplo Card), con tus valores por defecto en descriptor.defaultProps. No se ejecuta código de terceros; los ajustes son los del bloque original.
  • uiExtensions muestra tu propia interfaz (el archivo indicado en entry).

Para una interfaz, los fields del descriptor se convierten en los ajustes del bloque en el editor de página. La description de cada campo sirve de etiqueta. Tipos reconocidos:

Tipo Ajuste que se muestra
text (y cualquier tipo desconocido) Campo de texto.
textarea Área de texto.
number Campo numérico.
boolean Elección Sí / No.
color Selector de color.
select, radio Lista de las options declaradas.
array Lista de elementos cuyos subcampos se describen en arrayFields.
event_ticket Lista de las entradas del evento. El valor es el identificador de la entrada.

Los valores que introduce el organizador se envían a tu interfaz al arrancar (ver abajo). defaultProps da los valores por defecto.

resourceFields añade columnas a los participantes del evento, que rellena tu interfaz:

"resourceFields": [
{
"resource": "participant",
"column": { "name": "seat", "type": "text", "required": true, "source": "ui", "unique": true }
}
]
  • name: minúsculas, cifras y _, empezando por una letra. firstname, lastname y email están reservados.
  • type: text, number, date, boolean o select (con options).
  • source: "ui": el valor viene de tu interfaz, nunca del formulario de compra.
  • unique: true: el valor solo puede pertenecer a un participante del evento (un asiento, una franja horaria). Sweescape rechaza un pedido cuyo valor ya está cogido o reservado por un carrito en curso.

Estas columnas aparecen en la lista de participantes mientras la extensión esté activa en el evento.

Tu interfaz se ejecuta en un iframe sandbox="allow-scripts" con un origen aislado. No recibe ningún token y solo se comunica con la página mediante postMessage.

Mensaje enviado por el iframe Efecto
{ type: "sweescape:ready" } Pide arrancar. La página responde sweescape:init con config (los ajustes del bloque) y context.
{ type: "sweescape:resize", height } Ajusta la altura del iframe, en píxeles.
{ type: "sweescape:set-field", field, value } Guarda un valor de columna de participante para la compra en curso.
{ type: "sweescape:checkout", ticketId, fields } Abre la ventana de compra con esta entrada, cantidad 1, con esos valores de columnas.
{ type: "sweescape:checkout", items: [ { ticketId, quantity, units } ] } Abre la ventana de compra con varias entradas. units da los valores de columnas de cada entrada.
{ type: "sweescape:request", requestId, method, params } Llama a un método de la página, que responde { type: "sweescape:response", requestId, ok, data } o error.

Añade closeOnBack: true a un mensaje sweescape:checkout para que el botón de volver de la ventana de compra la cierre en lugar de mostrar la selección de entradas.

Métodos disponibles con sweescape:request:

Método Devuelve
context.get El evento (id, name, startDate, endDate, status, type, taxRate), las entradas a la venta (id, title, price sin impuestos, stock, minQuantity, maxQuantity) y las columnas de la extensión.
column.takenValues Con params: { column }: la lista de valores ya cogidos de una de tus columnas unique.
participant.current Los valores de columnas ya introducidos para la compra en curso.
ticketCode.unlock Con params: { code }: aplica un código de acceso y devuelve las entradas desbloqueadas y el cupo restante.
order.current Solo en la superficie checkout.confirmation: id, total, currency y ticketCount del pedido pagado.

rules describe una lógica sin código, evaluada por Sweescape. Una regla tiene un id, un label, un evento que la activa on (order.created, order.confirmed, participant.created…), unas branches evaluadas en orden (se aplica la primera cuya condición when es verdadera) y un else opcional.

Acciones posibles:

  • reject: rechaza el pedido con un message, antes de escribir nada;
  • adjust_price: propone un ajuste de precio, que Sweescape recalcula y limita entre bounds.minCents y bounds.maxCents;
  • set_field: escribe un valor en una columna de tu extensión;
  • assign_ticket: elige la entrada asignada al participante;
  • webhook: envía un POST a una URL https, solo con tus columnas y campos no personales del pedido (order_id, event_id, participant_id, ticket_id, total_amount, currency, status).

Las reglas se validan al guardar el manifiesto: una regla sin el permiso <recurso>:subscribe de su evento, o una acción sin su permiso, se rechaza.

Haz clic en Guardar. El mensaje «Extensión guardada» confirma la validación. Si no:

Mensaje Causa
«El manifiesto no es un JSON válido» Error de sintaxis JSON (coma de más, comilla que falta).
«El manifiesto no es válido» Falta una clave o tiene un valor rechazado: id mal formado, version que no es semver, superficie o tipo desconocido, columna reservada…
«El manifiesto usa una capacidad que no declara en «scopes»» Añade el permiso que exige una contribución.
«El “id” del manifiesto debe coincidir con el nombre del paquete» El id es distinto del nombre del paquete indicado al crearla.
«Este nombre de paquete ya está en uso» Al crearla: elige otro nombre de paquete.
  1. Abre uno de tus eventos, pestaña Extensiones, y haz clic en Activar junto a tu extensión. Aparece aunque no esté publicada, en cuanto su manifiesto está guardado.

  2. Abre el editor de página, arrastra el bloque desde la última categoría de la lista (OTHER) y rellena sus ajustes.

  3. Comprueba el resultado con el botón Vista previa del editor y después en la página publicada.

Para modificar la extensión, haz clic en el icono de lápiz junto a ella en Mis extensiones: la ventana Editar extensión vuelve a abrir el manifiesto y los archivos. El nuevo manifiesto se aplica en cuanto se guarda, en todos los eventos donde la extensión está activa.

  • Estado. Una extensión nueva lleva la insignia Private: solo la ves tú. La publicación en el marketplace (insignia Published) la hace el equipo de Sweescape; el botón Publicar no aparece para las demás cuentas.
  • Eliminar. El icono de papelera elimina la extensión y sus archivos, sin confirmación. Si aún está activa en algún evento, la ventana No se puede eliminar la extensión los lista: desactívala antes en cada uno.