El precio va escrito en el botón
Quien va a pagar la tasa de extranjería (el NIE, la tarjeta de residencia, la prórroga de estancia) rellena el impreso en la sede de la Policía y marca su trámite. El precio no se lo dice el servidor, el ordenador de la Policía que le envía la página. Venía escrito en la propia página, en el botón que acaba de marcar, y la suma la hace su navegador.
- Dominio
- sede.policia.gob.es
- Guardado
- 31 AGO 2026
- Comprobado en vivo
- 15 SEP 2026
- Citas
- 10 en 2 ficheros guardados
- Lectura
- unos 4 min

En la lista, cada trámite es una fila con su nombre y un botón redondo a la derecha. Una página web es un texto en HTML, hecho de etiquetas que le dicen al navegador qué pintar, y cada etiqueta lleva atributos, datos pegados a ella. En la etiqueta de ese botón está el precio, dos veces. Una en el atributo title, el globo de ayuda que sale al pasar el ratón por encima. Otra en un atributo que no existe en el estándar HTML y que se inventó para esto, con el número del trámite metido en el nombre. Los 28 precios viajan dentro de la página, a la vista de cualquiera que sepa mirar.

- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 46,2 kB
- sha256
- 03dcead6ef21…
| 293 | <div class="alert " id="tasa1Info" hidden> |
| 294 | <b>Introduzca el número total de días de prórroga que va a solicitar y luego pulse el retorno de carro. (El importe por este trámite será: la tasa base correspondiente al trámite más el número de días solicitados multiplicado por la cantidad e… |
| 295 | <input id=" |
| 296 | <input id="tasa35Importe" type="text" name="tasa35Importe" style="visibility: hidden" value="1.09"/> |
| 297 | </b> |
| 298 | </div> |
| 299 | |
| 300 | |
| 301 | </td> |
| 302 | <td> |
| 303 | <input id="tasa1Input" type="radio" name="tramiteSeleccionado" tasa1Importe="17.49" title="17.49 €" value="1" > |
| 304 | </td> |
| 305 | </tr> |
| 306 | <tr id="tasa2"> |
| 307 | <td>.Prórroga de estancia de corta duración con visado.</td> |
| 308 | <td><input id="tasa2Input" type="radio" name="tramiteSeleccionado" tasa2Importe="32.17" title="32.17 €" value="2" ></td> |
| 309 | </tr> |
| 310 | <tr id="tasa32"> |
| 311 | <td>.Prórroga desplazamiento temporal menor extranjero fines de tratamiento médico.</td> |
| 312 | <td><input id="tasa32Input" type="radio" name="tramiteSeleccionado" tasa32Importe="17.49" title="17.49 €" value="32" ></td> |
| 313 | </tr> |
| 314 | </tbody> |
El servidor los escribe ahí al generar la página. A partir de ese momento, la conversación sobre cuánto cuesta el trámite ocurre entre el navegador y el papel, sin volver a molestar a nadie. El programa busca el botón marcado y le pide el atributo cuyo nombre construye pegando «tasa», el número del trámite e «Importe». Con ese número hace las cuentas. Para los certificados lo multiplica por la cantidad pedida; para la prórroga de estancia le suma los días. Al final resta la cantidad complementaria, la casilla donde se declara lo ya ingresado.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 48,3 kB
- sha256
- 79c3025587a8…
| 361 | } |
| 362 | |
| 363 | if (!regular |
| 364 | errores+= |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | if ( $(" |
| 369 | tramite |
| 370 | //window.console && console.log(" |
| 371 | // |
| 372 | tramite |
| 373 | // |
| 374 | // |
| 375 | if (regular |
| 376 | complementaria |
| 377 | // |
| 378 | // |
| 379 | } |
| 380 | |
| 381 | if (tramiteSeleccionado=='1'){ |
| 382 | if (regular |
| 383 | numero |
| 404 | // |
| 405 | // |
| 406 | } |
| 407 | |
| 408 | if (!is |
| 409 | if (tramite |
| 410 | errores+= |
| 411 | } |
| 412 | else{ |
| 413 | // |
| 414 | // |
| 415 | importeFinal= |
| 416 | // |
| 417 | // |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | if (errores==""){ |
| 423 | $(" |
| 424 | //console.log("errores vacio, importe final asignado="+importeFinal); |
| 425 | //alert("errores vacio, importe final asignado="+importeFinal); |
| 426 | }else{ |
El resultado se escribe en la casilla «Importe euros» del impreso, la que uno esperaría que rellenase la Administración. Si las comprobaciones del navegador han encontrado algún fallo, una fecha mal puesta o un trámite sin marcar, la casilla se queda vacía. Si no, recibe el número con sus dos decimales. Es el mismo número que, unos segundos después, viajará al servidor como importe a pagar.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 48,3 kB
- sha256
- 79c3025587a8…
| 412 | else{ |
| 413 | // |
| 414 | // |
| 415 | importeFinal= |
| 416 | // |
| 417 | // |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | if (errores==""){ |
| 423 | $(" |
| 424 | //console.log("errores vacio, importe final asignado="+importeFinal); |
| 425 | //alert("errores vacio, importe final asignado="+importeFinal); |
| 426 | }else{ |
| 427 | $("#total").val(''); |
| 428 | //console.log("errores no vacio, importe final asignado=sin importe"); |
| 429 | //alert("errores no vacio, importe final asignado=sin importe"); |
| 430 | } |
| 431 | |
| 432 | /* |
| 433 | console.log(" |
| 434 | console.log(" |
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 46,2 kB
- sha256
- 03dcead6ef21…
| 520 | </div> |
| 521 | <div class="panel-body"> |
| 522 | <div class="row"> |
| 523 | <!--<div class="form-group">--> |
| 524 | <div class="col-sm-offset-2 col-sm-4"> |
| 525 | <label for="total" class=" |
| 526 | <div class="input-group"> |
| 527 | |
| 528 | <input type="text" class="form-control text-right" placeholder="" |
| 529 | value="" |
| 530 | name="total" id="total" |
| 531 | maxlength="6" disabled> |
| 532 | <span class=" |
| 533 | </div> |
| 534 | </div> |
| 535 | |
| 536 | <div id="formapago" > |
| 537 | <label for="inputFormaPago" class="control-label">Forma de pago (*)</label> |
| 538 | <div id="inputFormaPago" class="input-group col-sm-4" > |
| 539 | |
| 540 | <label class="radio-inline"> |
| 541 | |
| 542 | <input name="efectivoOAdeudo" id="efectivo" |
La casilla llega desde el servidor con el atributo disabled: aparece en gris y no se puede escribir en ella. Un campo deshabilitado tampoco se envía con el formulario. Por eso, en el instante anterior al envío, el programa se lo quita, a esa casilla y a las otras dos zonas apagadas del impreso, y solo entonces manda el formulario. El importe que sale hacia el servidor es el que ha calculado el navegador.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 48,3 kB
- sha256
- 79c3025587a8…
| 105 | if (sin |
| 106 | formularioNewSubmit=true; |
| 107 | } |
| 108 | } else{ |
| 109 | formularioNewSubmit=true; |
| 110 | } |
| 111 | |
| 112 | if (formularioNewSubmit){ |
| 113 | $(' |
| 114 | $(' |
| 115 | $(' |
| 116 | var formulario = document. |
| 117 | formulario.submit(); |
| 118 | } |
| 119 | |
| 120 | |
| 121 | } else { |
| 122 | $(" |
| 123 | $(" |
| 124 | $(" |
| 125 | $(" |
| 126 | $(" |
Hay un trámite con precio variable: la prórroga de estancia de corta duración sin visado, 17,49 € de base más una cantidad por cada día. Esa cantidad por día también viaja en la página, en un campo de texto que vale 1.09 y que se esconde con una orden de estilo. Ocupa su sitio en el formulario, pero no se ve. Para leerlo, el programa toma el valor del campo escondido, le pega un punto y, detrás, el contenido de otra casilla: la de los decimales de la cantidad complementaria, que rellena quien presenta el impreso. Dos campos que no tienen nada que ver, unidos por un punto.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 46,2 kB
- sha256
- 03dcead6ef21…
| 285 | <tbody> |
| 286 | <tr id="tasa1"> |
| 287 | <td id="tasa1Literal"> |
| 288 | .Prórroga estancia corta duración sin visado (incrementado por cada día de prórroga). |
| 289 | <!--<div class="alert " id="tasa1Info" hidden> |
| 290 | <b>(Trámite de importe variable. La cantidad se asignará en la Dependencia Policial.)</b> |
| 291 | </div>--> |
| 292 | |
| 293 | <div class="alert " id="tasa1Info" hidden> |
| 294 | <b>Introduzca el número total de días de prórroga que va a solicitar y luego pulse el retorno de carro. (El importe por este trámite será: la tasa base correspondiente al trámite más el número de días solicitados multiplicado por la cantidad establecida p… |
| 295 | <input id=" |
| 296 | <input id="tasa35Importe" type="text" name="tasa35Importe" style="visibility: hidden" value="1.09"/> |
| 297 | </b> |
| 298 | </div> |
| 299 | |
| 300 | |
| 301 | </td> |
| 302 | <td> |
| 303 | <input id="tasa1Input" type="radio" name="tramiteSeleccionado" tasa1Importe="17.49" title="17.49 €" value="1" > |
| 304 | </td> |
| 305 | </tr> |
| 306 | <tr id="tasa2"> |
| 307 | <td>.Prórroga de estancia de corta duración con visado.</td> |
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 48,3 kB
- sha256
- 79c3025587a8…
| 373 | // |
| 374 | // |
| 375 | if (regular |
| 376 | complementaria |
| 377 | // |
| 378 | // |
| 379 | } |
| 380 | |
| 381 | if (tramiteSeleccionado=='1'){ |
| 382 | if (regular |
| 383 | numero |
| 384 | var importeDiarioTasa35 = parseFloat($(" |
| 385 | tramite |
| 386 | |
| 387 | } else { |
| 388 | errores += mensaje |
| 389 | } |
| 390 | } else if (tramite |
| 391 | if (regular |
| 392 | numero |
| 393 | // |
| 394 | // |
| 395 | tramite |
Lo que sale de ahí es la cadena “1.09.” seguida de lo que el ciudadano haya escrito en esa otra casilla. Si escribió un 5, el programa tiene delante “1.09.5”, que no es un número. Entra entonces parseFloat, la orden que convierte texto en cifra. Lee de izquierda a derecha y, en cuanto encuentra algo que ya no puede ser parte del número, se detiene y devuelve lo que llevaba. Al llegar al segundo punto, para. Devuelve 1,09, el valor que había que leer.
Lo medimos en un Chrome 126 antes de escribirlo, porque esto no se deduce, se ejecuta. El importe que ve el ciudadano es correcto, y lo es por accidente. De dónde viene el desliz se ve nueve líneas más arriba. Allí está la misma expresión aplicada a los dos campos para los que se escribió, el entero y el decimal de la cantidad complementaria, que sí son dos mitades de un número. Alguien copió esa línea a un campo que ya traía el número entero. El resultado es correcto por la misma razón por la que un reloj parado da la hora dos veces al día.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 48,3 kB
- sha256
- 79c3025587a8…
| 365 | } |
| 366 | } |
| 367 | |
| 368 | if ( $(" |
| 369 | tramite |
| 370 | //window.console && console.log(" |
| 371 | // |
| 372 | tramite |
| 373 | // |
| 374 | // |
| 375 | if (regular |
| 376 | complementaria |
| 377 | // |
| 378 | // |
| 379 | } |
| 380 | |
| 381 | if (tramiteSeleccionado=='1'){ |
| 382 | if (regular |
| 383 | numero |
| 384 | var importeDiarioTasa35 = parseFloat($(" |
| 385 | tramite |
| 386 | |
| 387 | } else { |
El fichero conserva el taller a la vista. Hay 19 líneas de console.log y 15 de alert comentadas, las que se usaron para seguir las cuentas desde el navegador. Y hay cuatro bloques enteros con versiones anteriores del mismo código. Una copiaba en la casilla del total el globo de ayuda del botón, «17.49 €», y otra le quitaba los dos últimos caracteres para dejar el número. El precio siempre ha vivido en la página; lo que ha ido cambiando es de qué atributo se saca.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 48,3 kB
- sha256
- 79c3025587a8…
| 1179 | }, 500); |
| 1180 | } |
| 1181 | );*/ |
| 1182 | |
| 1183 | |
| 1184 | |
| 1185 | |
| 1186 | /* |
| 1187 | $(" |
| 1188 | function () |
| 1189 | { |
| 1190 | //var importe= |
| 1191 | //$("#total").val(importe); |
| 1192 | validar |
| 1193 | } |
| 1194 | ); |
| 1195 | |
| 1196 | $(" |
| 1197 | function () |
| 1198 | { |
| 1199 | //var importe= |
| 1200 | //$("#total").val(importe); |
| 1201 | validar |
El impreso capturado el 31 de agosto de 2026 y el que sirve la sede el 15 de septiembre son el mismo fichero, byte a byte, y lo mismo vale para el programa que hace las cuentas. Qué hace el servidor con el importe que recibe, si lo vuelve a calcular por su cuenta antes de emitir el papel, no se ve desde fuera. Este museo no lo prueba. Lo que se ve es que el número sale del navegador de quien paga, y que ese navegador lo leyó del botón.
El impreso avisa de que la cantidad la asignará la Dependencia Policial. Hasta entonces, el precio va escrito en el botón y la suma la hace su navegador.
- Los 28 precios de la tasa 790-012 están escritos en el HTML de la página, en atributos propios de cada botón.
- El navegador los lee de ahí, multiplica, suma y resta, y escribe el resultado en la casilla del importe, que el servidor manda deshabilitada y el propio controlador habilita justo antes de enviar el formulario.
- Para el precio por día de la prórroga, la expresión que lo lee junta el valor de un campo con los decimales de otro; medido en Chrome 126, el resultado sigue siendo 1,09 porque
parseFloatse detiene en el segundo punto.
- No decimos que el servidor acepte sin más el importe que recibe: no lo hemos probado, no vamos a probarlo y desde fuera no se ve.
- No decimos que nadie haya pagado de más ni de menos.
- No decimos que la línea del importe diario cobre mal: hoy devuelve el número correcto.
- Y no decimos nada sobre el impreso en papel que se descarga al final, que no hemos pedido.
De dónde sale cada cita
Cada trozo de código de esta ficha está en un fichero guardado tal como lo entregó el servidor, con su huella (sha256) y su hora. Las citas entre «» se comprueban por programa contra ese fichero antes de publicar.
- https://sede.policia.gob.es/Tasa790_012/ImpresoRellenar31 de agosto de 2026, 16:17archivosha256 03dcead6ef2127e20f4c05e57635a91c2b8891471fc5c88c6aeaa2fd608f2369
- https://sede.policia.gob.es/Tasa790_012/ImpresoRellenar15 de septiembre de 2026, 01:51en vivo · byte a byte igualsha256 03dcead6ef2127e20f4c05e57635a91c2b8891471fc5c88c6aeaa2fd608f2369
- https://sede.policia.gob.es/Tasa790_012/Scripts/impresoRellenarControlador.js31 de agosto de 2026, 16:18archivosha256 79c3025587a8f988a300be3e77aa682a1c541ad55c44d8eec24c87a021a51708
- https://sede.policia.gob.es/Tasa790_012/Scripts/impresoRellenarControlador.js15 de septiembre de 2026, 01:51en vivo · byte a byte igualsha256 79c3025587a8f988a300be3e77aa682a1c541ad55c44d8eec24c87a021a51708