Integración
Esta página es la guía para consumidores que embeben mas-consents desde otra aplicación.
Describe el contrato de URL, mensajería con el host, llamadas backend que realiza el componente y el listener mínimo que debería implementar un host.
Destinos de embebido
mas-consents puede embeberse en:
- un iframe de navegador
- un flujo browser opener/popup
WebViewde React NativeWKWebViewde iOSWebViewde Android
Parámetros de URL
El frontend lee la query string una sola vez durante el arranque. Para llamadas reales al backend, el host debe proporcionar contexto suficiente para la API de consents.
| Parámetro | Comportamiento de runtime | Expectativa para consumidores |
|---|---|---|
token | Se reenvía como Authorization: Bearer <token> cuando está presente | Obligatorio para llamadas reales al backend |
customerId | Se usa en las rutas GET/POST | Obligatorio para llamadas reales al backend |
brand | Se usa en las rutas GET/POST. mock activa comportamiento mock local | Obligatorio para llamadas reales al backend |
sector | Se reenvía como header x-sector | Obligatorio para llamadas reales al backend |
view | Los parámetros repetidos se preservan y se envían como query params repetidos | Obligatorio en la mayoría de flujos host |
lang | Se normaliza contra la lista de locales soportados. Si falta o es inválido, cae a es | Recomendado |
locales | Lista opcional separada por comas. Por defecto usa todos los locales soportados | Opcional |
choice | Filtro opcional que se pasa al backend | Opcional |
actorId | Se incluye en el body POST como actor_id | Esperado para flujos reales de submit |
sellChannel | Se incluye en el body POST como sell_channel | Esperado para flujos reales de submit |
traceId | Se incluye en el body POST como trace_id | Esperado para trazabilidad y submit |
enableCustomConfig | Solo el valor exacto true tiene efecto. Hace que el componente espere configuración JSON del host | Opcional |
hostCommunication | opener activa el modo de comunicación con browser opener | Opcional |
hostOrigin | Obligatorio con hostCommunication=opener; los orígenes inválidos se ignoran | Opcional salvo que se use modo opener |
Locales soportados:
eseucaengl
Valores soportados de view:
MANDATORYPORTFOLIOSGDAINTERNALPRIVATEWHATSAPPOGW_DEVICE_SWAPOGW_SIM-SWAPOGW_NUMBER-VERIFICATIONOGW_KYC-MATCH
Varios views se pasan como parámetros repetidos:
?view=MANDATORY&view=PORTFOLIOValores soportados de choice:
PENDINGREJECTEDACCEPTED
Limpieza de cadena de consulta
El componente lee los parámetros de URL durante el arranque y después limpia la cadena de consulta de la URL del navegador.
La única excepción es brand=mock, que conserva la cadena de consulta visible para facilitar depuración local.
Mensajería con el host
El bridge emite mensajes estructurados y mantiene eventos de texto del navegador por compatibilidad. Las integraciones nuevas deberían soportar mensajes estructurados.
Mensajes estructurados salientes
Los hosts en iframe reciben mensajes estructurados como objetos:
{
"source": "consents",
"version": "v1",
"type": "CONSENTS_LOADED",
"payload": {}
}React Native, Android WebView, iOS WebView y los flujos browser opener reciben el mismo payload serializado como texto JSON.
Los hosts de esos entornos deben parsear el texto antes de leer source o type.
Tipos de mensaje saliente:
CONSENTS_CONFIG_REQUIRED: se emite solo cuandoenableCustomConfig=trueCONSENTS_LOADED: se emite después de cargar consentimientosCONSENTS_COMPLETED: se emite tras un submit correcto o cuando el backend indica que la campaña ya está completadaCONSENTS_ERROR: se emite con payload de error backend cuando falla la carga o el submit
Eventos de texto de compatibilidad
Los hosts en iframe también reciben mensajes de texto planos:
consents-loadedconsents-successconsents-error
Estos eventos existen por compatibilidad con integraciones host antiguas. No deberían ser el único contrato para consumidores nuevos.
Mensajes entrantes
El componente acepta actualmente un mensaje enviado por el host:
SET_CONFIG
SET_CONFIG solo es obligatorio cuando la URL contiene enableCustomConfig=true.
Sin ese flag, el componente renderiza inmediatamente con el tema por defecto e ignora SET_CONFIG inbound.
El mensaje puede enviarse como objeto en integraciones iframe o como texto JSON en integraciones de bridge nativo:
{
"type": "SET_CONFIG",
"payload": {
"theme": "light"
}
}Escucha recomendada para navegador
const iframe = document.querySelector<HTMLIFrameElement>('#mas-consents')
window.addEventListener('message', (event) => {
const data = event.data
if (typeof data === 'string') {
if (data === 'consents-loaded') {
// Señal de ready de compatibilidad
}
if (data === 'consents-success') {
// Señal de finalización de compatibilidad
}
if (data === 'consents-error') {
// Señal de error de compatibilidad
}
return
}
if (data?.source !== 'consents') return
switch (data.type) {
case 'CONSENTS_CONFIG_REQUIRED':
iframe?.contentWindow?.postMessage(
{
type: 'SET_CONFIG',
payload: {
theme: 'light',
},
},
'*',
)
break
case 'CONSENTS_LOADED':
// Señal ready
break
case 'CONSENTS_COMPLETED':
// Señal de finalización
break
case 'CONSENTS_ERROR':
// El payload de error backend está disponible en data.payload
break
}
})Las aplicaciones host deben validar event.origin según su propio modelo de seguridad.
Llamadas HTTP que realiza el componente
El host no llama directamente al backend de consents para el flujo embebido. El componente realiza estas llamadas usando el contexto de URL.
GET consents
GET /v2/orgs/{brand}/customers/{customerId}/consentsHeaders reenviados:
Authorization: Bearer <token>cuando existetokenAccept-Languagedesde el locale normalizadox-sectordesde la URLx-keysi fue devuelto por una respuesta anterior
Query params reenviados:
viewrepetidochoiceopcionalconsent_idsopcional cuando se proporcionaCustomConfig.consentIdsdisplay_format=SHORTopcional en móvilxs
POST consents
POST /v2/orgs/{brand}/customers/{customerId}/consentsHeaders reenviados:
Authorization: Bearer <token>cuando existetokenAccept-Languagex-sectorx-keycapturado de la respuesta GET previa
El body POST incluye actor_id, sell_channel, trace_id, selecciones del cliente, IP y fecha de captura.
Configuración de plataforma de runtime
El frontend también carga /config.json al arrancar.
Este archivo lo aporta el despliegue y normalmente no lo suministra el host consumidor.
Campos soportados:
ENVAPI_GATEWAYFARO_COLLECTOR_URLIS_OBSERVABILITY_ENABLEDTRACING_WHITELIST_URLS
Si /config.json no puede cargarse y el runtime sigue en LOCAL, se mantiene la configuración local por defecto.
Ejemplos
Los ejemplos siguientes usan enableCustomConfig=true para mostrar el handshake completo.
Si el host no necesita personalizar el componente, elimina enableCustomConfig=true de la URL y no envíes SET_CONFIG.
Iframe de navegador
<iframe
id="mas-consents"
src="https://consents.masstack.com/?token=JWT&customerId=123&brand=masmovil§or=telco&view=MANDATORY&lang=es&actorId=agent-123&sellChannel=ecare&traceId=trace-123&enableCustomConfig=true"
style="width: 100%; height: 900px; border: 0;"
></iframe>
<script>
const iframe = document.getElementById('mas-consents')
window.addEventListener('message', (event) => {
const data = event.data
if (typeof data === 'string') {
if (data === 'consents-success') {
window.location.href = '/next-step'
}
if (data === 'consents-error') {
console.error('Consents failed')
}
return
}
if (data?.source !== 'consents') return
if (data.type === 'CONSENTS_CONFIG_REQUIRED') {
iframe.contentWindow?.postMessage(
{
type: 'SET_CONFIG',
payload: {
theme: 'light',
uiOptions: {
groupDisplay: 'flat',
collapsible: true,
},
},
},
'*',
)
}
if (data.type === 'CONSENTS_COMPLETED') {
window.location.href = '/next-step'
}
if (data.type === 'CONSENTS_ERROR') {
console.error(data.payload)
}
})
</script>Popup/opener de navegador
const consentsUrl = new URL('https://consents.masstack.com/')
consentsUrl.search = new URLSearchParams({
token: 'JWT',
customerId: '123',
brand: 'masmovil',
sector: 'telco',
view: 'MANDATORY',
lang: 'es',
actorId: 'agent-123',
sellChannel: 'ecare',
traceId: 'trace-123',
enableCustomConfig: 'true',
hostCommunication: 'opener',
hostOrigin: window.location.origin,
}).toString()
const popup = window.open(consentsUrl.toString(), 'mas-consents', 'width=480,height=900')
window.addEventListener('message', (event) => {
if (event.origin !== 'https://consents.masstack.com') return
const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data
if (data?.source !== 'consents') return
if (data.type === 'CONSENTS_CONFIG_REQUIRED') {
popup?.postMessage(
JSON.stringify({
type: 'SET_CONFIG',
payload: {
theme: 'light',
},
}),
'https://consents.masstack.com',
)
}
if (data.type === 'CONSENTS_COMPLETED') {
popup?.close()
}
})React Native WebView
import { useRef } from 'react'
import { WebView } from 'react-native-webview'
const consentsUrl =
'https://consents.masstack.com/?token=JWT&customerId=123&brand=masmovil§or=telco&view=MANDATORY&lang=es&actorId=agent-123&sellChannel=app&traceId=trace-123&enableCustomConfig=true'
export function ConsentsScreen() {
const webViewRef = useRef<WebView>(null)
const sendConfig = () => {
const message = JSON.stringify({
type: 'SET_CONFIG',
payload: {
theme: 'light',
uiOptions: {
groupDisplay: 'flat',
},
},
})
webViewRef.current?.injectJavaScript(`
window.dispatchEvent(new MessageEvent('message', { data: ${JSON.stringify(message)} }));
true;
`)
}
return (
<WebView
ref={webViewRef}
source={{ uri: consentsUrl }}
onMessage={(event) => {
const data = JSON.parse(event.nativeEvent.data)
if (data.type === 'CONSENTS_CONFIG_REQUIRED') {
sendConfig()
}
if (data.type === 'CONSENTS_COMPLETED') {
// Continuar el flujo host
}
if (data.type === 'CONSENTS_ERROR') {
// Gestionar data.payload
}
}}
/>
)
}iOS WKWebView
import UIKit
import WebKit
final class ConsentsViewController: UIViewController, WKScriptMessageHandler {
private lazy var webView: WKWebView = {
let contentController = WKUserContentController()
contentController.add(self, name: "consentsBridge")
let config = WKWebViewConfiguration()
config.userContentController = contentController
return WKWebView(frame: .zero, configuration: config)
}()
override func viewDidLoad() {
super.viewDidLoad()
view = webView
var components = URLComponents(string: "https://consents.masstack.com/")!
components.queryItems = [
URLQueryItem(name: "token", value: "JWT"),
URLQueryItem(name: "customerId", value: "123"),
URLQueryItem(name: "brand", value: "masmovil"),
URLQueryItem(name: "sector", value: "telco"),
URLQueryItem(name: "view", value: "MANDATORY"),
URLQueryItem(name: "lang", value: "es"),
URLQueryItem(name: "actorId", value: "agent-123"),
URLQueryItem(name: "sellChannel", value: "ios"),
URLQueryItem(name: "traceId", value: "trace-123"),
URLQueryItem(name: "enableCustomConfig", value: "true")
]
webView.load(URLRequest(url: components.url!))
}
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
guard let raw = message.body as? String,
let data = raw.data(using: .utf8),
let payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
payload["source"] as? String == "consents"
else { return }
if payload["type"] as? String == "CONSENTS_CONFIG_REQUIRED" {
let config = #"{"type":"SET_CONFIG","payload":{"theme":"light"}}"#
webView.evaluateJavaScript(
"window.dispatchEvent(new MessageEvent('message', { data: '\(config)' }));"
)
}
}
}Android WebView
class ConsentsActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
webView = WebView(this)
setContentView(webView)
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(ConsentsBridge(), "NativeBridge")
val url = Uri.parse("https://consents.masstack.com/").buildUpon()
.appendQueryParameter("token", "JWT")
.appendQueryParameter("customerId", "123")
.appendQueryParameter("brand", "masmovil")
.appendQueryParameter("sector", "telco")
.appendQueryParameter("view", "MANDATORY")
.appendQueryParameter("lang", "es")
.appendQueryParameter("actorId", "agent-123")
.appendQueryParameter("sellChannel", "android")
.appendQueryParameter("traceId", "trace-123")
.appendQueryParameter("enableCustomConfig", "true")
.build()
webView.loadUrl(url.toString())
}
inner class ConsentsBridge {
@JavascriptInterface
fun postMessage(raw: String) {
val message = JSONObject(raw)
if (message.optString("source") != "consents") return
if (message.optString("type") == "CONSENTS_CONFIG_REQUIRED") {
val config = JSONObject()
.put("type", "SET_CONFIG")
.put("payload", JSONObject().put("theme", "light"))
.toString()
runOnUiThread {
webView.evaluateJavascript(
"window.dispatchEvent(new MessageEvent('message', { data: ${JSONObject.quote(config)} }));",
null,
)
}
}
}
}
}