prestashop-carrier-mrw · git:20260930.8a68a16 · 2026-09-30 · sha256 f87f99b375973067
prestashop-carrier-mrw git:20260930.8a68a16A
Immutable. This exact content never changes and is served at /api/v1/blob/f87f99b375973067.
---
name: prestashop-carrier-mrw
description: API SOAP de MRW (SAGEC) tal y como la usan los módulos oficiales mrwcarrier 8.0.7 y mrwtracking 3.8.1 de PrestaShop. Endpoints .asmx reales de producción y pruebas, la cabecera SOAP AuthInfo, las claves exactas de ps_configuration y la tabla de abonados donde el comerciante ya tiene puestas sus credenciales, seguimiento, etiquetas PDF y las trampas del código. Usar al integrar MRW, al leer sus credenciales desde otro módulo o al consultar el estado de un envío MRW.
---
# MRW — API real, leída de los módulos oficiales
**Fuente:** `mrwcarrier` (versión
**8.0.7**, `config.xml:5`) y `…\mrwtracking\` (versión **3.8.1**, `config.xml:5`), ambos de MRW
IBERIA. **Solo lectura: no se modifica ni un byte.**
MRW se reparte en **dos módulos**:
| Módulo | Qué hace | Web service |
|---|---|---|
| `mrwcarrier` | Crea envíos, pide etiquetas, consulta puntos de recogida, consulta estados | `sagec.mrw.es/MRWEnvio.asmx` + `trackingservice.mrw.es/TrackingService.svc` |
| `mrwtracking` | Solo seguimiento nacional | `seguimiento.mrw.es/swc/wssgmntnvs.asmx` |
Toda la API es **SOAP 1.1 sobre `.asmx`** (SAGEC, *Servicio Automatizado de Generación de
Envíos*). Los módulos la llaman con la extensión `SoapClient` de PHP leyendo el WSDL, **no con
XML a pelo**.
---
## 1. Endpoints reales
### 1.1 Envíos y etiquetas — SAGEC
`mrwcarrier/mrwcarrier.php:2720-2725`, `:4407-4413`, `:6679-6687`,
`mrwcarrier/controllers/admin/AdminMrwCarrierController.php:345-353`, `:423-431`:
| Entorno | URL | WSDL |
|---|---|---|
| **Producción** (`MRWCARRIER_ENTORNO = 'mrw_pro'`) | `https://sagec.mrw.es/MRWEnvio.asmx` | `https://sagec.mrw.es/MRWEnvio.asmx?WSDL` |
| **Pruebas** (`= 'mrw_pre'`) | `https://sagec-test.mrw.es/MRWEnvio.asmx` | `https://sagec-test.mrw.es/MRWEnvio.asmx?WSDL` |
El módulo construye la URL como `$url . '?WSDL'` (`mrwcarrier.php:2726`).
**Operaciones confirmadas en el código:**
| Operación | Devuelve | Dónde |
|---|---|---|
| `TransmEnvio` | `TransmEnvioResult` → `Estado`, `Mensaje`, `NumeroSolicitud`, `NumeroEnvio` | `mrwcarrier.php:2753`, `:2766-2796` |
| `TransmEnvioInternacional` | `TransmEnvioInternacionalResult` → los mismos campos | `mrwcarrier.php:2908`, `:2921-2951` |
| `EtiquetaEnvio` | **`GetEtiquetaEnvioResult`** → `Estado`, `Mensaje`, `EtiquetaFile` | `mrwcarrier.php:4428`, `:4435-4442`, `:6742` |
| `EtiquetaEnvioInternacional` | **`GetEtiquetaEnvioInternacionalResult`** → los mismos campos | `AdminMrwCarrierController.php:481-487`, `mrwcarrier.php:6833` |
| `GetPointsByCP` | `GetPointsByCPResult` (puntos de recogida por código postal) | `mrwcarrier/ajax_locations.php:339-345` |
> **El nombre de la operación y el del resultado NO coinciden.** Se llama a `EtiquetaEnvio` y el
> nodo de respuesta es `GetEtiquetaEnvioResult`. Es así en el WSDL de MRW; no es una errata del
> módulo.
Para `GetPointsByCP` el WSDL exige los nodos anidados con su espacio de nombres explícito
(`ajax_locations.php:329-339`), y por eso el módulo usa `SoapVar`:
```
GetPointsByCP → request → Point → CodigoPostal (todos en xmlns http://www.mrw.es/)
```
El WSDL que usa esa llamada está escrito en minúsculas: `https://sagec.mrw.es/mrwenvio.asmx?WSDL`
(`ajax_locations.php:305`). Es el mismo servicio.
### 1.2 Seguimiento — TrackingService (usado por `mrwcarrier`)
`mrwcarrier/mrwcarrier.php:7352-7357`:
| Entorno | Servicio | WSDL |
|---|---|---|
| Producción | `https://trackingservice.mrw.es/TrackingService.svc/TrackingServices` | `https://trackingservice.mrw.es/TrackingService.svc?wsdl` |
| Pruebas | `https://trackingservice-test.mrw.es/TrackingService.svc/TrackingServices` | `https://trackingservice-test.mrw.es/TrackingService.svc?wsdl` |
Operación: **`GetEnvios`** (`mrwcarrier.php:7392`).
### 1.3 Seguimiento — wssgmntnvs (usado por `mrwtracking`)
`mrwtracking/mrwtracking.php:121` (valor por defecto de `MRWTRACKING_WS_TRACKING`):
```
https://seguimiento.mrw.es/swc/wssgmntnvs.asmx (+ '?WSDL', mrwtracking.php:761)
```
Operación: **`SeguimientoNumeroEnvioMRWNacional`** (`mrwtracking.php:787`).
**NO CONFIRMADO:** no hay entorno de pruebas para este servicio en el código.
### 1.4 Páginas públicas de seguimiento
- `https://www.mrw.es/seguimiento_envios/MRW_historico_nacional.asp?enviament=@`
— es la `url` del transportista, con `@` como marcador
(`mrwcarrier/mrwcarrier.php:43`, `mrwtracking/mrwtracking.php:1025`)
- `https://www.mrw.es/seguimiento/envio-historico.asp?envio=<numero>`
(`mrwcarrier/mrwcarrier.php:5126`)
- Buscador de franquicias:
`https://www.mrw.es/oficina_transporte_urgente/MRW_buscador_franquicia.asp`
(`mrwcarrier.php:2260`)
---
## 2. Autenticación
**Cabecera SOAP `AuthInfo` en el espacio de nombres `http://www.mrw.es/`**, con cinco campos.
`mrwcarrier/mrwcarrier.php:3335-3341` y `:2749`:
```php
$cabeceras = array(
'CodigoFranquicia' => $rowSubscriber['agency'], // Obligatorio
'CodigoAbonado' => $rowSubscriber['subscriber'], // Obligatorio
'CodigoDepartamento' => $rowSubscriber['department'], // Opcional
'UserName' => $rowSubscriber['user'], // Obligatorio
'Password' => $rowSubscriber['password'], // Obligatorio
);
$header = new SoapHeader('http://www.mrw.es/', 'AuthInfo', $cabeceras);
$clientMRW->__setSoapHeaders($header);
```
Los comentarios de «Obligatorio» / «Opcional» son del propio módulo.
**Los dos servicios de seguimiento NO usan `AuthInfo`:** meten las credenciales **en los
parámetros** de la operación.
- `TrackingService.GetEnvios` (`mrwcarrier.php:7380-7388`): `login`, `pass`, `codigoIdioma`
(`'3082'` = español de España), `tipoFiltro` (`'0'`), `valorFiltroDesde`, `valorFiltroHasta`,
`tipoInformacion` (`'0'`).
- `SeguimientoNumeroEnvioMRWNacional` (`mrwtracking.php:845-852`): `Franquicia`, `Cliente`,
`Password`, `NumeroMRW`, `Referencia`, `Agrupado` (`0`).
**No hay token ni OAuth.** La contraseña viaja en cada petición.
---
## 3. Claves EXACTAS de configuración
### 3.1 `mrwcarrier` — prefijo `MRWCARRIER_`
El prefijo se construye en `mrwcarrier/mrwcarrier.php:44`:
`$this->rename = strtoupper($this->name) . '_';`
Lista literal de claves (todas con el prefijo `MRWCARRIER_`):
```
MRWCARRIER_ENTORNO 'mrw_pre' | 'mrw_pro'
MRWCARRIER_FRANQUICIA código de franquicia
MRWCARRIER_ABONADO código de abonado
MRWCARRIER_USUARIO usuario SAGEC
MRWCARRIER_PASSWORD contraseña SAGEC
MRWCARRIER_URLWEBSERVICE URL del WS
MRWCARRIER_CARRIER_ID_MRW id(s) del transportista MRW creado al instalar
MRWCARRIER_SERVICE_MRW servicio nacional por defecto
MRWCARRIER_SERVICE_MRW_INT servicio internacional por defecto
MRWCARRIER_BEFORE_SENDING_MRW estado de pedido que dispara el envío
MRWCARRIER_AFTER_SENDING_MRW estado al que pasa el pedido tras enviarlo
MRWCARRIER_LAST_EXECUTE_MRW marca de tiempo de la última ejecución
MRWCARRIER_ACTIVE_DEBUG_MRW '0' | '1'
MRWCARRIER_ACCEPTANCE_TERMS_MRW
MRWCARRIER_ALLOW_POINT_DELIVERY entrega en Point
MRWCARRIER_DEFAULT_SLOT_MRW franja horaria por defecto
MRWCARRIER_DNI_REQUIRED_MRW
MRWCARRIER_MAIL_WHENSENT_MRW
MRWCARRIER_MESSAGE_TYPE_MRW
MRWCARRIER_MULTITRANS_MRW
MRWCARRIER_TICKET_MRW
MRWCARRIER_GESTION_BULTOS
MRWCARRIER_AGRUPAR_BULTOS
MRWCARRIER_DESGLOSE_BULTOS
MRWCARRIER_CRON_TOKEN token del cron de estados
```
Referencias: `mrwcarrier.php:48-65`, `:103-111`, y los `Configuration::updateValue` de
`displayFrmConfig()`.
### 3.2 IMPORTANTE: las credenciales VIVAS están en `ps_mrwcarrier_subs`
El módulo es **multiabonado**. Las claves `MRWCARRIER_FRANQUICIA`, `_ABONADO`, `_USUARIO` y
`_PASSWORD` son el juego heredado; lo que realmente se manda en la cabecera `AuthInfo` sale de la
tabla `ps_mrwcarrier_subs` (`mrwcarrier.php:447-462`):
```sql
CREATE TABLE ps_mrwcarrier_subs (
id_subscriber int(11) AUTO_INCREMENT,
id_shop int(11) DEFAULT 1,
id_name varchar(25),
environment varchar(10), -- 'mrw_pre' | 'mrw_pro'
agency varchar(10), -- CodigoFranquicia
subscriber varchar(10), -- CodigoAbonado
department varchar(100), -- CodigoDepartamento
user varchar(100), -- UserName
password varchar(100), -- Password (EN CLARO, sin cifrar)
service varchar(5),
service_int varchar(5),
is_default varchar(1) DEFAULT 'S',
date DATE
)
```
Se lee con `getHeaderConfigMRW()` (`mrwcarrier.php:3317-3344`), que busca el abonado del pedido en
`ps_mrwcarrier_mrw` y, si no lo encuentra, cae en `getDefaultSubscriber($id_shop)`
(`:3066-3071`). **Un módulo nuevo debe leer de esa tabla, no de `ps_configuration`.**
La contraseña está **en claro**. No hace falta descifrar nada, pero tampoco se debe volcar a un
registro.
### 3.3 Tabla de envíos: `ps_mrwcarrier_mrw`
`mrwcarrier.php:377-392`:
```sql
id_mrwcarrier_mrw, id_shop, order_id, send_num_mrw (VARCHAR 100 = NÚMERO DE ENVÍO),
params_mrw, date, print, cant, subscriber, service, saturday, agency,
backReturn, mrw_warehouse, mrw_slot
```
### 3.4 `mrwtracking` — prefijo `MRWTRACKING_`
`mrwtracking/mrwtracking.php:25`, `:121-123`, `:136-141`:
```
MRWTRACKING_WS_TRACKING por defecto https://seguimiento.mrw.es/swc/wssgmntnvs.asmx
MRWTRACKING_FRANQUICIA
MRWTRACKING_ABONADO
MRWTRACKING_PASSWORD (sin usuario: este WS solo pide franquicia + cliente + contraseña)
MRWTRACKING_ACTIVE_DEBUG
MRWTRACKING_FLG_ENTREGADO 1 = al detectar «entregado», pasa el pedido al estado 5
```
Tabla propia: `ps_mrwtracking_tracking` (`mrwtracking.php:58-72`) con
`id_order`, `num_expedicion`, `estado`, `file_firma`, `json_response`, `date_updated`.
**Precedente de reutilización entre módulos:** `mrwtracking` lee una clave de `mrwcarrier`,
`Configuration::get('MRWCARRIER_AFTER_SENDING_MRW')` (`mrwtracking.php:602`). Exactamente lo que
un módulo nuevo debería hacer con las credenciales.
---
## 4. Detectar el módulo
```php
if (Module::isInstalled('mrwcarrier') && Module::isEnabled('mrwcarrier')) {
$abonado = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
'SELECT `environment`, `agency`, `subscriber`, `department`, `user`, `password`
FROM `' . _DB_PREFIX_ . 'mrwcarrier_subs`
WHERE `id_shop` = ' . (int) Context::getContext()->shop->id . '
AND `is_default` = "S"'
);
}
```
Nombres en `ps_module`: **`mrwcarrier`** y **`mrwtracking`** (`config.xml:3` de cada uno).
Para saber si un `id_carrier` es de MRW: `Configuration::get('MRWCARRIER_CARRIER_ID_MRW')`
(`mrwcarrier.php:103`; puede contener varios id).
---
## 5. Seguimiento
### 5.1 Vía `TrackingService.GetEnvios` (lo que usa `mrwcarrier`)
`mrwcarrier/mrwcarrier.php:7344-7470`.
```php
$params = array(
'login' => $usuario,
'pass' => $contrasena,
'codigoIdioma' => '3082', // español de España
'tipoFiltro' => '0',
'valorFiltroDesde' => $numeroEnvio,
'valorFiltroHasta' => $numeroEnvio, // el mismo: consulta de un solo envío
'tipoInformacion' => '0',
);
$response = $client->GetEnvios($params);
```
La respuesta **no tiene una forma estable**: el módulo prueba tres estructuras
(`mrwcarrier.php:7414-7447`):
1. `GetEnviosResult → Seguimiento → Abonado → SeguimientoAbonado → Seguimiento[] → EstadoDescripcion`
2. `GetEnviosResult → Seguimiento → Seguimiento[] → EstadoDescripcion`
3. `GetEnviosResult → Seguimiento → EstadoDescripcion`
Se queda con el **último** elemento del array (`end($seguimientos)`).
**Mapeo de estado** (`mrwcarrier.php:7449-7455`): solo hay dos resultados.
| `EstadoDescripcion` (en mayúsculas) | Devuelve |
|---|---|
| `ENTREGADO` | `'ENTREGADO'` |
| cualquier otra cosa | `'EN_TRANSITO'` |
Si en `MensajeSeguimiento` viene el texto `No se encontraron resultados`, el módulo también lo
da por `EN_TRANSITO` (`:7457-7462`).
### 5.2 Vía `SeguimientoNumeroEnvioMRWNacional` (lo que usa `mrwtracking`)
`mrwtracking/mrwtracking.php:758-830`.
```php
$params = array(
'Franquicia' => Configuration::get('MRWTRACKING_FRANQUICIA'),
'Cliente' => Configuration::get('MRWTRACKING_ABONADO'),
'Password' => Configuration::get('MRWTRACKING_PASSWORD'),
'NumeroMRW' => $numeroEnvio,
'Referencia' => 'PEDIDO-' . $id_order,
'Agrupado' => 0,
);
$r = $client->SeguimientoNumeroEnvioMRWNacional($params);
```
Respuesta: `SeguimientoNumeroEnvioMRWNacionalResult` con
- `Estado` — **0 = la petición ha fallado, 1 = OK** (`mrwtracking.php:799`, `:807`)
- `Mensaje` — texto del error
- `Envio` → `Numero`, `Estado`, `EstadoDescripcion`, `FechaEntrega`, `HoraEntrega`
**Código de estado del envío** (`mrwtracking.php:586`, `:642`):
| `Envio->Estado` | Significado |
|---|---|
| `'00'` | **ENTREGADO** — el módulo guarda el seguimiento como definitivo y, si `MRWTRACKING_FLG_ENTREGADO = 1`, pone el pedido en el estado **5** (`$this->finalStatus`, `mrwtracking.php:26`) |
| cualquier otro | en curso — se vuelve a consultar en la siguiente pasada |
**NO CONFIRMADO:** la tabla completa de códigos de `Envio->Estado` de MRW. El código solo
distingue `'00'`. No hay ninguna otra lista en ninguno de los dos módulos. Lo único que se
aprovecha además es el texto `EstadoDescripcion`, que se guarda en mayúsculas en
`ps_mrwtracking_tracking.estado`.
---
## 6. Etiquetas
Operación **`EtiquetaEnvio`** (nacional) / **`EtiquetaEnvioInternacional`**.
`mrwcarrier/mrwcarrier.php:4531-4545` y `AdminMrwCarrierController.php:376-408`:
```php
$params = array(
'request' => array(
'NumeroEnvio' => $numeros, // uno o varios, separados
'SeparadorNumerosEnvio' => ',',
'FechaInicioEnvio' => '', // o 'd/m/Y'
'FechaFinEnvio' => '',
'TipoEtiquetaEnvio' => '0',
'ReportTopMargin' => '1100', // internacional: '20'
'ReportLeftMargin' => '650', // internacional: '20'
),
);
$r = $client->EtiquetaEnvio($params);
// $r->GetEtiquetaEnvioResult->Estado: 0 = error (mirar ->Mensaje), 1 = OK
// $r->GetEtiquetaEnvioResult->EtiquetaFile = el PDF
```
**Formato: PDF.** El campo del WSDL es `base64Binary`, y **`SoapClient` ya lo descodifica**: el
módulo escribe `EtiquetaFile` directamente en el fichero, sin `base64_decode`
(`mrwcarrier.php:6865-6874`, `generateLabelsPDF()`). Si se construye el SOAP a mano con cURL,
**hay que descodificar el base64 uno mismo**.
Con varios pedidos, el módulo pide una sola llamada con los números unidos por `,`, guarda
`MRWNacLabels.pdf` y `MRWIntLabels.pdf` y, si hay de los dos tipos, los mete en un ZIP
(`AdminMrwCarrierController.php:239-296`).
Hay además una URL de etiqueta por navegador que se monta con
`&NumSol=<NumeroSolicitud>&Us=<UserName>&NumEnv=<NumeroEnvio>` (`mrwcarrier.php:2794-2795`).
---
## 7. Número de seguimiento
De `TransmEnvio` sale `TransmEnvioResult->NumeroEnvio` (`mrwcarrier.php:2796`), y el módulo lo
escribe en **tres sitios** (`mrwcarrier.php:3092`, `:3109`, `:3167`):
```
ps_mrwcarrier_mrw.send_num_mrw varchar(100)
ps_orders.shipping_number
ps_order_carrier.tracking_number (solo si pasa Validate::isTrackingNumber(), :3166)
```
**NO CONFIRMADO:** el formato (longitud, prefijo) del `NumeroEnvio`. Los módulos no lo validan ni
lo describen. **No se puede adivinar que un número es de MRW solo mirándolo.**
---
## 8. Trampas vistas en este código
1. **La extensión SOAP de PHP es obligatoria.** Los dos módulos lo comprueban con
`class_exists('SoapClient')` y avisan en pantalla si falta (`mrwcarrier.php:1102`,
`mrwtracking.php:267`). Sin ella no funciona nada.
2. **Nombre de operación ≠ nombre del resultado.** `EtiquetaEnvio` → `GetEtiquetaEnvioResult`.
3. **`Estado` invertido respecto a lo intuitivo.** En MRW **0 = error** y **1 = OK**
(`mrwcarrier.php:2770`, `:2787`; `mrwtracking.php:799`, `:807`). El HTTP siempre es 200.
4. **La respuesta de `GetEnvios` cambia de forma.** Tres estructuras posibles; hay que probarlas
todas o el estado se pierde en silencio (`mrwcarrier.php:7414-7447`).
5. **`GetPointsByCP` exige `SoapVar` con espacio de nombres explícito.** Pasar un array llano no
funciona: el WSDL espera `GetPointsByCP → request → Point → CodigoPostal` con
`xmlns="http://www.mrw.es/"` en cada nodo (`ajax_locations.php:329-339`).
6. **TLS sin verificar** en el cliente de seguimiento: `verify_peer` y `verify_peer_name` a
`false` en el `stream_context` (`mrwcarrier.php:7367-7370`).
7. **Códigos postales recortados por país** antes de mandarlos: `PT` a 4 dígitos, `ES` a 5
(`mrwcarrier.php:3357-3360`). Si se manda el código postal portugués completo (`1234-567`),
MRW lo rechaza.
8. **`checkMrwShipmentStatus` devuelve `false` si falta cualquiera de los tres datos**
(`mrwcarrier.php:7346-7348`), sin decir cuál.
9. **`$rowSubscriber` puede no existir.** `getHeaderConfigMRW()` (`:3317`) usa `$rowSubscriber`
sin comprobar que `getDefaultSubscriber()` haya devuelto algo: si no hay ningún abonado por
defecto, en PHP 8 salta un aviso y viajan cabeceras vacías → «usuario o contraseña
incorrectos» que no tiene nada que ver con la contraseña.
10. **La contraseña está en claro en `ps_mrwcarrier_subs.password`.** No volcarla a ningún
registro ni a ninguna respuesta AJAX.
---
## 9. Fragmento PHP listo para copiar (estructura legacy, sin Composer)
Consulta el estado de un envío MRW reutilizando el abonado que el módulo `mrwcarrier` ya tiene
configurado. Usa `SoapClient`, que es una **extensión de PHP**, no una dependencia de Composer.
```php
<?php
/**
* Seguimiento MRW reutilizando la configuracion del modulo oficial 'mrwcarrier'.
* Sin Composer, sin namespace. SoapClient es una extension de PHP, no una dependencia.
*/
if (!defined('_PS_VERSION_')) {
exit;
}
class MiMrwTracking
{
/**
* Abonado por defecto de la tienda, desde la tabla del modulo oficial.
* Devuelve array con environment, agency, subscriber, department, user, password.
*/
public static function getAbonado($idShop = null)
{
if ($idShop === null) {
$idShop = (int) Context::getContext()->shop->id;
}
$fila = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
'SELECT `environment`, `agency`, `subscriber`, `department`, `user`, `password`
FROM `' . _DB_PREFIX_ . 'mrwcarrier_subs`
WHERE `id_shop` = ' . (int) $idShop . '
AND `is_default` = "S"'
);
if (!is_array($fila) || empty($fila['user'])) {
// Sin abonado por defecto en esta tienda: probamos el primero que haya.
$fila = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
'SELECT `environment`, `agency`, `subscriber`, `department`, `user`, `password`
FROM `' . _DB_PREFIX_ . 'mrwcarrier_subs`
ORDER BY `id_subscriber` ASC'
);
}
return is_array($fila) ? $fila : array();
}
/** Numero de envio MRW de un pedido (tabla del modulo oficial). */
public static function getNumeroEnvio($idOrder)
{
$numero = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'SELECT `send_num_mrw` FROM `' . _DB_PREFIX_ . 'mrwcarrier_mrw`
WHERE `order_id` = ' . (int) $idOrder
);
return $numero ? (string) $numero : '';
}
/**
* Estado del envio via TrackingService.GetEnvios.
*
* @return string|false 'ENTREGADO', 'EN_TRANSITO' o false si no se pudo consultar
*/
public static function getEstado($numeroEnvio)
{
if (!class_exists('SoapClient')) {
return false; // la extension SOAP de PHP no esta activa
}
$numeroEnvio = trim((string) $numeroEnvio);
if ($numeroEnvio === '') {
return false;
}
$abonado = self::getAbonado();
if (empty($abonado['user']) || empty($abonado['password'])) {
return false;
}
$wsdl = (isset($abonado['environment']) && $abonado['environment'] === 'mrw_pro')
? 'https://trackingservice.mrw.es/TrackingService.svc?wsdl'
: 'https://trackingservice-test.mrw.es/TrackingService.svc?wsdl';
try {
$client = new SoapClient($wsdl, array(
'trace' => true,
'exceptions' => true,
'soap_version' => SOAP_1_1,
'connection_timeout' => 10,
));
} catch (SoapFault $e) {
return false;
}
try {
$respuesta = $client->GetEnvios(array(
'login' => $abonado['user'],
'pass' => $abonado['password'],
'codigoIdioma' => '3082',
'tipoFiltro' => '0',
'valorFiltroDesde' => $numeroEnvio,
'valorFiltroHasta' => $numeroEnvio,
'tipoInformacion' => '0',
));
} catch (SoapFault $e) {
return false;
}
$descripcion = self::extraerDescripcion($respuesta);
if ($descripcion === null) {
return false;
}
return (Tools::strtoupper($descripcion) === 'ENTREGADO') ? 'ENTREGADO' : 'EN_TRANSITO';
}
/**
* La respuesta de GetEnvios llega con TRES estructuras distintas segun el caso.
* Hay que probarlas todas o el estado se pierde sin un solo error.
*/
private static function extraerDescripcion($respuesta)
{
$result = null;
if (is_object($respuesta) && isset($respuesta->GetEnviosResult)) {
$result = $respuesta->GetEnviosResult;
} elseif (is_array($respuesta) && isset($respuesta['GetEnviosResult'])) {
$result = (object) $respuesta['GetEnviosResult'];
}
if (!$result || !isset($result->Seguimiento)) {
return null;
}
$wrapper = is_array($result->Seguimiento) ? $result->Seguimiento[0] : $result->Seguimiento;
// 1) Abonado -> SeguimientoAbonado -> Seguimiento[]
if (isset($wrapper->Abonado)) {
$abonado = is_array($wrapper->Abonado) ? $wrapper->Abonado[0] : $wrapper->Abonado;
if (isset($abonado->SeguimientoAbonado)) {
$sa = is_array($abonado->SeguimientoAbonado)
? $abonado->SeguimientoAbonado[0]
: $abonado->SeguimientoAbonado;
if (isset($sa->Seguimiento)) {
$lista = is_array($sa->Seguimiento) ? $sa->Seguimiento : array($sa->Seguimiento);
$ultimo = end($lista);
return isset($ultimo->EstadoDescripcion) ? trim((string) $ultimo->EstadoDescripcion) : null;
}
}
}
// 2) Seguimiento -> Seguimiento[]
if (isset($wrapper->Seguimiento)) {
$lista = is_array($wrapper->Seguimiento) ? $wrapper->Seguimiento : array($wrapper->Seguimiento);
$ultimo = end($lista);
return isset($ultimo->EstadoDescripcion) ? trim((string) $ultimo->EstadoDescripcion) : null;
}
// 3) EstadoDescripcion directo
if (isset($wrapper->EstadoDescripcion)) {
return trim((string) $wrapper->EstadoDescripcion);
}
return null;
}
}
```
Uso:
```php
$numero = MiMrwTracking::getNumeroEnvio(1234);
$estado = $numero ? MiMrwTracking::getEstado($numero) : false; // 'ENTREGADO' | 'EN_TRANSITO' | false
```
---
## 10. Qué NO se ha podido confirmar leyendo el código
- **Tabla completa de códigos de estado de MRW.** El código solo distingue `Envio->Estado == '00'`
(entregado) y el texto `EstadoDescripcion == 'ENTREGADO'`. No hay ninguna lista de códigos en
ninguno de los dos módulos.
- **Formato del `NumeroEnvio`**: no se valida ni se describe.
- **`SOAPAction` y espacio de nombres para llamar por cURL** a `wssgmntnvs.asmx` y a
`TrackingService.svc`: los módulos usan `SoapClient` con el WSDL, así que esos datos no aparecen
en el código. El único espacio de nombres confirmado es `http://www.mrw.es/`, y solo para la
cabecera `AuthInfo` de `MRWEnvio.asmx`.
- **Estructura completa del `TransmEnvio`**: `getParamsMRW()` monta decenas de campos a lo largo
de cientos de líneas de `mrwcarrier.php`. Para replicarlo hay que leer esa función.
- **Entorno de pruebas de `seguimiento.mrw.es`**: no aparece.
- **Códigos de error de SAGEC**: solo llega el texto de `Mensaje`; no hay tabla.